Spec-Zone.ru › Ruby 3.4

класс OpenStruct

Родительский класс:
Object

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 для хранения атрибутов и значений и может быть даже инициализирован с его помощью:

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) может сделать приложение уязвимым для атаки типа «отказ в обслуживании символами», поскольку ключи создают методы, а имена методов никогда не удаляются сборщиком мусора.

Это также может стать причиной несовместимости между версиями 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

Публичные методы класса

json_create (object) Показать исходный код
# File ext/json/lib/json/add/ostruct.rb, line 13
def self.json_create(object)
  new(object['t'] || object[:t])
end

См. as_json.

new (hash=nil) Показать исходный код
# 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

Создаёт новый объект OpenStruct. По умолчанию полученный объект OpenStruct не будет иметь атрибутов.

Необязательный аргумент hash, если он задан, создаёт атрибуты и значения (это может быть Hash, OpenStruct или Struct). Например:

require "ostruct"
hash = { "country" => "Australia", :capital => "Canberra" }
data = OpenStruct.new(hash)

data   # => #<OpenStruct country="Australia", capital="Canberra">

Публичные методы экземпляра

== (other) Показать исходный код
# File lib/ostruct.rb, line 423
def ==(other)
  return false unless other.kind_of?(OpenStruct)
  @table == other.table!
end

Сравнивает этот объект с other на равенство. Объект OpenStruct равен other, если other является объектом OpenStruct и таблицы Hash этих двух объектов равны.

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
ostruct[name] → object Показать исходный код
# File lib/ostruct.rb, line 303
def [](name)
  @table[name.to_sym]
end

Возвращает значение атрибута или nil, если такого атрибута нет.

require "ostruct"
person = OpenStruct.new("name" => "John Smith", "age" => 70)
person[:age]   # => 70, same as person.age
ostruct[name] = obj → obj Показать исходный код
# File lib/ostruct.rb, line 318
def []=(name, value)
  name = name.to_sym
  new_ostruct_member!(name)
  @table[name] = value
end

Устанавливает значение атрибута.

require "ostruct"
person = OpenStruct.new("name" => "John Smith", "age" => 70)
person[:age] = 42   # equivalent to person.age = 42
person.age          # => 42
Также имеет псевдоним: set_ostruct_member_value!
as_json (*) Показать исходный код
# File ext/json/lib/json/add/ostruct.rb, line 33
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

Методы 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>
delete_field (name) { || ... } Показать исходный код
# 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

Удаляет из объекта поле с указанным именем и возвращает значение, которое содержало это поле, если оно было определено. Можно также передать блок. Если поле не определено, возвращается результат выполнения блока либо вызывается исключение 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
dig(name, *identifiers) → object Показать исходный код
# 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

Находит и возвращает объект во вложенных объектах, указанный аргументами 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
each_pair {|name, value| block } → ostruct Показать исходный код
each_pair → Enumerator
# 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

Передаёт в блок все атрибуты (в виде символов) вместе с соответствующими значениями или возвращает перечислитель, если блок не задан.

require "ostruct"
data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
data.each_pair.to_a   # => [[:country, "Australia"], [:capital, "Canberra"]]
eql? (other) Показать исходный код
# File lib/ostruct.rb, line 433
def eql?(other)
  return false unless other.kind_of?(OpenStruct)
  @table.eql?(other.table!)
end

Сравнивает этот объект с other на равенство. Объект OpenStruct считается eql? с other, если other является объектом OpenStruct и таблицы Hash этих двух объектов равны по eql?.

freeze () Показать исходный код
# File lib/ostruct.rb, line 269
def freeze
  @table.freeze
  super
end
Вызывает метод суперкласса Object#freeze
inspect () Показать исходный код
# 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

Возвращает строку с подробным описанием ключей и значений.

Также имеет псевдоним: to_s
to_h (&block) Показать исходный код
# File lib/ostruct.rb, line 182
def to_h(&block)
  if block
    @table.to_h(&block)
  else
    @table.dup
  end
end
to_json (*args) Показать исходный код
# File ext/json/lib/json/add/ostruct.rb, line 51
def to_json(*args)
  as_json.to_json(*args)
end

Возвращает строку JSON, представляющую self:

require 'json/add/ostruct'
puts OpenStruct.new('name' => 'Rowdy', :age => nil).to_json

Результат:

{"json_class":"OpenStruct","t":{'name':'Rowdy',"age":null}}
to_s ()
Псевдоним для: inspect

Приватные методы экземпляра

set_ostruct_member_value!
Псевдоним для: []=

Ruby Core © 1993–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API