रेल 4 में चिंताओं का उपयोग कैसे करें


628

डिफ़ॉल्ट रेल 4 प्रोजेक्ट जनरेटर अब नियंत्रक और मॉडल के तहत निर्देशिका "चिंताओं" का निर्माण करता है। मैंने रूटिंग चिंताओं का उपयोग करने के बारे में कुछ स्पष्टीकरण पाया है, लेकिन नियंत्रकों या मॉडल के बारे में कुछ भी नहीं।

मुझे पूरा यकीन है कि इसे समुदाय में वर्तमान "DCI प्रवृत्ति" के साथ करना होगा और इसे एक कोशिश देना होगा।

सवाल यह है कि मैं इस सुविधा का उपयोग करने वाला कैसे हूं, क्या यह नामकरण / वर्ग पदानुक्रम को परिभाषित करने के लिए एक कन्वेंशन है ताकि इसे काम किया जा सके? मैं किसी मॉडल या नियंत्रक में चिंता कैसे शामिल कर सकता हूं?

जवाबों:


617

तो मैंने इसे अपने आप से बाहर पाया। यह वास्तव में एक बहुत ही सरल लेकिन शक्तिशाली अवधारणा है। यह कोड पुन: उपयोग के साथ करना है जैसा कि नीचे दिए गए उदाहरण में है। मूल रूप से, मॉडल को साफ करने और उन्हें बहुत अधिक वसा और गन्दा होने से बचाने के लिए कोड के सामान्य और / या संदर्भ विशिष्ट खंड निकालने का विचार है।

एक उदाहरण के रूप में, मैं एक प्रसिद्ध पैटर्न, टैग करने योग्य पैटर्न रखूँगा:

# app/models/product.rb
class Product
  include Taggable

  ...
end

# app/models/concerns/taggable.rb
# notice that the file name has to match the module name 
# (applying Rails conventions for autoloading)
module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, as: :taggable
    has_many :tags, through: :taggings

    class_attribute :tag_limit
  end

  def tags_string
    tags.map(&:name).join(', ')
  end

  def tags_string=(tag_string)
    tag_names = tag_string.to_s.split(', ')

    tag_names.each do |tag_name|
      tags.build(name: tag_name)
    end
  end

  # methods defined here are going to extend the class, not the instance of it
  module ClassMethods

    def tag_limit(value)
      self.tag_limit_value = value
    end

  end

end

इसलिए उत्पाद के नमूने का पालन करते हुए, आप अपनी इच्छा के अनुसार किसी भी वर्ग में टैगगेबल जोड़ सकते हैं और उसकी कार्यक्षमता साझा कर सकते हैं।

यह DHH द्वारा बहुत अच्छी तरह से समझाया गया है :

रेल 4 में, हम प्रोग्रामर को डिफ़ॉल्ट ऐप / मॉडल / चिंताओं और ऐप / कंट्रोलर / चिंताओं निर्देशिकाओं के साथ चिंताओं का उपयोग करने के लिए आमंत्रित करने जा रहे हैं जो स्वचालित रूप से लोड पथ का हिस्सा हैं। ActiveSupport :: कंसर्न रैपर के साथ मिलकर, इस लाइट-वेट फैक्टरिंग मेकेनिज्म को चमकदार बनाने के लिए बस पर्याप्त सपोर्ट है।


11
DCI एक संदर्भ के साथ काम करता है, एक मानसिक मॉडल / उपयोग के मामले को कोड करने के लिए पहचानकर्ता के रूप में Roles का उपयोग करता है, और उपयोग करने के लिए किसी रैपर की आवश्यकता नहीं होती है (विधियाँ सीधे रनटाइम पर ऑब्जेक्ट से बंधी होती हैं) इसलिए इसका DCI से कोई संबंध नहीं है।
5

2
@yagooar रनटाइम पर भी इसे DCI नहीं बना सकेगा। यदि आप एक रूबी DCI उदाहरण कार्यान्वयन देखना चाहते हैं। या तो fulloo.info या github.com/runefs/Moby के उदाहरण देखें या रूबी में DCI करने के लिए मैरून का उपयोग कैसे करें और DCI क्या है runefs.com (क्या DCI है।) पोस्ट की एक श्रृंखला है। अभी हाल ही में शुरू)
Rune FS

1
@RuneFS && ciscoheat आप दोनों सही थे। मैंने सिर्फ लेखों और तथ्यों का फिर से विश्लेषण किया। और, मैं पिछले सप्ताह के अंत में एक रूबी सम्मेलन में गया था जहाँ एक बात डीसीआई के बारे में थी और आखिरकार मैं इसके दर्शन के बारे में थोड़ा और समझ गया। पाठ को बदल दिया ताकि यह डीसीआई का उल्लेख न करे।
yagooar

9
यह ध्यान देने योग्य है (और शायद एक उदाहरण सहित) कि वर्ग विधियों को एक विशेष रूप से नामित मॉड्यूल क्लासमेथोड्स में परिभाषित किया जाना चाहिए, और यह कि बेस बेस द्वारा विस्तारित यह मॉड्यूल ActiveSupport :: Concern भी है।
फेबेलिंग

1
इस उदाहरण के लिए धन्यवाद, मुख्य रूप से b / c मैं गूंगा हुआ जा रहा था और ClassMethods मॉड्यूल के अंदर अपने वर्ग स्तर के तरीकों को स्वयं के साथ परिभाषित कर रहा था। अभी भी, और यह काम नहीं करता है = P
रयान क्रू

379

मैं मॉडल के बारे में त्वचा-वसा के मॉडल के साथ-साथ आपके मॉडल कोड की DRY को पढ़ रहा हूँ । यहाँ उदाहरण के साथ एक स्पष्टीकरण है:

1) मॉडल कोडों को पूरा करना

एक अनुच्छेद मॉडल, एक घटना मॉडल और एक टिप्पणी मॉडल पर विचार करें। एक लेख या एक घटना में कई टिप्पणियां हैं। एक टिप्पणी या तो अनुच्छेद या घटना के अंतर्गत आती है।

परंपरागत रूप से, मॉडल इस तरह दिख सकते हैं:

टिप्पणी मॉडल:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

लेख मॉडल:

class Article < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #return the article with least number of comments
  end
end

इवेंट मॉडल

class Event < ActiveRecord::Base
  has_many :comments, as: :commentable 

  def find_first_comment
    comments.first(created_at DESC)
  end

  def self.least_commented
   #returns the event with least number of comments
  end
end

जैसा कि हम देख सकते हैं, इवेंट और आर्टिकल दोनों के लिए कॉमन कॉमन का एक महत्वपूर्ण हिस्सा है। चिंताओं का उपयोग करके हम एक अलग मॉड्यूल में इस सामान्य कोड को निकाल सकते हैं।

इसके लिए ऐप / मॉडल / सरोकारों में एक टिप्पणी करने योग्य.आरबी फ़ाइल बनाएँ।

module Commentable
  extend ActiveSupport::Concern

  included do
    has_many :comments, as: :commentable
  end

  # for the given article/event returns the first comment
  def find_first_comment
    comments.first(created_at DESC)
  end

  module ClassMethods
    def least_commented
      #returns the article/event which has the least number of comments
    end
  end
end

और अब आपके मॉडल इस तरह दिखते हैं:

टिप्पणी मॉडल:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

लेख मॉडल:

class Article < ActiveRecord::Base
  include Commentable
end

इवेंट मॉडल:

class Event < ActiveRecord::Base
  include Commentable
end

2) त्वचा-वसा वसा मॉडल।

एक इवेंट मॉडल पर विचार करें। एक घटना में कई परिचारक और टिप्पणियां हैं।

आमतौर पर, इवेंट मॉडल इस तरह दिख सकता है

class Event < ActiveRecord::Base   
  has_many :comments
  has_many :attenders


  def find_first_comment
    # for the given article/event returns the first comment
  end

  def find_comments_with_word(word)
    # for the given event returns an array of comments which contain the given word
  end 

  def self.least_commented
    # finds the event which has the least number of comments
  end

  def self.most_attended
    # returns the event with most number of attendes
  end

  def has_attendee(attendee_id)
    # returns true if the event has the mentioned attendee
  end
end

कई संघों वाले मॉडल और अन्यथा अधिक से अधिक कोड जमा करने और असहनीय बनने की प्रवृत्ति है। चिंताएँ त्वचा-वसा के वसा मॉड्यूल का एक तरीका प्रदान करती हैं जिससे वे अधिक सुव्यवस्थित और समझने में आसान हो जाते हैं।

उपरोक्त मॉडल को नीचे दी गई चिंताओं का उपयोग करके फिर से बनाया जा सकता है: एप्लिकेशन / मॉडल / चिंताओं / ईवेंट फ़ोल्डर में एक attendable.rbऔर commentable.rbफ़ाइल बनाएं

attendable.rb

module Attendable
  extend ActiveSupport::Concern

  included do 
    has_many :attenders
  end

  def has_attender(attender_id)
    # returns true if the event has the mentioned attendee
  end

  module ClassMethods
    def most_attended
      # returns the event with most number of attendes
    end
  end
end

commentable.rb

module Commentable
  extend ActiveSupport::Concern

  included do 
    has_many :comments
  end

  def find_first_comment
    # for the given article/event returns the first comment
  end

  def find_comments_with_word(word)
    # for the given event returns an array of comments which contain the given word
  end

  module ClassMethods
    def least_commented
      # finds the event which has the least number of comments
    end
  end
end

और अब चिंता का उपयोग करते हुए, आपका ईवेंट मॉडल कम हो जाता है

class Event < ActiveRecord::Base
  include Commentable
  include Attendable
end

* जबकि 'तकनीकी' समूहीकरण के बजाय 'डोमेन' आधारित ग्रुपिंग के लिए जाने की सलाह दी जाती है। डोमेन आधारित ग्रुपिंग 'कमेंटेबल', 'फोटोबल', 'अटेंडेबल' की तरह है। टेक्निकल ग्रुपिंग का अर्थ होगा 'वैलिडेशनमेथोड्स', 'फाइंडरमैथोड्स' आदि


6
तो चिंता सिर्फ विरासत या इंटरफेस या कई विरासत का उपयोग करने का एक तरीका है? एक सामान्य आधार वर्ग बनाने और उस सामान्य आधार वर्ग से उपवर्ग बनाने में क्या गलत है?
च्लोए

3
वास्तव में @ च्लोए, मैं कुछ जहां लाल, 'चिंताओं' निर्देशिका के साथ एक रेल एप्लिकेशन वास्तव में एक 'चिंता का विषय' है ...
Ziyan Junaideen

आप अपने सभी तरीकों को परिभाषित करने के लिए 'शामिल' ब्लॉक का उपयोग कर सकते हैं और इसमें शामिल हैं: वर्ग तरीके ( def self.my_class_methodउदाहरण के लिए), उदाहरण के तरीके और विधि कॉल और निर्देशांक वर्ग के दायरे में। इसके लिए कोई ज़रूरत नहीं हैmodule ClassMethods
एक फैदर डार्कली

1
मेरे पास समस्याओं के साथ समस्या यह है कि वे सीधे मॉडल में कार्यक्षमता जोड़ते हैं। इसलिए अगर दो चिंताएं लागू होती हैं add_item, उदाहरण के लिए, आप खराब हैं। मुझे याद है कि जब कुछ मान्यताओं ने काम करना बंद कर दिया था, तब रेल टूट गई थी, लेकिन किसी ने any?चिंता में लागू कर दिया था । मैं एक अलग समाधान प्रस्तावित करता हूं: एक अलग भाषा में एक इंटरफ़ेस की तरह चिंता का उपयोग करें। कार्यक्षमता को परिभाषित करने के बजाय, यह एक अलग वर्ग उदाहरण के संदर्भ को परिभाषित करता है जो उस कार्यक्षमता को संभालता है। तब आपके पास छोटे, नट वर्ग होते हैं जो एक काम करते हैं ...
ए फादर डार्कली

@aaditi_jain: गलतफहमी से बचने के लिए कृपया छोटे परिवर्तन को सही करें। "" एप्लिकेशन / मॉडल / चिंताओं / ईवेंट फ़ोल्डर में एक attendable.rd और commentable.rb फ़ाइल बनाएँ "-> attendable.rd को attendable.rb होना चाहिए
Rubyist

97

यह उल्लेख के लायक है कि चिंताओं का उपयोग करना कई लोगों द्वारा बुरा विचार माना जाता है।

  1. इस आदमी की तरह
  2. और ये वाला

कुछ कारणों से:

  1. पर्दे के पीछे कुछ काला जादू हो रहा है - चिंता पैचिंग includeविधि है, एक पूरी निर्भरता हैंडलिंग प्रणाली है - जिस तरह से कुछ अच्छे पुराने रूबी मिश्रण पैटर्न के लिए बहुत अधिक जटिलता है।
  2. आपकी कक्षाएं कम सूखी नहीं हैं। यदि आप 50 सार्वजनिक विधियों को विभिन्न मॉड्यूल में शामिल करते हैं और उन्हें शामिल करते हैं, तो आपकी कक्षा में अभी भी 50 सार्वजनिक विधियां हैं, यह सिर्फ इतना है कि आप उस कोड गंध को छिपाते हैं, जैसे कि अपना कचरा दराज में डालते हैं।
  3. कोडबेस वास्तव में उन सभी चिंताओं के साथ नेविगेट करने के लिए कठिन है।
  4. क्या आप सुनिश्चित हैं कि आपकी टीम के सभी सदस्यों को समान समझ है कि वास्तव में चिंता का विकल्प क्या होना चाहिए?

चिंताएं अपने आप को पैर में गोली मारने का आसान तरीका है, उनसे सावधान रहें।


1
मुझे पता है कि एसओ इस चर्चा के लिए सबसे अच्छी जगह नहीं है, लेकिन अन्य प्रकार के रूबी मिश्रण आपकी कक्षाओं को सूखा रखते हैं? ऐसा लगता है जैसे आपके तर्क में # 1 और # 2 कारण काउंटर हैं, जब तक कि आप केवल बेहतर ओओ डिजाइन, सेवाओं की परत, या कुछ और के लिए मामला नहीं बना रहे हैं जो मैं याद कर रहा हूं? (मैं असहमत नहीं हूं - मैं सुझाव दे रहा हूं कि विकल्प जोड़ना मदद करता है!)
toobulkeh

2
का उपयोग करते हुए github.com/AndyObtiva/super_module एक विकल्प है, एक और एक अच्छे पुराने ClassMethods पैटर्न का उपयोग कर रहा है। और अलग-अलग वस्तुओं (सेवाओं की तरह) का उपयोग करके अलग-अलग चिंताओं को साफ करना निश्चित रूप से जाने का तरीका है।
Dr.Strangelove

4
डाउनवोटिंग क्योंकि यह सवाल का जवाब नहीं है। यह एक राय है। यह एक राय है कि मुझे यकीन है कि यह योग्यता है, लेकिन यह StackOverflow पर एक सवाल का जवाब नहीं होना चाहिए।
एडम

2
@ एडम यह एक राय है। कल्पना कीजिए कि कोई व्यक्ति पूछेगा कि रेल में वैश्विक चर का उपयोग कैसे किया जाता है, निश्चित रूप से उल्लेख है कि चीजों को करने के बेहतर तरीके हैं (यानी Redis.current बनाम $ redis) विषय स्टार्टर के लिए उपयोगी जानकारी हो सकती है? सॉफ्टवेयर विकास स्वाभाविक रूप से एक राय वाला अनुशासन है, इसके आसपास कोई नहीं है। वास्तव में, मैं राय को उन उत्तरों और चर्चाओं के रूप में देखता हूं, जिनका उत्तर
स्टैकओवरफ्लो

2
ज़रूर, सवाल के अपने जवाब के साथ इसका उल्लेख करना ठीक लगता है। आपके जवाब में कुछ भी वास्तव में ओपी के सवाल का जवाब नहीं देता है। अगर आप चाहते हैं कि किसी को चेतावनी दी जाए कि वे चिंताओं या वैश्विक चर का उपयोग क्यों न करें, तो यह एक अच्छी टिप्पणी के लिए करेगा जिसे आप उनके प्रश्न में जोड़ सकते हैं, लेकिन यह वास्तव में एक अच्छे उत्तर के लिए नहीं है।
एडम

56

इस पोस्ट ने मुझे चिंताओं को समझने में मदद की।

# app/models/trader.rb
class Trader
  include Shared::Schedule
end

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end

1
इंटरनेट आर्काइव संस्करण: web.archive.org/web/20130712014326/http://blog.andywaite.com/…
MZB

1
यह उत्तर कुछ भी स्पष्ट नहीं करता है।

46

मैंने महसूस किया कि यहाँ के अधिकांश उदाहरणों ने मूल्य moduleको ActiveSupport::Concernजोड़ने की अपेक्षा शक्ति का प्रदर्शन किया module

उदाहरण 1: अधिक पठनीय मॉड्यूल।

तो इस चिंता के बिना कि एक ठेठ कैसे moduleहोगा।

module M
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  def instance_method
    ...
  end

  module ClassMethods
    ...
  end
end

के साथ refactoring के बाद ActiveSupport::Concern

require 'active_support/concern'

module M
  extend ActiveSupport::Concern

  included do
    scope :disabled, -> { where(disabled: true) }
  end

  class_methods do
    ...
  end

  def instance_method
    ...
  end
end

आप उदाहरण के तरीकों, वर्ग विधियों और शामिल ब्लॉक को कम गन्दा देखते हैं। चिंताएँ उन्हें उचित रूप से आपके लिए इंजेक्ट करेंगी। यह एक फायदा है ActiveSupport::Concern


उदाहरण 2: संभालो मॉड्यूल निर्भरता इनायत से।

module Foo
  def self.included(base)
    base.class_eval do
      def self.method_injected_by_foo_to_host_klass
        ...
      end
    end
  end
end

module Bar
  def self.included(base)
    base.method_injected_by_foo_to_host_klass
  end
end

class Host
  include Foo # We need to include this dependency for Bar
  include Bar # Bar is the module that Host really needs
end

इस उदाहरण Barमें वह मॉड्यूल है जिसकी Hostवास्तव में आवश्यकता है। लेकिन चूंकि Barसाथ निर्भरता है वर्ग के लिए है (लेकिन इंतजार क्यों करता है के बारे में जानना चाहते हैं ? इसे टाला जा सकता है?)।FooHostinclude FooHostFoo

इसलिए Barनिर्भरता हर जगह जाती है। और समावेश का आदेश भी यहां मायने रखता है। यह विशाल कोड आधार पर बहुत अधिक जटिलता / निर्भरता जोड़ता है।

के साथ refactoring के बाद ActiveSupport::Concern

require 'active_support/concern'

module Foo
  extend ActiveSupport::Concern
  included do
    def self.method_injected_by_foo_to_host_klass
      ...
    end
  end
end

module Bar
  extend ActiveSupport::Concern
  include Foo

  included do
    self.method_injected_by_foo_to_host_klass
  end
end

class Host
  include Bar # It works, now Bar takes care of its dependencies
end

अब यह आसान लग रहा है।

अगर आप सोच रहे हैं कि हम मॉड्यूल में ही Fooनिर्भरता क्यों नहीं जोड़ सकते Bar? यह काम नहीं करेगा क्योंकि method_injected_by_foo_to_host_klassएक ऐसे वर्ग में इंजेक्शन लगाया जाना चाहिए जो मॉड्यूल Barपर Barही नहीं है।

स्रोत: रेल्स एक्टिव: चिंता


उसके लिए धन्यवाद। मुझे आश्चर्य होने लगा था कि उनका क्या फायदा है ...
हरि करम सिंह

FWIW यह डॉक्स से लगभग कॉपी-पेस्ट है ।
डेव न्यूटन

7

चिंताओं में फ़ाइल का नाम फ़ाइल करें

उदाहरण के लिए, मैं अपने आवेदन में चाहता हूं जहां विशेषता create_by मौजूद है वहां 1 से मान अपडेट करें, और 0 के लिए update_by

module TestConcern 
  extend ActiveSupport::Concern

  def checkattributes   
    if self.has_attribute?(:created_by)
      self.update_attributes(created_by: 1)
    end
    if self.has_attribute?(:updated_by)
      self.update_attributes(updated_by: 0)
    end
  end

end

यदि आप कार्रवाई में तर्क पारित करना चाहते हैं

included do
   before_action only: [:create] do
     blaablaa(options)
   end
end

उसके बाद इस तरह से अपने मॉडल में शामिल करें:

class Role < ActiveRecord::Base
  include TestConcern
end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.