2012-04-05 14 views
6

तो, सोचा कि मैंने कल रात यह काम किया था, इसे शपथ ली थी। अब यह कोई काम नहीं है, और मुझे मदद के लिए समय के बारे में पता चलता है।रेलवे पर गतिशील विशेषता फ़ील्ड डीबी से method_missing समस्याओं का उपयोग कर

डेटाबेस, अर्द्ध EAV शैली में

इम परिभाषित गतिशील क्षेत्र, और की सुविधा देता है बस अभी राज्य मैं ध्यान न चाहे EAV एक अच्छा विचार है पर अपनी राय सुनने के लिए है या नहीं :)

फिर भी मैं यह एक छोटे कर अतीत में किए गए Ive से अलग, मूल रूप से जब कोई विशेषता (या फ़ील्ड) जोड़ा जाता है, तो मैं किसी विशेष विशेषता तालिका माइग्रेशन में एक एड कॉलम बनाता हूं और इसे चलाता हूं (या इसे हटा देता हूं) - किसी भी तरह, क्योंकि एक श्रेणी परत है बीच में बैठे जो प्रत्यक्ष संबंध हैं जहां सभी विशेषताओं को परिभाषित किया गया है, मैं वास्तविक विशेषता नाम को कॉलम नाम के रूप में उपयोग नहीं कर सकता, क्योंकि गुण श्रेणी विशिष्ट हैं।

इसलिए, यह आप

Entity 
    belongs_to :category 

    Category 
    has_many :entities 

    EntityAttribute 
    belongs_to :category 

    EntityAttributeValue 
    belongs_to :entity_attribute 
    belongs_to :entity   

कल्पना में मदद करता है और EAV तालिका क्षैतिज तक फैला के रूप में नई विशेषताओं लेबल attribute_1 attribute_2 कॉलम है, जो कि विशेष रूप से इकाई के लिए मान होने के साथ बनाया जाता है, अगर।

फिर भी - मैं तरीकों इकाई मॉडल पर गतिशील बनाने के लिए कोशिश कर रहा हूँ, इसलिए मैं नहीं बल्कि

यहाँ entity.entity_attribute_value.field_5 @ से @ entity.actual_attribute_name कॉल कर सकते हैं, कोड मैंने सोचा था कि काम कर रहा था है - -

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 के रूप में प्रतिनिधित्व किया गया है।)

तो इसे देखने के बाद और ऐसा लगता है कि मुझे पहले से ही एप्यूपेट जोड़ना था या पहले से ही विशेषता को सहेजने के लिए कॉलबैक को पहले से सहेजना था, और जहां मैंने देखा, कॉलबैक में, यह method_missing कॉलबैक चलाएगा, जैसे कि ऑब्जेक्ट था डुप्लीकेट किया जा रहा है (और नई वस्तु मूल वस्तु की प्रति थी), या कुछ, मुझे पूरा यकीन नहीं है। लेकिन कुछ समय पर सहेजने की प्रक्रिया या पहले, मेरी विशेषता विस्मरण में गायब हो जाती है।

तो, मुझे लगता है कि मैंने इसे टाइप करने के बाद अपने स्वयं के प्रश्न का आधा रास्ता जवाब दिया है, मुझे एक इंस्टेंस वैरिएबल सेट करने की आवश्यकता है और यह देखने के लिए जांचें कि क्या यह मेरी method_missing विधि (दाएं?) की शुरुआत में मौजूद है या नहीं, शायद यह नहीं है ऐसा नहीं हो रहा है, लेकिन मैं यह भी पूछ रहा हूं कि क्या करने का एक बेहतर तरीका है जो मैं करने की कोशिश कर रहा हूं।

और यदि विधि_मissing का उपयोग करना एक बुरा विचार है, तो कृपया बताएं कि क्यों, गायब विधि के बारे में पदों के माध्यम से मैंने कुछ लोगों को यह कहते हुए सुना है, लेकिन उन लोगों में से एक ने उचित स्पष्टीकरण की पेशकश करने पर परेशान नहीं किया क्योंकि विधि गायब क्यों खराब समाधान था ।

अग्रिम धन्यवाद।

उत्तर

2

यह method_missing विभाग में कुछ गंभीरता से गहन प्रोग्रामिंग चल रहा है। आपके पास क्या होना चाहिए:

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

आप इसे दो हिस्सों में आजमा सकते हैं और इसे तोड़ सकते हैं। पहला एक तरीका बना रहा है जो यह तय करता है कि यह किसी दिए गए नाम के साथ कॉल को संभाल सकता है, यहां dynamic_attribute_method_for, और दूसरा प्रश्न में वास्तविक विधि है।पूर्व का काम यह सुनिश्चित करना है कि बाद में काम करता है, संभवतः 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 बुलाने की आप object.dynamic_attributes.attribute_name फोन चाहते हैं जहां इस मामले में dynamic_attributes मांग पर बनाई गई है:

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

जब उस वस्तु आरंभ नहीं हो जाता यह अपने आप में जो कुछ तरीकों के लिए आवश्यक हैं के साथ कॉन्फ़िगर पूर्व जाएगा और आप निपटने के लिए की ज़रूरत नहीं होगी इस विधि के साथ सामान गुम है।

+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

उदाहरण के लिए हम एसटीआई हमारे उत्पाद मॉडल के लिए उपयोग करते हैं और हमने उनके लिए गुणों को जोड़ा है।

सबसे पहले हम सार गुण मॉडल

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 

अब हम आधार उत्पाद वर्ग

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 

तो हम जोड़ा #eav_attr_model विधि है जो एक प्रॉक्सी हमारे संबद्ध मॉडल के लिए विधि और .eav विधि है जो विशेषता तरीकों उत्पन्न करता है वर्णन करने के लिए की जरूरत है ।

यह सब कुछ है। अब हम अपने उत्पाद मॉडल बना सकते हैं जिन्हें उत्पाद कक्षा से विरासत में मिला है।

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 देख सकते हैं जो लागू करता है प्राप्त करने के लिए क्वेरी तरीकों उपयोग करने के लिए अनुमति देता है की जरूरत है Active_record मॉडल के लिए EAV

+0

धन्यवाद के रूप में स्वीकार किया। मैं सिर्फ एक ईएवी मॉडल प्राप्त करने की कोशिश कर रहा हूं, और यह सहायक रहा है। –