2016-07-12 4 views
0

に設定されたオブジェクトのプロパティ:ハッシュを有するようなハッシュマップを有するルビー

Rubyで
{:foo => 1, :bar => 2} 

、自動的にこれが起こることを引き起こすために、オブジェクトのプロパティとしてこれらの値を割り当てるための簡単な方法があります:

obj.foo = 1 
obj.bar = 2 

正確には、やっていくつかのルビー、慣用的な方法:

hashmap.each { |k,v| obj.send("#{k}=", v) } 

objがないオブジェクトでありますActiveModelを継承し、それはStructではなく、サードパーティのライブラリから来ているので、タイプを制御することはできません。

私はRailsを使用しています。その答えがRailsから来た場合、それは受け入れられます。

+0

これは、例えば、ActiveRecordインスタンスの場合は 'obj.attributes = hashmap'です – mdesantis

+0

私はあなたの質問にStructがあなたのものであると思います探している。 http://ruby-doc.org/core-2.2.0/Struct.html –

+0

objは既に存在するサードパーティクラスのオブジェクトです。それを反映するように質問を更新しました。 – Pablo

答えて

0

データを動的に設定する必要がある場合はmethod_missingを使用できます。

それでは、これを試してみましょう、モバイル上でこれを行う
class Foo 
    def method_missing(sym, *args) 
    super unless instance_variables.include?("@#{sym}".to_sym) 
    instance_variable_get("@#{sym}") 
    end 
end 

obj = Foo.new 

h = {:foo => 1, :bar => 2} 

h.each { |k, v| obj.instance_variable_set("@#{k}", v) } 

obj.foo 
# => 1 
obj.bar 
# => 2 
+1

これははるかに優れています'instance_variable_set'の中の' define_method'に 'method_missing'をオーバーロードします。 – mudasobwa

+0

'self.singleton_class.define_method'? – mudasobwa

+2

ああ、それは 'プライベート'だよ、残念なことだ! https://repl.it/CbLI/2 – mudasobwa

0

class Something 
     attr_reader :foo, :bar 

      def initialize(hash = {}) 
       foo = hash[:foo] 
       bar = hash[:bar] 
      end 
    end 

    obj = Something.new({foo: 1, bar: 2}) 

    obj.foo = 1 
    obj.bar =2 
+0

オブジェクトを作成していません。既に存在しています。 – Pablo

1

たぶん、あなたが戻ってそれを変換、その後、あなたはOpenStructとその属性を必要とするものは何でも、あなたのハッシュからOpenStructを作成することができますハッシュに? Ruby docsから

require 'ostruct' 

h = {:foo => 1, :bar => 2} 
o = OpenStruct.new(h) 
o.foo # Output: => 1 
o.bar # Output: => 2 

# if necessary, convert it back into a hash 
h = o.to_h 

アンOpenStructは、それに付随する値を持つ任意の属性の 定義を可能にするハッシュと同様のデータ構造です。 これは、Rubyのメタプログラミングを使用して、クラス自体にメソッド を定義することによって達成されます。

OpenStructはRubyのメソッド検索構造を使用して検索し、 はプロパティに必要なメソッドを定義します。これはメソッドmethod_missingとdefine_methodによって を実行します。

ハッシュ や構造体を使用する場合と比較して、これらのプロパティの設定では、はるか のオーバーヘッドがあるとして、作成されたオブジェクトの パフォーマンスが懸念される場合、これは検討する必要があります。

hashmap.each { |k,v| obj.send("#{k}=", v) } 

一つだけを改善するために、左にあります:

hashmap.each { |k,v| obj.public_send("#{k}=", v) } 

使用public_sendではなく、あなたが最も簡潔で読みやすい、慣用的な解決策が既に(ほぼ)が存在してい何

+0

OPから: "objはActiveModelを継承しないオブジェクトであり、構造体ではなく、サードパーティのライブラリから来ているので型を制御できません" – mudasobwa

+0

彼はその型を制御しません。彼はハッシュの内容を使って新しいOpenStructを作成し、OpenStruct(ドット表記で属性にアクセスする)とハッシュに変換する必要があれば何でもします。それほどエレガントではありませんが、不要なメタプログラミングを使用することで複雑さを増すことはありません。とにかく、あなたは有効なポイントを持っています、多分私の提案は実行可能ではありません... – BrunoFacca

2

sendを使用して、メソッド名を動的に渡すだけで、アクセス制限を回避しないように他の人に明示します。

+0

アクセサーがすでにクラスに定義されているかどうかは私には分かりませんでした。もしそうでなければ、彼は 'Foo.attr_accessor:bar、:foo}'でいつもそれらを追加することができます。(おそらくダースの他のオプションもあります) –

+2

@JayDorseyダイレクトコールの何が間違っていますか? foo'? – mudasobwa

+1

OPはオブジェクトがOPのコントロールの外にある(コメントする)ことを明記しています(これは修正するべきではないことを意味しますが、もちろんこれはRubyでは常に可能ですが)、既存のアクセサーとキーが一致する彼らがしなければ、例外で失敗することは正しいことです。 –

0
class Klass 
    def initialize(h) 
    h.each { |iv, val| instance_variable_set("@#{iv}", val) } 
    end 
end 

k = Klass.new(:foo => 1, :bar => 2) 
    #=> #<Klass:0x007ff1d9073118 @foo=1, @bar=2> 
0

あなたが提案された解決策にRailsの、マイナーな改良を使用している場合、アクティブサポートの拡張documentation 1として

hashmap.each {|k,v| obj.try "#{k}=", v } 

からObject#tryを使用することであろう、

は、パブリックメソッドを呼び出します受信者がそれに応答しない場合を除き、 public_sendのように、最初の引数として名前が付けられます。 呼び出しは例外を送出するのではなくnilを返します。

関連する問題