如果有人可以为我批评这种方法,我将不胜感激。
我正在构建一个应用程序,用户可以在其中定义多个属性并生成一个表单以与他的用户共享,就像Wufoo一样。
这是我的方法。
用户 has_many sources & Source embeds_many source_attributes
在这里,所有用户定义的字段都存储在 SourceAttributes 中。当我需要生成表单时,我会像这样动态生成模型。
#source.rb
def initialize_set
model_name = self.set_name.gsub(' ','').classify
# Unique Class name
klass_name = "#{model_name}#{self.user.id}"
object = self
klass = Class.new do
include Mongoid::Document
# Collection names have to be set manually or they don't seem to work
store_in collection: self.collection_name
# Pick up attributes from SourceAttribute & define fields here
object.source_attributes.each do |m|
field m.field_name.gsub(' ','').underscore, type: object.class.mapping[m.field_type]
end
def self.collection_name
self.name.tableize
end
end
# I get a warning here when the class is re-loaded. Should i worry? I'm considering Object.send :remove_const here
Object.const_set klass_name, klass
end
使用这种方法,我将为用户定义的每个表单创建一个单独的集合。这似乎有效,但我有几个问题。
- 类型安全
我在上面生成的字段似乎不是类型安全的。我能够将字符串保存在整数字段中。
类型似乎已正确设置。我通过调用 klass 上的字段并检查 options[:type] 属性再次检查了这一点。
我使用的是简单的形式,它不能自动识别类型。我必须使用 as 明确提及它:
- 可扩展性
我不确定我动态生成模型的方法是否正确。我以前从未见过有人用过这个。
转移到一个单独的问题。请检查评论。
更新 1:
所以我错了。我的模型是类型安全的,但它似乎默默地失败了。有没有办法让它抛出异常?
# age is correctly defined as an Integer
klass.fields.slice "age"
=> {"age"=>#<Mongoid::Fields::Standard:0xb7edf64 @name="age", @options={:type=>Integer, :klass=>Engineer50bc91a481ee9e19ab000006}, @label=nil, @default_val=nil, @type=Integer>}
# silently sets age to 0
klass.create!(age: "ABC")
=> #<Engineer50bc91a481ee9e19ab000006 _id: 50cff12181ee9e16f1000003, _type: nil, employee_code: nil, name: nil, age: 0, years: nil, number: nil>
# returns true when saved
object = klass.new
object.age = "ABC"
=> "ABC"
object.save
=> true
PS:这是我第一次使用 mongoid :)
更新 2:
我对整数验证的问题可能与 Ruby 而不是 mongoid 有关。
正如我之前的更新中提到的,
# silently sets age to 0
klass.create!(age: "ABC")
=> #<Engineer50bc91a481ee9e19ab000006 _id: 50cff12181ee9e16f1000003, _type: nil, employee_code: nil, name: nil, age: 0, years: nil, number: nil>
我相信“ABC”正在被打字
"ABC".to_i
=> 0
我试图通过像这样使用 validates_numericality_of 来克服这个问题
object.model_attributes.where(field_type: "Number").map(&:field_name).each do |o|
validates_numericality_of o.gsub(' ','').underscore.to_sym
end
不幸的是,这也会检查是否存在。我试过这个解决方法
validates_numericality_of o.gsub(' ','').underscore.to_sym, allow_nil: true
但这完全忽略了数值验证。也尝试了allow_blank,但没有运气。