2010-01-22 35 views
1

基本上我有两个模块: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 

更新:2010年1月24日

我的问题的背景是,我尝试将巨大的嵌套散列 结构转换为JSON字符串。这个散列表中的每个键都是 中的典型下划线表示法中的Ruby Symbol。使用JSON 数据的JavaScript库期望这些密钥是camelcase符号中的字符串。我认为重写Symbol#to_json方法可能是最简单的方法。但是 没有解决问题,因为Hash#to_json在每个密钥上首先调用#to_s和之后的 #to_json。所以我觉得这可能是一个解决方案来扩展 通过Symbol#to_s与覆盖该特定字符串实例的 #to_json方法返回拥有 一个#to_json方法,它返回自己在驼峰表示法的字符串模块returnd所有字符串。

我不确定是否有简单的方法来猴子补丁Hash#to_json

如果有人想看看到我使用JSON实现,这里是链接:http://github.com/flori/json/blob/master/lib/json/pure/generator.rb(线239和下面是利息)

回答

2

你的第二个猴补丁的工作,因为你重新打开Symbol类。

第一个并不是因为所有包含的都是将模块添加到所包含模块的列表中。只有当类本身没有定义特定的方法,或者如果该方法调用超类,这些才会被调用。所以你的代码永远不会被调用。

如果你想使用一个模块,您必须使用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和猴补丁,与其#to_s,如:

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

我的第一个想法是猴补丁Symbol#to_json但不会像你指出的那样工作,因为在调用to_json之前,Hash将强制键入字符串,因为JavaScript键必须是字符串。所以你可以monkeatch哈希代替:

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

我有一个巨大的嵌套散列结构。每个键都是下划线符号的红宝石符号。我尝试将每个符号转换为camelcase符号中的字符串,因为使用JSON数据的JavaScript库期望在此符号中使用键。我认为我的方法可能比遍历整个散列更快。 例如:{:chunky_bacon =>“某个值”} .to_json应该像'{“chunkyBacon”:“某个值”}' – t6d 2010-01-23 15:08:05

+0

感谢您的澄清。不要犹豫,相应地修改你的问题。查看我的更新回答 – 2010-01-23 19:06:14

+0

到目前为止,非常感谢您的帮助。我更新了我的问题。问题是Hash#to_json不会调用某事物。如符号#to_json,而不是符号#to_s#to_json。如果有一个简单的方法来修补Hash#to_json补丁,我真的不会。 – t6d 2010-01-24 11:48:38

0

这看起来有点复杂。我可能不明白你想要达到什么目的,但是这样的事情呢?

#!/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