Dam*_*che 14 ruby ruby-on-rails
我以前有:
serialize :params, JSON
Run Code Online (Sandbox Code Playgroud)
但是这将返回JSON并将散列键符号转换为字符串.我想使用符号引用哈希,这在使用哈希时最常见.我给它添加符号,Rails返回字符串.为了避免这种情况,我创建了自己的getter/setter.setter很简单(JSON编码),getter是:
def params
read_attribute(:params) || JSON.parse(read_attribute(:params).to_json).with_indifferent_access
end
Run Code Online (Sandbox Code Playgroud)
我无法params直接引用,因为这会导致循环,所以我正在使用read_attribute,现在我的哈希键可以用符号或字符串引用.但是,这不会更新哈希:
model.params.merge!(test: 'test')
puts model.params # => returns default params without merge
Run Code Online (Sandbox Code Playgroud)
这让我觉得哈希是由副本引用的.
我的问题是双重的.我可以扩展活动记录JSON序列化以返回无关紧要的访问哈希(或不将符号转换为字符串),并且仍然具有如上所述的哈希工作和合并吗?如果没有,我该怎么做才能改善我的吸气剂以便model.params.merge!起作用?
我希望有一些东西(有效):
def params_merge!(hash)
write_attribute(:params, read_attribute(:params).merge(hash))
end
# usage: model.params_merge!(test: 'test')
Run Code Online (Sandbox Code Playgroud)
更好的是,只需让Rails返回一个具有无差别访问权限的哈希值,或者不将我的符号转换为字符串!感谢任何帮助.
m_x*_*m_x 16
使用内置serialize方法:
class Whatever < ActiveRecord::Base
serialize :params, HashWithIndifferentAccess
end
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅序列化中的ActiveRecord :: Base文档.
根据@ fguillen的要求发表评论作为答案......警告:我通常不是一个Rubyist ......所以这可能不是惯用的或有效的.从功能上说,它让我得到了我想要的东西.似乎在Rails 3.2和4.0中工作......
在application_helper.rb:
module ApplicationHelper
class JSONWithIndifferentAccess
def self.load(str)
obj = HashWithIndifferentAccess.new(JSON.load(str))
#...or simply: obj = JSON.load(str, nil, symbolize_names:true)
obj.freeze #i also want it set all or nothing, not piecemeal; ymmv
obj
end
def self.dump(obj)
JSON.dump(obj)
end
end
end
Run Code Online (Sandbox Code Playgroud)
在我的模型中,我有一个字段叫做rule_spec,序列化为一个text字段:
serialize :rule_spec, ApplicationHelper::JSONWithIndifferentAccess
Run Code Online (Sandbox Code Playgroud)
最终,我意识到我只想要符号,而不是无关紧要的访问,但通过调整加载方法,您可以获得任一行为.
我最终使用了 bimsapi 解决方案的变体,您不仅可以使用简单的非嵌套 JSON,还可以使用任何JSON。
一旦加载...
module JsonHelper
class JsonWithIndifferentAccess
def self.load(str)
self.indifferent_access JSON.load(str)
end
def self.dump(obj)
JSON.dump(obj)
end
private
def self.indifferent_access(obj)
if obj.is_a? Array
obj.map!{|o| self.indifferent_access(o)}
elsif obj.is_a? Hash
obj.with_indifferent_access
else
obj
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
然后而不是打电话
JSON.load(http_response)
Run Code Online (Sandbox Code Playgroud)
你只要打电话
JsonHelper::JsonWithIndifferentAccess.load(http_response)
Run Code Online (Sandbox Code Playgroud)
做同样的事情,但所有嵌套的哈希都是无关紧要的访问。
应该会很好地为您服务,但在将其设为所有解析的默认方法之前请考虑一下,因为大量 JSON 有效负载将在本机 JSON 解析器之上添加重要的 ruby 操作,该解析器在 C 中进行了优化,并针对性能进行了更全面的设计。
使用HashWithIndifferentAccess很好,但它仍然像一个Hash,它只能在数据库中序列化为YAML。
我更喜欢使用 Postgres 9.3 及更高版本,是在 Postgres 中使用 json 列类型。这意味着在读取表时,ActiveRecord 将直接从 Postgres 获取一个 Hash。
create_table "gadgets" do |t|
t.json "info"
end
Run Code Online (Sandbox Code Playgroud)
ActiveRecord的serialize要求您提供一个单一类既负责读取/写入数据和序列化/反序列化它。
因此,您可以通过继承自HashWithIndifferentAccess或我的偏好Hashie::Mash. 然后将序列化实现为dump和load类方法。
class HashieMashStoredAsJson < Hashie::Mash
def self.dump(obj)
ActiveSupport::JSON.encode(obj.to_h)
end
def self.load(raw_hash)
new(raw_hash || {})
end
end
Run Code Online (Sandbox Code Playgroud)
在您的模型中,您可以指定此类进行序列化。
class Gadget < ActiveRecord::Base
serialize :info, HashieMashStoredAsJson
# This allows the field to be set as a Hash or anything compatible with it.
def info=(new_value)
self[:info] = HashieMashStoredAsJson.new new_value
end
end
Run Code Online (Sandbox Code Playgroud)
如果在 Postgres 中不使用 json 列类型,则实现略有变化
完整代码和文档在这里:使用 JSON 列类型和使用字符串列类型。