【问题标题】:How to correctly format instance variables on a Rails model class如何在 Rails 模型类上正确格式化实例变量
【发布时间】:2017-03-08 05:59:06
【问题描述】:

我有一个表 Materials,has_one 与 MaterialCosts 相关,has_one 与 MaterialCharges 相关。它们都属于材料,在材料中删除或记录时应dependent: :destroy

我不确定如何构建 Material 类。必须使用来自每个 Material 实例的两个属性来计算 MaterialCosts 和 MaterialCharges 中的字段。它们是cost_per_sqmink_per_sqmfactor 是 Material 中的一个字段,也用于 MaterialCharges。为了将实例变量用于创建方法,我是否需要在 Material 中初始化它们?还是只需要attr_accessor

我知道代码需要重构,但当我尝试播种数据时也出现此错误:

NoMethodError: undefined method `[]' for nil:NilClass on line 26 seeds.rb

第 26 行种子.rb:

Material.create({ product_name: "Novajet Art 255gsm", guk_name: "none", roll_width_in: 44, roll_length_m: 30, factor: 7, rounded_sale_price: 71, list_price: 240.00, cost_per_sqm: 7.16, ink_per_sqm: 3, supplier_discount: 0, sell_per_sqm: 71.11 })

这是我的代码:

  class Material < ApplicationRecord

  has_and_belongs_to_many :job_entries
  has_one :material_cost,  dependent: :destroy
  has_one :material_charge, dependent:  :destroy

  attr_accessor :cost_per_sqm, :ink_per_sqm, :factor

  def initialize(options)
    @cost_per_sqm = options['cost_per_sqm'].to_f
    @ink_per_sqm = options['ink_per_sqm'].to_f
    @factor = options['@factor'].to_f
    @total_cost = @cost_per_sqm * @ink_per_sqm
  end


  after_create :set_material_cost, :set_material_charge


  A4_FACTOR = 0.0626514876
  A3_FACTOR = 0.124548139
  A2_FACTOR = 0.249096276
  A1_FACTOR = 0.499702226
  B0_FACTOR = 1.41585523
  B1_FACTOR = 0.706656651
  B2_FACTOR = 0.353328326
  B3_FACTOR = 0.176664163
  B4_FACTOR = 0.0878836952

  private



  def set_material_cost
    @material_cost = self.create_material_cost(
    cost_a4: @total_cost * A4_FACTOR,
    cost_a3: @total_cost * A3_FACTOR,
    cost_a2: @total_cost * A2_FACTOR,
    cost_a1: @total_cost * A1_FACTOR,
    cost_b0: @total_cost * B0_FACTOR,
    cost_b1: @total_cost * B1_FACTOR,
    cost_b2: @total_cost * B2_FACTOR,
    cost_b3: @total_cost * B3_FACTOR,
    cost_b4: @total_cost * B4_FACTOR
    )
  end



def set_material_charge
    @material_charge = self.create_material_charge(
    sell_a4: @total_cost * A4_FACTOR * @factor,
    sell_a3: @total_cost * A3_FACTOR * @factor,
    sell_a2: @total_cost * A2_FACTOR * @factor,
    sell_a1: @total_cost * A1_FACTOR * @factor,
    sell_b0: @total_cost * B0_FACTOR * @factor,
    sell_b1: @total_cost * B1_FACTOR * @factor,
    sell_b2: @total_cost * B2_FACTOR * @factor,
    sell_b3: @total_cost * B3_FACTOR * @factor,
    sell_b4: @total_cost * B4_FACTOR * @factor
    )

  end


    end

    class MaterialCost < ApplicationRecord
      belongs_to :material
    end

    class MaterialCharge < ApplicationRecord
      belongs_to :material
    end

代码应该是这样的吗?

【问题讨论】:

  • 不要覆盖初始化函数

标签: ruby-on-rails ruby


【解决方案1】:

正如 Tsao 所说,不要覆盖 initialize,但如果有,则必须调用 super

# use '*args' so super can use any arguments to 'new'
def initialize(*args)
  # as first line so Rails will do it's "magic" before yours
  super

  # these have got values by Rails (in super) IF you have them
  # in the call to 'new'
  @cost_per_sqm = @cost_per_sqm.to_f
  @ink_per_sqm  = @ink_per_sqm.to_f
  # (you have forgot this as attr_accessor?)
  @factor       = @factor.to_f
  @total_cost   = @cost_per_sqm * @ink_per_sqm
end

但我宁愿使用另一种方法,而不是与initialize混淆

# instead of overriding 'initialize'
def init_after_create
  return if @total_cost
  # these have got values by Rails (in new) IF you have them
  # in the call to 'new'
  @cost_per_sqm = @cost_per_sqm.to_f
  @ink_per_sqm  = @ink_per_sqm.to_f
  # (you have forgot this as attr_accessor?)
  @factor       = @factor.to_f
  @total_cost   = @cost_per_sqm * @ink_per_sqm
end

def set_material_cost
  init_after_create
  @material_cost = self.create_material_cost(
  cost_a4: @total_cost * A4_FACTOR,
  cost_a3: @total_cost * A3_FACTOR,
  ....
end

def set_material_charge
  init_after_create
  @material_charge = self.create_material_charge(
  sell_a4: @total_cost * A4_FACTOR * @factor,
  sell_a3: @total_cost * A3_FACTOR * @factor,
  ...
end

提示,如果您在方法调用或Array 中将Hash 作为最后一个参数,则不必使用{ ... }

# example method
def some_method(options)
  ...
end
some_method(xxx: 1, yyy: 2)

# example array
arr = [ 1, 2, xxx: 1, yyy: 2 ]
#=> [1, 2, {:xxx=>1, :yyy=>2}]

【讨论】:

  • 谢谢@244an。我设法通过进行这些更改、保留 attr_accessor、保留 after_create :set_material_costs:set_material_charges 语句为数据库播种,并取消了初始化。奇怪的是sell_a4 应该给我:@ 987654334@ 上的 4.6750,而是给我:4.4577 .....当然它应该是@cost_per_sqm + @ink_per_sqm,但是在进行此更改后,小数点已经过时了.. .令人不安的
  • @godhar,当我在计算器上进行控制时,它会给我4.45577 你问题中的值,所以它似乎是正确的。
  • 你是对的,这个计算是正确的。但是,对于当前的语法配置,Material 表中缺少这些值。那三个必填项没有出现在Material 的数据库字段中?当我删除 attr_accessor 时,它们会出现,但没有输入到依赖它的后续表中。有什么想法吗?
  • @godhar,好的,那么init_after_create 中的哪些字段在数据库中?对于 db 中的字段,您必须使用其中一个,例如self.total_cost = ...self['total_cost'] = (如果 total_cost 在 db 中)并将其从 attr_accessor 中删除。当使用它们时,例如some_thing = total_costsome_thing = self['total_cost']。最好使用self[...] - 除非属性在变量中。
  • 我通过不在attr_accessor 设置实例变量、删除after_init 并以我看来“不干”的方式使用字段变量来解决了这个问题。 @total_cost 变得多余,我在每个 self.create 方法中进行即时计算,因为我知道我可以从那里访问这些变量。没有实例变量声明和大量重复,也许这就是它需要的样子,多么长的学习曲线!但它有效。感谢您的帮助@244an
猜你喜欢
  • 2021-11-21
  • 2019-08-05
  • 2018-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多