Учитывая, насколько выразителен Ruby, мне интересно, пытался ли кто-нибудь создать класс или модуль, который будет имитировать синтаксис JS-объекта. Например, в JS я, конечно, могу это сделать:
[1] var obj = {a: 'b'};
[2] obj.c = 'd';
[3] obj.a = 123
[4] obj['e'] = 'f';
[5] obj.e = obj['a']
В Ruby я могу, на данный момент, иметь код, который позволит мне сделать что-то вроде этого:
[1] obj = {'a' => 'b'}.to_js
[2] # obj.c = 'd' << This is what I can't solve, 'c' is first defined here.
[3] obj.a = 123;
[4] obj['e'] = 'f'
[5] obj.e = obj['a']
Пока я получаю символ через квадратные скобки или при инициализации, я могу легко сохранить пару K/V и создать instance methods
для сеттеров и геттеров.
Тем не менее, мне не удалось выяснить, как создать объект, который будет отвечать на "c", если он не определен, а затем сделать некоторую магию. Например,
[]=(k, v)
для обозначения скобок и делать то же самое.Здесь очень специфический Object#respond_to_missing?
, который может принимать в нем определенный символ; но не может ответить в общем случае.
Есть три возможных способа, которые я могу решить, чтобы решить прокомментированный, второй стиль вызова:
class A def b; puts 'c'; end end def always A.new end c = always c.b
Учитывая это, может быть возможный способ сделать некоторую ошибку, а затем перехватить объект соответствующим образом.
Используйте некоторую фантастическую рубиновую функцию, о которой я не слышал. Ruby - такой богатый язык, это может быть уже довольно легко.
Как-то переопределить механизм обработки ошибок. Я не знаю, как кто-то сделает это в большом общем случае, но в основном вы бы сказали рубину, что если какой-либо экземпляр какого-либо класса не смог ответить на метод, у вас был бы некоторый обобщенный обработчик для решения вещи.
В любом случае, если у кого-то есть идея, это было бы очень веселое упражнение, я думаю. Спасибо!
Вероятно, лучшие способы сделать это:
class Hash
def method_missing(symbol, opts = nil)
string = symbol.to_s
self[string[0..-2].to_sym] = opts if string[-1..-1] == '=' and opts
self[symbol]
end
end
Если вы хотите подкласс, то вы можете сделать это:
class JSHash < Hash
def method_missing(symbol, opts = nil)
string = symbol.to_s
self[string[0..-2].to_sym] = opts if string[-1..-1] == '=' and opts
self[symbol]
end
end
class Hash
def to_js
JSHash.new.merge! self
end
end
Я буду держать это открытым какое-то время, если у кого-то есть идея лучше здесь.
Я столкнулся с этим вопросом спустя годы, но все же кто-то может найти это полезным.
Hashie:: Mash обеспечивает именно такое поведение.
require 'hashie/mash'
obj = Hashie::Mash.new ({'a' => 'b'})
obj.c = 'd'
obj.a = 123
obj['e'] = 'f'
obj.e = obj['a']
p obj # #<Hashie::Mash a=123 c="d" e=123>
p obj.to_hash # {"a"=>123, "c"=>"d", "e"=>123}
Он также использует свою магию при назначении вложенных хэшей и конвертирует их в выемки:
require 'hashie/mash'
obj = Hashie::Mash.new ({'a' => 'b'})
obj.c = 3
obj.d = { e: { f: {g: { h: 5 } } } }
puts obj.d.e.f.g.h # 5
puts obj.to_hash # {"a"=>"b", "c"=>3, "d"=>{"e"=>{"f"=>{"g"={"h"=>5}}}}}
Цена здесь - производительность. В простом измерении Hashie:: Mash - приложение. В 10 раз медленнее, чем Хэш:
require 'benchmark'
require 'hashie/mash'
n = 1000000
time = Benchmark.measure do
hash = {}
(0..n).each do |key|
hash[key] = key.to_s
end
end
puts 'Standard Hash'
puts time
n = 1000000
time = Benchmark.measure do
hash = Hashie::Mash.new
(0..n).each do |key|
hash[key] = key.to_s
end
end
puts 'Hashie::Mash'
puts time
На моей машине получены следующие результаты:
Standard Hash
0.330000 0.030000 0.360000 ( 0.367405)
Hashie::Mash
3.350000 0.040000 3.390000 ( 3.394001)