squeel 0.8.6 → 0.8.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -38,14 +38,14 @@ module Squeel
38
38
  # already bound to the proper table.
39
39
  #
40
40
  # Squeel, on the other hand, needs to do its best to ensure the predicates are still
41
- # winding up against the proper table. Merging relations is a really nifty shortcut
42
- # but another little corner of ActiveRecord where the magic quickly fades. :(
43
- def merge(r, association_name = nil)
44
- if association_name || relation_with_different_base?(r)
45
- r = r.clone
46
- association_name ||= infer_association_for_relation_merge(r)
47
- prepare_relation_for_association_merge!(r, association_name)
48
- self.joins_values += [association_name] if reflect_on_association(association_name)
41
+ # winding up against the proper table. The most "correct" way I can think of to do
42
+ # this is to try to emulate the default AR behavior -- that is, de-squeelifying
43
+ # the *_values, erm... values by visiting them and converting them to ARel nodes
44
+ # before merging. Merging relations is a nifty little trick, but it's another
45
+ # little corner of ActiveRecord where the magic quickly fades. :(
46
+ def merge(r)
47
+ if relation_with_different_base?(r)
48
+ r = r.clone.visit!
49
49
  end
50
50
 
51
51
  super(r)
@@ -56,11 +56,6 @@ module Squeel
56
56
  base_class.name != r.klass.base_class.name
57
57
  end
58
58
 
59
- def infer_association_for_relation_merge(r)
60
- default_association = reflect_on_all_associations.detect {|a| a.class_name == r.klass.name}
61
- default_association ? default_association.name : r.table_name.to_sym
62
- end
63
-
64
59
  def prepare_relation_for_association_merge!(r, association_name)
65
60
  r.where_values.map! {|w| Squeel::Visitors::PredicateVisitor.can_visit?(w) ? {association_name => w} : w}
66
61
  r.having_values.map! {|h| Squeel::Visitors::PredicateVisitor.can_visit?(h) ? {association_name => h} : h}
@@ -71,6 +66,19 @@ module Squeel
71
66
  r.includes_values.map! {|i| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(i.class) ? {association_name => i} : i}
72
67
  end
73
68
 
69
+ def visit!
70
+ predicate_viz = predicate_visitor
71
+ attribute_viz = attribute_visitor
72
+
73
+ @where_values = predicate_viz.accept((@where_values - ['']).uniq)
74
+ @having_values = predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})
75
+ @group_values = attribute_viz.accept(@group_values.uniq.reject{|g| g.blank?})
76
+ @order_values = attribute_viz.accept(@order_values.uniq.reject{|o| o.blank?})
77
+ @select_values = attribute_viz.accept(@select_values.uniq)
78
+
79
+ self
80
+ end
81
+
74
82
  def build_arel
75
83
  arel = table
76
84
 
@@ -38,13 +38,14 @@ module Squeel
38
38
  # already bound to the proper table.
39
39
  #
40
40
  # Squeel, on the other hand, needs to do its best to ensure the predicates are still
41
- # winding up against the proper table. Merging relations is a really nifty shortcut
42
- # but another little corner of ActiveRecord where the magic quickly fades. :(
43
- def merge(r, association_name = nil)
44
- if association_name || relation_with_different_base?(r)
45
- r = r.clone
46
- association_name ||= infer_association_for_relation_merge(r)
47
- prepare_relation_for_association_merge!(r, association_name)
41
+ # winding up against the proper table. The most "correct" way I can think of to do
42
+ # this is to try to emulate the default AR behavior -- that is, de-squeelifying
43
+ # the *_values, erm... values by visiting them and converting them to ARel nodes
44
+ # before merging. Merging relations is a nifty little trick, but it's another
45
+ # little corner of ActiveRecord where the magic quickly fades. :(
46
+ def merge(r)
47
+ if relation_with_different_base?(r)
48
+ r = r.clone.visit!
48
49
  end
49
50
 
50
51
  super(r)
@@ -55,11 +56,6 @@ module Squeel
55
56
  base_class.name != r.klass.base_class.name
56
57
  end
57
58
 
58
- def infer_association_for_relation_merge(r)
59
- default_association = reflect_on_all_associations.detect {|a| a.class_name == r.klass.name}
60
- default_association ? default_association.name : r.table_name.to_sym
61
- end
62
-
63
59
  def prepare_relation_for_association_merge!(r, association_name)
64
60
  r.where_values.map! {|w| Squeel::Visitors::PredicateVisitor.can_visit?(w) ? {association_name => w} : w}
65
61
  r.having_values.map! {|h| Squeel::Visitors::PredicateVisitor.can_visit?(h) ? {association_name => h} : h}
@@ -70,6 +66,19 @@ module Squeel
70
66
  r.includes_values.map! {|i| [Symbol, Hash, Nodes::Stub, Nodes::Join, Nodes::KeyPath].include?(i.class) ? {association_name => i} : i}
71
67
  end
72
68
 
69
+ def visit!
70
+ predicate_viz = predicate_visitor
71
+ attribute_viz = attribute_visitor
72
+
73
+ @where_values = predicate_viz.accept((@where_values - ['']).uniq)
74
+ @having_values = predicate_viz.accept(@having_values.uniq.reject{|h| h.blank?})
75
+ @group_values = attribute_viz.accept(@group_values.uniq.reject{|g| g.blank?})
76
+ @order_values = attribute_viz.accept(@order_values.uniq.reject{|o| o.blank?})
77
+ @select_values = attribute_viz.accept(@select_values.uniq)
78
+
79
+ self
80
+ end
81
+
73
82
  def build_arel
74
83
  arel = table.from table
75
84
 
@@ -263,7 +272,7 @@ module Squeel
263
272
  end
264
273
 
265
274
  def collapse_wheres(arel, wheres)
266
- wheres = [wheres] unless Array === wheres
275
+ wheres = Array(wheres)
267
276
  binaries = wheres.grep(Arel::Nodes::Binary)
268
277
 
269
278
  groups = binaries.group_by {|b| [b.class, b.left]}
@@ -21,9 +21,9 @@ module Squeel
21
21
 
22
22
  # Comparison with other nodes
23
23
  def eql?(other)
24
- self.class == other.class &&
25
- self.left == other.left &&
26
- self.right == other.right
24
+ self.class.eql?(other.class) &&
25
+ self.left.eql?(other.left) &&
26
+ self.right.eql?(other.right)
27
27
  end
28
28
  alias :== :eql?
29
29
 
@@ -52,10 +52,10 @@ module Squeel
52
52
 
53
53
  # Compare with other objects
54
54
  def eql?(other)
55
- self.class == other.class &&
56
- self._name == other._name &&
57
- self._type == other._type &&
58
- self._klass == other._klass
55
+ self.class.eql?(other.class) &&
56
+ self._name.eql?(other._name) &&
57
+ self._type.eql?(other._type) &&
58
+ self._klass.eql?(other._klass)
59
59
  end
60
60
  alias :== :eql?
61
61
 
@@ -42,10 +42,10 @@ module Squeel
42
42
 
43
43
  # Object comparison
44
44
  def eql?(other)
45
- self.class == other.class &&
46
- self.path == other.path &&
45
+ self.class.eql?(other.class) &&
46
+ self.path.eql?(other.path) &&
47
47
  self.endpoint.eql?(other.endpoint) &&
48
- self.absolute? == other.absolute?
48
+ self.absolute?.eql?(other.absolute?)
49
49
  end
50
50
 
51
51
  # Allow KeyPath to function like its endpoint, in the case where its endpoint
@@ -161,6 +161,7 @@ module Squeel
161
161
  # @return [KeyPath] The updated KeyPath
162
162
  def method_missing(method_id, *args)
163
163
  super if method_id == :to_ary
164
+
164
165
  if endpoint.respond_to? method_id
165
166
  @endpoint = @endpoint.send(method_id, *args)
166
167
  self
@@ -42,8 +42,8 @@ module Squeel
42
42
 
43
43
  # Object comparison
44
44
  def eql?(other)
45
- self.class == other.class &&
46
- self.expr == other.expr
45
+ self.class.eql?(other.class) &&
46
+ self.expr.eql?(other.expr)
47
47
  end
48
48
 
49
49
  # To support object equality tests
@@ -33,10 +33,15 @@ module Squeel
33
33
  self
34
34
  end
35
35
 
36
+ # Implemented for equality testing
37
+ def hash
38
+ [self.class].concat(self.children).hash
39
+ end
40
+
36
41
  # Object comparison
37
42
  def eql?(other)
38
- self.class == other.class &&
39
- self.children == other.children
43
+ self.class.eql?(other.class) &&
44
+ self.children.eql?(other.children)
40
45
  end
41
46
  alias :== :eql?
42
47
 
@@ -37,8 +37,8 @@ module Squeel
37
37
 
38
38
  # Object comparison
39
39
  def eql?(other)
40
- self.class == other.class &&
41
- self.symbol == other.symbol
40
+ self.class.eql?(other.class) &&
41
+ self.symbol.eql?(other.symbol)
42
42
  end
43
43
 
44
44
  # To support object equality tests
@@ -18,8 +18,8 @@ module Squeel
18
18
 
19
19
  # Object comparison
20
20
  def eql?(other)
21
- self.class == other.class &&
22
- self.expr == other.expr
21
+ self.class.eql?(other.class) &&
22
+ self.expr.eql?(other.expr)
23
23
  end
24
24
  alias :== :eql?
25
25
 
@@ -1,3 +1,3 @@
1
1
  module Squeel
2
- VERSION = "0.8.6"
2
+ VERSION = "0.8.7"
3
3
  end
@@ -189,6 +189,15 @@ module Squeel
189
189
  arel.to_sql.should match /ORDER BY "parents_people_2"."id" ASC/
190
190
  end
191
191
 
192
+ it 'does not inadvertently convert KeyPaths to booleans when uniqing where_values' do
193
+ 100.times do # Doesn't happen reliably because of #hash behavior
194
+ persons = Person.joins{[outgoing_messages.outer, incoming_messages.outer]}
195
+ persons = persons.where { (outgoing_messages.author_id.not_eq 7) & (incoming_messages.author_id.not_eq 7) }
196
+ persons = persons.where{(outgoing_messages.recipient_id.not_eq 7) & (incoming_messages.recipient_id.not_eq 7)}
197
+ expect { persons.to_sql }.not_to raise_error TypeError
198
+ end
199
+ end
200
+
192
201
  end
193
202
 
194
203
  describe '#to_sql' do
@@ -616,7 +625,7 @@ module Squeel
616
625
  end
617
626
 
618
627
  it 'merges relations with a different base' do
619
- relation = Person.where{name == 'bob'}.joins(:articles).merge(Article.where{title == 'Hello world!'}, :articles)
628
+ relation = Person.where{name == 'bob'}.joins(:articles).merge(Article.where{title == 'Hello world!'})
620
629
  sql = relation.to_sql
621
630
  sql.should match /INNER JOIN "articles" ON "articles"."person_id" = "people"."id"/
622
631
  sql.should match /"people"."name" = 'bob'/
@@ -369,4 +369,4 @@ module Squeel
369
369
 
370
370
  end
371
371
  end
372
- end
372
+ end
@@ -16,6 +16,15 @@ class Person < ActiveRecord::Base
16
16
  :source => :comments
17
17
  has_many :notes, :as => :notable
18
18
  has_many :unidentified_objects
19
+
20
+ has_many :outgoing_messages, :class_name => 'Message', :foreign_key => :author_id
21
+ has_many :incoming_messages, :class_name => 'Message', :foreign_key => :recipient_id
22
+
23
+ end
24
+
25
+ class Message < ActiveRecord::Base
26
+ belongs_to :author, :class_name => 'Person'
27
+ belongs_to :recipient, :class_name => 'Person'
19
28
  end
20
29
 
21
30
  class UnidentifiedObject < ActiveRecord::Base
@@ -55,6 +64,11 @@ module Schema
55
64
  t.integer :salary
56
65
  end
57
66
 
67
+ create_table :messages, :force => true do |t|
68
+ t.integer :author_id
69
+ t.integer :recipient_id
70
+ end
71
+
58
72
  create_table :unidentified_objects, :id => false, :force => true do |t|
59
73
  t.integer :person_id
60
74
  t.string :name
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squeel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.6
4
+ version: 0.8.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-01 00:00:00.000000000 -04:00
13
- default_executable:
12
+ date: 2011-08-08 00:00:00.000000000Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: activerecord
17
- requirement: &70148779727860 !ruby/object:Gem::Requirement
16
+ requirement: &70170737358180 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ~>
@@ -22,10 +21,10 @@ dependencies:
22
21
  version: '3.0'
23
22
  type: :runtime
24
23
  prerelease: false
25
- version_requirements: *70148779727860
24
+ version_requirements: *70170737358180
26
25
  - !ruby/object:Gem::Dependency
27
26
  name: activesupport
28
- requirement: &70148779727360 !ruby/object:Gem::Requirement
27
+ requirement: &70170737357560 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ~>
@@ -33,10 +32,10 @@ dependencies:
33
32
  version: '3.0'
34
33
  type: :runtime
35
34
  prerelease: false
36
- version_requirements: *70148779727360
35
+ version_requirements: *70170737357560
37
36
  - !ruby/object:Gem::Dependency
38
37
  name: rspec
39
- requirement: &70148779726900 !ruby/object:Gem::Requirement
38
+ requirement: &70170737356940 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ~>
@@ -44,10 +43,10 @@ dependencies:
44
43
  version: 2.6.0
45
44
  type: :development
46
45
  prerelease: false
47
- version_requirements: *70148779726900
46
+ version_requirements: *70170737356940
48
47
  - !ruby/object:Gem::Dependency
49
48
  name: machinist
50
- requirement: &70148779726440 !ruby/object:Gem::Requirement
49
+ requirement: &70170737356480 !ruby/object:Gem::Requirement
51
50
  none: false
52
51
  requirements:
53
52
  - - ~>
@@ -55,10 +54,10 @@ dependencies:
55
54
  version: 1.0.6
56
55
  type: :development
57
56
  prerelease: false
58
- version_requirements: *70148779726440
57
+ version_requirements: *70170737356480
59
58
  - !ruby/object:Gem::Dependency
60
59
  name: faker
61
- requirement: &70148779725980 !ruby/object:Gem::Requirement
60
+ requirement: &70170737356000 !ruby/object:Gem::Requirement
62
61
  none: false
63
62
  requirements:
64
63
  - - ~>
@@ -66,10 +65,10 @@ dependencies:
66
65
  version: 0.9.5
67
66
  type: :development
68
67
  prerelease: false
69
- version_requirements: *70148779725980
68
+ version_requirements: *70170737356000
70
69
  - !ruby/object:Gem::Dependency
71
70
  name: sqlite3
72
- requirement: &70148779725520 !ruby/object:Gem::Requirement
71
+ requirement: &70170737355320 !ruby/object:Gem::Requirement
73
72
  none: false
74
73
  requirements:
75
74
  - - ~>
@@ -77,7 +76,7 @@ dependencies:
77
76
  version: 1.3.3
78
77
  type: :development
79
78
  prerelease: false
80
- version_requirements: *70148779725520
79
+ version_requirements: *70170737355320
81
80
  description: ! "\n Squeel unlocks the power of ARel in your Rails 3 application
82
81
  with\n a handy block-based syntax. You can write subqueries, access named\n
83
82
  \ functions provided by your RDBMS, and more, all without writing\n SQL
@@ -170,7 +169,6 @@ files:
170
169
  - spec/squeel/visitors/symbol_visitor_spec.rb
171
170
  - spec/support/schema.rb
172
171
  - squeel.gemspec
173
- has_rdoc: true
174
172
  homepage: http://metautonomo.us/projects/squeel
175
173
  licenses: []
176
174
  post_install_message: ! '
@@ -204,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
204
202
  version: '0'
205
203
  requirements: []
206
204
  rubyforge_project: squeel
207
- rubygems_version: 1.6.2
205
+ rubygems_version: 1.8.6
208
206
  signing_key:
209
207
  specification_version: 3
210
208
  summary: ActiveRecord 3, improved.