类 OpenStruct
一个 OpenStruct
是一个数据结构,类似于 Hash
,它允许定义任意属性及其对应的值。这是通过使用 Ruby 的元编程在类本身定义方法来实现的。
示例¶ ↑
require "ostruct" person = OpenStruct.new person.name = "John Smith" person.age = 70 person.name # => "John Smith" person.age # => 70 person.address # => nil
一个 OpenStruct
在内部使用 Hash
来存储属性和值,甚至可以用一个 Hash
初始化。
australia = OpenStruct.new(:country => "Australia", :capital => "Canberra") # => #<OpenStruct country="Australia", capital="Canberra">
Hash
键包含空格或通常不能用于方法调用的字符(例如 ()[]*
)不会立即在 OpenStruct
对象上作为方法用于检索或赋值,但仍然可以通过 Object#send
方法或使用 [] 访问。
measurements = OpenStruct.new("length (in inches)" => 24) measurements[:"length (in inches)"] # => 24 measurements.send("length (in inches)") # => 24 message = OpenStruct.new(:queued? => true) message.queued? # => true message.send("queued?=", false) message.queued? # => false
删除属性的存在需要执行 delete_field
方法,因为将属性值设置为 nil
不会删除属性。
first_pet = OpenStruct.new(:name => "Rowdy", :owner => "John Smith") second_pet = OpenStruct.new(:name => "Rowdy") first_pet.owner = nil first_pet # => #<OpenStruct name="Rowdy", owner=nil> first_pet == second_pet # => false first_pet.delete_field(:owner) first_pet # => #<OpenStruct name="Rowdy"> first_pet == second_pet # => true
Ractor
兼容性:一个包含可共享值的冻结 OpenStruct
本身是可共享的。
注意事项¶ ↑
一个 OpenStruct
利用 Ruby 的方法查找结构来查找和定义属性所需的必要方法。这是通过方法 method_missing 和 define_singleton_method 实现的。
如果对创建对象的性能有顾虑,这一点应该考虑,因为与使用 Hash
或 Struct
相比,设置这些属性的开销要大得多。从一个小的 Hash
创建一个开放结构并访问其中几个条目,可能比直接访问哈希慢 200 倍。
这是一个潜在的安全问题;从不受信任的用户数据(例如 OpenStruct
来自 JSON
的 Web 请求)构建 OpenStruct
可能容易受到“符号拒绝服务”攻击,因为键会创建方法,而方法的名称永远不会被垃圾回收。
这也可能是 Ruby 版本之间不兼容的来源。
o = OpenStruct.new o.then # => nil in Ruby < 2.6, enumerator for Ruby >= 2.6
内置方法可以通过这种方式被覆盖,这可能是错误或安全问题的来源。
o = OpenStruct.new o.methods # => [:to_h, :marshal_load, :marshal_dump, :each_pair, ... o.methods = [:foo, :bar] o.methods # => [:foo, :bar]
为了帮助解决冲突,OpenStruct
仅使用以 !
结尾的受保护/私有方法,并通过添加 !
为内置公共方法定义别名。
o = OpenStruct.new(make: 'Bentley', class: :luxury) o.class # => :luxury o.class! # => OpenStruct
建议(但未强制执行)不要使用以 !
结尾的字段;请注意,子类的方法不能被覆盖,OpenStruct 自己的以 !
结尾的方法也不能被覆盖。
由于所有这些原因,请考虑完全不使用 OpenStruct
。
常量
- HAS_PERFORMANCE_WARNINGS
- VERSION
公共类方法
参见 as_json
。
# File ext/json/lib/json/add/ostruct.rb, line 10 def self.json_create(object) new(object['t'] || object[:t]) end
创建一个新的 OpenStruct
对象。默认情况下,生成的 OpenStruct
对象将没有属性。
可选的 hash
(如果给出)将生成属性和值(可以是 Hash
、OpenStruct
或 Struct
)。例如
require "ostruct" hash = { "country" => "Australia", :capital => "Canberra" } data = OpenStruct.new(hash) data # => #<OpenStruct country="Australia", capital="Canberra">
# File lib/ostruct.rb, line 134 def initialize(hash=nil) if HAS_PERFORMANCE_WARNINGS && Warning[:performance] warn "OpenStruct use is discouraged for performance reasons", uplevel: 1, category: :performance end if hash update_to_values!(hash) else @table = {} end end
公共实例方法
比较此对象和 other
以确定是否相等。当 other
是 OpenStruct
并且两个对象的 Hash
表相等时,OpenStruct
等于 other
。
require "ostruct" first_pet = OpenStruct.new("name" => "Rowdy") second_pet = OpenStruct.new(:name => "Rowdy") third_pet = OpenStruct.new("name" => "Rowdy", :age => nil) first_pet == second_pet # => true first_pet == third_pet # => false
# File lib/ostruct.rb, line 423 def ==(other) return false unless other.kind_of?(OpenStruct) @table == other.table! end
返回属性的值,如果不存在此属性,则返回 nil
。
require "ostruct" person = OpenStruct.new("name" => "John Smith", "age" => 70) person[:age] # => 70, same as person.age
# File lib/ostruct.rb, line 303 def [](name) @table[name.to_sym] end
设置属性的值。
require "ostruct" person = OpenStruct.new("name" => "John Smith", "age" => 70) person[:age] = 42 # equivalent to person.age = 42 person.age # => 42
# File lib/ostruct.rb, line 318 def []=(name, value) name = name.to_sym new_ostruct_member!(name) @table[name] = value end
方法 OpenStruct#as_json
和 OpenStruct.json_create
可用于序列化和反序列化 OpenStruct 对象;参见 Marshal
。
方法 OpenStruct#as_json
序列化 self
,返回一个表示 self
的包含两个元素的哈希。
require 'json/add/ostruct' x = OpenStruct.new('name' => 'Rowdy', :age => nil).as_json # => {"json_class"=>"OpenStruct", "t"=>{:name=>'Rowdy', :age=>nil}}
方法 JSON.create
反序列化此类哈希,返回一个 OpenStruct 对象。
OpenStruct.json_create(x) # => #<OpenStruct name='Rowdy', age=nil>
# File ext/json/lib/json/add/ostruct.rb, line 30 def as_json(*) klass = self.class.name klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" { JSON.create_id => klass, 't' => table, } end
从对象中删除指定名称的字段,并返回该字段包含的值(如果已定义)。您可以选择提供一个代码块。如果字段未定义,则返回代码块的结果,或者如果未提供代码块,则引发 NameError
。
require "ostruct" person = OpenStruct.new(name: "John", age: 70, pension: 300) person.delete_field!("age") # => 70 person # => #<OpenStruct name="John", pension=300>
将值设置为 nil
不会删除属性。
person.pension = nil person # => #<OpenStruct name="John", pension=nil> person.delete_field('number') # => NameError person.delete_field('number') { 8675_309 } # => 8675309
# File lib/ostruct.rb, line 371 def delete_field(name, &block) sym = name.to_sym begin singleton_class.remove_method(sym, "#{sym}=") rescue NameError end @table.delete(sym) do return yield if block raise! NameError.new("no field `#{sym}' in #{self}", sym) end end
查找并返回嵌套对象中由 name
和 identifiers
指定的对象。嵌套对象可以是各种类的实例。参见 Dig 方法。
示例
require "ostruct" address = OpenStruct.new("city" => "Anytown NC", "zip" => 12345) person = OpenStruct.new("name" => "John Smith", "address" => address) person.dig(:address, "zip") # => 12345 person.dig(:business_address, "zip") # => nil
# File lib/ostruct.rb, line 340 def dig(name, *names) begin name = name.to_sym rescue NoMethodError raise! TypeError, "#{name} is not a symbol nor a string" end @table.dig(name, *names) end
生成所有属性(作为符号)及其对应值,或者如果未提供代码块,则返回一个枚举器。
require "ostruct" data = OpenStruct.new("country" => "Australia", :capital => "Canberra") data.each_pair.to_a # => [[:country, "Australia"], [:capital, "Canberra"]]
# File lib/ostruct.rb, line 211 def each_pair return to_enum(__method__) { @table.size } unless defined?(yield) @table.each_pair{|p| yield p} self end
比较此对象和 other
的相等性。当 other
是一个 OpenStruct
并且两个对象的 Hash
表相等时,OpenStruct
与 other
相等。
# File lib/ostruct.rb, line 433 def eql?(other) return false unless other.kind_of?(OpenStruct) @table.eql?(other.table!) end
Object#freeze
# File lib/ostruct.rb, line 269 def freeze @table.freeze super end
返回一个包含键和值详细摘要的字符串。
# File lib/ostruct.rb, line 388 def inspect ids = (Thread.current[InspectKey] ||= []) if ids.include?(object_id) detail = ' ...' else ids << object_id begin detail = @table.map do |key, value| " #{key}=#{value.inspect}" end.join(',') ensure ids.pop end end ['#<', self.class!, detail, '>'].join end
# File lib/ostruct.rb, line 182 def to_h(&block) if block @table.to_h(&block) else @table.dup end end
返回一个表示 self
的 JSON
字符串。
require 'json/add/ostruct' puts OpenStruct.new('name' => 'Rowdy', :age => nil).to_json
输出
{"json_class":"OpenStruct","t":{'name':'Rowdy',"age":null}}
# File ext/json/lib/json/add/ostruct.rb, line 48 def to_json(*args) as_json.to_json(*args) end