2012-04-05 13 views
6

だから、私はこの昨晩働いていると思って、それを誓った。今はうまくいきません。私は時間を割いて助けを求めます。ruby​​ on rails method_missingの問題を使ってDBの動的属性フィールド

、データベース内の動的なフィールドを定義イム、半EAVスタイル、と私はEAVが

とにかくイムはそれを少しやって:)良いアイデアであるかどうかについて、あなたの意見を聞くように注意してはいけない、今だけを述べることができます過去に行われたのとは違って、基本的に属性(またはフィールド)が追加されたときに、特定の属性テーブルへの追加列を作成して実行します(または削除します)。いずれにしても、すべての属性が定義されている直接の関係である真ん中に座って、属性がカテゴリ固有であるため、実際の属性名を列名として使用することはできません。

それはあなたが

Entity 
    belongs_to :category 

    Category 
    has_many :entities 

    EntityAttribute 
    belongs_to :category 

    EntityAttributeValue 
    belongs_to :entity_attribute 
    belongs_to :entity   

そしてEAVテーブルを視覚化することができます場合は、新しい属性が作成されるように、その特定のエンティティのための値が含まれていattribute_2 ATTRIBUTE_1ラベルの列と、水平に広がっています。とにかく

- 私はエンティティモデルのメソッドを動的にしようとしていますので、私はここでentity.actual_attribute_name @、というよりもentity.entity_attribute_value.field_5

@呼び出すことができ、私が働いていたと思ったコードがあります - -

def method_missing(method, *args) 

     return if self.project_category.blank? 

     puts "Sorry, I don't have #{method}, let me try to find a dynamic one." 
     puts "let me try to find a dynamic one" 

     keys = self.project_category.dynamic_fields.collect {|o| o.name.to_sym } 

     if keys.include?(method) 
     field = self.project_category.dynamic_fields.select { |field| field.name.to_sym == method.to_sym && field.project_category.id == self.project_category.id }.first 
     fields = self.project_category.dynamic_field_values.select {|field| field.name.to_sym == method } 
     self.project_category_field_value.send("field_#{field.id}".to_sym, *args) 
     end 

    end 

私は戻ってコードに行ってきましたように私は、レコードを保存したときに、今日、私はレールコンソールで属性を設定することができ、それが正しいフィールドを返すだろうが、私が実現し、EntityAttributeValueが更新されていませんでした(上記のself.project_category_field_valueで表されます)

これをさらに調べてみると、before_updateまたはbefore_saveコールバックを手動で属性を保存するために追加しなければならないように見えました。コールバックでは、オブジェクトがあるかのように、コールバックでmethod_missingコールバックを再実行します。複製されている(そして、新しいオブジェクトは元のオブジェクトのコピー)、または何か、私はかなり確かではありません。しかし、セーブポイントやセーブポイントでは、私の属性は忘却に消えてしまいます。

私はそれを入力した後、私自身の質問に半分答えたと思います。インスタンス変数を設定し、method_missingメソッドの最初に存在するかどうかを確認する必要があります。起こっていることは分かりませんが、私がやろうとしていることをやるより良い方法があるかどうかを質問しています。

method_missingを使用していないと悪い考えがある場合は、方法の欠如に関する記事を読むと、なぜ人々がそれを叩いたと聞いたのですが、その人の誰かが、方法の欠如が悪い解決策。

ありがとうございます。

答えて

2

これは、method_missing部門で行われるいくつかの深刻なプログラミングです。あなたが持っている必要があるものは、次のようなものです。

def method_missing(name, *args) 
    if (method_name = dynamic_attribute_method_for(name)) 
    method_name.send(*args) 
    else 
    super 
    end 
end 

これを2つの部分に分けて試してみましょう。最初は、特定の名前(ここではdynamic_attribute_method_for)のコールを処理できるかどうかを判断するメソッドを作成し、2番目は問題の実際のメソッドです。前者の仕事は、後で同じメソッド名にアクセスしたときにすべてをやり直す必要がないように、おそらくdefine_methodを使用して、呼び出された時点で後者の動作を保証することです。

方法は次のようになります。私は構造が明確ではない、それはあなたのアプリケーションのコンテキストに大きく依存しているようだとして、あなたの方法で何が起こっているのかわかりません

def dynamic_attribute_method_for(name) 
    dynamic_attributes = ... 

    type = :reader 

    attribute_name = name.to_s.sub(/=$/) do 
    type = :writer 
    '' 
    end 

    unless (dynamic_attributes.include?(attribute_name)) 
    return 
    end 

    case (type) 
    when :writer 
    define_method(name) do |value| 
     # Whatever you need 
    end 
    else 
    define_method(name) do 
     # Whatever you need 
    end 
    end 

    name 
end 

に。

デザインの観点から見ると、この機能をすべてカプセル化した特別な目的のラッパークラスを作る方が簡単です。代わりにobject.attribute_nameを呼び出すこの場合にはdynamic_attributesをオンデマンドで作成された場合、あなたはobject.dynamic_attributes.attribute_nameを呼びたい:

def dynamic_attributes 
    @dynamic_attributes ||= DynamicAccessor.new(self) 
end 

そのオブジェクトが初期化されるときには、必要とされるどんな方法で自分自身-のconfigureを事前にされますし、対処する必要はありません。この方法は欠けている。私が把握できたようなものと同じ種類のが、問題を抱え、限り問題/ソリューションをやろうとしている他の誰のために

+0

レスポンスのおかげで、私はそれが上記のコードを経由して一人で作業を取得することができませんでしたが、私は答えとして共有するための – thrice801

0

は以下の通りであった。

1)私は、次のコードは動作するだろうと思ったものの:

self.project_category_field_value.send("field_#{field.id}".to_sym, *args) 

これは、毎回関連するモデルの新しいインスタンスを返すため、失われていました。

2)関連するモデルを保存しないため、関連するオブジェクトを手動で保存する必要があります。フラグが存在する場合、モデルにフラグを設定し、関連モデルを保存するコールバックを追加しました。

case(type) 

when :writer 
    self.update_dynamic_attributes=(true) 
    etc...... 

、その後、コールバック、

before_update :update_dynamic_attributes, :if => :update_dynamic_attributes? 

    def update_dynamic_attributes? 
    instance_variable_get("@update_dynamic_attributes") 
    end 

    def update_dynamic_attributes=(val) 
    instance_variable_set("@update_dynamic_attributes",val) 
    end 

    def update_dynamic_attributes 
    self.project_category_field_value.save 
    end 

3)戻る#1に、最大の問題は、新しいオブジェクトのインスタンスが毎回返されてありました。私はこの質問をする前にdefine_methodメソッドを使ってみましたが、それは私のためには機能しませんでした。 - ソリューションは、私はむしろ愚か感じましたが、イム必ず他の人が同様にそれに実行されますので、あなたがアクティブレコードクラス内で直接define_method使用している場合、あなたは

self.class.send(:define_method, name) 

代わりの

呼び出すことを確認してください
self.send(:define_method, name) 

か、:(とき、それは何のworky

3

私は、関連するモデル EAV with ActiveRecord

にメソッドを委譲する方法を説明したところあなたは私のプレゼンテーションを見ることができなくなります

たとえば、STI製品モデルを使用し、属性モデルを関連付けました。まず

は、我々は、すべて私たちの属性モデルは、このクラスから継承される抽象属性モデル

class Attribute < ActiveRecord::Base 
    self.abstract_class = true 
    attr_accessible :name, :value 
    belongs_to :entity, polymorphic: true, touch: true, autosave: true 
end 

を作成します。

class IntegerAttribute < Attribute 
end 

class StringAttribute < Attribute 
end 

今、私たちは私たちは、関連するモデルへのプロキシ方法および属性メソッドを生成.eav方法である#eav_attr_modelメソッドを追加しましたベース製品クラスに

class Product < ActiveRecord::Base 
    %w(string integer float boolean).each do |type| 
    has_many :"#{type}_attributes", as: :entity, autosave: true, dependent: :delete_all 
    end 

    def eav_attr_model(name, type) 
    attributes = send("#{type}_attributes") 
    attributes.detect { |attr| attr.name == name } || attributes.build(name: name) 
    end 

    def self.eav(name, type) 
    attr_accessor name 

    attribute_method_matchers.each do |matcher| 
     class_eval <<-EOS, __FILE__, __LINE__ + 1 
     def #{matcher.method_name(name)}(*args) 
      eav_attr_model('#{name}', '#{type}').send :#{matcher.method_name('value')}, *args 
     end 
     EOS 
    end 
    end 
end 

を記述する必要があります。

それはすべてです。今、私たちは製品クラスから継承されている当社の製品モデルを作成することができます。

class SimpleProduct < Product 
    attr_accessible :name 

    eav :code, :string 
    eav :price, :float 
    eav :quantity, :integer 
    eav :active, :boolean 
end 

使用法:

SimpleProduct.create(code: '#1', price: 2.75, quantity: 5, active: true) 
product = SimpleProduct.find(1) 
product.code  # "#1" 
product.price # 2.75 
product.quantity # 5 
product.active? # true 

product.price_changed? # false 
product.price = 3.50 
product.code_changed? # true 
product.code_was  # 2.75 

あなたは、実行時に属性を作成するか、を実装して、私の宝石hydra_attributeを見ることができるデータを得るために、照会メソッドを使用することができますAAより複雑なソリューションを必要とする場合active_recordモデルのEAV

+0

感謝を受け入れたので、私はリファクタリングにそれのいくつかを使用していました。私はちょうどEAVモデルを手に入れようとしています。これが役に立ちました。 –