Ruby: Можете ли вы определить, вызывается ли у объекта один из его методов?
Я не уверен, что задаю правильный вопрос. Возможно, я неправильно подхожу к проблеме, но в основном у меня такая ситуация:
obj = get_user(params)
obj.profile => {:name => "John D", :age => 40, :sex => "male"} #Has to be of class Hash
obj.profile.name => "John D"
obj.profile[:name] => "John D"
obj.profile.job => nil
В общем, я должен выполнить все эти условия, и я не уверен, как именно к этому подойти (я только что выучил Ruby сегодня).
Обратите внимание на точечную запись для доступа к внутренним переменным, в противном случае я хотел бы, чтобы профиль был хэшем символов. Итак, я попробовал два метода, которые только помогают мне
Способ 1: сделать профиль OpenStruct
Так что это позволяет мне получить доступ к имени, возрасту и полу, используя точечную запись, и автоматически возвращает nil, если ключ не существует, однако obj.profile имеет тип OpenStruct вместо Hash
Способ 2: сделать профиль своим собственным классом
При этом я устанавливаю их как переменные экземпляра и могу использовать method_missing для возврата nil, если они не существуют. Но я снова сталкиваюсь с проблемой, что obj.profile не является правильным типом / классом
Я что-то упускаю? Есть ли способ отличить
obj.profile
obj.profile.name
в функции получения и вернуть хеш или иначе?
Могу ли я изменить то, что возвращает мой пользовательский класс для профиля, так что вместо этого он возвращает хэш?
Я даже пытался проверить args и **kwargs в функции get для obj.profile, и ни один из них, похоже, не помогает или не заполняется, если я вызываю obj.profile.something
3 ответа
Только для нескольких ключей хеша вы можете легко определить одноэлементные методы следующим образом:
def define_getters(hash)
hash.instance_eval do
def name
get_val(__method__)
end
def job
get_val(__method__)
end
def get_val(key)
self[key.to_sym]
end
end
end
profile = person.profile #=> {name: "John Doe", age: 40, gender: "M"}
define_getters(profile)
person.profile.name #=> "John Doe"
person.profile.job #=> nil
Отражает также измененные значения (если вам интересно):
person.profile[:name] = "Ralph Lauren"
person.profile.name #=> "Ralph Lauren"
При таком подходе вам не придется переопределять method_missing
создавать новые классы, наследуя от Hash
или обезьяна-патч Hash
учебный класс.
Тем не менее, чтобы иметь возможность доступа к неизвестным ключам через вызовы методов и возврата nil
вместо ошибок вам придется задействовать method_missing
,
Это Hash
переопределение выполнит то, что вы пытаетесь сделать. Все, что вам нужно сделать, это включить его в один из ваших файлов классов, который вы уже загружаете.
class Hash
def method_missing(*args)
if args.size == 1
self[args[0].to_sym]
else
self[args[0][0..-2].to_sym] = args[1] # last char is chopped because the equal sign is included in the string, print out args[0] to see for yourself
end
end
end
См. Следующий вывод IRB для подтверждения:
1.9.3-p194 :001 > test_hash = {test: "testing"}
=> {:test=>"testing"}
1.9.3-p194 :002 > test_hash.test
=> "testing"
1.9.3-p194 :003 > test_hash[:test]
=> "testing"
1.9.3-p194 :004 > test_hash.should_return_nil
=> nil
1.9.3-p194 :005 > test_hash.test = "hello"
=> "hello"
1.9.3-p194 :006 > test_hash[:test]
=> "hello"
1.9.3-p194 :007 > test_hash[:test] = "success"
=> "success"
1.9.3-p194 :008 > test_hash.test
=> "success"
1.9.3-p194 :009 > test_hash.some_new_key = "some value"
=> "some value"
1.9.3-p194 :011 > test_hash[:some_new_key]
=> "some value"
Если это обязательно должно быть Hash
:
require 'pp'
module JSHash
refine Hash do
def method_missing(name, *args, &block)
if !args.empty? || block
super(name, *args, &block)
else
self[name]
end
end
end
end
using JSHash
profile = {:name => "John D", :age => 40, :sex => "male"}
pp profile.name # "John D"
pp profile[:name] # "John D"
pp profile.job # nil
pp profile.class # Hash
Но все же лучше не быть Hash
Если только это абсолютно не нужно:
require 'pp'
class Profile < Hash
def initialize(hash)
self.merge!(hash)
end
def method_missing(name, *args, &block)
if !args.empty? || block
super(name, *args, &block)
else
self[name]
end
end
end
profile = Profile.new({:name => "John D", :age => 40, :sex => "male"})
pp profile.name
pp profile[:name]
pp profile.job