2014-04-02 25 views
1

我有以下型号设置轨道4:composed_of映射到JSON店属性

# task.rb 
class Task << AR 
    # everything all task objects have in common 
end 

# login_request.rb 
class Tasks::LoginRequest < Task 
    store :data, accessors: [:email, :first_name, :last_name, :expires_at] 

    composed_of :valid_until, class_name: 'DateTime', mapping: %w(expires_at to_s), constructor: Proc.new { |date| (date && date.to_datetime) || DateTime.now }, converter: Proc.new { |value| value.to_s.to_datetime } 
end 

我用我的形式datetime_select帮手:

# _form.html.haml 
= f.datetime_select :valid_until 

这工作得很好,但当我使用提交的表单数据在我的控制器中调用update时,我收到以下错误消息:

1 error(s) on assignment of multiparameter attributes [error on assignment [2014, 4, 2, 9, 48] to valid_until (can't write unknown attribute 'expires_at')] 

所以,我猜测更新的方法试图直接操作attributes散列,但显然它找不到属性expires_at,因为它是JSON列data的简单访问器方法。

我知道我可以简单地将此字段添加到数据库,它可能会工作 - 虽然没有必要然后有一个composed_of声明。但我宁愿不走这条路,因为不是每个任务都有一个expires_at列。

我该如何克服这个错误?或者我错过了什么?

回答

2

当前compose_of不支持这种情况,因为它直接写入假定在数据库中的属性。我写了一个扭捏compose_of版本在支持(基于Rails的4.0.2版本)

在初始化文件夹中把这样的:

#/initialize/support_store_in_composed_of.rb 
module ActiveRecord 
    module Aggregations 
    extend ActiveSupport::Concern 

    def clear_aggregation_cache #:nodoc: 
     @aggregation_cache.clear if persisted? 
    end 

    module ClassMethods 

     def composed_of_with_store_support(part_id, options = {}) 
     options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter, :store) 

     name  = part_id.id2name 
     class_name = options[:class_name] || name.camelize 
     mapping  = options[:mapping]  || [ name, name ] 
     mapping  = [ mapping ] unless mapping.first.is_a?(Array) 
     allow_nil = options[:allow_nil] || false 
     constructor = options[:constructor] || :new 
     converter = options[:converter] 

     reader_method(name, class_name, mapping, allow_nil, constructor, options[:store]) 
     writer_method(name, class_name, mapping, allow_nil, converter, options[:store]) 

     create_reflection(:composed_of, part_id, nil, options, self) 
     end 

     private 
     def reader_method(name, class_name, mapping, allow_nil, constructor, store=nil) 
      define_method(name) do 
      if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? }) 
       if store.present? 
       attrs = mapping.collect {|pair| send(pair.first)} 
       else 
       attrs = mapping.collect {|pair| read_attribute(pair.first)} 
       end 

       object = constructor.respond_to?(:call) ? 
       constructor.call(*attrs) : 
       class_name.constantize.send(constructor, *attrs) 

       @aggregation_cache[name] = object 
      end 
      @aggregation_cache[name] 
      end 
     end 

     def writer_method(name, class_name, mapping, allow_nil, converter, store=nil) 

      define_method("#{name}=") do |part| 
      klass = class_name.constantize 
      unless part.is_a?(klass) || converter.nil? || part.nil? 
       part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part) 
      end 
      if part.nil? && allow_nil 
       mapping.each { |pair| self[pair.first] = nil } 
       @aggregation_cache[name] = nil 
      else 
       if store.present?  
       mapping.each { |pair| send("#{pair.first}=", part.send(pair.last)) } 
       else 
       mapping.each { |pair| self[pair.first] = part.send(pair.last) } 
       end 
       @aggregation_cache[name] = part.freeze 

      end 
      end 
     end 
    end 
    end 
end 

,并用它像这样可以解决你的问题。

class Task < ActiveRecord::Base 

    store :data, accessors: [:email, :first_name, :last_name, :expires_at] 


    composed_of_with_store_support :valid_until, class_name: 'DateTime', mapping: %w(expires_at to_s), 
    constructor: Proc.new { |date| (date && date.to_datetime) || DateTime.now }, 
    converter: Proc.new { |value| value.to_s.to_datetime }, 
    store: true 

end 
+0

感谢您的回复。我有'valid_until'直接作为商店访问者,但是我不能使用'datetime_select'助手对它的魔术仍然工作,因为我得到提到的多参数错误。而且我知道我可以创建一个手动包装器,但我想避免自己编程,因为所有功能基本上都存在 - 但它不能按预期工作。 – Vapire

+0

我明白了。它现在不能这样工作,所以我写了这个版本。 – drKreso

+0

非常感谢! – Vapire