2014-11-24 70 views
0

我想从对象TranFeeRate组成对象Transaction撰写对象,而不初始化不在散列的对象

class Transaction 

    attr_reader :tranfee, :rate 

    def initialize(hash) 
    @tranfee = PaymentType::TranFee.new(hash) 
    @rate = PaymentType::Rate.new(hash) 
    end 
end 

module PaymentType 

    def initialize(args = {}, regex) 

    args.each do |key,value| 
     if key =~ regex 
     instance_variable_set("@#{key}", value) unless value.nil? 
     eigenclass = class << self; self; end 
     eigenclass.class_eval do 
      attr_reader key 
     end 
     end 
    end 
    end 

    class TranFee 
    include PaymentType 

    def initialize(args, regex = /\Atran.*/) 
     super(args, regex) 
    end 
    end 

    class Rate 
    include PaymentType 

    def initialize(args, regex = /\Arate.*/) 
     super(args, regex) 
    end 
    end 
end 

Rate和TranFee对象是从像下面这样的散列创建的。

reg_debit = {"name" => "reg_debit", "rate_base" => 0.0005, 
"tran_fee" => 0.21, "rate_basis_points" => 0.002, "tran_auth_fee" => 0.10} 

我初始化基于正则表达式的对象,因为哈希最终将包含更多的价值,我想程序调整为更多的项目/添加类。

此外,还有一些情况下没有密钥以“tran”开头。有没有人知道如何使Transaction只创建一个Rate对象如果TranFee里面没有实例变量?

当哈希看起来像这样reg_debit = {"name" => "reg_debit", "rate_base" => 0.0005, "rate_basis_points" => 0.002}一个例子是,现在的产量是

#<Transaction:0x007ff98c070548 @tranfee=#<PaymentType::TranFee:0x007ff98c070520>, @rate=#<PaymentType::Rate:0x007ff98c0704a8 @rate_base=0.0005, @rate_basis_points=0.002>> 

所以我得到一个TranFee对象包含任何东西(。换句话说,如果当keys =~ /\Atran.*/哈希没有返回)它和我想在这种情况下下降。不知道是否有更好的方法来设计这个?我试图想出一种使用ostruct或struct的方法,但我很想弄明白。感谢您的帮助。

回答

2

我相信你的策略是很成问题 - 创建属性从用户输入一个类听起来并不像一个很好的主意。

此外,向每个实例添加方法(如attr_reader)可能会有严重的performance issues

如果你只想要一个数据结构来保存你的数据,继续使用Hash。如果你想要一个结构,你可以使用点符号而不是括号表示查询,你可能想要考虑像hashiehashr这样的宝石。

如果你想一些代码以使扁平数据结构分层的,我可以建议是这样的:

hierarchical_hash = hash.each_with_object({}) do |(k, v), h| 
    if k.match(/^([^_]+)_(.+)$/) 
    root_key = $1 
    child_key = $2 
    h[root_key] ||= {} 
    h[root_key][child_key] = v 
    else 
    h[k] = v 
    end 
end 
# => { 
# =>  "name" => "reg_debit", 
# =>  "rate" => { 
# =>     "base" => 0.0005, 
# =>   "basis_points" => 0.002 
# =>  }, 
# =>  "tran" => { 
# =>    "fee" => 0.21, 
# =>   "auth_fee" => 0.1 
# =>  } 
# => } 
+0

好点。你可能会考虑'root_key,_child_key = k.partition('_')'代替正则表达式。 – 2014-11-24 16:06:00

0

你的问题引发了一些有趣的问题。我会尽力解释你如何解决它,但正如@Uri提到的那样,可能有更好的方法来解决你的问题。

我认为@tranfee将被设置等于其主要始于"tran"哈希的第一个值和@rate将被设置等于其主要始于"rate"哈希的第一个值。如果这种解释不正确,请告诉我。

请注意,我已经把initializePaymentType模块在一个类(Papa)并取得TranFeeRate子类。这是您可以在该类别的子类中使用内initialize的唯一方法。

代码

class Transaction 
    attr_reader :tranfee, :rate 

    def initialize(hash={}) 
    o = PaymentType::TranFee.new(hash) 
    @tranfee = o.instance_variable_get(o.instance_variables.first) 
    o = PaymentType::Rate.new(hash) 
    @rate = o.instance_variable_get(o.instance_variables.first) 
    end 
end 

module PaymentType 
    class Papa 
    def initialize(hash, prefix) 
     key, value = hash.find { |key,value| key.start_with?(prefix) && value } 
     (raise ArgumentError, "No key beginning with #{prefix}") unless key 
     instance_variable_set("@#{key}", value) 
     self.class.singleton_class.class_eval { attr_reader key } 
    end 
    end 

    class TranFee < Papa 
    def initialize(hash) 
     super hash, "tran" 
    end 
    end 

    class Rate < Papa 
    def initialize(hash) 
     super hash, "rate" 
    end 
    end  
end 

我相信,因为红宝石1.9.3 Object#singleton_class已经可用的方法。

reg_debit = {"name" => "reg_debit", "rate_base" => 0.0005, "tran_fee" => 0.21, 
      "rate_basis_points" => 0.002, "tran_auth_fee" => 0.10} 

a = Transaction.new reg_debit 
p Transaction.instance_methods(false) #=> [:tranfee, :rate] 
p a.instance_variables    #=> [:@tranfee, :@rate] 
p a.tranfee       #=> 0.21 
p a.rate        #=> 0.0005