Ссылка на объект в стиле JS в Ruby - PullRequest
2 голосов
/ 30 августа 2011

Учитывая, насколько выразителен 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 - такой богатый язык, это уже может быть довольно легко.

  • Как-то переопределить механику обработки ошибок. Я понятия не имею, как кто-то будет делать это в большом общем случае, но, по сути, вы бы сказали ruby, что если какой-либо экземпляр какого-либо класса не сможет ответить на метод, у вас будет какой-то обобщенный обработчик, с которым придется иметь дело. вещи.

В любом случае, если у кого-то есть идея, я думаю, это было бы довольно забавное упражнение Спасибо!

Ответы [ 2 ]

2 голосов
/ 30 августа 2011

Вероятно, лучшие способы сделать это:

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

Я пока оставлю это открытымВ случае, если у кого-то есть идея лучшего пути здесь.

1 голос
/ 29 сентября 2017

Я наткнулся на эти вопросы спустя годы, но все же кто-то может найти это полезным.

Драгоценный камень 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)
...