meta_where 0.9.9.2 → 0.9.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|