ransack 1.1.0 → 1.2.0
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 +4 -4
- data/.travis.yml +12 -4
- data/CONTRIBUTING.md +10 -4
- data/Gemfile +12 -9
- data/README.md +46 -11
- data/lib/ransack.rb +4 -2
- data/lib/ransack/adapters/active_record.rb +1 -1
- data/lib/ransack/adapters/active_record/3.0/compat.rb +16 -6
- data/lib/ransack/adapters/active_record/3.0/context.rb +32 -16
- data/lib/ransack/adapters/active_record/3.1/context.rb +32 -15
- data/lib/ransack/adapters/active_record/3.2/context.rb +1 -1
- data/lib/ransack/adapters/active_record/base.rb +9 -6
- data/lib/ransack/adapters/active_record/context.rb +193 -2
- data/lib/ransack/configuration.rb +4 -4
- data/lib/ransack/constants.rb +81 -18
- data/lib/ransack/context.rb +27 -12
- data/lib/ransack/helpers/form_builder.rb +126 -91
- data/lib/ransack/helpers/form_helper.rb +34 -12
- data/lib/ransack/naming.rb +2 -1
- data/lib/ransack/nodes/attribute.rb +6 -4
- data/lib/ransack/nodes/bindable.rb +3 -1
- data/lib/ransack/nodes/condition.rb +40 -27
- data/lib/ransack/nodes/grouping.rb +19 -13
- data/lib/ransack/nodes/node.rb +3 -3
- data/lib/ransack/nodes/sort.rb +5 -3
- data/lib/ransack/nodes/value.rb +2 -2
- data/lib/ransack/predicate.rb +18 -9
- data/lib/ransack/ransacker.rb +4 -4
- data/lib/ransack/search.rb +9 -12
- data/lib/ransack/translate.rb +42 -21
- data/lib/ransack/version.rb +1 -1
- data/lib/ransack/visitor.rb +4 -4
- data/ransack.gemspec +17 -7
- data/spec/blueprints/notes.rb +2 -0
- data/spec/blueprints/people.rb +4 -1
- data/spec/console.rb +3 -3
- data/spec/ransack/adapters/active_record/base_spec.rb +149 -22
- data/spec/ransack/adapters/active_record/context_spec.rb +5 -5
- data/spec/ransack/configuration_spec.rb +17 -8
- data/spec/ransack/dependencies_spec.rb +8 -0
- data/spec/ransack/helpers/form_builder_spec.rb +37 -14
- data/spec/ransack/helpers/form_helper_spec.rb +5 -5
- data/spec/ransack/predicate_spec.rb +6 -3
- data/spec/ransack/search_spec.rb +95 -73
- data/spec/ransack/translate_spec.rb +14 -0
- data/spec/spec_helper.rb +14 -8
- data/spec/support/en.yml +6 -0
- data/spec/support/schema.rb +76 -31
- metadata +48 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b7aa5bc2bb1d2e9d78aea7431f3f3124b09ffdb
|
4
|
+
data.tar.gz: f16d483b98b63bd8d427678a185cb8ce7e6e56a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03ca0f504276420d2bc2f3ed1b2e2685091043f01fac0bcf538802d8ddae0ba6ee8c4fc36224d12aaf955beb3f709581dd7a207ef9b98a4eb10af136c0c38f2f
|
7
|
+
data.tar.gz: f77643a092da8bc3119b4f85d8cdc90977df7b78fc8738b3c0a490d18a21c492bed5e84df349bf4d3214016783c32ae07a82f14ea48435d94abbc63774af72b8
|
data/.travis.yml
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
before_install:
|
4
|
+
- travis_retry gem install bundler
|
5
|
+
|
1
6
|
rvm:
|
2
|
-
- 1.9.3
|
3
7
|
- 2.0.0
|
4
8
|
|
5
9
|
env:
|
6
|
-
-
|
10
|
+
- RAILS=4-1-stable DB=sqlite3
|
11
|
+
- RAILS=4-1-stable DB=mysql
|
12
|
+
- RAILS=4-1-stable DB=postgres
|
13
|
+
- RAILS=4-0-stable DB=sqlite3
|
7
14
|
- RAILS=4-0-stable DB=mysql
|
8
15
|
- RAILS=4-0-stable DB=postgres
|
9
16
|
- RAILS=3-2-stable DB=sqlite
|
@@ -17,5 +24,6 @@ env:
|
|
17
24
|
- RAILS=3-0-stable DB=postgres
|
18
25
|
|
19
26
|
before_script:
|
20
|
-
- mysql -e 'create database ransack;'
|
21
|
-
-
|
27
|
+
- mysql -e 'create database ransack collate utf8_general_ci;'
|
28
|
+
- mysql -e 'use ransack;show variables like "%character%";show variables like "%collation%";'
|
29
|
+
- psql -c 'create database ransack;' -U postgres
|
data/CONTRIBUTING.md
CHANGED
@@ -8,13 +8,17 @@ When filing an issue on the Ransack project, please provide these details:
|
|
8
8
|
* The version of Ransack *and* the version of Rails.
|
9
9
|
* Any relevant stack traces ("Full trace" preferred)
|
10
10
|
|
11
|
-
In 99% of cases, this information is enough to determine the cause and
|
11
|
+
In 99% of cases, this information is enough to determine the cause and
|
12
|
+
solution to the problem that is being described.
|
12
13
|
|
13
|
-
Any issue that is open for 14 days without actionable information or activity
|
14
|
+
Any issue that is open for 14 days without actionable information or activity
|
15
|
+
will be marked as "stalled" and then closed. Stalled issues can be re-opened
|
16
|
+
if the information requested is provided.
|
14
17
|
|
15
18
|
## Pull requests
|
16
19
|
|
17
|
-
We gladly accept pull requests to fix bugs and, in some circumstances, add new
|
20
|
+
We gladly accept pull requests to fix bugs and, in some circumstances, add new
|
21
|
+
features to Ransack.
|
18
22
|
|
19
23
|
Here's a quick guide:
|
20
24
|
|
@@ -32,7 +36,9 @@ a test!
|
|
32
36
|
|
33
37
|
4. Make the test pass.
|
34
38
|
|
35
|
-
5. Push to your fork and submit a pull request. If the changes will apply
|
39
|
+
5. Push to your fork and submit a pull request. If the changes will apply
|
40
|
+
cleanly to the latest stable branches and master branch, you will only need
|
41
|
+
to submit one pull request.
|
36
42
|
|
37
43
|
At this point you're waiting on us. We like to at least comment on, if not
|
38
44
|
accept, pull requests within three business days (and, typically, one business
|
data/Gemfile
CHANGED
@@ -1,30 +1,33 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
gemspec
|
3
3
|
|
4
4
|
gem 'rake'
|
5
5
|
|
6
|
-
rails = ENV['RAILS'] || '
|
6
|
+
rails = ENV['RAILS'] || 'master'
|
7
7
|
|
8
|
-
gem '
|
8
|
+
gem 'polyamorous', github: 'activerecord-hackery/polyamorous'
|
9
9
|
|
10
10
|
case rails
|
11
11
|
when /\// # A path
|
12
|
-
gem 'activesupport', :
|
13
|
-
gem 'activemodel', :
|
14
|
-
gem 'activerecord', :
|
15
|
-
gem 'actionpack', :
|
12
|
+
gem 'activesupport', path: "#{rails}/activesupport"
|
13
|
+
gem 'activemodel', path: "#{rails}/activemodel"
|
14
|
+
gem 'activerecord', path: "#{rails}/activerecord"
|
15
|
+
gem 'actionpack', path: "#{rails}/activerecord"
|
16
16
|
when /^v/ # A tagged version
|
17
|
-
git 'git://github.com/rails/rails.git', :
|
17
|
+
git 'git://github.com/rails/rails.git', tag: rails do
|
18
18
|
gem 'activesupport'
|
19
19
|
gem 'activemodel'
|
20
20
|
gem 'activerecord'
|
21
21
|
gem 'actionpack'
|
22
22
|
end
|
23
23
|
else
|
24
|
-
git 'git://github.com/rails/rails.git', :
|
24
|
+
git 'git://github.com/rails/rails.git', branch: rails do
|
25
25
|
gem 'activesupport'
|
26
26
|
gem 'activemodel'
|
27
27
|
gem 'activerecord'
|
28
28
|
gem 'actionpack'
|
29
29
|
end
|
30
|
+
if rails == '3-0-stable'
|
31
|
+
gem 'mysql2', '< 0.3'
|
32
|
+
end
|
30
33
|
end
|
data/README.md
CHANGED
@@ -1,29 +1,42 @@
|
|
1
1
|
# Ransack
|
2
2
|
|
3
|
-
[](https://travis-ci.org/activerecord-hackery/ransack)
|
4
4
|
|
5
|
-
Ransack is a rewrite of [MetaSearch](https://github.com/
|
5
|
+
Ransack is a rewrite of [MetaSearch](https://github.com/activerecord-hackery/meta_search) created by [Ernie Miller](http://twitter.com/erniemiller) and maintained by [Ryan Bigg](http://twitter.com/ryanbigg), [Jon Atack](http://twitter.com/jonatack) and a great group of [contributors](https://github.com/activerecord-hackery/ransack/graphs/contributors). While it
|
6
6
|
supports many of the same features as MetaSearch, its underlying implementation differs
|
7
7
|
greatly from MetaSearch, and _backwards compatibility is not a design goal._
|
8
8
|
|
9
9
|
Ransack enables the creation of both simple and [advanced](http://ransack-demo.herokuapp.com/users/advanced_search)
|
10
|
-
search forms against your application's models. If you're looking for something that
|
10
|
+
search forms against your application's models (demo source code [here](https://github.com/activerecord-hackery/ransack_demo)). If you're looking for something that
|
11
11
|
simplifies query generation at the model or controller layer, you're probably not looking
|
12
12
|
for Ransack (or MetaSearch, for that matter). Try
|
13
|
-
[Squeel](https://github.com/
|
13
|
+
[Squeel](https://github.com/activerecord-hackery/squeel) instead.
|
14
14
|
|
15
15
|
## Getting started
|
16
16
|
|
17
17
|
In your Gemfile:
|
18
18
|
|
19
19
|
```ruby
|
20
|
-
gem "ransack" # Last officially released gem (Rails 3 and 4)
|
20
|
+
gem "ransack" # Last officially released gem (compatible Rails 3.x and 4.0, but not 4.1)
|
21
21
|
```
|
22
22
|
|
23
|
-
Or if you want to use the
|
23
|
+
Or if you want to use the latest updates (Rails 3.x and 4.0, but not 4.1):
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
gem "ransack", github: "
|
26
|
+
gem "ransack", github: "activerecord-hackery/ransack" # Track git repo
|
27
|
+
```
|
28
|
+
|
29
|
+
If you are on Rails 4.0, you may prefer to use the streamlined, legacy code-free, latest-updates version of Ransack on the [Rails 4 branch](https://github.com/activerecord-hackery/ransack/tree/rails-4):
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
gem "ransack", github: "activerecord-hackery/ransack", branch: "rails-4"
|
33
|
+
```
|
34
|
+
|
35
|
+
Finally, if you are on Rails 4.1 (or 4.2.0.alpha), use the [Rails 4.1 branch](https://github.com/activerecord-hackery/ransack/tree/rails-4.1) which contains the latest updates also on master and rails-4. In your Gemfile you'll need to include (for the moment) both Ransack and Polyamorous as follows:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
gem "ransack", github: "activerecord-hackery/ransack", branch: "rails-4.1"
|
39
|
+
gem "polyamorous", github: "activerecord-hackery/polyamorous"
|
27
40
|
```
|
28
41
|
|
29
42
|
## Usage
|
@@ -41,9 +54,11 @@ If you're coming from MetaSearch, things to note:
|
|
41
54
|
primarily to shorten query strings, though advanced queries (below) will still
|
42
55
|
run afoul of URL length limits in most browsers and require a switch to HTTP
|
43
56
|
POST requests. This key is
|
44
|
-
[configurable](https://github.com/
|
57
|
+
[configurable](https://github.com/activerecord-hackery/ransack/wiki/Configuration).
|
58
|
+
|
45
59
|
2. `form_for` is now `search_form_for`, and validates that a Ransack::Search object
|
46
60
|
is passed to it.
|
61
|
+
|
47
62
|
3. Common ActiveRecord::Relation methods are no longer delegated by the search object.
|
48
63
|
Instead, you will get your search results (an ActiveRecord::Relation in the case of
|
49
64
|
the ActiveRecord adapter) via a call to `Search#result`. If passed `distinct: true`,
|
@@ -78,7 +93,7 @@ In your view:
|
|
78
93
|
```
|
79
94
|
|
80
95
|
`cont` (contains) and `start` (starts with) are just two of the available search predicates.
|
81
|
-
See [Constants](https://github.com/
|
96
|
+
See [Constants](https://github.com/activerecord-hackery/ransack/blob/master/lib/ransack/constants.rb) for a full list and the [wiki](https://github.com/activerecord-hackery/ransack/wiki/Basic-Searching) for more description.
|
82
97
|
|
83
98
|
### Advanced Mode
|
84
99
|
|
@@ -116,7 +131,16 @@ end
|
|
116
131
|
|
117
132
|
Once you've done so, you can make use of the helpers in Ransack::Helpers::FormBuilder to
|
118
133
|
construct much more complex search forms, such as the one on the
|
119
|
-
[demo page](http://ransack-demo.heroku.com).
|
134
|
+
[demo page](http://ransack-demo.heroku.com) (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
135
|
+
|
136
|
+
### Ransack #search method
|
137
|
+
|
138
|
+
Ransack will try to to make `#search` available in your models, but in the case that `#search` has already been defined, you can use `#ransack` instead. For example the following would be equivalent:
|
139
|
+
|
140
|
+
```
|
141
|
+
Article.search(params[:q])
|
142
|
+
Article.ransack(params[:q])
|
143
|
+
```
|
120
144
|
|
121
145
|
### has_many and belongs_to associations
|
122
146
|
|
@@ -173,6 +197,17 @@ end
|
|
173
197
|
<% end %>
|
174
198
|
```
|
175
199
|
|
200
|
+
## Using SimpleForm
|
201
|
+
|
202
|
+
If you want to combine form builders of ransack and SimpleForm, just set the RANSACK_FORM_BUILDER environment variable before Rails started, e.g. in ``config/application.rb`` before ``require 'rails/all'`` and of course use ``gem 'simple_form'`` in your ``Gemfile``:
|
203
|
+
```ruby
|
204
|
+
require File.expand_path('../boot', __FILE__)
|
205
|
+
|
206
|
+
ENV['RANSACK_FORM_BUILDER'] = '::SimpleForm::FormBuilder'
|
207
|
+
|
208
|
+
require 'rails/all'
|
209
|
+
```
|
210
|
+
|
176
211
|
## I18n
|
177
212
|
|
178
213
|
Take a look at our locale file on ``lib/ransack/locale/en.yml`` to check all available messages. You may also be interested in one of the many translations that are available on:
|
@@ -190,4 +225,4 @@ To support the project:
|
|
190
225
|
|
191
226
|
## Copyright
|
192
227
|
|
193
|
-
Copyright © 2011 [Ernie Miller](http://twitter.com/erniemiller)
|
228
|
+
Copyright © 2011-2014 [Ernie Miller](http://twitter.com/erniemiller)
|
data/lib/ransack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
1
3
|
require 'ransack/configuration'
|
2
4
|
|
3
5
|
module Ransack
|
@@ -8,7 +10,7 @@ end
|
|
8
10
|
|
9
11
|
Ransack.configure do |config|
|
10
12
|
Ransack::Constants::AREL_PREDICATES.each do |name|
|
11
|
-
config.add_predicate name, :
|
13
|
+
config.add_predicate name, arel_predicate: name
|
12
14
|
end
|
13
15
|
|
14
16
|
Ransack::Constants::DERIVED_PREDICATES.each do |args|
|
@@ -23,4 +25,4 @@ require 'ransack/adapters/active_record' if defined?(::ActiveRecord::Base)
|
|
23
25
|
require 'ransack/helpers'
|
24
26
|
require 'action_controller'
|
25
27
|
|
26
|
-
ActionController::Base.helper Ransack::Helpers::FormHelper
|
28
|
+
ActionController::Base.helper Ransack::Helpers::FormHelper
|
@@ -2,7 +2,7 @@
|
|
2
2
|
if Arel::Nodes::And < Arel::Nodes::Binary
|
3
3
|
class Ransack::Visitor
|
4
4
|
def visit_Ransack_Nodes_And(object)
|
5
|
-
nodes = object.values.map {|o| accept(o)}.compact
|
5
|
+
nodes = object.values.map { |o| accept(o) }.compact
|
6
6
|
return nil unless nodes.size > 0
|
7
7
|
|
8
8
|
if nodes.size > 1
|
@@ -132,9 +132,15 @@ module Arel
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def visit_Arel_Nodes_NamedFunction o
|
135
|
-
"#{
|
136
|
-
|
137
|
-
|
135
|
+
"#{
|
136
|
+
o.name
|
137
|
+
}(#{
|
138
|
+
o.distinct ? 'DISTINCT ' : ''
|
139
|
+
}#{
|
140
|
+
o.expressions.map { |x| visit x }.join(', ')
|
141
|
+
})#{
|
142
|
+
o.alias ? " AS #{visit o.alias}" : ''
|
143
|
+
}"
|
138
144
|
end
|
139
145
|
|
140
146
|
def visit_Arel_Nodes_And o
|
@@ -146,13 +152,17 @@ module Arel
|
|
146
152
|
end
|
147
153
|
|
148
154
|
def visit_Arel_Nodes_Values o
|
149
|
-
"VALUES (#{
|
155
|
+
"VALUES (#{
|
156
|
+
o.expressions.zip(o.columns)
|
157
|
+
.map { |value, attr|
|
150
158
|
if Nodes::SqlLiteral === value
|
151
159
|
visit_Arel_Nodes_SqlLiteral value
|
152
160
|
else
|
153
161
|
quote(value, attr && column_for(attr))
|
154
162
|
end
|
155
|
-
}
|
163
|
+
}
|
164
|
+
.join ', '
|
165
|
+
})"
|
156
166
|
end
|
157
167
|
end
|
158
168
|
end
|
@@ -26,9 +26,11 @@ module Ransack
|
|
26
26
|
viz = Visitor.new
|
27
27
|
relation = @object.where(viz.accept(search.base))
|
28
28
|
if search.sorts.any?
|
29
|
-
relation = relation.except(:order)
|
29
|
+
relation = relation.except(:order)
|
30
|
+
.reorder(viz.accept(search.sorts))
|
30
31
|
end
|
31
|
-
opts[:distinct] ?
|
32
|
+
opts[:distinct] ?
|
33
|
+
relation.select("DISTINCT #{@klass.quoted_table_name}.*") : relation
|
32
34
|
end
|
33
35
|
|
34
36
|
def attribute_method?(str, klass = @klass)
|
@@ -39,10 +41,15 @@ module Ransack
|
|
39
41
|
elsif (segments = str.split(/_/)).size > 1
|
40
42
|
remainder = []
|
41
43
|
found_assoc = nil
|
42
|
-
while !found_assoc && remainder.unshift(segments.pop) &&
|
43
|
-
|
44
|
+
while !found_assoc && remainder.unshift(segments.pop) &&
|
45
|
+
segments.size > 0 do
|
46
|
+
assoc, poly_class = unpolymorphize_association(
|
47
|
+
segments.join('_')
|
48
|
+
)
|
44
49
|
if found_assoc = get_association(assoc, klass)
|
45
|
-
exists = attribute_method?(
|
50
|
+
exists = attribute_method?(
|
51
|
+
remainder.join('_'), poly_class || found_assoc.klass
|
52
|
+
)
|
46
53
|
end
|
47
54
|
end
|
48
55
|
end
|
@@ -56,7 +63,9 @@ module Ransack
|
|
56
63
|
|
57
64
|
def type_for(attr)
|
58
65
|
return nil unless attr && attr.valid?
|
59
|
-
klassify(attr.parent)
|
66
|
+
klassify(attr.parent)
|
67
|
+
.columns_hash[attr.arel_attribute.name.to_s]
|
68
|
+
.type
|
60
69
|
end
|
61
70
|
|
62
71
|
private
|
@@ -69,22 +78,26 @@ module Ransack
|
|
69
78
|
elsif (segments = str.split(/_/)).size > 1
|
70
79
|
remainder = []
|
71
80
|
found_assoc = nil
|
72
|
-
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
81
|
+
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
82
|
+
!found_assoc do
|
73
83
|
assoc, klass = unpolymorphize_association(segments.join('_'))
|
74
84
|
if found_assoc = get_association(assoc, parent)
|
75
|
-
join = build_or_find_association(
|
76
|
-
|
85
|
+
join = build_or_find_association(
|
86
|
+
found_assoc.name, parent, klass
|
87
|
+
)
|
88
|
+
parent, attr_name = get_parent_and_attribute_name(
|
89
|
+
remainder.join('_'), join
|
90
|
+
)
|
77
91
|
end
|
78
92
|
end
|
79
93
|
end
|
80
|
-
|
81
94
|
[parent, attr_name]
|
82
95
|
end
|
83
96
|
|
84
97
|
def get_association(str, parent = @base)
|
85
98
|
klass = klassify parent
|
86
99
|
ransackable_association?(str, klass) &&
|
87
|
-
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
100
|
+
klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
|
88
101
|
end
|
89
102
|
|
90
103
|
def join_dependency(relation)
|
@@ -114,9 +127,9 @@ module Ransack
|
|
114
127
|
association_joins = buckets['association_join'] || []
|
115
128
|
stashed_association_joins = buckets['stashed_join'] || []
|
116
129
|
join_nodes = buckets['join_node'] || []
|
117
|
-
string_joins = (buckets['string_join'] || [])
|
118
|
-
|
119
|
-
|
130
|
+
string_joins = (buckets['string_join'] || [])
|
131
|
+
.map { |x| x.strip }
|
132
|
+
.uniq
|
120
133
|
|
121
134
|
join_list = relation.send :custom_join_sql, (string_joins + join_nodes)
|
122
135
|
|
@@ -134,13 +147,16 @@ module Ransack
|
|
134
147
|
end
|
135
148
|
|
136
149
|
def build_or_find_association(name, parent = @base, klass = nil)
|
137
|
-
found_association = @join_dependency.join_associations
|
150
|
+
found_association = @join_dependency.join_associations
|
151
|
+
.detect do |assoc|
|
138
152
|
assoc.reflection.name == name &&
|
139
153
|
assoc.parent == parent &&
|
140
154
|
(!klass || assoc.reflection.klass == klass)
|
141
155
|
end
|
142
156
|
unless found_association
|
143
|
-
@join_dependency.send(
|
157
|
+
@join_dependency.send(
|
158
|
+
:build, Polyamorous::Join.new(name, @join_type, klass), parent
|
159
|
+
)
|
144
160
|
found_association = @join_dependency.join_associations.last
|
145
161
|
# Leverage the stashed association functionality in AR
|
146
162
|
@object = @object.joins(found_association)
|
@@ -25,9 +25,12 @@ module Ransack
|
|
25
25
|
viz = Visitor.new
|
26
26
|
relation = @object.where(viz.accept(search.base))
|
27
27
|
if search.sorts.any?
|
28
|
-
relation = relation.except(:order)
|
28
|
+
relation = relation.except(:order)
|
29
|
+
.reorder(viz.accept(search.sorts))
|
29
30
|
end
|
30
|
-
opts[:distinct] ?
|
31
|
+
opts[:distinct] ?
|
32
|
+
relation.select("DISTINCT #{@klass.quoted_table_name}.*") :
|
33
|
+
relation
|
31
34
|
end
|
32
35
|
|
33
36
|
def attribute_method?(str, klass = @klass)
|
@@ -38,10 +41,15 @@ module Ransack
|
|
38
41
|
elsif (segments = str.split(/_/)).size > 1
|
39
42
|
remainder = []
|
40
43
|
found_assoc = nil
|
41
|
-
while !found_assoc && remainder.unshift(segments.pop) &&
|
42
|
-
|
44
|
+
while !found_assoc && remainder.unshift(segments.pop) &&
|
45
|
+
segments.size > 0 do
|
46
|
+
assoc, poly_class = unpolymorphize_association(
|
47
|
+
segments.join('_')
|
48
|
+
)
|
43
49
|
if found_assoc = get_association(assoc, klass)
|
44
|
-
exists = attribute_method?(
|
50
|
+
exists = attribute_method?(
|
51
|
+
remainder.join('_'), poly_class || found_assoc.klass
|
52
|
+
)
|
45
53
|
end
|
46
54
|
end
|
47
55
|
end
|
@@ -75,11 +83,16 @@ module Ransack
|
|
75
83
|
elsif (segments = str.split(/_/)).size > 1
|
76
84
|
remainder = []
|
77
85
|
found_assoc = nil
|
78
|
-
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
86
|
+
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
87
|
+
!found_assoc do
|
79
88
|
assoc, klass = unpolymorphize_association(segments.join('_'))
|
80
89
|
if found_assoc = get_association(assoc, parent)
|
81
|
-
join = build_or_find_association(
|
82
|
-
|
90
|
+
join = build_or_find_association(
|
91
|
+
found_assoc.name, parent, klass
|
92
|
+
)
|
93
|
+
parent, attr_name = get_parent_and_attribute_name(
|
94
|
+
remainder.join('_'), join
|
95
|
+
)
|
83
96
|
end
|
84
97
|
end
|
85
98
|
end
|
@@ -90,7 +103,7 @@ module Ransack
|
|
90
103
|
def get_association(str, parent = @base)
|
91
104
|
klass = klassify parent
|
92
105
|
ransackable_association?(str, klass) &&
|
93
|
-
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
106
|
+
klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
|
94
107
|
end
|
95
108
|
|
96
109
|
def join_dependency(relation)
|
@@ -120,11 +133,12 @@ module Ransack
|
|
120
133
|
association_joins = buckets['association_join'] || []
|
121
134
|
stashed_association_joins = buckets['stashed_join'] || []
|
122
135
|
join_nodes = buckets['join_node'] || []
|
123
|
-
string_joins = (buckets['string_join'] || [])
|
124
|
-
|
125
|
-
|
136
|
+
string_joins = (buckets['string_join'] || [])
|
137
|
+
.map { |x| x.strip }
|
138
|
+
.uniq
|
126
139
|
|
127
|
-
join_list = relation.send :custom_join_ast,
|
140
|
+
join_list = relation.send :custom_join_ast,
|
141
|
+
relation.table.from(relation.table), string_joins
|
128
142
|
|
129
143
|
join_dependency = JoinDependency.new(
|
130
144
|
relation.klass,
|
@@ -140,13 +154,16 @@ module Ransack
|
|
140
154
|
end
|
141
155
|
|
142
156
|
def build_or_find_association(name, parent = @base, klass = nil)
|
143
|
-
found_association = @join_dependency.join_associations
|
157
|
+
found_association = @join_dependency.join_associations
|
158
|
+
.detect do |assoc|
|
144
159
|
assoc.reflection.name == name &&
|
145
160
|
assoc.parent == parent &&
|
146
161
|
(!klass || assoc.reflection.klass == klass)
|
147
162
|
end
|
148
163
|
unless found_association
|
149
|
-
@join_dependency.send(
|
164
|
+
@join_dependency.send(
|
165
|
+
:build, Polyamorous::Join.new(name, @join_type, klass), parent
|
166
|
+
)
|
150
167
|
found_association = @join_dependency.join_associations.last
|
151
168
|
# Leverage the stashed association functionality in AR
|
152
169
|
@object = @object.joins(found_association)
|