2010-01-22 37 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