Переопределяющий метод получения OpenStruct для его печати в виде хэша
ЦЕЛЬ: Значения объекта OpenStruct должны быть напечатаны как хеш, а не как объект
ВОЗМОЖНОЕ РЕШЕНИЕ: переопределить метод получения класса OpenStruct
MyOpenStruct
Переопределение new
, to_h
а также []
из OpenStruct
,
class MyOpenStruct < OpenStruct
def initialize(object=nil)
@table = {}
@hash_table = {}
if object
object.each do |k,v|
if v.is_a?(Array)
other = Array.new()
v.each { |e| other.push(self.class.new(entry)) }
v = other
end
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
@hash_table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
def [](val)
@hash_table[val.to_sym]
end
end
Но переопределение []
не имеет никакого значения. Например
irb(main):007:0> temp = MyOpenStruct.new({"name"=>"first", "place"=>{"animal"=>"thing"}})
=> #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">>
irb(main):008:0> temp.name
=> "first"
irb(main):009:0> temp.place
=> #<MyOpenStruct animal="thing">
irb(main):010:0> temp["place"]
=> {"animal"=>"thing"}
irb(main):011:0> temp[:place]
=> {"animal"=>"thing"}
irb(main):012:0> temp
=> #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">>
Только когда я получаю доступ к ключам, используя []
хеш возвращается!!
Как я могу это исправить??
2 ответа
Я не верю, что создание вложенного OpenStruct имеет смысл, если вы возвращаете его как Hash. Вот как работает OpenStruct:
require 'ostruct'
struct = OpenStruct.new(name: 'first', place: { animal: 'thing' })
struct.place
# => {:animal=>"thing"}
struct.place[:animal]
# => "thing"
struct.place.animal
# => NoMethodError: undefined method `animal' for {:animal=>"thing"}:Hash
Итак, если вы хотите использовать точечную запись, чтобы получить struct.place.animal
вам нужно создать вложенные объекты OpenStruct, как вы это сделали. Но, как я уже сказал, вам не нужно переопределять []
метод. Использование вашего класса без переопределения []
Я получаю это:
struct = MyOpenStruct.new(name: 'first', place: { animal: 'thing' })
# => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">>
struct.place
# => #<MyOpenStruct animal="thing">
struct.place.animal
# => "thing"
В любом случае, если вы действительно хотите, чтобы точечная нотация работала так, как вы просили, вы можете переопределить new_ostruct_member
метод, который используется внутри для создания динамических атрибутов при установке объекта OpenStruct. Вы можете попробовать что-то вроде этого, но я не рекомендую:
class MyOpenStruct < OpenStruct
def initialize(object=nil)
@table = {}
@hash_table = {}
if object
object.each do |k,v|
if v.is_a?(Array)
other = Array.new()
v.each { |e| other.push(self.class.new(entry)) }
v = other
end
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
@hash_table[k.to_sym] = v
new_ostruct_member(k)
end
end
end
def [](val)
@hash_table[val.to_sym]
end
protected
def new_ostruct_member(name)
name = name.to_sym
unless respond_to?(name)
# use the overrided `[]` method, to return a hash
define_singleton_method(name) { self[name] }
define_singleton_method("#{name}=") { |x| modifiable[name] = x }
end
name
end
end
struct = MyOpenStruct.new(name: 'first', place: { animal: 'thing' })
# => #<MyOpenStruct name="first", place=#<MyOpenStruct animal="thing">>
struct.place
# => {:animal=>"thing"}
struct.place[:animal]
# => "thing"
Ответ @ Догуиты верен во всех отношениях. Я просто хотел ответить на ваш вопрос: "Если это невозможно, могу ли я напечатать хэш всего объекта? temp
"Да, вы можете. Вам просто нужно переопределить to_h
для рекурсивного обхода ваших ключей и значений и преобразования экземпляров MyOpenStruct в хэш:
def to_h
@table.each_with_object({}) do |(key, val), table|
table[key] = to_h_convert(val)
end
end
private
def to_h_convert(val)
case val
when self.class
val.to_h
when Hash
val.each_with_object({}) do |(key, val), hsh|
hsh[key] = to_h_convert(val)
end
when Array
val.map {|item| to_h_convert(item) }
else
val
end
end