【问题标题】:Neat way to get and set keys of json column in Rails在 Rails 中获取和设置 json 列的键的简洁方法
【发布时间】:2019-07-02 10:21:21
【问题描述】:

我有一个模型/表,其中有一个 json 列,如下所示

t.json :options, default: {}

该列可以包含许多键,类似于这样

options = {"details" : {key1: "Value1", key2: "Value2"}}

我想轻松设置和获取这些值。所以我为相同的方法制作了 getter 和 setter。

def key1
  options['details']&.[]('key1')
end

def key1=(value)
  options['details'] ||= {}
  options['details']['key1'] ||=0 
  options['details']['key1'] += value 
end

但这只是在我的代码中添加了行,并且在添加更多细节时它不会扩展。你能建议一个干净整洁的方法吗?

【问题讨论】:

  • options['details']&.[]('key1') 使用安全运算符的巧妙方法!
  • @Tom 聪明的方法是使用Hash#default_proc :)
  • @AlekseiMatiushkin 是的,这是一种方式。使用Hash#[] 的长格式以便可以使用安全运算符表明对Ruby 有很好的理解。我想指出这是一个通常不会被想到的巧妙的小技巧。

标签: ruby-on-rails json ruby ruby-on-rails-4 getter-setter


【解决方案1】:

使用动态方法创建:

options['details'].default_proc = ->(_,_) {{}}
ALLOWED_KEYS = %i[key1 key2 key3]

ALLOWED_KEYS.each do |key|
  define_method key do
    options['details'][key] if options['details'].key?(key)
  end
  define_method "#{key}=" do |value|
    (options['details'][key] ||= 0) += value
  end
end

【讨论】:

  • define_method "#{key}=", value do 应该是 define_method "#{key}=" do |value| 否则一个很好的答案。或者,->(_,_) {{}} 可以替换为 proc { {} },以使答案不那么神秘,但这只是个人喜好问题。
  • 我也刚刚意识到您不能在类上下文中访问options,只能在实例上下文中访问。这意味着该行将导致错误:options['details'].default_proc = ->(_,_) {{}}
  • @JohanWentholt 感谢do |value|,当然,已更新。其余的:我从未承诺免费编写代码。我刚刚展示了有效的解决方案。将初始化(初始化器?)放在哪里取决于 OP。
【解决方案2】:

您也可以将密钥作为参数传递,对吗?

  def get_key key=:key1
   options['details']&.[](key)
  end

  def set_key= value, key=:key1
   options['details'] ||= {}
   options['details'][key] ||=0 
   options['details'][key] += value 
  end

【讨论】:

  • 这如何回答有关创建 getter 和 setter 的问题?
【解决方案3】:

简单而简短

根据可重用性,您可以选择不同的选项。简短的选择是使用循环结合#define_method 来简单地定义方法。

class SomeModel < ApplicationRecord
  option_accessors = ['key1', 'key2']

  option_accessors.map(&:to_s).each do |accessor_name|
    #                     ^ in case you provide symbols in option_accessors
    #                       this can be left out if know this is not the case

    define_method accessor_name do
      options.dig('details', accessor_name)
    end

    define_method "#{accessor_name}=" do |value|
      details = options['details'] ||= {}
      details[accessor_name] ||= 0
      details[accessor_name] += value
    end
  end
end

编写模块

或者,您可以编写一个模块,将上述内容作为助手提供。一个简单的模块可能看起来像这样:

# app/model_helpers/option_details_attribute_accessors.rb
module OptionDetailsAttributeAccessors
  def option_details_attr_reader(*accessors)
    accessors.map(&:to_s).each do |accessor|
      define_method accessor do
        options.dig('details', accessor)
      end
    end
  end

  def option_details_attr_writer(*accessors)
    accessors.map(&:to_s).each do |accessor|
      define_method "#{accessor}=" do |value|
        details = options['details'] ||= {}
        details[accessor] ||= 0
        details[accessor] += value
      end
    end
  end

  def option_details_attr_accessor(*accessors)
    option_details_attr_reader(*accessors)
    option_details_attr_writer(*accessors)
  end
end

现在您可以使用这些帮助程序简单地扩展您的类并轻松添加读取器/写入器。

class SomeModel < ApplicationRecord
  extend OptionDetailsAttributeAccessors

  option_details_attr_accessor :key1, :key2
end

如果有任何不清楚的地方,请在 cmets 中询问。

【讨论】:

    猜你喜欢
    • 2015-11-10
    • 1970-01-01
    • 2015-08-08
    • 2014-05-22
    • 2016-07-15
    • 2018-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多