ransack 0.7.2 → 1.0.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 +7 -0
- data/.travis.yml +10 -3
- data/CONTRIBUTING.md +1 -2
- data/Gemfile +3 -3
- data/README.md +78 -57
- data/lib/ransack/adapters/active_record.rb +2 -0
- data/lib/ransack/adapters/active_record/3.0/context.rb +9 -15
- data/lib/ransack/adapters/active_record/3.1/context.rb +9 -14
- data/lib/ransack/adapters/active_record/3.2/context.rb +44 -0
- data/lib/ransack/adapters/active_record/context.rb +11 -9
- data/lib/ransack/constants.rb +6 -1
- data/lib/ransack/context.rb +15 -1
- data/lib/ransack/helpers/form_builder.rb +2 -1
- data/lib/ransack/helpers/form_helper.rb +3 -2
- data/lib/ransack/locale/es.yml +1 -1
- data/lib/ransack/locale/fr.yml +70 -0
- data/lib/ransack/translate.rb +9 -8
- data/lib/ransack/version.rb +1 -1
- data/ransack.gemspec +3 -3
- data/spec/ransack/adapters/active_record/context_spec.rb +6 -0
- data/spec/ransack/helpers/form_builder_spec.rb +16 -6
- data/spec/ransack/helpers/form_helper_spec.rb +57 -10
- data/spec/ransack/predicate_spec.rb +37 -4
- data/spec/ransack/search_spec.rb +50 -35
- data/spec/spec_helper.rb +2 -0
- data/spec/support/en.yml +3 -1
- data/spec/support/schema.rb +40 -35
- metadata +16 -29
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ae3f27c548f10f431a0d5c13b3a9863d7e707e73
|
4
|
+
data.tar.gz: 83840f65158cb74f04e8bd5e0b63d1251bcd3272
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1883d781609fbae07044a265169f8960c4416e2836d3c332ad66ffb2ec8a4427bd8f3114d7539bb62f7ed0d132478004751763bb51d96e782b6ca904f0d82f88
|
7
|
+
data.tar.gz: 8a817a0a03b09622c527a63c24502d3c9da37c46096ccf376a2577cf05d54c6302c9ba13e866dc2789f79e588c2b262a5e799760a15dfeae0c9339da6d016f8a
|
data/.travis.yml
CHANGED
@@ -1,9 +1,16 @@
|
|
1
1
|
rvm:
|
2
2
|
- 1.8.7
|
3
3
|
- 1.9.2
|
4
|
-
-
|
5
|
-
-
|
6
|
-
- ruby-head
|
4
|
+
- 1.9.3
|
5
|
+
- 2.0.0
|
7
6
|
|
8
7
|
env:
|
8
|
+
- RAILS=4-0-stable
|
9
9
|
- RAILS=3-2-stable
|
10
|
+
- RAILS=3-1-stable
|
11
|
+
- RAILS=3-0-stable
|
12
|
+
|
13
|
+
matrix:
|
14
|
+
exclude:
|
15
|
+
- rvm: 1.8.7
|
16
|
+
env: RAILS=4-0-stable
|
data/CONTRIBUTING.md
CHANGED
@@ -24,8 +24,7 @@ Here's a quick guide:
|
|
24
24
|
to know that you have a clean slate:
|
25
25
|
|
26
26
|
$ bundle install
|
27
|
-
$ bundle exec rake
|
28
|
-
$ bundle exec rake
|
27
|
+
$ bundle exec rake spec
|
29
28
|
|
30
29
|
3. Add a test for your change. Only refactoring and documentation changes
|
31
30
|
require no new tests. If you are adding functionality or fixing a bug, we need
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# Ransack
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/ernie/ransack)
|
4
|
+
|
5
|
+
Ransack is a rewrite of [MetaSearch](https://github.com/ernie/meta_search). While it
|
4
6
|
supports many of the same features as MetaSearch, its underlying implementation differs
|
5
7
|
greatly from MetaSearch, and _backwards compatibility is not a design goal._
|
6
8
|
|
@@ -8,18 +10,21 @@ Ransack enables the creation of both simple and [advanced](http://ransack-demo.h
|
|
8
10
|
search forms against your application's models. If you're looking for something that
|
9
11
|
simplifies query generation at the model or controller layer, you're probably not looking
|
10
12
|
for Ransack (or MetaSearch, for that matter). Try
|
11
|
-
[Squeel](
|
13
|
+
[Squeel](https://github.com/ernie/squeel) instead.
|
12
14
|
|
13
15
|
## Getting started
|
14
16
|
|
15
17
|
In your Gemfile:
|
16
18
|
|
17
|
-
|
19
|
+
```ruby
|
20
|
+
gem "ransack" # Last officially released gem (Rails 3 and 4)
|
21
|
+
```
|
18
22
|
|
19
23
|
Or if you want to use the bleeding edge:
|
20
24
|
|
21
|
-
|
22
|
-
|
25
|
+
```ruby
|
26
|
+
gem "ransack", github: "ernie/ransack" # Track git repo
|
27
|
+
```
|
23
28
|
|
24
29
|
## Usage
|
25
30
|
|
@@ -41,32 +46,36 @@ If you're coming from MetaSearch, things to note:
|
|
41
46
|
is passed to it.
|
42
47
|
3. Common ActiveRecord::Relation methods are no longer delegated by the search object.
|
43
48
|
Instead, you will get your search results (an ActiveRecord::Relation in the case of
|
44
|
-
the ActiveRecord adapter) via a call to `Search#result`. If passed
|
49
|
+
the ActiveRecord adapter) via a call to `Search#result`. If passed `distinct: true`,
|
45
50
|
`result` will generate a `SELECT DISTINCT` to avoid returning duplicate rows, even if
|
46
51
|
conditions on a join would otherwise result in some.
|
47
52
|
|
48
53
|
Please note that for many databases, a sort on an associated table's columns will
|
49
|
-
result in invalid SQL with
|
54
|
+
result in invalid SQL with `distinct: true` -- in those cases, you're on your own,
|
50
55
|
and will need to modify the result as needed to allow these queries to work. Thankfully,
|
51
56
|
9 times out of 10, sort against the search's base is sufficient, though, as that's
|
52
57
|
generally what's being displayed on your results page.
|
53
58
|
|
54
59
|
In your controller:
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
61
|
+
```ruby
|
62
|
+
def index
|
63
|
+
@q = Person.search(params[:q])
|
64
|
+
@people = @q.result(distinct: true)
|
65
|
+
end
|
66
|
+
```
|
60
67
|
|
61
68
|
In your view:
|
62
69
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
+
```erb
|
71
|
+
<%= search_form_for @q do |f| %>
|
72
|
+
<%= f.label :name_cont %>
|
73
|
+
<%= f.text_field :name_cont %>
|
74
|
+
<%= f.label :articles_title_start %>
|
75
|
+
<%= f.text_field :articles_title_start %>
|
76
|
+
<%= f.submit %>
|
77
|
+
<% end %>
|
78
|
+
```
|
70
79
|
|
71
80
|
`cont` (contains) and `start` (starts with) are just two of the available search predicates.
|
72
81
|
See [Constants](https://github.com/ernie/ransack/blob/master/lib/ransack/constants.rb) for a full list and the [wiki](https://github.com/ernie/ransack/wiki/Basic-Searching) for more description.
|
@@ -81,23 +90,29 @@ parameter string will typically force you to use the HTTP POST method instead of
|
|
81
90
|
|
82
91
|
This means you'll need to tweak your routes...
|
83
92
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
93
|
+
```ruby
|
94
|
+
resources :people do
|
95
|
+
collection do
|
96
|
+
match 'search' => 'people#search', via: [:get, :post], as: :search
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
89
100
|
|
90
101
|
... and add another controller action ...
|
91
102
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
103
|
+
```ruby
|
104
|
+
def search
|
105
|
+
index
|
106
|
+
render :index
|
107
|
+
end
|
108
|
+
```
|
96
109
|
|
97
110
|
... and update your `search_form_for` line in the view ...
|
98
111
|
|
99
|
-
|
100
|
-
|
112
|
+
```erb
|
113
|
+
<%= search_form_for @q, url: search_people_path,
|
114
|
+
html: { method: :post } do |f| %>
|
115
|
+
```
|
101
116
|
|
102
117
|
Once you've done so, you can make use of the helpers in Ransack::Helpers::FormBuilder to
|
103
118
|
construct much more complex search forms, such as the one on the
|
@@ -109,48 +124,54 @@ You can easily use Ransack to search in associated objects.
|
|
109
124
|
|
110
125
|
Given you have these associations ...
|
111
126
|
|
112
|
-
|
113
|
-
|
127
|
+
```ruby
|
128
|
+
class Employee < ActiveRecord::Base
|
129
|
+
belongs_to :supervisor
|
114
130
|
|
115
|
-
|
116
|
-
|
131
|
+
# has attribute last_name:string
|
132
|
+
end
|
117
133
|
|
118
|
-
|
119
|
-
|
134
|
+
class Department < ActiveRecord::Base
|
135
|
+
has_many :supervisors
|
120
136
|
|
121
|
-
|
122
|
-
|
137
|
+
# has attribute title:string
|
138
|
+
end
|
123
139
|
|
124
|
-
|
125
|
-
|
126
|
-
|
140
|
+
class Supervisor < ActiveRecord::Base
|
141
|
+
belongs_to :department
|
142
|
+
has_many :employees
|
127
143
|
|
128
|
-
|
129
|
-
|
144
|
+
# has attribute last_name:string
|
145
|
+
end
|
146
|
+
```
|
130
147
|
|
131
148
|
... and a controller ...
|
132
149
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
150
|
+
```ruby
|
151
|
+
class SupervisorsController < ApplicationController
|
152
|
+
def index
|
153
|
+
@search = Supervisor.search(params[:q])
|
154
|
+
@supervisors = @search.result(distinct: true)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
139
158
|
|
140
159
|
... you might set up your form like this ...
|
141
160
|
|
142
|
-
|
143
|
-
|
144
|
-
|
161
|
+
```erb
|
162
|
+
<%= search_form_for @search do |f| %>
|
163
|
+
<%= f.label :last_name_cont %>
|
164
|
+
<%= f.text_field :last_name_cont %>
|
145
165
|
|
146
|
-
|
147
|
-
|
166
|
+
<%= f.label :department_title_cont %>
|
167
|
+
<%= f.text_field :department_title_cont %>
|
148
168
|
|
149
|
-
|
150
|
-
|
169
|
+
<%= f.label :employees_last_name_cont %>
|
170
|
+
<%= f.text_field :employees_last_name_cont %>
|
151
171
|
|
152
|
-
|
153
|
-
|
172
|
+
<%= f.submit "search" %>
|
173
|
+
<% end %>
|
174
|
+
```
|
154
175
|
|
155
176
|
|
156
177
|
## Contributions
|
@@ -7,6 +7,8 @@ when /^3\.0\./
|
|
7
7
|
require 'ransack/adapters/active_record/3.0/context'
|
8
8
|
when /^3\.1\./
|
9
9
|
require 'ransack/adapters/active_record/3.1/context'
|
10
|
+
when /^3\.2\./
|
11
|
+
require 'ransack/adapters/active_record/3.2/context'
|
10
12
|
else
|
11
13
|
require 'ransack/adapters/active_record/context'
|
12
14
|
end
|
@@ -10,17 +10,23 @@ module Ransack
|
|
10
10
|
# Because the AR::Associations namespace is insane
|
11
11
|
JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
|
12
12
|
JoinBase = JoinDependency::JoinBase
|
13
|
-
|
13
|
+
|
14
|
+
# Redefine a few things for ActiveRecord 3.0.
|
15
|
+
|
14
16
|
def initialize(object, options = {})
|
15
17
|
super
|
16
18
|
@arel_visitor = Arel::Visitors.visitor_for @engine
|
17
19
|
end
|
18
20
|
|
21
|
+
def relation_for(object)
|
22
|
+
object.scoped
|
23
|
+
end
|
24
|
+
|
19
25
|
def evaluate(search, opts = {})
|
20
26
|
viz = Visitor.new
|
21
27
|
relation = @object.where(viz.accept(search.base))
|
22
28
|
if search.sorts.any?
|
23
|
-
relation = relation.except(:order).
|
29
|
+
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
24
30
|
end
|
25
31
|
opts[:distinct] ? relation.select("DISTINCT #{@klass.quoted_table_name}.*") : relation
|
26
32
|
end
|
@@ -48,18 +54,6 @@ module Ransack
|
|
48
54
|
parent.table
|
49
55
|
end
|
50
56
|
|
51
|
-
def klassify(obj)
|
52
|
-
if Class === obj && ::ActiveRecord::Base > obj
|
53
|
-
obj
|
54
|
-
elsif obj.respond_to? :klass
|
55
|
-
obj.klass
|
56
|
-
elsif obj.respond_to? :active_record
|
57
|
-
obj.active_record
|
58
|
-
else
|
59
|
-
raise ArgumentError, "Don't know how to klassify #{obj}"
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
57
|
def type_for(attr)
|
64
58
|
return nil unless attr && attr.valid?
|
65
59
|
klassify(attr.parent).columns_hash[attr.arel_attribute.name.to_s].type
|
@@ -158,4 +152,4 @@ module Ransack
|
|
158
152
|
end
|
159
153
|
end
|
160
154
|
end
|
161
|
-
end
|
155
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'ransack/context'
|
2
|
+
require 'ransack/adapters/active_record/compat'
|
2
3
|
require 'polyamorous'
|
3
4
|
|
4
5
|
module Ransack
|
@@ -8,17 +9,23 @@ module Ransack
|
|
8
9
|
# Because the AR::Associations namespace is insane
|
9
10
|
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
10
11
|
JoinPart = JoinDependency::JoinPart
|
11
|
-
|
12
|
+
|
13
|
+
# Redefine a few things for ActiveRecord 3.1.
|
14
|
+
|
12
15
|
def initialize(object, options = {})
|
13
16
|
super
|
14
17
|
@arel_visitor = Arel::Visitors.visitor_for @engine
|
15
18
|
end
|
16
19
|
|
20
|
+
def relation_for(object)
|
21
|
+
object.scoped
|
22
|
+
end
|
23
|
+
|
17
24
|
def evaluate(search, opts = {})
|
18
25
|
viz = Visitor.new
|
19
26
|
relation = @object.where(viz.accept(search.base))
|
20
27
|
if search.sorts.any?
|
21
|
-
relation = relation.except(:order).
|
28
|
+
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
22
29
|
end
|
23
30
|
opts[:distinct] ? relation.select("DISTINCT #{@klass.quoted_table_name}.*") : relation
|
24
31
|
end
|
@@ -46,18 +53,6 @@ module Ransack
|
|
46
53
|
parent.table
|
47
54
|
end
|
48
55
|
|
49
|
-
def klassify(obj)
|
50
|
-
if Class === obj && ::ActiveRecord::Base > obj
|
51
|
-
obj
|
52
|
-
elsif obj.respond_to? :klass
|
53
|
-
obj.klass
|
54
|
-
elsif obj.respond_to? :active_record
|
55
|
-
obj.active_record
|
56
|
-
else
|
57
|
-
raise ArgumentError, "Don't know how to klassify #{obj}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
56
|
def type_for(attr)
|
62
57
|
return nil unless attr && attr.valid?
|
63
58
|
name = attr.arel_attribute.name.to_s
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'ransack/context'
|
2
|
+
require 'ransack/adapters/active_record/3.1/context'
|
3
|
+
require 'ransack/adapters/active_record/compat'
|
4
|
+
require 'polyamorous'
|
5
|
+
|
6
|
+
module Ransack
|
7
|
+
module Adapters
|
8
|
+
module ActiveRecord
|
9
|
+
class Context < ::Ransack::Context
|
10
|
+
|
11
|
+
# Redefine a few things for ActiveRecord 3.2.
|
12
|
+
|
13
|
+
def initialize(object, options = {})
|
14
|
+
super
|
15
|
+
@arel_visitor = @engine.connection.visitor
|
16
|
+
end
|
17
|
+
|
18
|
+
def relation_for(object)
|
19
|
+
object.scoped
|
20
|
+
end
|
21
|
+
|
22
|
+
def type_for(attr)
|
23
|
+
return nil unless attr && attr.valid?
|
24
|
+
name = attr.arel_attribute.name.to_s
|
25
|
+
table = attr.arel_attribute.relation.table_name
|
26
|
+
|
27
|
+
schema_cache = @engine.connection.schema_cache
|
28
|
+
raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
|
29
|
+
schema_cache.columns_hash[table][name].type
|
30
|
+
end
|
31
|
+
|
32
|
+
def evaluate(search, opts = {})
|
33
|
+
viz = Visitor.new
|
34
|
+
relation = @object.where(viz.accept(search.base))
|
35
|
+
if search.sorts.any?
|
36
|
+
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
37
|
+
end
|
38
|
+
opts[:distinct] ? relation.uniq : relation
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'ransack/context'
|
2
|
-
require 'ransack/adapters/active_record/3.
|
2
|
+
require 'ransack/adapters/active_record/3.2/context'
|
3
3
|
require 'ransack/adapters/active_record/compat'
|
4
4
|
require 'polyamorous'
|
5
5
|
|
@@ -8,33 +8,35 @@ module Ransack
|
|
8
8
|
module ActiveRecord
|
9
9
|
class Context < ::Ransack::Context
|
10
10
|
|
11
|
-
# Redefine a few things that have changed with 3.2.
|
12
|
-
|
13
11
|
def initialize(object, options = {})
|
14
12
|
super
|
15
13
|
@arel_visitor = @engine.connection.visitor
|
16
14
|
end
|
17
15
|
|
16
|
+
def relation_for(object)
|
17
|
+
object.all
|
18
|
+
end
|
19
|
+
|
18
20
|
def type_for(attr)
|
19
21
|
return nil unless attr && attr.valid?
|
20
22
|
name = attr.arel_attribute.name.to_s
|
21
23
|
table = attr.arel_attribute.relation.table_name
|
22
24
|
|
23
|
-
schema_cache = @engine.connection.schema_cache
|
24
|
-
raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
|
25
|
-
schema_cache.columns_hash
|
25
|
+
schema_cache = @engine.connection.schema_cache
|
26
|
+
raise "No table named #{table} exists" unless schema_cache.table_exists?(table)
|
27
|
+
schema_cache.columns_hash(table)[name].type
|
26
28
|
end
|
27
29
|
|
28
30
|
def evaluate(search, opts = {})
|
29
31
|
viz = Visitor.new
|
30
32
|
relation = @object.where(viz.accept(search.base))
|
31
33
|
if search.sorts.any?
|
32
|
-
relation = relation.except(:order).
|
34
|
+
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
33
35
|
end
|
34
|
-
opts[:distinct] ? relation.
|
36
|
+
opts[:distinct] ? relation.distinct : relation
|
35
37
|
end
|
36
38
|
|
37
39
|
end
|
38
40
|
end
|
39
41
|
end
|
40
|
-
end
|
42
|
+
end
|