Skip to content
cavalle edited this page Dec 23, 2012 · 13 revisions

Version #1

http://weblog.jamisbuck.org/2007/1/17/concerns-in-activerecord

module Folder
  def self.included(base)
    base.has_many :files, :as => :owner, :dependent => :destroy
  end

  # This returns the path where files are to be uploaded
  # for this specific "folder".
  def attachment_path
    "/path/to/files/for/#{id}"
  end
end
class Message < ActiveRecord::Base
  include Folder
  #...
end

class Comment < ActiveRecord::Base
  include Folder
  #...
end

Version #2

module DiscountCalculator
  def total_discount
    basket_items.collect(&:discount).inject(:+)
  end
end
class Basket < ActiveRecord::Base
  has_many :basket_items
  include DiscountCalculator
end

Version #3

http://www.rvdh.de/2011/12/01/splitting-your-rails-classes-into-modules-how-does-it-work/

# app/models/user/authentication.rb
class User
  module Authentication
    extend ActiveSupport::Concern

    included do
      has_secure_password
    end

    def create_password_reset_token
      token = SecureRandom.base64.tr('+/=lIO0', 'zomglol')
      self.update_attribute(:password_reset_token, token)
    end
  end
end
# app/models/user.rb
class User < ActiveRecord::Base
  include Authentication
  include Validations
  # ...
end

Version #4

http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns

module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, as: :taggable, dependent: :destroy
    has_many :tags, through: :taggings 
  end

  def tag_names
    tags.map(&:name)
  end
end
# current_account.posts.visible_to(current_user)
module Visible
  extend ActiveSupport::Concern

  module ClassMethods
    def visible_to(person)
      where \
        "(#{table_name}.bucket_id IN (?) AND
          #{table_name}.bucket_type = 'Project') OR
         (#{table_name}.bucket_id IN (?) AND
          #{table_name}.bucket_type = 'Calendar')",
        person.projects.pluck('projects.id'), 
        calendar_scope.pluck('calendars.id')
    end
  end
end
module Dropboxed
  extend ActiveSupport::Concern

  included do
    before_create :generate_dropbox_key
  end

  def rekey_dropbox
    generate_dropbox_key
    save!
  end

  private
    def generate_dropbox_key
      self.dropbox_key = SignalId::Token.unique(24) do |key| 
        self.class.find_by_dropbox_key(key)
      end
    end
end

Version #5

class User < ActiveRecord::Base
  attr_accessible :email, :username, :password, :password_confirmation

  validates_presence_of :username, :email
  validates_uniqueness_of :username
  validates_confirmation_of :password
  
  include Authentication
  include Searchable
  include CsvConversion
  include Inviter
  include PasswordResettable
end
# app/models/concerns/user/authentication.rb
class User
  module Authentication
    extend ActiveSupport::Concern

    included do
      has_secure_password
    end

    module ClassMethods
      def authenticate(username, password)
        user = find_by_username(username)
        user if user && user.authenticate(password)
      end

      def from_omniauth(auth)
        where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
          user.provider = auth[:provider]
          user.uid = auth[:uid]
          user.username = auth[:info][:nickname]
          user.save!
        end
      end
    end
  end
end

Version #6

# app/models/user.rb
class User < ActiveRecord::Base
  include Tickets
  include ApiKeys

  has_many :events
end
# app/models/user/tickets.rb
module User::Tickets
  extend ActiveSupport::Concern

  included do
    has_many :tickets
  end

  def most_recent_ticket
    # huh, obviously code here
  end

  private
    def recent_tickets

    end
end
# app/models/user/api_keys.rb
module User::ApiKeys
  extend ActiveSupport::Concern

  included do
    after_create :create_api_keys
  end

  private
    def create_api_keys

    end

    def random_key

    end
end

Version #7

# create an extensions directory in models
# app/models/extensions/popular.rb
 
module Extensions
  module Popular
    extend ActiveSupport::Concern
 
    # you can include other things here
    included do
      include Extensions::OtherCoolStuff
      # or if you're using Mongoid you can also add fields
      field :points, :type => Integer, :default => 0
    end
 
    # include class methods here
    # like User.most_popular
    module ClassMethods
      def most_popular(limit=10)
         order_by(:points.desc).limit(limit).all
      end
    end
 
    # include Instance methods
    # like @user.popularity
    module InstanceMethods
      def popularity
        1+(self.points/100)
      end
    end
  end
end
class User
  include Extensions::Popular
end
class Tags
  include Extensions::Popular
end

Clone this wiki locally