meta_where 0.9.9.2 → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -1,3 +1,14 @@
1
+ Changes since 0.9.9 (2010-11-15):
2
+ * Multiple conditions on the same column with the same predicate will be "OR"ed
3
+ instead of dropping all but the last one. This tracks the same behavior in
4
+ Rails 3-0-stable.
5
+ * You can specify a polymorphic belongs_to join via something like:
6
+ Model.joins(:joinable.type(PolymorphicClass))
7
+ * You can specify ActiveRecord objects as values:
8
+ Developer.joins(:projects).where(:projects => Project.first) is the same as
9
+ Developer.joins(:projects).where(:projects => {:id => Project.first.id}), but
10
+ less hashy.
11
+
1
12
  Changes since 0.9.6 (2010-09-30):
2
13
  * ARel 2.x and Rails 3.0.2 compatibility
3
14
  * Allow MetaWhere::And and MetaWhere::Or on the value side of a condition hash
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.9.2
1
+ 0.9.10
@@ -25,6 +25,10 @@ class Symbol
25
25
  MetaWhere::JoinType.new(self, Arel::Nodes::OuterJoin)
26
26
  end
27
27
 
28
+ def type(klass)
29
+ MetaWhere::JoinType.new(self, Arel::Nodes::InnerJoin, klass)
30
+ end
31
+
28
32
  def asc
29
33
  MetaWhere::Column.new(self, :asc)
30
34
  end
@@ -0,0 +1,16 @@
1
+ module MetaWhere
2
+ module BelongsToPolymorphicAssociation
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :conditions, :metawhere
6
+ alias :sql_conditions :conditions_with_metawhere
7
+ end
8
+ end
9
+
10
+ # How does this even work in core? Oh, nevermind, it doesn't. Patch submitted. :)
11
+ def conditions_with_metawhere
12
+ @conditions ||= interpolate_sql(association_class.send(:sanitize_sql, @reflection.options[:conditions])) if @reflection.options[:conditions]
13
+ end
14
+
15
+ end
16
+ end
@@ -4,6 +4,7 @@ module MetaWhere
4
4
  def self.included(base)
5
5
  base.class_eval do
6
6
  alias_method_chain :build, :metawhere
7
+ alias_method_chain :find_join_association, :metawhere
7
8
  end
8
9
  end
9
10
 
@@ -12,14 +13,26 @@ module MetaWhere
12
13
  class AssociationNotFoundError < StandardError; end
13
14
 
14
15
  def build_with_metawhere(associations, parent = nil, join_type = Arel::Nodes::InnerJoin)
16
+ parent ||= @joins.last
15
17
  if MetaWhere::JoinType === associations
16
- parent||= @joins.last
17
- reflection = parent.reflections[associations.name] or
18
- raise AssociationNotFoundError, "Association named '#{ associations.name }' was not found; perhaps you misspelled it?"
19
- unless association = find_join_association(reflection, parent)
18
+ klass = associations.klass
19
+ join_type = associations.join_type
20
+ associations = associations.name
21
+ end
22
+
23
+ case associations
24
+ when Symbol, String
25
+ reflection = parent.reflections[associations.to_s.intern] or
26
+ raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?"
27
+ unless (association = find_join_association(reflection, parent)) && (!klass || association.active_record == klass)
20
28
  @reflections << reflection
21
- association = build_join_association(reflection, parent)
22
- association.join_type = associations.join_type
29
+ if reflection.options[:polymorphic]
30
+ raise ArgumentError, "You can't create a polymorphic belongs_to join without specifying the polymorphic class!" unless klass
31
+ association = build_polymorphic_join_association(reflection, parent, klass)
32
+ else
33
+ association = build_join_association(reflection, parent)
34
+ end
35
+ association.join_type = join_type
23
36
  @joins << association
24
37
  end
25
38
  association
@@ -27,5 +40,65 @@ module MetaWhere
27
40
  build_without_metawhere(associations, parent, join_type)
28
41
  end
29
42
  end
43
+
44
+ def find_join_association_with_metawhere(name_or_reflection, parent)
45
+ case name_or_reflection
46
+ when MetaWhere::JoinType
47
+ join_associations.detect do |j|
48
+ (j.reflection.name == name_or_reflection.name) &&
49
+ (j.reflection.klass == name_or_reflection.klass) &&
50
+ (j.parent == parent)
51
+ end
52
+ else
53
+ find_join_association_without_metawhere(name_or_reflection, parent)
54
+ end
55
+ end
56
+
57
+ def build_polymorphic_join_association(reflection, parent, klass)
58
+ PolymorphicJoinAssociation.new(reflection, self, klass, parent)
59
+ end
60
+ end
61
+
62
+ class PolymorphicJoinAssociation < ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
63
+
64
+ def initialize(reflection, join_dependency, polymorphic_class, parent = nil)
65
+ reflection.check_validity!
66
+ @active_record = polymorphic_class
67
+ @cached_record = {}
68
+ @join_dependency = join_dependency
69
+ @parent = parent || join_dependency.join_base
70
+ @reflection = reflection.clone
71
+ @reflection.instance_variable_set :"@klass", polymorphic_class
72
+ @aliased_prefix = "t#{ join_dependency.joins.size }"
73
+ @parent_table_name = @parent.active_record.table_name
74
+ @aliased_table_name = aliased_table_name_for(table_name)
75
+ @join = nil
76
+ @join_type = Arel::Nodes::InnerJoin
77
+ end
78
+
79
+ def ==(other)
80
+ other.class == self.class &&
81
+ other.reflection == reflection &&
82
+ other.active_record == active_record &&
83
+ other.parent == parent
84
+ end
85
+
86
+ def association_join
87
+ return @join if @join
88
+
89
+ aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine)
90
+ parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine)
91
+
92
+ @join = [
93
+ aliased_table[options[:primary_key] || reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name]),
94
+ parent_table[options[:foreign_type]].eq(active_record.name)
95
+ ]
96
+
97
+ if options[:conditions]
98
+ @join << interpolate_sql(sanitize_sql(options[:conditions], aliased_table_name))
99
+ end
100
+
101
+ @join
102
+ end
30
103
  end
31
104
  end
@@ -1,22 +1,43 @@
1
1
  module MetaWhere
2
2
  class JoinType
3
- attr_reader :name, :join_type
3
+ attr_reader :name, :join_type, :klass
4
4
 
5
- def initialize(name, join_type = Arel::Nodes::InnerJoin)
5
+ def initialize(name, join_type = Arel::Nodes::InnerJoin, klass = nil)
6
6
  @name = name
7
7
  @join_type = join_type
8
+ @klass = klass
8
9
  end
9
10
 
10
11
  def ==(other)
11
12
  self.class == other.class &&
12
13
  name == other.name &&
13
- join_type == other.join_type
14
+ join_type == other.join_type &&
15
+ klass == other.klass
14
16
  end
15
17
 
16
18
  alias_method :eql?, :==
17
19
 
18
20
  def hash
19
- [name, join_type].hash
21
+ [name, join_type, klass].hash
22
+ end
23
+
24
+ def outer
25
+ @join_type = Arel::Nodes::OuterJoin
26
+ self
27
+ end
28
+
29
+ def inner
30
+ @join_type = Arel::Nodes::InnerJoin
31
+ self
32
+ end
33
+
34
+ def type(klass)
35
+ @klass = klass
36
+ self
37
+ end
38
+
39
+ def to_sym
40
+ self
20
41
  end
21
42
  end
22
43
  end
@@ -34,7 +34,7 @@ module MetaWhere
34
34
 
35
35
  def scope_for_create_with_metawhere
36
36
  @scope_for_create ||= begin
37
- @create_with_value || predicate_wheres.inject({}) do |hash, where|
37
+ @create_with_value || predicates_without_conflicting_equality.inject({}) do |hash, where|
38
38
  if is_equality_predicate?(where)
39
39
  hash[where.left.name] = where.right.respond_to?(:value) ? where.right.value : where.right
40
40
  end
@@ -108,7 +108,7 @@ module MetaWhere
108
108
  arel.joins(arel)
109
109
  end unless defined?(:custom_join_sql)
110
110
 
111
- def predicate_wheres
111
+ def predicates_without_conflicting_equality
112
112
  remove_conflicting_equality_predicates(flatten_predicates(@where_values, predicate_visitor))
113
113
  end
114
114
 
@@ -155,19 +155,9 @@ module MetaWhere
155
155
 
156
156
  arel = build_intelligent_joins(arel, visitor) if @joins_values.present?
157
157
 
158
- predicate_wheres = remove_conflicting_equality_predicates(flatten_predicates(@where_values, visitor))
158
+ predicate_wheres = flatten_predicates(@where_values.uniq, visitor)
159
159
 
160
- predicate_wheres.each do |where|
161
- next if where.blank?
162
-
163
- case where
164
- when Arel::Nodes::SqlLiteral
165
- arel = arel.where(where)
166
- else
167
- sql = where.is_a?(String) ? where : where.to_sql
168
- arel = arel.where(Arel::Nodes::SqlLiteral.new("(#{sql})"))
169
- end
170
- end
160
+ arel = collapse_wheres(arel, (predicate_wheres - ['']).uniq)
171
161
 
172
162
  arel = arel.having(*flatten_predicates(@having_values, visitor).reject {|h| h.blank?}) unless @having_values.empty?
173
163
 
@@ -244,6 +234,27 @@ module MetaWhere
244
234
  }.reverse
245
235
  end
246
236
 
237
+ def collapse_wheres(arel, wheres)
238
+ binaries = wheres.grep(Arel::Nodes::Binary)
239
+
240
+ groups = binaries.group_by do |binary|
241
+ [binary.class, binary.left]
242
+ end
243
+
244
+ groups.each do |_, bins|
245
+ test = bins.inject(bins.shift) do |memo, expr|
246
+ memo.or(expr)
247
+ end
248
+ arel = arel.where(test)
249
+ end
250
+
251
+ (wheres - binaries).each do |where|
252
+ where = Arel.sql(where) if String === where
253
+ arel = arel.where(Arel::Nodes::Grouping.new(where))
254
+ end
255
+ arel
256
+ end
257
+
247
258
  def flatten_predicates(predicates, visitor)
248
259
  predicates.map {|p|
249
260
  predicate = visitor.can_accept?(p) ? visitor.accept(p) : p
@@ -266,7 +277,7 @@ module MetaWhere
266
277
  end
267
278
 
268
279
  def stashed_association_joins
269
- @mw_stashed_association_joins ||= unique_joins.select {|j| j.is_a?(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)}
280
+ @mw_stashed_association_joins ||= unique_joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
270
281
  end
271
282
 
272
283
  def non_association_joins
@@ -2,6 +2,10 @@ module MetaWhere
2
2
  module Utility
3
3
  private
4
4
 
5
+ def array_of_activerecords(val)
6
+ val.is_a?(Array) && !val.empty? && val.all? {|v| v.is_a?(ActiveRecord::Base)}
7
+ end
8
+
5
9
  def association_from_parent_and_column(parent, column)
6
10
  parent.is_a?(Symbol) ? nil : @join_dependency.send(:find_join_association, column, parent)
7
11
  end
@@ -9,7 +9,8 @@ module MetaWhere
9
9
  end
10
10
 
11
11
  def visit_Hash(o, parent)
12
- table = build_table(parent)
12
+ parent = parent.name if parent.is_a? MetaWhere::JoinType
13
+ table = tables[parent]
13
14
  built_attributes = o.map do |column, value|
14
15
  if value.is_a?(Hash)
15
16
  association = association_from_parent_and_column(parent, column)
@@ -27,7 +28,7 @@ module MetaWhere
27
28
  end
28
29
 
29
30
  def visit_Symbol(o, parent)
30
- table = self.build_table(parent)
31
+ table = tables[parent]
31
32
 
32
33
  unless attribute = table[o]
33
34
  raise ::ActiveRecord::StatementInvalid, "No attribute named `#{o}` exists for table `#{table.name}`"
@@ -42,7 +43,7 @@ module MetaWhere
42
43
  table_name, column_name = column_name.split('.', 2)
43
44
  table = Arel::Table.new(table_name, :engine => parent.arel_engine)
44
45
  else
45
- table = self.build_table(parent)
46
+ table = tables[parent]
46
47
  end
47
48
 
48
49
  unless attribute = table[column_name]
@@ -5,12 +5,13 @@ module MetaWhere
5
5
  class Predicate < Visitor
6
6
 
7
7
  def self.visitables
8
- [Hash, MetaWhere::Or, MetaWhere::And, MetaWhere::Condition, MetaWhere::Function]
8
+ [Hash, Array, MetaWhere::Or, MetaWhere::And, MetaWhere::Condition, MetaWhere::Function]
9
9
  end
10
10
 
11
11
  def visit_Hash(o, parent)
12
12
  parent ||= join_dependency.join_base
13
- table = build_table(parent)
13
+ parent = parent.name if parent.is_a? MetaWhere::JoinType
14
+ table = tables[parent]
14
15
  predicates = o.map do |column, value|
15
16
  if value.is_a?(Hash)
16
17
  association = association_from_parent_and_column(parent, column)
@@ -21,6 +22,9 @@ module MetaWhere
21
22
  elsif value.is_a?(Array) && !value.empty? && value.all? {|v| can_accept?(v)}
22
23
  association = association_from_parent_and_column(parent, column)
23
24
  value.map {|val| accept(val, association || column)}
25
+ elsif (value.is_a?(ActiveRecord::Base) || array_of_activerecords(value)) &&
26
+ reflection = parent.active_record.reflect_on_association(column.is_a?(MetaWhere::JoinType) ? column.name : column)
27
+ accept_activerecord_values(column, value, parent, reflection)
24
28
  else
25
29
  if column.is_a?(MetaWhere::Column)
26
30
  method = column.method
@@ -34,15 +38,16 @@ module MetaWhere
34
38
  table = Arel::Table.new(table_name, :engine => parent.arel_engine)
35
39
  end
36
40
 
37
- unless attribute = attribute_from_column_and_table(column, table)
38
- raise ::ActiveRecord::StatementInvalid, "No attribute named `#{column}` exists for table `#{table.name}`"
39
- end
40
-
41
41
  unless valid_comparison_method?(method)
42
42
  raise ::ActiveRecord::StatementInvalid, "No comparison method named `#{method}` exists for column `#{column}`"
43
43
  end
44
44
 
45
- attribute.send(method, args_for_predicate(value))
45
+ if attribute = attribute_from_column_and_table(column, table)
46
+ attribute.send(method, args_for_predicate(value))
47
+ else
48
+ raise ::ActiveRecord::StatementInvalid, "No attribute named `#{column}` exists for table `#{table.name}`"
49
+ end
50
+
46
51
  end
47
52
  end
48
53
 
@@ -56,6 +61,14 @@ module MetaWhere
56
61
  end
57
62
  end
58
63
 
64
+ def visit_Array(o, parent)
65
+ if o.first.is_a? String
66
+ join_dependency.join_base.send(:sanitize_sql, o)
67
+ else
68
+ o.map {|e| accept(e, parent)}.flatten
69
+ end
70
+ end
71
+
59
72
  def visit_MetaWhere_Or(o, parent)
60
73
  accept(o.condition1, parent).or(accept(o.condition2, parent))
61
74
  end
@@ -65,7 +78,7 @@ module MetaWhere
65
78
  end
66
79
 
67
80
  def visit_MetaWhere_Condition(o, parent)
68
- table = self.build_table(parent)
81
+ table = tables[parent]
69
82
 
70
83
  unless attribute = attribute_from_column_and_table(o.column, table)
71
84
  raise ::ActiveRecord::StatementInvalid, "No attribute named `#{o.column}` exists for table `#{table.name}`"
@@ -78,11 +91,55 @@ module MetaWhere
78
91
  end
79
92
 
80
93
  def visit_MetaWhere_Function(o, parent)
81
- self.table = self.build_table(parent)
94
+ self.table = tables[parent]
82
95
 
83
96
  o.to_sqlliteral
84
97
  end
85
98
 
99
+ private
100
+
101
+ def accept_activerecord_values(column, value, parent, reflection)
102
+ groups = Array.wrap(value).group_by {|v| v.class.base_class}
103
+ unless reflection.options[:polymorphic] || groups.keys.all? {|k| reflection.klass == k}
104
+ raise ArgumentError, "An object you supplied to :#{reflection.name} is not a #{reflection.klass}!"
105
+ end
106
+ case reflection.macro
107
+ when :has_many, :has_one, :has_and_belongs_to_many
108
+ conditions = nil
109
+ groups.each do |klass, values|
110
+ condition = {
111
+ (reflection.options[:foreign_key] || reflection.klass.primary_key).to_sym => values.size == 1 ? values.first.id : values.map(&:id)
112
+ }
113
+ conditions = conditions ? conditions | condition : condition
114
+ end
115
+
116
+ accept(conditions, association_from_parent_and_column(parent, column) || column)
117
+ when :belongs_to
118
+ conditions = nil
119
+ groups.each do |klass, values|
120
+ condition = if reflection.options[:polymorphic]
121
+ {
122
+ (reflection.options[:foreign_key] || reflection.primary_key_name).to_sym => values.size == 1 ? values.first.id : values.map(&:id),
123
+ reflection.options[:foreign_type].to_sym => klass.name
124
+ }
125
+ else
126
+ {(reflection.options[:foreign_key] || reflection.primary_key_name).to_sym => values.size == 1 ? values.first.id : values.map(&:id)}
127
+ end
128
+ conditions = conditions ? conditions | condition : condition
129
+ end
130
+
131
+ accept(conditions, parent)
132
+ end
133
+ end
134
+
135
+ def sanitize_or_accept_reflection_conditions(reflection, parent, column)
136
+ if !can_accept?(reflection.options[:conditions])
137
+ reflection.sanitized_conditions
138
+ else
139
+ accept(reflection.options[:conditions], association_from_parent_and_column(parent, column) || column)
140
+ end
141
+ end
142
+
86
143
  end
87
144
  end
88
145
  end
@@ -5,16 +5,17 @@ module MetaWhere
5
5
  class Visitor
6
6
  include MetaWhere::Utility
7
7
 
8
- attr_reader :join_dependency
8
+ attr_reader :join_dependency, :tables
9
9
 
10
10
  def initialize(join_dependency)
11
11
  @join_dependency = join_dependency
12
12
  jb = join_dependency.join_base
13
13
  @engine = jb.arel_engine
14
14
  @default_table = Arel::Table.new(jb.table_name, :as => jb.aliased_table_name, :engine => @engine)
15
+ @tables = Hash.new {|hash, key| hash[key] = get_table(key)}
15
16
  end
16
17
 
17
- def build_table(parent_or_table_name = nil)
18
+ def get_table(parent_or_table_name = nil)
18
19
  if parent_or_table_name.is_a?(Symbol)
19
20
  Arel::Table.new(parent_or_table_name, :engine => @engine)
20
21
  elsif parent_or_table_name.respond_to?(:aliased_table_name)
data/lib/meta_where.rb CHANGED
@@ -39,8 +39,10 @@ require 'core_ext/hash'
39
39
  require 'meta_where/visitors/attribute'
40
40
  require 'meta_where/visitors/predicate'
41
41
  require 'meta_where/association_reflection'
42
+ require 'meta_where/belongs_to_polymorphic_association'
42
43
  require 'meta_where/relation'
43
44
  require 'meta_where/join_dependency'
44
45
  ActiveRecord::Relation.send(:include, MetaWhere::Relation)
45
46
  ActiveRecord::Reflection::AssociationReflection.send(:include, MetaWhere::AssociationReflection)
46
- ActiveRecord::Associations::ClassMethods::JoinDependency.send(:include, MetaWhere::JoinDependency)
47
+ ActiveRecord::Associations::ClassMethods::JoinDependency.send(:include, MetaWhere::JoinDependency)
48
+ ActiveRecord::Associations::BelongsToPolymorphicAssociation.send(:include, MetaWhere::BelongsToPolymorphicAssociation)
data/meta_where.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{meta_where}
8
- s.version = "0.9.9.2"
8
+ s.version = "0.9.10"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Ernie Miller"]
12
- s.date = %q{2010-11-17}
12
+ s.date = %q{2011-01-06}
13
13
  s.description = %q{
14
14
  MetaWhere offers the ability to call any Arel predicate methods
15
15
  (with a few convenient aliases) on your Model's attributes instead
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
37
37
  "lib/core_ext/symbol_operators.rb",
38
38
  "lib/meta_where.rb",
39
39
  "lib/meta_where/association_reflection.rb",
40
+ "lib/meta_where/belongs_to_polymorphic_association.rb",
40
41
  "lib/meta_where/column.rb",
41
42
  "lib/meta_where/compound.rb",
42
43
  "lib/meta_where/condition.rb",
@@ -1,3 +1,3 @@
1
1
  class Note < ActiveRecord::Base
2
- belongs_to :notable, :polymorphic => true
2
+ belongs_to :notable, :polymorphic => true, :conditions => '1=1'
3
3
  end
@@ -1,79 +1,95 @@
1
1
  peter:
2
+ id : 1
2
3
  notable_type: Developer
3
4
  notable_id : 1
4
5
  note : A straight shooter with upper management written all over him.
5
-
6
+
6
7
  michael:
8
+ id : 2
7
9
  notable_type: Developer
8
10
  notable_id : 2
9
11
  note : Doesn't like the singer of the same name. The nerve!
10
12
 
11
13
  samir:
14
+ id : 3
12
15
  notable_type: Developer
13
16
  notable_id : 3
14
17
  note : Naga.... Naga..... Not gonna work here anymore anyway.
15
18
 
16
19
  herb:
20
+ id : 4
17
21
  notable_type: Developer
18
22
  notable_id : 4
19
23
  note : Will show you what he's doing.
20
24
 
21
25
  dude:
26
+ id : 5
22
27
  notable_type: Developer
23
28
  notable_id : 5
24
29
  note : Nothing of note.
25
30
 
26
31
  ernie:
32
+ id : 6
27
33
  notable_type: Developer
28
34
  notable_id : 6
29
35
  note : Complete slacker. Should probably be fired.
30
36
 
31
37
  someone:
38
+ id : 7
32
39
  notable_type: Developer
33
40
  notable_id : 7
34
41
  note : Just another developer.
35
42
 
36
43
  another:
44
+ id : 8
37
45
  notable_type: Developer
38
46
  notable_id : 8
39
47
  note : Placing a note in this guy's file for insubordination.
40
48
 
41
49
  initech:
50
+ id : 9
42
51
  notable_type: Company
43
52
  notable_id : 1
44
53
  note : Innovation + Technology!
45
54
 
46
55
  aos:
56
+ id : 10
47
57
  notable_type: Company
48
58
  notable_id : 2
49
59
  note : Advanced solutions of an optical nature.
50
60
 
51
61
  mission_data:
62
+ id : 11
52
63
  notable_type: Company
53
64
  notable_id : 3
54
65
  note : Best design + development shop in the 'ville.
55
66
 
56
67
  y2k:
68
+ id : 12
57
69
  notable_type: Project
58
70
  notable_id : 1
59
71
  note : It may have already passed but that's no excuse to be unprepared!
60
72
 
61
73
  virus:
74
+ id : 13
62
75
  notable_type: Project
63
76
  notable_id : 2
64
77
  note : It could bring the company to its knees.
65
78
 
66
79
  awesome:
80
+ id : 14
67
81
  notable_type: Project
68
82
  notable_id : 3
69
83
  note : This note is AWESOME!!!
70
84
 
71
85
  metasearch:
86
+ id : 15
72
87
  notable_type: Project
73
88
  notable_id : 4
74
89
  note : A complete waste of the developer's time.
75
90
 
76
91
  another:
92
+ id : 16
77
93
  notable_type: Project
78
94
  notable_id : 5
79
95
  note : This is another project note.
@@ -314,5 +314,99 @@ class TestRelations < Test::Unit::TestCase
314
314
  assert_match /IN \(1, 2, 3\)/, query.to_sql
315
315
  assert_same_elements Developer.all, query.all
316
316
  end
317
+
318
+ should "merge multiple conditions on the same column and predicate with ORs" do
319
+ assert_match /"developers"."name" = 'blah' OR "developers"."name" = 'blah2'/,
320
+ @r.where(:name => 'blah').where(:name => 'blah2').to_sql
321
+ assert_match /"developers"."name" LIKE '%blah%' OR "developers"."name" LIKE '%blah2%'/,
322
+ @r.where(:name.matches => '%blah%').where(:name.matches => '%blah2%').to_sql
323
+ end
324
+
325
+ should "erge multiple conditions on the same column but different predicate with ANDs" do
326
+ assert_match /"developers"."name" = 'blah' AND "developers"."name" LIKE '%blah2%'/,
327
+ @r.where(:name => 'blah').where(:name.matches => '%blah2%').to_sql
328
+ end
329
+ end
330
+
331
+ context "A relation" do
332
+ should "allow conditions on a belongs_to polymorphic association with an object" do
333
+ dev = Developer.first
334
+ assert_equal dev, Note.where(:notable.type(Developer) => dev).first.notable
335
+ end
336
+
337
+ should "allow conditions on a belongs_to association with an object" do
338
+ company = Company.first
339
+ assert_same_elements Developer.where(:company_id => company.id),
340
+ Developer.where(:company => company).all
341
+ end
342
+
343
+ should "allow conditions on a has_and_belongs_to_many association with an object" do
344
+ project = Project.first
345
+ assert_same_elements Developer.joins(:projects).where(:projects => {:id => project.id}),
346
+ Developer.joins(:projects).where(:projects => project)
347
+ end
348
+
349
+ should "not allow an object of the wrong class to be passed to a non-polymorphic association" do
350
+ company = Company.first
351
+ assert_raise ArgumentError do
352
+ Project.where(:developers => company).all
353
+ end
354
+ end
355
+
356
+ should "allow multiple AR objects on the value side of an association condition" do
357
+ projects = [Project.first, Project.last]
358
+ assert_same_elements Developer.joins(:projects).where(:projects => {:id => projects.map(&:id)}),
359
+ Developer.joins(:projects).where(:projects => projects)
360
+ end
361
+
362
+ should "allow multiple different kinds of AR objects on the value side of a polymorphic belongs_to" do
363
+ dev1 = Developer.first
364
+ dev2 = Developer.last
365
+ project = Project.first
366
+ company = Company.first
367
+ assert_same_elements Note.where(
368
+ {:notable_type => project.class.base_class.name, :notable_id => project.id} |
369
+ {:notable_type => dev1.class.base_class.name, :notable_id => [dev1.id, dev2.id]} |
370
+ {:notable_type => company.class.base_class.name, :notable_id => company.id}
371
+ ),
372
+ Note.where(:notable => [dev1, dev2, project, company]).all
373
+ end
374
+
375
+ should "allow an AR object on the value side of a polymorphic has_many condition" do
376
+ note = Note.first
377
+ peter = Developer.first
378
+ assert_equal [peter],
379
+ Developer.joins(:notes).where(:notes => note).all
380
+ end
381
+
382
+ should "allow a join of a polymorphic belongs_to relation with a type specified" do
383
+ dev = Developer.first
384
+ company = Company.first
385
+ assert_equal [company.notes.first],
386
+ Note.joins(:notable.type(Company) => :developers).where(:notable => {:developers => dev}).all
387
+ end
388
+
389
+ should "allow selection of a specific polymorphic join by name in the where clause" do
390
+ dev = Developer.first
391
+ company = Company.first
392
+ project = Project.first
393
+ dev_note = dev.notes.first
394
+ company_note = company.notes.first
395
+ project_note = project.notes.first
396
+ # Have to use outer joins since one inner join will cause remaining rows to be missing
397
+ # This is pretty convoluted, and way beyond the normal use case for polymorphic belongs_to
398
+ # joins anyway.
399
+ @r = Note.joins(:notable.type(Company).outer => :notes.outer, :notable.type(Developer).outer => :notes.outer, :notable.type(Project).outer => :notes.outer)
400
+ assert_equal [dev_note],
401
+ @r.where(:notable.type(Developer) => {:notes => dev_note}).all
402
+ assert_equal [company_note],
403
+ @r.where(:notable.type(Company) => {:notes => company_note}).all
404
+ assert_equal [project_note],
405
+ @r.where(:notable.type(Project) => {:notes => project_note}).all
406
+ end
407
+
408
+ should "maintain belongs_to conditions in a polymorphic join" do
409
+ assert_match /1=1/, Note.joins(:notable.type(Company)).to_sql
410
+ end
317
411
  end
318
412
  end
metadata CHANGED
@@ -5,9 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 9
9
- - 2
10
- version: 0.9.9.2
8
+ - 10
9
+ version: 0.9.10
11
10
  platform: ruby
12
11
  authors:
13
12
  - Ernie Miller
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-11-17 00:00:00 -05:00
17
+ date: 2011-01-06 00:00:00 -05:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -98,6 +97,7 @@ files:
98
97
  - lib/core_ext/symbol_operators.rb
99
98
  - lib/meta_where.rb
100
99
  - lib/meta_where/association_reflection.rb
100
+ - lib/meta_where/belongs_to_polymorphic_association.rb
101
101
  - lib/meta_where/column.rb
102
102
  - lib/meta_where/compound.rb
103
103
  - lib/meta_where/condition.rb