Program Tip

Rails 4에서 관심사를 사용하는 방법

programtip 2020. 10. 2. 23:06
반응형

Rails 4에서 관심사를 사용하는 방법


기본 Rails 4 프로젝트 생성기는 이제 컨트롤러 및 모델 아래에 "concerns"디렉토리를 생성합니다. 라우팅 문제를 사용하는 방법에 대한 설명을 찾았지만 컨트롤러 나 모델에 대한 설명은 없습니다.

나는 그것이 커뮤니티의 현재 "DCI 트렌드"와 관련이 있다고 확신하고 그것을 시도하고 싶습니다.

문제는이 기능을 어떻게 사용해야하는지, 작동하도록 이름 지정 / 클래스 계층을 정의하는 방법에 대한 규칙이 있습니까? 모델 또는 컨트롤러에 문제를 어떻게 포함시킬 수 있습니까?


그래서 나는 그것을 스스로 발견했습니다. 실제로 매우 간단하지만 강력한 개념입니다. 아래 예제와 같이 코드 재사용과 관련이 있습니다. 기본적으로 아이디어는 모델을 정리하고 너무 뚱뚱하고 지저분 해지는 것을 방지하기 위해 공통 및 / 또는 컨텍스트 특정 코드 덩어리를 추출하는 것입니다.

예를 들어 잘 알려진 패턴 인 태그 지정 가능 패턴을 넣겠습니다.

# 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

따라서 제품 샘플에 따라 원하는 클래스에 Taggable을 추가하고 기능을 공유 할 수 있습니다.

이것은 DHH에 의해 꽤 잘 설명됩니다 .

Rails 4에서는 자동으로로드 경로의 일부인 기본 app / models / concerns 및 app / controllers / concerns 디렉토리에 대한 문제를 사용하도록 프로그래머를 초대 할 것입니다. ActiveSupport :: Concern 래퍼와 함께이 경량 팩터링 메커니즘을 빛나게하는 데 충분한 지원입니다.


나는 모델 문제사용하여 뚱뚱한 모델을 피부 화하고 모델 코드를 건조시키는 것에 대해 읽었 습니다. 예를 들어 설명은 다음과 같습니다.

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

알 수 있듯이 Event와 Article 모두에 공통된 중요한 코드가 있습니다. 관심사를 사용하여 별도의 모듈 Commentable에서이 공통 코드를 추출 할 수 있습니다.

이를 위해 app / models / concerns에 commentable.rb 파일을 만듭니다.

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

연관성이 많고 그렇지 않은 모델은 점점 더 많은 코드를 축적하고 관리 할 수 ​​없게되는 경향이 있습니다. 우려 사항은 지방 모듈을 피부 화하는 방법을 제공하여 모듈화되고 이해하기 쉽게 만듭니다.

위의 모델은 아래와 같은 관심사를 사용하여 리팩토링 할 수 있습니다. app / models / concerns / event 폴더에 attendable.rbcommentable.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

이제 Concerns를 사용하면 이벤트 모델이

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

* 사용하는 동안 '기술적'그룹화보다는 '도메인'기반 그룹화를 사용하는 것이 좋습니다. 도메인 기반 그룹화는 '주석 가능', '사진 가능', '참석 가능'과 같습니다. 기술 그룹은 'ValidationMethods', 'FinderMethods'등을 의미합니다.


우려 사항을 사용하는 것은 많은 사람들에게 나쁜 생각으로 간주된다는 점을 언급 할 가치가 있습니다.

  1. 이 사람처럼
  2. 그리고 이것

몇 가지 이유 :

  1. 배후에서 어떤 어두운 마법이 일어나고 있습니다. 문제는 패치 include방법이고, 전체 종속성 처리 시스템이 있습니다. 사소하고 좋은 오래된 Ruby 믹스 인 패턴에 비해 너무 복잡합니다.
  2. 수업이 덜 건조하지 않습니다. 다양한 모듈에 50 개의 퍼블릭 메서드를 채우고 포함 시키면 클래스에는 여전히 50 개의 퍼블릭 메서드가 있습니다. 단지 코드 냄새를 숨기고 쓰레기를 서랍에 넣는 것입니다.
  3. 코드베이스는 실제로 이러한 모든 문제를 해결하기가 더 어렵습니다.
  4. 팀의 모든 구성원이 실제로 우려 사항을 대체해야하는 사항을 동일하게 이해하고 있습니까?

우려 사항은 다리를 쏘는 쉬운 방법이므로 조심하십시오.


This post helped me understand concerns.

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

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

I felt most of the examples here demonstrating the power of module rather than how ActiveSupport::Concern adds value to module.

Example 1: More readable modules.

So without concerns this how a typical module will be.

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

After refactoring with 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

You see instance methods, class methods and included block are less messy. Concerns will inject them appropriately for you. That's one advantage of using ActiveSupport::Concern.


Example 2: Handle module dependencies gracefully.

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

In this example Bar is the module that Host really needs. But since Bar has dependency with Foo the Host class have to include Foo (but wait why Host want to know about Foo? can it be avoided?).

So Bar adds dependency everywhere it goes. And **order of inclusion also matters here. **This adds lot of complexity/dependency to huge code base.

After refactoring with 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

Now it looks simple.

If you are thinking why cant we add Foo dependency in Bar module itself ? That wont work since method_injected_by_foo_to_host_klass have to be injected in class thats including Bar not on Bar module itself.

Source: Rails ActiveSupport::Concern


In concerns make file filename.rb

For example I want in my application where attribute create_by exist update there value by 1, and 0 for updated_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

If you want to pass arguments in action

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

after that include in your model like this:

class Role < ActiveRecord::Base
  include TestConcern
end

참고URL : https://stackoverflow.com/questions/14541823/how-to-use-concerns-in-rails-4

반응형