【问题标题】:ActiveRecord instance settingsActiveRecord 实例设置
【发布时间】:2011-12-26 11:46:39
【问题描述】:

我很好奇是否有宝石或方法可以进行每个模型的设置。例如,用户偏好。

我希望每个模型都有默认值(比如一个类属性),但可以定义(在另一个表上!我不希望我的模型上有一个可序列化的字段)。

例如:

user = User.find(1)
user.settings.newsletter # => true

会有一个 UserSetting 模型,其模式为 key => string、value => string、type => string(布尔、日期、字符串等)

更新:

这是我最后的解决方案。支持设置的值类型(布尔值、时间等)

    def setting(key, whiny=true)
      s = user_settings.where(:key => key).first

      if s
        case s.value_type
        when 'Boolean'
          s.value.to_i == 1
        when 'Time'
          Time.parse(s.value)
        else
          s.value
        end
      else
        if whiny
          raise NameError, "Setting key #{key} does not exist for #{name}."
        else
          nil
        end
      end
    end

【问题讨论】:

    标签: ruby-on-rails activerecord


    【解决方案1】:

    has_settings gem 似乎可以做到这一点,但可惜似乎没有得到维护。 This fork 至少支持 Rails 3。

    如果您找不到适合您需要的 gem,您可以很简单地使用简单的 has_many 关系和一点关联扩展魔法自行实现,例如(未经测试):

    class User < ActiveRecord::Base
      has_many :settings do
        # association extension
        def method_missing name, *args
          key = name.to_s
    
          # setter
          if key.ends_with? '='
            key.chop!
            new_val = args.first
    
            setting = find_or_create_by_key key
    
            setting.update_attribute(:value, new_val) &&
              @settings[key] = new_val                && # KISS cache
              true  # to mirror the semantics of update_attribute
    
          # getter
          elsif @settings.has_key? name # is it cached?
            @settings[name]
    
          elsif setting = where(:key => name).select(:value).first
            @settings[name] = setting.value # KISS cache again
    
          else
            super
          end
    
        end
      end
    
    end
    
    class Setting < ActiveRecord::Base
      belongs_to :user
    
      serialize :value # so we don't have to bother with an extra :type attribute
    end
    
    class CreateSettings < ActiveRecord::Migration
      def up
        create_table :settings do |t|
          t.references :user
          t.string :key
          t.string :value
        end
      end
    
      def down
        remove_table :settings
      end
    end
    

    显然这可以完成工作,但显然不是很健壮,因此您可能会遇到问题。

    【讨论】:

      【解决方案2】:

      我将使用 User has_many UserSettings 来实现这一点,而 UserSetting 是 user_id、key 和 value。 也许有一种方便的方法来访问它们,如下所示:

      class User < ActiveRecord::Base
        has_many :user_settings
        def setting(key)
          user_settings.where(:key => key).first.try(&:value)
        end
      end
      
      class UserSetting < ActiveRecord::Base
        belongs_to :user
      end
      

      那么,你可以走了

      user = User.find(1)
      user.setting('newsletter') # => true/false (or nil if that setting doesn't exist)
      

      【讨论】:

      • 看起来不错,只需要.try(:value)
      • @RobertRoss 哎呀,是的。解决了这个问题。谢谢。
      • 如果您对最终结果感兴趣,请更新原始帖子。
      猜你喜欢
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 2011-02-25
      • 2014-04-20
      • 1970-01-01
      相关资源
      最近更新 更多