Ссылка на объект в стиле JS в Ruby

2

Учитывая, насколько выразителен 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", если он не определен, а затем сделать некоторую магию. Например,

  • Я могу перебирать хэш и настраивать accessors
  • Я могу использовать []=(k, v) для обозначения скобок и делать то же самое.

Мой прогресс во втором стиле до этой точки.

Здесь очень специфический Object#respond_to_missing?, который может принимать в нем определенный символ; но не может ответить в общем случае.

Есть три возможных способа, которые я могу решить, чтобы решить прокомментированный, второй стиль вызова:

  • Сделать объект не экземпляром класса, а методом во все времена. Например, следующие работы:
class A
  def b; puts 'c'; end
end
def always
  A.new
end

c = always
c.b

Учитывая это, может быть возможный способ сделать некоторую ошибку, а затем перехватить объект соответствующим образом.

  • Используйте некоторую фантастическую рубиновую функцию, о которой я не слышал. Ruby - такой богатый язык, это может быть уже довольно легко.

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

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

  • 0
    Вообще то я разобрался ... скоро выложу.
Теги:
syntax
coding-style

2 ответа

2
Лучший ответ

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

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

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

  • 0
    Я поместил эту реализацию в этом проекте: github.com/Oblong/js-Ruby
1

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

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)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню