Module: ActsAsSourceable::ActMethod

Defined in:
lib/acts_as_sourceable/acts_as_sourceable.rb

Instance Method Summary collapse

Instance Method Details

#acts_as_sourceable(options = {}) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/acts_as_sourceable/acts_as_sourceable.rb', line 3

def acts_as_sourceable(options = {})
  options.assert_valid_keys :through, :cache_column, :used_by
  raise "Can't have a cache column and be sourced through an association" if options[:through] && options [:cache_column]
  class_attribute :acts_as_sourceable_options
  self.acts_as_sourceable_options = options

  # INSTANCE SETUP
  include ActsAsSourceable::InstanceMethods

  # If we get our sources through an association use that,
  # Else use the sourceable institutions table
  class_eval do
    if acts_as_sourceable_options[:through]
      def sources; send(acts_as_sourceable_options[:through]) || []; end
    else
      has_many :sourceable_registry_entries, :class_name => 'ActsAsSourceable::RegistryEntry', :as => :sourceable, :dependent => :delete_all
      def sources; self.sourceable_registry_entries.includes(:source).collect(&:source); end
    end
  end

  # CLASS SETUP
  extend ActsAsSourceable::ClassMethods

  # If a cache column is provided, use that to determine which records are sourced and unsourced
  # Elsif the records can be derived, we need to check the flattened item tables for any references
  # Else we check the registry_entries to see if the record has a recorded source
  if options[:cache_column]
    scope :sourced,   lambda { where(options[:cache_column] => true) }
    scope :unsourced, lambda { where(options[:cache_column] => false) }
  elsif options[:through]
    scope :sourced,   lambda { from(unscoped.joins(options[:through]).group("#{table_name}.#{primary_key}"), table_name) }
    scope :unsourced, lambda { readonly(false).joins("LEFT OUTER JOIN (#{sourced.to_sql}) sourced ON sourced.id = #{table_name}.id").where("sourced.id IS NULL") }
  else
    scope :sourced,   lambda { from(unscoped.joins(:sourceable_registry_entries).group("#{table_name}.#{primary_key}"), table_name) }
    scope :unsourced, lambda { readonly(false).joins("LEFT OUTER JOIN (#{ActsAsSourceable::RegistryEntry.select('sourceable_id AS id').where(:sourceable_type => self).to_sql}) sourced ON sourced.id = #{table_name}.id").where("sourced.id IS NULL") }
  end

  # Add a way of finding everything sourced by a particular set of records
  if options[:through]
    scope :sourced_by, lambda { |source| readonly(false).joins(options[:through]).where(reflect_on_association(options[:through]).table_name => {:id => source.id}) }
  else
    scope :sourced_by, lambda { |source| readonly(false).joins(:sourceable_registry_entries).where(ActsAsSourceable::RegistryEntry.table_name => {:source_type => source.class, :source_id => source.id}).uniq }
  end

  # Create a scope that returns record that is not used by the associations in options[:used_by]
  if options[:used_by]
    scope :unused,   lambda { where(Array(options[:used_by]).collect {|usage_association| "#{table_name}.id NOT IN (" + select("#{table_name}.id").joins(usage_association).group("#{table_name}.id").to_sql + ")"}.join(' AND ')) }
    scope :used,     lambda { where(Array(options[:used_by]).collect {|usage_association| "#{table_name}.id IN (" + select("#{table_name}.id").joins(usage_association).group("#{table_name}.id").to_sql + ")"}.join(' OR ')) }
    scope :orphaned, lambda { unsourced.unused }
  else
    scope :orphaned, lambda { unsourced }
  end
end