squeel 1.1.1 → 1.2.1
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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.travis.yml +36 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/README.md +47 -6
- data/Rakefile +14 -2
- data/lib/squeel.rb +9 -1
- data/lib/squeel/adapters/active_record.rb +0 -1
- data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
- data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
- data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
- data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
- data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
- data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
- data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
- data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
- data/lib/squeel/adapters/active_record/context.rb +7 -7
- data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
- data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
- data/lib/squeel/core_ext/symbol.rb +3 -3
- data/lib/squeel/dsl.rb +1 -1
- data/lib/squeel/nodes.rb +1 -0
- data/lib/squeel/nodes/as.rb +12 -0
- data/lib/squeel/nodes/join.rb +8 -4
- data/lib/squeel/nodes/key_path.rb +10 -1
- data/lib/squeel/nodes/node.rb +21 -0
- data/lib/squeel/nodes/stub.rb +8 -4
- data/lib/squeel/nodes/subquery_join.rb +44 -0
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors.rb +2 -0
- data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
- data/lib/squeel/visitors/order_visitor.rb +9 -2
- data/lib/squeel/visitors/predicate_visitor.rb +11 -0
- data/lib/squeel/visitors/preload_visitor.rb +12 -0
- data/lib/squeel/visitors/visitor.rb +89 -13
- data/spec/config.travis.yml +13 -0
- data/spec/config.yml +12 -0
- data/spec/console.rb +3 -12
- data/spec/core_ext/symbol_spec.rb +3 -3
- data/spec/helpers/squeel_helper.rb +8 -5
- data/spec/spec_helper.rb +4 -16
- data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
- data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
- data/spec/squeel/core_ext/symbol_spec.rb +3 -3
- data/spec/squeel/nodes/join_spec.rb +4 -4
- data/spec/squeel/nodes/stub_spec.rb +3 -3
- data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
- data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
- data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
- data/spec/squeel/visitors/visitor_spec.rb +7 -6
- data/spec/support/models.rb +99 -15
- data/spec/support/schema.rb +109 -4
- data/squeel.gemspec +8 -6
- metadata +89 -107
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/spec/blueprints/articles.rb +0 -5
- data/spec/blueprints/comments.rb +0 -5
- data/spec/blueprints/notes.rb +0 -3
- data/spec/blueprints/people.rb +0 -4
- data/spec/blueprints/tags.rb +0 -3
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3d3196690094de459e3e30179f68ce63ee2032e1
|
4
|
+
data.tar.gz: 827a3e061d72e29cac9b615cbc3fb66c6c1e3462
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fcf69aceda5afe483dfad4a91d40b21d8472b250c5a9eb719afaa6ccc1252d9488d176c0cc9d41d7a68e18a66f0dd5ff33dbcf4d77b59fd12c673e311cae680c
|
7
|
+
data.tar.gz: 6a934cabda1f179d9a920cf4974fe8776b68d4a58578ee2c067ae4f1e7fac78a0989268920e711845d78367d66521c297e0cb8215e5d8fe85b336d275ae0d249
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
CHANGED
@@ -1,8 +1,40 @@
|
|
1
|
+
before_script:
|
2
|
+
- mysql -e "create database squeel_test_examples;"
|
3
|
+
- psql -c "create database squeel_test_examples;" -U postgres
|
4
|
+
|
5
|
+
script:
|
6
|
+
- bundle exec rake test:$ADAPTER
|
7
|
+
|
1
8
|
rvm:
|
2
9
|
- 1.9.3
|
10
|
+
- 2.0.0
|
11
|
+
- 2.1.1
|
12
|
+
- 2.1.2
|
3
13
|
|
4
14
|
env:
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
15
|
+
global:
|
16
|
+
- SQ_CONFIG_FILE=$TRAVIS_BUILD_DIR/spec/config.travis.yml
|
17
|
+
matrix:
|
18
|
+
- RAILS=master AREL=master ADAPTER=sqlite3
|
19
|
+
- RAILS=4-1-stable AREL=5-0-stable ADAPTER=sqlite3
|
20
|
+
- RAILS=4-0-stable AREL=4-0-stable ADAPTER=sqlite3
|
21
|
+
- RAILS=3-2-stable AREL=3-0-stable ADAPTER=sqlite3
|
22
|
+
- RAILS=3-1-stable AREL=2-2-stable ADAPTER=sqlite3
|
23
|
+
- RAILS=3-0-stable AREL=2-0-stable ADAPTER=sqlite3
|
24
|
+
- RAILS=master AREL=master ADAPTER=mysql
|
25
|
+
- RAILS=4-1-stable AREL=5-0-stable ADAPTER=mysql
|
26
|
+
- RAILS=4-0-stable AREL=4-0-stable ADAPTER=mysql
|
27
|
+
- RAILS=3-2-stable AREL=3-0-stable ADAPTER=mysql
|
28
|
+
- RAILS=3-1-stable AREL=2-2-stable ADAPTER=mysql
|
29
|
+
- RAILS=3-0-stable AREL=2-0-stable ADAPTER=mysql
|
30
|
+
- RAILS=master AREL=master ADAPTER=mysql2
|
31
|
+
- RAILS=4-1-stable AREL=5-0-stable ADAPTER=mysql2
|
32
|
+
- RAILS=4-0-stable AREL=4-0-stable ADAPTER=mysql2
|
33
|
+
- RAILS=3-2-stable AREL=3-0-stable ADAPTER=mysql2
|
34
|
+
- RAILS=3-1-stable AREL=2-2-stable ADAPTER=mysql2
|
35
|
+
- RAILS=master AREL=master ADAPTER=postgresql
|
36
|
+
- RAILS=4-1-stable AREL=5-0-stable ADAPTER=postgresql
|
37
|
+
- RAILS=4-0-stable AREL=4-0-stable ADAPTER=postgresql
|
38
|
+
- RAILS=3-2-stable AREL=3-0-stable ADAPTER=postgresql
|
39
|
+
- RAILS=3-1-stable AREL=2-2-stable ADAPTER=postgresql
|
40
|
+
- RAILS=3-0-stable AREL=2-0-stable ADAPTER=postgresql
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## 1.2.1 (Unreleased)
|
2
|
+
|
3
|
+
* Run all specs against sqlite, mysql and postgresql!
|
4
|
+
* Genereta table names correctly when joining through an association. Fixes#302.
|
5
|
+
* Enable Arel nodes in Squeel with "|" operator. Fixes #314.
|
6
|
+
* Properly append binds from a relation used in a subquery. Fixes #272.
|
7
|
+
|
8
|
+
## 1.2.0 (2014-07-16)
|
9
|
+
|
10
|
+
* Add compatibility to Ruby 2.0+ with Rails 4.1 and 4.2.0.alpha.
|
11
|
+
Fixes #301, #305, #307
|
12
|
+
* Enable using a relation as a subquery. Fixes #309
|
13
|
+
* Bind params correctly in subquery using associations. Fixes #312
|
14
|
+
* Use the correct attribute name when finding a Join node. Fixes #273.
|
15
|
+
|
1
16
|
## 1.1.1 (2013-09-03)
|
2
17
|
|
3
18
|
* Update relation extensions to support new count behavior in Active Record
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Squeel [](http://travis-ci.org/activerecord-hackery/squeel) [](http://coderwall.com/ernie)
|
2
2
|
|
3
3
|
Squeel lets you write your Active Record queries with fewer strings, and more Ruby,
|
4
4
|
by making the Arel awesomeness that lies beneath Active Record more accessible.
|
@@ -23,8 +23,10 @@ just a simple example -- Squeel's capable of a whole lot more. Keep reading.
|
|
23
23
|
In your Gemfile:
|
24
24
|
|
25
25
|
```ruby
|
26
|
+
# Make sure you are using the latest version of polyamorous
|
27
|
+
gem "polyamorous", :git => "git://github.com/activerecord-hackery/polyamorous.git"
|
26
28
|
gem "squeel" # Last officially released gem
|
27
|
-
# gem "squeel", :git => "git://github.com/
|
29
|
+
# gem "squeel", :git => "git://github.com/activerecord-hackery/squeel.git" # Track git repo
|
28
30
|
```
|
29
31
|
|
30
32
|
Then bundle as usual.
|
@@ -86,6 +88,24 @@ A Squeel keypath is essentially a more concise and readable alternative to a
|
|
86
88
|
deeply nested hash. For instance, in standard Active Record, you might join several
|
87
89
|
associations like this to perform a query:
|
88
90
|
|
91
|
+
#### Rails 4+
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
Person.joins(:articles => {:comments => :person}).references(:all)
|
95
|
+
# => SELECT "people".* FROM "people"
|
96
|
+
# LEFT OUTER JOIN "articles" ON "articles"."person_id" = "people"."id"
|
97
|
+
# LEFT OUTER JOIN "comments" ON "comments"."article_id" = "articles"."id"
|
98
|
+
# LEFT OUTER JOIN "people" "people_comments" ON "people_comments"."id" = "comments"."person_id"
|
99
|
+
```
|
100
|
+
|
101
|
+
With a keypath, this would look like:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
Person.joins{articles.comments.person}.references(:all)
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Rails 3.x
|
108
|
+
|
89
109
|
```ruby
|
90
110
|
Person.joins(:articles => {:comments => :person})
|
91
111
|
# => SELECT "people".* FROM "people"
|
@@ -413,6 +433,27 @@ Person.joins{children.parent.children}.
|
|
413
433
|
|
414
434
|
Keypaths were used here for clarity, but nested hashes would work just as well.
|
415
435
|
|
436
|
+
You can also use a subquery in a join.
|
437
|
+
|
438
|
+
Notice:
|
439
|
+
1. Squeel can only accept an ActiveRecord::Relation class of subqueries in a join.
|
440
|
+
2. Use the chain with caution. You should call `as` first to get a Nodes::As, then call `on` to get a join node.
|
441
|
+
|
442
|
+
```ruby
|
443
|
+
subquery = OrderItem.group(:orderable_id).select { [orderable_id, sum(quantity * unit_price).as(amount)] }
|
444
|
+
Seat.joins { [payment.outer, subquery.as('seat_order_items').on { id == seat_order_items.orderable_id}.outer] }.
|
445
|
+
select { [seat_order_items.amount, "seats.*"] }
|
446
|
+
# => SELECT "seat_order_items"."amount", seats.*
|
447
|
+
# FROM "seats"
|
448
|
+
# LEFT OUTER JOIN "payments" ON "payments"."id" = "seats"."payment_id"
|
449
|
+
# LEFT OUTER JOIN (
|
450
|
+
# SELECT "order_items"."orderable_id",
|
451
|
+
# sum("order_items"."quantity" * "order_items"."unit_price") AS amount
|
452
|
+
# FROM "order_items"
|
453
|
+
# GROUP BY "order_items"."orderable_id"
|
454
|
+
# ) seat_order_items ON "seats"."id" = "seat_order_items"."orderable_id"
|
455
|
+
```
|
456
|
+
|
416
457
|
### Functions
|
417
458
|
|
418
459
|
You can call SQL functions just like you would call a method in Ruby...
|
@@ -470,7 +511,7 @@ As you can see, just like functions, these operations can be given aliases.
|
|
470
511
|
To select more than one attribute (or calculated attribute) simply put them into an array:
|
471
512
|
|
472
513
|
```ruby
|
473
|
-
p = Person.select{[ name.op('||', '-diddly').as(flanderized_name),
|
514
|
+
p = Person.select{[ name.op('||', '-diddly').as(flanderized_name),
|
474
515
|
coalesce(name, '<no name given>').as(name_with_default) ]}.first
|
475
516
|
p.flanderized_name
|
476
517
|
# => "Aric Smith-diddly"
|
@@ -537,9 +578,9 @@ For further information, see
|
|
537
578
|
[this post to the Rails list](https://groups.google.com/forum/?fromgroups=#!topic/rubyonrails-core/NQJJzZ7R7S0),
|
538
579
|
[this commit](https://github.com/lifo/docrails/commit/50c5005bafe7e43f81a141cd2c512379aec74325) to
|
539
580
|
the [Active Record guides](http://edgeguides.rubyonrails.org/active_record_querying.html#hash-conditions),
|
540
|
-
[#67](https://github.com/
|
541
|
-
[#75](https://github.com/
|
542
|
-
[#171](https://github.com/
|
581
|
+
[#67](https://github.com/activerecord-hackery/squeel/issues/67),
|
582
|
+
[#75](https://github.com/activerecord-hackery/squeel/issues/75), and
|
583
|
+
[#171](https://github.com/activerecord-hackery/squeel/issues/171).
|
543
584
|
|
544
585
|
## Compatibility with MetaWhere
|
545
586
|
|
data/Rakefile
CHANGED
@@ -4,10 +4,22 @@ require 'rspec/core/rake_task'
|
|
4
4
|
Bundler::GemHelper.install_tasks
|
5
5
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec) do |rspec|
|
7
|
-
rspec.rspec_opts = ['--backtrace']
|
7
|
+
rspec.rspec_opts = ['--backtrace', '--color', '--format documentation']
|
8
8
|
end
|
9
9
|
|
10
|
-
task :default =>
|
10
|
+
task :default => 'test_sqlite3'
|
11
|
+
|
12
|
+
%w(sqlite3 mysql mysql2 postgresql).each do |adapter|
|
13
|
+
namespace :test do
|
14
|
+
task(adapter => ["#{adapter}:env", "spec"])
|
15
|
+
end
|
16
|
+
|
17
|
+
namespace adapter do
|
18
|
+
task(:env) { ENV['SQ_DB'] = adapter }
|
19
|
+
end
|
20
|
+
|
21
|
+
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
|
22
|
+
end
|
11
23
|
|
12
24
|
desc "Open an irb session with Squeel and the sample data used in specs"
|
13
25
|
task :console do
|
data/lib/squeel.rb
CHANGED
@@ -1,7 +1,16 @@
|
|
1
1
|
require 'squeel/configuration'
|
2
|
+
require 'polyamorous'
|
2
3
|
|
3
4
|
module Squeel
|
4
5
|
|
6
|
+
if defined?(Arel::InnerJoin)
|
7
|
+
InnerJoin = Arel::InnerJoin
|
8
|
+
OuterJoin = Arel::OuterJoin
|
9
|
+
else
|
10
|
+
InnerJoin = Arel::Nodes::InnerJoin
|
11
|
+
OuterJoin = Arel::Nodes::OuterJoin
|
12
|
+
end
|
13
|
+
|
5
14
|
extend Configuration
|
6
15
|
|
7
16
|
# Prevent warnings on the console when doing things some might describe as "evil"
|
@@ -30,7 +39,6 @@ module Squeel
|
|
30
39
|
alias_predicate aliaz, original
|
31
40
|
end
|
32
41
|
end
|
33
|
-
|
34
42
|
end
|
35
43
|
|
36
44
|
require 'squeel/dsl'
|
@@ -1,7 +1,6 @@
|
|
1
1
|
case ActiveRecord::VERSION::MAJOR
|
2
2
|
when 3, 4
|
3
3
|
ActiveRecord::Relation.send :include, Squeel::Nodes::Aliasing
|
4
|
-
require 'squeel/adapters/active_record/join_dependency_extensions'
|
5
4
|
require 'squeel/adapters/active_record/base_extensions'
|
6
5
|
|
7
6
|
adapter_directory = "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'squeel/adapters/active_record/join_dependency_extensions'
|
@@ -54,8 +54,9 @@ module Squeel
|
|
54
54
|
end
|
55
55
|
|
56
56
|
stashed_association_joins = joins.grep(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation)
|
57
|
+
subquery_joins = joins.grep(Nodes::SubqueryJoin)
|
57
58
|
|
58
|
-
non_association_joins = (joins - association_joins - stashed_association_joins)
|
59
|
+
non_association_joins = (joins - association_joins - stashed_association_joins - subquery_joins)
|
59
60
|
custom_joins = custom_join_sql(*non_association_joins)
|
60
61
|
|
61
62
|
self.join_dependency = JoinDependency.new(@klass, association_joins, custom_joins)
|
@@ -79,6 +80,16 @@ module Squeel
|
|
79
80
|
relation = relation.join(left, join_type).on(*right)
|
80
81
|
end
|
81
82
|
|
83
|
+
subquery_joins.each do |join|
|
84
|
+
relation = relation.
|
85
|
+
join(
|
86
|
+
Arel::Nodes::TableAlias.new(
|
87
|
+
join.subquery.right,
|
88
|
+
Arel::Nodes::Grouping.new(join.subquery.left.arel.ast)),
|
89
|
+
join.type).
|
90
|
+
on(*where_visit(join.constraints))
|
91
|
+
end
|
92
|
+
|
82
93
|
relation = relation.join(custom_joins)
|
83
94
|
end
|
84
95
|
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'squeel/adapters/active_record/join_dependency_extensions'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'squeel/adapters/active_record/join_dependency_extensions'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'squeel/adapters/active_record/join_dependency_extensions'
|
@@ -30,6 +30,24 @@ module Squeel
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
def where_unscoping(target_value)
|
34
|
+
target_value = target_value.to_s
|
35
|
+
|
36
|
+
where_values.reject! do |rel|
|
37
|
+
case rel
|
38
|
+
when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
|
39
|
+
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
|
40
|
+
subrelation.name == target_value
|
41
|
+
when Hash
|
42
|
+
rel.stringify_keys.has_key?(target_value)
|
43
|
+
when Squeel::Nodes::Predicate
|
44
|
+
rel.expr.symbol.to_s == target_value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
bind_values.reject! { |col,_| col.name == target_value }
|
49
|
+
end
|
50
|
+
|
33
51
|
def build_arel
|
34
52
|
arel = Arel::SelectManager.new(table.engine, table)
|
35
53
|
|
@@ -52,9 +70,49 @@ module Squeel
|
|
52
70
|
arel.from(build_from) if from_value
|
53
71
|
arel.lock(lock_value) if lock_value
|
54
72
|
|
73
|
+
# Reorder bind indexes when joins or subqueries include more bindings.
|
74
|
+
# Special for PostgreSQL
|
75
|
+
if bind_values.size > 1
|
76
|
+
bvs = bind_values
|
77
|
+
arel.ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i|
|
78
|
+
column = bvs[i].first
|
79
|
+
bp.replace connection.substitute_at(column, i)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
55
83
|
arel
|
56
84
|
end
|
57
85
|
|
86
|
+
def build_where(opts, other = [])
|
87
|
+
case opts
|
88
|
+
when String, Array
|
89
|
+
super
|
90
|
+
else # Let's prevent PredicateBuilder from doing its thing
|
91
|
+
[opts, *other].map do |arg|
|
92
|
+
case arg
|
93
|
+
when Array # Just in case there's an array in there somewhere
|
94
|
+
@klass.send(:sanitize_sql, arg)
|
95
|
+
when Hash
|
96
|
+
attributes = @klass.send(:expand_hash_conditions_for_aggregates, arg)
|
97
|
+
|
98
|
+
attributes.values.grep(::ActiveRecord::Relation) do |rel|
|
99
|
+
self.bind_values += rel.bind_values
|
100
|
+
end
|
101
|
+
|
102
|
+
preprocess_attrs_with_ar(attributes)
|
103
|
+
|
104
|
+
when Squeel::Nodes::Node
|
105
|
+
arg.grep(::ActiveRecord::Relation) do |rel|
|
106
|
+
self.bind_values += rel.bind_values
|
107
|
+
end
|
108
|
+
arg
|
109
|
+
else
|
110
|
+
arg
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
58
116
|
def build_from
|
59
117
|
opts, name = from_visit(from_value)
|
60
118
|
case opts
|
@@ -109,6 +167,40 @@ module Squeel
|
|
109
167
|
self
|
110
168
|
end
|
111
169
|
|
170
|
+
def where_values_hash_with_squeel(relation_table_name = table_name)
|
171
|
+
equalities = find_equality_predicates(where_visit(with_default_scope.where_values), relation_table_name)
|
172
|
+
|
173
|
+
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
174
|
+
|
175
|
+
Hash[equalities.map { |where|
|
176
|
+
name = where.left.name
|
177
|
+
[name, binds.fetch(name.to_s) { where.right }]
|
178
|
+
}]
|
179
|
+
end
|
180
|
+
|
181
|
+
def to_sql_with_binding_params
|
182
|
+
@to_sql ||= begin
|
183
|
+
relation = self
|
184
|
+
connection = klass.connection
|
185
|
+
|
186
|
+
if eager_loading?
|
187
|
+
find_with_associations { |rel| relation = rel }
|
188
|
+
end
|
189
|
+
|
190
|
+
ast = relation.arel.ast
|
191
|
+
binds = relation.bind_values.dup
|
192
|
+
|
193
|
+
visitor = connection.visitor.clone
|
194
|
+
visitor.class_eval do
|
195
|
+
include ::Arel::Visitors::BindVisitor
|
196
|
+
end
|
197
|
+
|
198
|
+
visitor.accept(ast) do
|
199
|
+
connection.quote(*binds.shift.reverse)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
112
204
|
private
|
113
205
|
|
114
206
|
def dehashified_order_values
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'squeel/adapters/active_record/compat'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Associations
|
5
|
+
class AssociationScope
|
6
|
+
def eval_scope(klass, scope, owner)
|
7
|
+
if scope.is_a?(Relation)
|
8
|
+
scope
|
9
|
+
else
|
10
|
+
klass.unscoped.instance_exec(owner, &scope).visited
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'squeel/adapters/active_record/context'
|
2
|
+
|
3
|
+
module Squeel
|
4
|
+
module Adapters
|
5
|
+
module ActiveRecord
|
6
|
+
class Context < ::Squeel::Context
|
7
|
+
class NoParentFoundError < RuntimeError; end
|
8
|
+
|
9
|
+
def initialize(object)
|
10
|
+
super
|
11
|
+
@base = object.join_root
|
12
|
+
@engine = @base.base_klass.arel_engine
|
13
|
+
@arel_visitor = get_arel_visitor
|
14
|
+
@default_table = Arel::Table.new(@base.table_name, :as => @base.aliased_table_name, :engine => @engine)
|
15
|
+
end
|
16
|
+
|
17
|
+
def find(object, parent = @base)
|
18
|
+
if ::ActiveRecord::Associations::JoinDependency::JoinPart === parent
|
19
|
+
case object
|
20
|
+
when String, Symbol, Nodes::Stub
|
21
|
+
assoc_name = object.to_s
|
22
|
+
find_string_symbol_stub_association(@base.children, @base, assoc_name, parent)
|
23
|
+
when Nodes::Join
|
24
|
+
find_node_join_association(@base.children, @base, object, parent)
|
25
|
+
else
|
26
|
+
find_other_association(@base.children, @base, object, parent)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find!(object, parent = @base)
|
32
|
+
if ::ActiveRecord::Associations::JoinDependency::JoinPart === parent
|
33
|
+
result =
|
34
|
+
case object
|
35
|
+
when String, Symbol, Nodes::Stub
|
36
|
+
assoc_name = object.to_s
|
37
|
+
find_string_symbol_stub_association(@base.children, @base, assoc_name, parent)
|
38
|
+
when Nodes::Join
|
39
|
+
find_node_join_association(@base.children, @base, object, parent)
|
40
|
+
else
|
41
|
+
find_other_association(@base.children, @base, object, parent)
|
42
|
+
end
|
43
|
+
|
44
|
+
result || raise(NoParentFoundError, "can't find #{object} in #{parent}")
|
45
|
+
else
|
46
|
+
raise NoParentFoundError, "can't find #{object} in #{parent}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def traverse!(keypath, parent = @base, include_endpoint = false)
|
51
|
+
parent = @base if keypath.absolute?
|
52
|
+
keypath.path_without_endpoint.each do |key|
|
53
|
+
parent = find!(key, parent)
|
54
|
+
end
|
55
|
+
parent = find!(keypath.endpoint, parent) if include_endpoint
|
56
|
+
|
57
|
+
parent
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def find_string_symbol_stub_association(join_associations, current_parent, assoc_name, target_parent)
|
62
|
+
join_associations.each do |assoc|
|
63
|
+
return assoc if assoc.reflection.name.to_s == assoc_name && current_parent.equal?(target_parent)
|
64
|
+
child_assoc = find_string_symbol_stub_association(assoc.children, assoc, assoc_name, target_parent)
|
65
|
+
return child_assoc if child_assoc
|
66
|
+
end && false
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_node_join_association(join_associations, current_parent, object, target_parent)
|
70
|
+
join_associations.each do |assoc|
|
71
|
+
return assoc if assoc.reflection.name == object._name && current_parent.equal?(target_parent) &&
|
72
|
+
(object.polymorphic? ? assoc.reflection.klass == object._klass : true)
|
73
|
+
child_assoc = find_node_join_association(assoc.children, assoc, object, target_parent)
|
74
|
+
return child_assoc if child_assoc
|
75
|
+
end && false
|
76
|
+
end
|
77
|
+
|
78
|
+
def find_other_association(join_associations, current_parent, object, target_parent)
|
79
|
+
join_associations.each do |assoc|
|
80
|
+
return assoc if assoc.reflection == object && current_parent.equal?(target_parent)
|
81
|
+
child_assoc = find_other_association(assoc.children, assoc, object, target_parent)
|
82
|
+
return child_assoc if child_assoc
|
83
|
+
end && false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|