Расширения ядра Ruby с модулями - PullRequest
1 голос
/ 22 января 2010

В основном у меня есть два модуля: CoreExtensions::CamelcasedJsonString и …::CamelcasedJsonSymbol. Последний переопределяет Symbol#to_s, так что метод возвращает String, который расширяется первым модулем. Я не хочу, чтобы каждая строка была CamelcasedJsonString. По этой причине я пытаюсь применить конкретный экземпляр расширения.

Моя проблема в том, что Symbol#to_s, похоже, снова переопределяется после того, как я включил свой модуль (последняя спецификация не работает):

require 'rubygems' if RUBY_VERSION < '1.9'
require 'spec'

module CoreExtensions

  module CamelcasedJsonString; end

  module CamelcasedJsonSymbol

    alias to_s_before_core_extension to_s
    def to_s(*args)
      to_s_before_core_extension(*args).extend(CamelcasedJsonString)
    end

  end
  ::Symbol.send :include, CamelcasedJsonSymbol

end

describe Symbol do

  subject { :chunky_bacon }

  it "should be a CamelcasedJsonSymbol" do
    subject.should be_a(CoreExtensions::CamelcasedJsonSymbol)
  end

  it "should respond to #to_s_before_core_extension" do
    subject.should respond_to(:to_s_before_core_extension)
  end

  specify "#to_s should return a CamelcasedJsonString" do
    subject.to_s.should be_a(CoreExtensions::CamelcasedJsonString)
  end

end

Однако работает следующий пример:

require 'rubygems' if RUBY_VERSION < '1.9'
require 'spec'

module CoreExtensions
  module CamelcasedJsonString; end
end

class Symbol
  alias to_s_before_core_extension to_s
  def to_s(*args)
    to_s_before_core_extension(*args).extend(CoreExtensions::CamelcasedJsonString)
  end
end

describe Symbol do

  subject { :chunky_bacon }

  it "should respond to #to_s_before_core_extension" do
    subject.should respond_to(:to_s_before_core_extension)
  end

  specify "#to_s should return a CamelcasedJsonString" do
    subject.to_s.should be_a(CoreExtensions::CamelcasedJsonString)
  end

end

Обновление: 24 января 2010 г.

Основой моей проблемы является то, что я пытаюсь преобразовать огромный вложенный хеш структурировать в строку JSON. Каждый ключ в этом хэше - это Ruby Symbol в типичные обозначения подчеркивания. Библиотека JavaScript, которая использует JSON Данные ожидают, что ключи будут строками в нотации верблюдов. я думал так переопределение метода Symbol#to_json может быть самым простым способом. Но это не работает с Hash#to_json звонки сначала #to_s и потом #to_json на каждую клавишу. Поэтому я подумал, что это может быть решение расширить все строки возвращаются на Symbol#to_s с помощью модуля, который отменяет #to_json метод этого конкретного экземпляра строки, чтобы вернуть строку, которая имеет #to_json метод, который возвращает себя в нотации верблюда.

Я не уверен, есть ли простой способ обезьяньего патча Hash#to_json.

Если кто-то хочет взглянуть на реализацию JSON, которую я использую, вот ссылка: http://github.com/flori/json/blob/master/lib/json/pure/generator.rb (интересуют строки 239 и последующие)

Ответы [ 2 ]

2 голосов
/ 22 января 2010

Ваш второй monkeypatch работает, так как вы снова открываете класс Symbol.

Первый - нет, потому что все, что делает включение, это добавляет модуль в список включенных модулей. Они вызываются, только если сам класс не определяет конкретный метод или этот метод вызывает super. Таким образом, ваш код никогда не вызывается.

Если вы хотите использовать модуль, вы должны использовать обратный вызов included:

  module CamelcasedJsonSymbol
    def self.included(base)
      base.class_eval do
        alias_method_chain :to_s, :camelcase_json
      end
    end

    def to_s_with_camelcase_json(*args)
      to_s_without_camelcase_json(*args).extend(CamelcasedJsonString)
    end
  end

Я использовал active_record alias_method_chain, что вы всегда должны делать при исправлении обезьян. Он побуждает вас использовать правильные имена и, таким образом, избегать коллизий.

Это был технический ответ.

При более прагматичном подходе вы должны переосмыслить это. Неоднократное расширение строк, как это нехорошо, приведет к значительному снижению производительности в большинстве реализаций (например, очищает весь кэш методов в MRI) и вызывает сильный запах кода.

Я не знаю достаточно о проблеме, чтобы быть уверенным, или предложить другие решения (может быть, класс Делегат может быть правильным, чтобы вернуться?), Но я чувствую, что это не правильный способ достичь ваших целей .


Поскольку вы хотите преобразовать ключи хэша, вы можете передать параметр в #to_json и monkeypatch вместо #to_s, например:

{ :chunky_bacon => "good" }.to_json(:camelize => true)

Моей первой идеей было создать monkeypatch Symbol#to_json, но это не сработает, как вы указали, поскольку Hash принудительно заставит ключи к строкам перед вызовом to_json, потому что ключи javascript должны быть строками. Таким образом, вы можете вместо этого использовать Hash:

module CamelizeKeys
  def self.included(base)
    base.class_eval do
      alias_method_chain :to_json, :camelize_option
    end
  end

  def to_json_with_camelize_option(*args)
    if args.empty? || !args.first[:camelize]
      to_json_without_camelize_option(*args)
    else
      pairs = map do |key, value|
        "#{key.to_s.camelize.to_json(*args)}: #{value.to_json(*args)}"
      end
      "{" << pairs.join(",\n") << "}"
    end
  end
end
0 голосов
/ 22 января 2010

Это выглядит довольно сложно. Я, наверное, не понимаю, чего вы пытаетесь достичь, но как насчет этого?

#!/usr/bin/ruby1.8

class Symbol

  alias_method :old_to_s, :to_s
  def to_s(*args)
    if args == [:upcase]
      old_to_s.upcase
    else
      old_to_s(*args)
    end
  end

end

puts :foo                   # => foo
puts :foo.to_s(:upcase)     # => FOO

и частичная спецификация:

describe :Symbol do

  it "should return the symbol as a string when to_s is called" do
    :foo.to_s.should eql 'foo'
  end

  it "should delegate to the original Symbol.to_s method when to_s is called with unknown arguments" do
    # Yeah, wish I knew how to test that
  end

  it "should return the symbol name as uppercase when to_s(:upcase) is called" do
    :foo.to_s(:upcase).should eql "FOO"
  end

end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...