Class: AssociationScope::Scope

Inherits:
Object
  • Object
show all
Defined in:
lib/association_scope/scope.rb,
lib/association_scope/scope/through_reflection.rb,
lib/association_scope/scope/has_many_reflection.rb,
lib/association_scope/scope/belongs_to_reflection.rb,
lib/association_scope/scope/has_and_belongs_to_many_reflection.rb

Defined Under Namespace

Classes: BelongsToReflection, HasAndBelongsToManyReflection, HasManyReflection, ThroughReflection

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model, association) ⇒ Scope

Returns a new instance of Scope.



7
8
9
10
# File 'lib/association_scope/scope.rb', line 7

def initialize(model, association)
  @model = model
  @association = association
end

Instance Attribute Details

#association ⇒ Object (readonly)

Returns the value of attribute association.



5
6
7
# File 'lib/association_scope/scope.rb', line 5

def association
  @association
end

#model ⇒ Object (readonly)

Returns the value of attribute model.



5
6
7
# File 'lib/association_scope/scope.rb', line 5

def model
  @model
end

Class Method Details

.inject_scopes(model, reflections) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/association_scope/scope.rb', line 12

def self.inject_scopes(model, reflections)
  unknown_association = reflections.find { |association| !model.reflections.key?(association) }
  if unknown_association
    raise AssociationMissingError.new(missing_in: model, association: unknown_association)
  end

  generated_scopes = reflections.map(&:pluralize)
  if generated_scopes.uniq.length != generated_scopes.length
    raise ArgumentError, "association scope names must be unique after pluralization"
  end

  generated_scopes.each do |scope_name|
    if model.respond_to?(scope_name)
      raise ArgumentError, "association scope :#{scope_name} is already defined on #{model}"
    end
  end

  model.reflections.slice(*reflections).each do |association, details|
    scope_type = details.class.to_s.split("::").last
    scope_type = "HasManyReflection" if scope_type == "HasOneReflection"

    "AssociationScope::Scope::#{scope_type}".constantize.new(model, association).apply
  end
end

.one_per_owner(relation, owner_key) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/association_scope/scope.rb', line 44

def self.one_per_owner(relation, owner_key)
  klass = relation.klass
  table = klass.arel_table
  orders = relation.arel.orders.presence || [table[klass.primary_key].asc]
  window = Arel::Nodes::Window.new.partition(owner_key).order(*orders)
  row_number = Arel::Nodes::NamedFunction.new("ROW_NUMBER", []).over(window)
  ranked = relation.except(:select, :order, :limit, :offset, :distinct)
    .select(table[Arel.star], row_number.as("association_scope_row_number"))

  # Keep ordering inside the window so offsets select a record for each owner.
  result = klass.unscoped.from(ranked, klass.table_name)
    .where(table[:association_scope_row_number].eq(relation.offset_value.to_i + 1))
    .distinct
  relation.select_values.any? ? result.select(*relation.select_values) : result
end

.target_relation(klass, *scopes) ⇒ Object



37
38
39
40
41
42
# File 'lib/association_scope/scope.rb', line 37

def self.target_relation(klass, *scopes)
  # Start independently of the owners' current scope, including for self joins.
  scopes.compact.reduce(klass.default_scoped) do |relation, scope|
    relation.instance_exec(&scope) || relation
  end
end