ransack 0.7.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/ernie/ransack.png?branch=master)](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
|