JSON 哈希值的布尔属性方法 - Rails 4.2,Postgres JSONB
Boolean Attribute Methods for JSON hash values - Rails 4.2, Postgres JSONB
我有一个带有 jsonb
类型列的模型 Foo
,它存储属性的布尔对:
架构:
...
add_column :foos, :roles, :jsonb, null: :false, default:
'{
"bar" : true,
"baz" : false,
"fizz" : false
}'
...
型号:
class Foo < ActiveRecord::Base
store_accessor :roles, :bar, :baz, :fizz
end
有没有办法在不为每个属性编写方法的情况下将这些属性作为布尔值访问:
能够做到:
foo = Foo.new
foo.bar? #<= should return 'true'
不加:
class Foo < ActiveRecord::Base
...
def bar?
bar == true
end
...
end
我认为最适合您的方法是动态定义此方法,使用 define_method
:
class Foo < ActiveRecord::Base
...
%w(baz bar fizz).each do |method|
define_method("#{method}?") do
send(method) == true
end
end
...
end
ActiveRecord::Store source code 概述了增强 getter 值的最常见方式是手动定义方法(您已经知道):
All stored values are automatically available through accessors on the
Active Record object, but sometimes you want to specialize this
behavior. This can be done by overwriting the default accessors (using
the same name as the attribute) and calling super to actually
change things.
不过,您使用问号方法的情况要简单一些。只要 jsonb
键基本上是字符串,您就可以将它们重构为在末尾带有问号(并具有相应的 "questioned" getter):
add_column :foos, :roles, :jsonb, null: :false,
default: '{ "bar?" : true, ... }'
class Foo < ActiveRecord::Base
store_accessor :roles, :bar?
另一种选择,正如@Ilya 所建议的那样,是用一些元编程来定义这些方法(尽管将 store_accessor :roles, method
移动到循环内部只是为了将所有与商店相关的定义放在一个地方是很诱人的) .
最后但并非最不重要的一点是修补 ActiveRecord::Store :) 只要 store_accessor
基本上只是定义 getter 和 setter 的快捷方式方法,你可以给它传递一些额外的参数,并根据它的值定义 questioned?
方法。
这是一个实现目标的单独方法:
module ActiveRecord
module Store
module ClassMethods
def store_accessor_with_question(store_attribute, *keys)
store_accessor(store_attribute, keys)
keys.flatten.each do |key|
define_method("#{key}?") do
send(key) == true
end
end
end
将其加载到您的 config/initializers
文件夹中,您应该能够执行以下操作:
class Foo < ActiveRecord::Base
store_accessor_with_question :roles, :bar, :baz
end
foo = Foo.new
foo.bar #<= true
foo.bar? #<= true
我有一个带有 jsonb
类型列的模型 Foo
,它存储属性的布尔对:
架构:
...
add_column :foos, :roles, :jsonb, null: :false, default:
'{
"bar" : true,
"baz" : false,
"fizz" : false
}'
...
型号:
class Foo < ActiveRecord::Base
store_accessor :roles, :bar, :baz, :fizz
end
有没有办法在不为每个属性编写方法的情况下将这些属性作为布尔值访问:
能够做到:
foo = Foo.new
foo.bar? #<= should return 'true'
不加:
class Foo < ActiveRecord::Base
...
def bar?
bar == true
end
...
end
我认为最适合您的方法是动态定义此方法,使用 define_method
:
class Foo < ActiveRecord::Base
...
%w(baz bar fizz).each do |method|
define_method("#{method}?") do
send(method) == true
end
end
...
end
ActiveRecord::Store source code 概述了增强 getter 值的最常见方式是手动定义方法(您已经知道):
All stored values are automatically available through accessors on the Active Record object, but sometimes you want to specialize this behavior. This can be done by overwriting the default accessors (using the same name as the attribute) and calling super to actually change things.
不过,您使用问号方法的情况要简单一些。只要 jsonb
键基本上是字符串,您就可以将它们重构为在末尾带有问号(并具有相应的 "questioned" getter):
add_column :foos, :roles, :jsonb, null: :false,
default: '{ "bar?" : true, ... }'
class Foo < ActiveRecord::Base
store_accessor :roles, :bar?
另一种选择,正如@Ilya 所建议的那样,是用一些元编程来定义这些方法(尽管将 store_accessor :roles, method
移动到循环内部只是为了将所有与商店相关的定义放在一个地方是很诱人的) .
最后但并非最不重要的一点是修补 ActiveRecord::Store :) 只要 store_accessor
基本上只是定义 getter 和 setter 的快捷方式方法,你可以给它传递一些额外的参数,并根据它的值定义 questioned?
方法。
这是一个实现目标的单独方法:
module ActiveRecord
module Store
module ClassMethods
def store_accessor_with_question(store_attribute, *keys)
store_accessor(store_attribute, keys)
keys.flatten.each do |key|
define_method("#{key}?") do
send(key) == true
end
end
end
将其加载到您的 config/initializers
文件夹中,您应该能够执行以下操作:
class Foo < ActiveRecord::Base
store_accessor_with_question :roles, :bar, :baz
end
foo = Foo.new
foo.bar #<= true
foo.bar? #<= true