我为我的ActiveModel创建了一个名为List
当我从Rails控制台运行Product.all
时,我得到:
NameError:未定义的局部变量或方法`parameters' 产品:分类
当参数更改为@parameters时,我收到此错误:
NoMethodError:未定义的方法`include?'为零:NilClass
哪个更好,使用常量PARAMETERS或@@参数?优点和缺点?
module List
extend ActiveSupport::Concern
require 'csv'
parameters = [
:visible,
:desc,
:value,
]
attr_accessor(*parameters)
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
module ClassMethods
def all
list = []
filename = File.join(Rails.root,"app/models/data/#{self.name.downcase}.csv")
CSV.foreach(filename, headers: true) do |row|
list << self.new(row.select{|key,_| parameters.include? key.to_sym })
end
return list
end
def visible
list = []
filename = File.join(Rails.root,"app/models/data/#{self.name.downcase}.csv")
CSV.foreach(filename, headers: true) do |row|
list << self.new(row.select{|key,_| parameters.include? key.to_sym }) if row['visible']=='1'
end
return list
end
end
end
答案 0 :(得分:2)
要获得快速解决方案,要使Product.all
和Product.visible
能够对现有代码进行最少量的修改,您可以在parameters
中定义module ClassMethods
方法。例如:
def parameters
@parameters ||= [:visible, :desc, :value]
end
如果您计划使用关注点之外的参数,或者子类可能想要定义自己的参数,则此方法解决方案也可以作为长期解决方案。
但是,如果参数仅用于此问题,并且此数据永远不会改变,至少不会通过任何应用程序逻辑,那么常量将是最佳解决方案,因为它向读者传达了正确的含义。我也会冻结它以防止修改:
PARAMETERS = [:visible, :desc, :value].freeze
Rich提到的另一个选择是定义一个类变量。请注意,无论您是在List
模块内部还是在ClassMethods
模块内部定义,该常量都将起作用。但是,如果您希望ClassMethods
能够将其称为Product
,则类变量仅适用于parameters
模块。
另请注意,self
中的任何方法都隐含ClassMethods
,因此您无需指定它。如果您定义了parameters
方法,则会将其视为Product
类方法,如果您在parameters
方法中使用all
,则会引用类方法,不是Rich建议的实例方法。
Ruby中通常不鼓励使用类变量,因为它们的副作用经常被误解。 Ruby样式指南建议避免使用它们:https://github.com/bbatsov/ruby-style-guide#no-class-vars
至于速度,我比较了方法和常数解,看起来常数更快:
require "benchmark/ips"
PARAMETERS = [:visible, :desc, :value].freeze
def parameters
@parameters ||= [:visible, :desc, :value]
end
def uses_constant
puts PARAMETERS
end
def uses_method
puts parameters
end
Benchmark.ips do |x|
x.report("constant") { uses_constant }
x.report("method") { uses_method }
x.compare!
end
结果:
Comparison:
constant: 45256.8 i/s
method: 44799.6 i/s - 1.01x slower
答案 1 :(得分:0)
使用def
创建的方法在定义方法时看不到局部变量,因此您的第一次尝试不起作用。
使用实例变量,您正在设置变量的对象(您的模块)和尝试读取它的对象(包含您的模块的类)是不同的。实例变量根本不参与继承,因此不起作用。
列表模块中的常量是什么,即
PARAMETERS = [:visible, :desc, :value]
因为你的类方法模块在List模块中,所以它里面的代码会找到List上设置的常量。常量查找首先查看词法范围(请参阅此搜索路径的Module.nesting),然后查看继承。
答案 2 :(得分:0)
将其设置为类var:
module List
extend ActiveSupport::Concern
@@parameters = [:visible, :desc, :value]
cattr_Accessor :parameters #-> List.parameters && List.new.parameters
您现在遇到的问题是您从类方法调用实例方法:
module ClassMethods
self.new(row.select{|key,_| parameters.include? key.to_sym })
使用类变量代码,您可以运行:
self.new(row.select{|key,_| self.parameters.include? key.to_sym })