Module: MetaWhere::Relation

Defined in:
lib/meta_where/relation.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/meta_where/relation.rb', line 4

def self.included(base)
  base.class_eval do
    alias_method_chain :reset, :metawhere
    alias_method_chain :where_values_hash, :metawhere
  end

  # We have to do this on the singleton to work with Ruby 1.8.7. Not sure why.
  base.instance_eval do
    alias_method :&, :merge
  end
end

Instance Method Details

#attribute_visitorObject



121
122
123
124
# File 'lib/meta_where/relation.rb', line 121

def attribute_visitor
  join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
  MetaWhere::Visitors::Attribute.new(join_dependency)
end

#build_arelObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/meta_where/relation.rb', line 150

def build_arel
  arel = table

  visitor = predicate_visitor

  arel = build_intelligent_joins(arel, visitor) if @joins_values.present?

  predicate_wheres = flatten_predicates(@where_values.uniq, visitor)

  arel = collapse_wheres(arel, (predicate_wheres - ['']).uniq)

  arel = arel.having(*flatten_predicates(@having_values, visitor).reject {|h| h.blank?}) unless @having_values.empty?

  arel = arel.take(@limit_value) if @limit_value
  arel = arel.skip(@offset_value) if @offset_value

  arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?

  arel = build_order(arel, attribute_visitor, @order_values) unless @order_values.empty?

  arel = build_select(arel, @select_values.uniq)

  arel = arel.from(@from_value) if @from_value
  arel = arel.lock(@lock_value) if @lock_value

  arel
end

#build_custom_joins(joins = [], arel = nil) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/meta_where/relation.rb', line 67

def build_custom_joins(joins = [], arel = nil)
  arel ||= table
  joins.each do |join|
    next if join.blank?

    @implicit_readonly = true

    case join
    when ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
      arel = arel.join(join.relation, Arel::Nodes::OuterJoin).on(*join.on)
    when Hash, Array, Symbol
      if array_of_strings?(join)
        join_string = join.join(' ')
        arel = arel.join(join_string)
      end
    else
      arel = arel.join(join)
    end
  end

  arel
end

#build_where(opts, other = []) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/meta_where/relation.rb', line 46

def build_where(opts, other = [])
  if opts.is_a?(String)
    [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
  else
    predicates = []
    [opts, *other].each do |arg|
      predicates += Array.wrap(
        case arg
        when Array
          @klass.send(:sanitize_sql, arg)
        when Hash
          @klass.send(:expand_hash_conditions_for_aggregates, arg)
        else
          arg
        end
      )
    end
    predicates
  end
end

#construct_limited_ids_condition(relation) ⇒ Object



142
143
144
145
146
147
148
# File 'lib/meta_where/relation.rb', line 142

def construct_limited_ids_condition(relation)
  visitor = relation.attribute_visitor

  relation.order_values.map! {|o| visitor.can_accept?(o) ? visitor.accept(o).to_sql : o}

  super
end

#custom_join_sql(*joins) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/meta_where/relation.rb', line 90

def custom_join_sql(*joins)
  arel = table
  joins.each do |join|
    next if join.blank?

    @implicit_readonly = true

    case join
    when Hash, Array, Symbol
      if array_of_strings?(join)
        join_string = join.join(' ')
        arel = arel.join(join_string)
      end
    else
      arel = arel.join(join)
    end
  end
  arel.joins(arel)
end

#debug_sqlObject

Simulate the logic that occurs in ActiveRecord::Relation.to_a

This will let us get a dump of the SQL that will be run against the DB for debug purposes without actually running the query.



132
133
134
135
136
137
138
139
140
# File 'lib/meta_where/relation.rb', line 132

def debug_sql
  if eager_loading?
    including = (@eager_load_values + @includes_values).uniq
    join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
    construct_relation_for_association_find(join_dependency).to_sql
  else
    arel.to_sql
  end
end

#merge(r, association_name = nil) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/meta_where/relation.rb', line 16

def merge(r, association_name = nil)
  if (r && (association_name || base_class.name != r.klass.base_class.name)) # Merging relations with different base.
    association_name ||= (default_association = reflect_on_all_associations.detect {|a| a.class_name == r.klass.name}) ?
                         default_association.name : r.table_name.to_sym
    r = r.clone
    r.where_values.map! {|w| MetaWhere::Visitors::Predicate.visitables.include?(w.class) ? {association_name => w} : w}
    r.joins_values.map! {|j| [Symbol, Hash, MetaWhere::JoinType].include?(j.class) ? {association_name => j} : j}
    self.joins_values += [association_name] if reflect_on_association(association_name)
  end

  super(r)
end

#predicate_visitorObject

Very occasionally, we need to get a visitor for another relation, so it makes sense to factor these out into a public method despite only being two lines long.



116
117
118
119
# File 'lib/meta_where/relation.rb', line 116

def predicate_visitor
  join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins, custom_joins)
  MetaWhere::Visitors::Predicate.new(join_dependency)
end

#predicates_without_conflicting_equalityObject



110
111
112
# File 'lib/meta_where/relation.rb', line 110

def predicates_without_conflicting_equality
  remove_conflicting_equality_predicates(flatten_predicates(@where_values, predicate_visitor))
end

#reset_with_metawhereObject



29
30
31
32
33
# File 'lib/meta_where/relation.rb', line 29

def reset_with_metawhere
  @mw_unique_joins = @mw_association_joins = @mw_non_association_joins =
    @mw_stashed_association_joins = @mw_custom_joins = nil
  reset_without_metawhere
end

#select(value = Proc.new) ⇒ Object



178
179
180
181
182
183
184
# File 'lib/meta_where/relation.rb', line 178

def select(value = Proc.new)
  if MetaWhere::Function === value
    value.table = self.arel_table
  end

  super
end

#where_values_hash_with_metawhereObject



35
36
37
38
39
40
41
42
43
44
# File 'lib/meta_where/relation.rb', line 35

def where_values_hash_with_metawhere
  Hash[flatten_nodes(flatten_predicates(@where_values, predicate_visitor)).find_all { |w|
    w.respond_to?(:operator) && w.operator == :== && w.left.relation.name == table_name
  }.map { |where|
    [
      where.left.name,
      where.right.respond_to?(:value) ? where.right.value : where.right
    ]
  }]
end