Resources

Download Source Code

Summary

# Terminal
rails g scaffold manufacture name:uniq
rails g scaffold automobiles manufacture:belongs_to name:uniq year:integer
rails g model part name:uniq price:decimal weight:decimal
rails g model automobile_part automobile:belongs_to part:belongs_to

# db/migrate/XXX_create_automobiles.rb
class CreateAutomobiles < ActiveRecord::Migration[6.0]
  def change
    create_table :automobiles do |t|
      t.belongs_to :manufacture, null: false, foreign_key: true
      t.string :name
      t.integer :year, limit: 4

      t.timestamps
    end
    add_index :automobiles, :name, unique: true
  end
end

# db/migrate/XXX_create_parts.rb
class CreateParts < ActiveRecord::Migration[6.0]
  def change
    create_table :parts do |t|
      t.string :name
      t.decimal :price, precision: 8, scale: 2
      t.decimal :weight, precision: 8, scale: 2

      t.timestamps
    end
    add_index :parts, :name, unique: true
  end
end

# models/automobile.rb
class Automobile < ApplicationRecord
  belongs_to :manufacture

  has_many :automobile_parts, dependent: :destroy
  has_many :parts, through: :automobile_parts
end

# models/automobile_part.rb
class AutomobilePart < ApplicationRecord
  belongs_to :automobile
  belongs_to :part
end

# models/part.rb
class Part < ApplicationRecord
  has_many :autombile_parts
  has_many :automobiles, through: :autombile_parts
end

# views/automobiles/_form.html.erb
<div class="field">
  <%= form.label :manufacture_id %>
  <%= form.collection_select :manufacture_id, Manufacture.all, :id, :name, {}, { class: 'form-control' } %>
</div>

<div class="field">
  <%= form.label :name %>
  <%= form.text_field :name, class: 'form-control' %>
</div>

<div class="field">
  <%= form.label :year %>
  <%= form.number_field :year, class: 'form-control' %>
</div>

<br>

<table class='table'>
  <thead>
    <tr>
      <th></th>
      <th>Name</th>
      <th class='text-right'>Price</th>
      <th class='text-right'>Weight</th>
    </tr>
  </thead>
  <tbody>
    <% Part.all.each do |part| %>
      <tr>
        <td><%= check_box_tag "automobile[part_ids][]", part.id, form.object.parts.include?(part) %></td>
        <td><%= part.name %></td>
        <td class='text-right'><%= number_to_currency part.price.to_f %></td>
        <td class='text-right'><%= "%.2f" % part.weight.to_f %></td>
      </tr>
    <% end %>
  </tbody>
</tbody>

# controllers/automobiles_controller.rb
def index
  @automobiles = Automobile.includes(:manufacture).all
end

private

def set_automobile
  @automobile = Automobile.includes(:parts).find(params[:id])
end

def automobile_params
  params.require(:automobile).permit(:manufacture_id, :name, :year, part_ids: [])
end

# views/automobiles/show.html.erb
<p>
  <strong>Manufacture:</strong>
  <%= @automobile.manufacture.name %>
</p>

<p>
  <strong>Price:</strong>
  <%= Automobiles::CalculatePrice.call(@automobile) %>
</p>

<p>
  <strong>Weight:</strong>
  <%= Automobiles::CalculateWeight.call(@automobile) %>
</p>

# app/services/automobiles/calculate_price.rb
module Automobiles
  class CalculatePrice
    # Automobiles::CalculatePrice.new(automobile).call
    # Automobiles::CalculatePrice.call(automobile)

    def self.call(automobile)
      new(automobile).call
    end

    attr_accessor :automobile
    def initialize(automobile)
      @automobile = automobile
    end

    def call
      automobile.parts.sum(:price)
    end
  end
end

# app/services/automobiles/calculate_weight.rb
module Automobiles
  class CalculateWeight
    # Automobiles::CalculateWeight.new(automobile).call
    # Automobiles::CalculateWeight.call(automobile)

    def self.call(automobile)
      new(automobile).call
    end

    attr_accessor :automobile
    def initialize(automobile)
      @automobile = automobile
    end

    def call
      automobile.parts.sum(:weight)
    end
  end
end