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 +11 -0
- data/VERSION +1 -1
- data/lib/core_ext/symbol.rb +4 -0
- data/lib/meta_where/belongs_to_polymorphic_association.rb +16 -0
- data/lib/meta_where/join_dependency.rb +79 -6
- data/lib/meta_where/join_type.rb +25 -4
- data/lib/meta_where/relation.rb +26 -15
- data/lib/meta_where/utility.rb +4 -0
- data/lib/meta_where/visitors/attribute.rb +4 -3
- data/lib/meta_where/visitors/predicate.rb +66 -9
- data/lib/meta_where/visitors/visitor.rb +3 -2
- data/lib/meta_where.rb +3 -1
- data/meta_where.gemspec +3 -2
- data/test/fixtures/note.rb +1 -1
- data/test/fixtures/notes.yml +17 -1
- data/test/test_relations.rb +94 -0
- metadata +4 -4
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.
|
1
|
+
0.9.10
|
data/lib/core_ext/symbol.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
22
|
-
|
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
|
data/lib/meta_where/join_type.rb
CHANGED
@@ -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
|
data/lib/meta_where/relation.rb
CHANGED
@@ -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 ||
|
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
|
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 =
|
158
|
+
predicate_wheres = flatten_predicates(@where_values.uniq, visitor)
|
159
159
|
|
160
|
-
predicate_wheres
|
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.
|
280
|
+
@mw_stashed_association_joins ||= unique_joins.grep(ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
|
270
281
|
end
|
271
282
|
|
272
283
|
def non_association_joins
|
data/lib/meta_where/utility.rb
CHANGED
@@ -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
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
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 =
|
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 =
|
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
|
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.
|
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{
|
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",
|
data/test/fixtures/note.rb
CHANGED
data/test/fixtures/notes.yml
CHANGED
@@ -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.
|
data/test/test_relations.rb
CHANGED
@@ -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
|
-
|
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:
|
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
|