Module: MetaWhere::Relation

Defined in:
lib/meta_where/relation.rb

Constant Summary collapse

JoinAssociation =
::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
JoinDependency =
::ActiveRecord::Associations::ClassMethods::JoinDependency

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#join_dependencyObject



22
23
24
# File 'lib/meta_where/relation.rb', line 22

def join_dependency
  @join_dependency ||= (build_join_dependency(table.from(table), @joins_values) && @join_dependency)
end

Class Method Details

.included(base) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
# File 'lib/meta_where/relation.rb', line 10

def self.included(base)
  base.class_eval do
    alias_method_chain :reset, :metawhere
    alias_method_chain :scope_for_create, :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



92
93
94
95
96
97
98
# File 'lib/meta_where/relation.rb', line 92

def attribute_visitor
  @attribute_visitor ||= begin
    visitor = MetaWhere::Visitors::Attribute.new
    visitor.join_dependency = join_dependency
    visitor
  end
end

#build_arelObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/meta_where/relation.rb', line 124

def build_arel
  arel = table.from table

  build_join_dependency(arel, @joins_values) unless @joins_values.empty?

  visitor = predicate_visitor

  predicate_wheres = flatten_predicates(@where_values.uniq, visitor)

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

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

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

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

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

  build_select(arel, @select_values.uniq)

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

  arel
end

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



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/meta_where/relation.rb', line 57

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



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

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

#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.



106
107
108
109
110
111
112
113
114
# File 'lib/meta_where/relation.rb', line 106

def debug_sql
  if eager_loading?
    including = (@eager_load_values + @includes_values).uniq
    join_dependency = JoinDependency.new(@klass, including, [])
    construct_relation_for_association_find(join_dependency).to_sql
  else
    arel.to_sql
  end
end

#merge(r, association_name = nil) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/meta_where/relation.rb', line 26

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.



84
85
86
87
88
89
90
# File 'lib/meta_where/relation.rb', line 84

def predicate_visitor
  @predicate_visitor ||= begin
    visitor = MetaWhere::Visitors::Predicate.new
    visitor.join_dependency = join_dependency
    visitor
  end
end

#predicates_without_conflicting_equalityObject



78
79
80
# File 'lib/meta_where/relation.rb', line 78

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

#reset_with_metawhereObject



39
40
41
42
43
# File 'lib/meta_where/relation.rb', line 39

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

#scope_for_create_with_metawhereObject



45
46
47
48
49
50
51
52
53
54
55
# File 'lib/meta_where/relation.rb', line 45

def scope_for_create_with_metawhere
  @scope_for_create ||= begin
    @create_with_value || predicates_without_conflicting_equality.inject({}) do |hash, where|
      if is_equality_predicate?(where)
        hash[where.left.name] = where.right.respond_to?(:value) ? where.right.value : where.right
      end

      hash
    end
  end
end

#select(value = Proc.new) ⇒ Object



152
153
154
155
156
157
158
# File 'lib/meta_where/relation.rb', line 152

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

  super
end