Взлом ActiveRecord: добавить глобальную именованную область - PullRequest
9 голосов
/ 30 сентября 2010

Я пытаюсь получить пакет очень общих именованных областей для моделей ActiveRecord, подобных этой:

module Scopes
  def self.included(base)
    base.class_eval do
      named_scope :not_older_than, lambda {|interval|
        {:conditions => ["#{table_name}.created_at >= ?", interval.ago]
      }
    end
  end
end
ActiveRecord::Base.send(:include, Scopes)

class User < ActiveRecord::Base
end

Если именованная область должна быть общей, нам нужно указать * table_name *, чтобы предотвратить проблемы с именамиесли это соединения, которые пришли из другой цепочки именованных областей.

Проблема в том, что мы не можем получить table_name, потому что он вызывается в ActiveRecord :: Base, а не в User.

User.not_older_than(1.week)

NoMethodError: undefined method `abstract_class?' for Object:Class
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:2207:in `class_of_active_record_descendant'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1462:in `base_class'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1138:in `reset_table_name'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1134:in `table_name'
from /home/bogdan/makabu/railsware/startwire/repository/lib/core_ext/active_record/base.rb:15:in `included'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:92:in `call'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:92:in `named_scope'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:97:in `call'
from /var/lib/gems/1.8/gems/activerecord-2.3.5/lib/active_record/named_scope.rb:97:in `not_older_than'

Как получить фактическое имя_таблицы в модуле Scopes?

Ответы [ 4 ]

14 голосов
/ 30 сентября 2010

Попробуйте использовать метод #scoped внутри метода класса ActiveRecord :: Base.Это должно работать:

module Scopes
  def self.included(base)
    base.class_eval do
      def self.not_older_than(interval)
        scoped(:conditions => ["#{table_name}.created_at > ?", interval.ago])
      end
    end
  end
end

ActiveRecord::Base.send(:include, Scopes)
4 голосов
/ 28 февраля 2018

Rails 5, ApplicationRecord (надеюсь, это поможет другим)

# app/models/concerns/not_older_than.rb

module NotOlderThan
  extend ActiveSupport::Concern

  included do
    scope :not_older_than, -> (time, table = self.table_name){ 
      where("#{table}.created_at >= ?", time.ago)
    }
  end
end

# app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  include NotOlderThan
end

# app/models/user.rb
class User < ApplicationRecord
  # Code
end

# Usage
User.not_older_than(1.week)

В Rails 5 все модели по умолчанию наследуются от ApplicationRecord. Если вы хотите применить эту область только для определенного набора моделей, добавьте операторы include только для этих классов моделей. Это также работает для запросов на соединение и связанных областей.

1 голос
/ 22 сентября 2015

Вот обновленное, Rails4 совместимое решение.
Мне говорят, что определение глобальных областей видимости может привести к конфликтам, caveat emptor и все такое, но иногда вам просто нужен простой охват для всех ваших моделей, верно?

Определить модуль.

# in /app/models/concerns/global_scopes.rb
module GlobalScopes
  def self.included(base)
    base.class_eval do
      def self.in_daterange(start_date, end_date)
        all.where(created_at: start_date.to_date.beginning_of_day..end_date.to_date.end_of_day)
      end
    end
  end
end

Включите модуль в ActiveRecord::Base.

# in /config/initializers/activerecord.rb
ActiveRecord::Base.send(:include, GlobalScopes)

Вот и все! Обратите внимание, что в Rails4 вам не нужно связываться с: scoped, но вместо этого вы используете: all и связываете свой запрос с ним.

1 голос
/ 24 апреля 2014

Дополнительные полезные области ниже:

module Scopes
  def self.included(base)
    base.class_eval do
      def self.created(date_start, date_end = nil)
          if date_start && date_end
            scoped(:conditions => ["#{table_name}.created_at >= ? AND #{table_name}.created_at <= ?", date_start, date_end])
          elsif date_start
            scoped(:conditions => ["#{table_name}.created_at >= ?", date_start])
          end
      end
      def self.updated(date_start, date_end = nil)
          if date_start && date_end
            scoped(:conditions => ["#{table_name}.updated_at >= ? AND #{table_name}.updated_at <= ?", date_start, date_end])
          elsif date_start
            scoped(:conditions => ["#{table_name}.updated_at >= ?", date_start])
          end
      end
    end
  end
end

ActiveRecord::Base.send(:include, Scopes)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...