ransack 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -10
- data/README.md +5 -0
- data/lib/ransack.rb +1 -1
- data/lib/ransack/adapters/active_record.rb +0 -1
- data/lib/ransack/adapters/active_record/base.rb +5 -0
- data/lib/ransack/constants.rb +6 -4
- data/lib/ransack/context.rb +4 -0
- data/lib/ransack/helpers/form_builder.rb +35 -21
- data/lib/ransack/nodes/sort.rb +1 -0
- data/lib/ransack/search.rb +1 -1
- data/lib/ransack/version.rb +1 -1
- data/ransack.gemspec +3 -0
- data/spec/blueprints/people.rb +1 -0
- data/spec/helpers/ransack_helper.rb +7 -0
- data/spec/ransack/adapters/active_record/base_spec.rb +38 -3
- data/spec/ransack/adapters/active_record/context_spec.rb +2 -2
- data/spec/ransack/predicate_spec.rb +25 -21
- data/spec/ransack/search_spec.rb +80 -10
- data/spec/spec_helper.rb +2 -1
- data/spec/support/schema.rb +22 -4
- metadata +45 -7
- data/lib/ransack/locale/cs.yml +0 -70
- data/lib/ransack/locale/es.yml +0 -70
- data/lib/ransack/locale/fr.yml +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa2e8ee70ef82d95b0bc2c83e0005505252e2b13
|
4
|
+
data.tar.gz: ae83094fdd4be127ba5471b48442a6c1f2922680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2d9b0b0be3432472e801b0fdbfc19075a71461bba475c64f0cafa7cc32f9b8606b9136488fd7e1f1ef0dc34b7f2877815d32fc30a626ad74c3c75d21422e1b8
|
7
|
+
data.tar.gz: dbb0b40594df15a6e7321d97e9ba134402168facc2de41b9a07cdf58683bb172f61aa01a6f5a61c5f73d6ff7053c597e2ce13b3b942cba2aea1b79f67de614cd
|
data/.travis.yml
CHANGED
@@ -1,16 +1,21 @@
|
|
1
1
|
rvm:
|
2
|
-
- 1.8.7
|
3
|
-
- 1.9.2
|
4
2
|
- 1.9.3
|
5
3
|
- 2.0.0
|
6
4
|
|
7
5
|
env:
|
8
|
-
|
9
|
-
- RAILS=
|
10
|
-
- RAILS=
|
11
|
-
- RAILS=3-
|
6
|
+
- RAILS=4-0-stable DB=sqlite
|
7
|
+
- RAILS=4-0-stable DB=mysql
|
8
|
+
- RAILS=4-0-stable DB=postgres
|
9
|
+
- RAILS=3-2-stable DB=sqlite
|
10
|
+
- RAILS=3-2-stable DB=mysql
|
11
|
+
- RAILS=3-2-stable DB=postgres
|
12
|
+
- RAILS=3-1-stable DB=sqlite
|
13
|
+
- RAILS=3-1-stable DB=mysql
|
14
|
+
- RAILS=3-1-stable DB=postgres
|
15
|
+
- RAILS=3-0-stable DB=sqlite
|
16
|
+
- RAILS=3-0-stable DB=mysql
|
17
|
+
- RAILS=3-0-stable DB=postgres
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
env: RAILS=4-0-stable
|
19
|
+
before_script:
|
20
|
+
- mysql -e 'create database ransack;'
|
21
|
+
- psql -c 'create database ransack;' -U postgres
|
data/README.md
CHANGED
@@ -173,6 +173,11 @@ end
|
|
173
173
|
<% end %>
|
174
174
|
```
|
175
175
|
|
176
|
+
## I18n
|
177
|
+
|
178
|
+
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:
|
179
|
+
|
180
|
+
http://www.localeapp.com/projects/2999
|
176
181
|
|
177
182
|
## Contributions
|
178
183
|
|
data/lib/ransack.rb
CHANGED
@@ -19,7 +19,7 @@ end
|
|
19
19
|
require 'ransack/translate'
|
20
20
|
require 'ransack/search'
|
21
21
|
require 'ransack/ransacker'
|
22
|
-
require 'ransack/adapters/active_record'
|
22
|
+
require 'ransack/adapters/active_record' if defined?(::ActiveRecord::Base)
|
23
23
|
require 'ransack/helpers'
|
24
24
|
require 'action_controller'
|
25
25
|
|
@@ -23,6 +23,11 @@ module Ransack
|
|
23
23
|
column_names + _ransackers.keys
|
24
24
|
end
|
25
25
|
|
26
|
+
def ransortable_attributes(auth_object = nil)
|
27
|
+
# Here so users can overwrite the attributes that show up in the sort_select
|
28
|
+
ransackable_attributes(auth_object)
|
29
|
+
end
|
30
|
+
|
26
31
|
def ransackable_associations(auth_object = nil)
|
27
32
|
reflect_on_all_associations.map {|a| a.name.to_s}
|
28
33
|
end
|
data/lib/ransack/constants.rb
CHANGED
@@ -23,10 +23,12 @@ module Ransack
|
|
23
23
|
module_function
|
24
24
|
# replace % \ to \% \\
|
25
25
|
def escape_wildcards(unescaped)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
case ActiveRecord::Base.connection.adapter_name
|
27
|
+
when "SQLite"
|
28
|
+
unescaped
|
29
|
+
else
|
30
|
+
# Necessary for PostgreSQL and MySQL
|
31
|
+
unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
data/lib/ransack/context.rb
CHANGED
@@ -131,6 +131,10 @@ module Ransack
|
|
131
131
|
traverse(str).ransackable_attributes(auth_object)
|
132
132
|
end
|
133
133
|
|
134
|
+
def sortable_attributes(str = '')
|
135
|
+
traverse(str).ransortable_attributes(auth_object)
|
136
|
+
end
|
137
|
+
|
134
138
|
def searchable_associations(str = '')
|
135
139
|
traverse(str).ransackable_associations(auth_object)
|
136
140
|
end
|
@@ -23,16 +23,11 @@ module Ransack
|
|
23
23
|
bases = [''] + association_array(options[:associations])
|
24
24
|
if bases.size > 1
|
25
25
|
@template.grouped_collection_select(
|
26
|
-
@object_name, :name,
|
26
|
+
@object_name, :name, searchable_attribute_collection_for_bases(bases), :last, :first, :first, :last,
|
27
27
|
objectify_options(options), @default_options.merge(html_options)
|
28
28
|
)
|
29
29
|
else
|
30
|
-
collection =
|
31
|
-
[
|
32
|
-
attr_from_base_and_column(bases.first, c),
|
33
|
-
Translate.attribute(attr_from_base_and_column(bases.first, c), :context => object.context)
|
34
|
-
]
|
35
|
-
end
|
30
|
+
collection = searchable_attribute_collection_for_base(bases.first)
|
36
31
|
@template.collection_select(
|
37
32
|
@object_name, :name, collection, :first, :last,
|
38
33
|
objectify_options(options), @default_options.merge(html_options)
|
@@ -46,19 +41,14 @@ module Ransack
|
|
46
41
|
bases = [''] + association_array(options[:associations])
|
47
42
|
if bases.size > 1
|
48
43
|
@template.grouped_collection_select(
|
49
|
-
@object_name, :name,
|
44
|
+
@object_name, :name, sortable_attribute_collection_for_bases(bases), :last, :first, :first, :last,
|
50
45
|
objectify_options(options), @default_options.merge(html_options)
|
51
46
|
) + @template.collection_select(
|
52
47
|
@object_name, :dir, [['asc', object.translate('asc')], ['desc', object.translate('desc')]], :first, :last,
|
53
48
|
objectify_options(options), @default_options.merge(html_options)
|
54
49
|
)
|
55
50
|
else
|
56
|
-
collection =
|
57
|
-
[
|
58
|
-
attr_from_base_and_column(bases.first, c),
|
59
|
-
Translate.attribute(attr_from_base_and_column(bases.first, c), :context => object.context)
|
60
|
-
]
|
61
|
-
end
|
51
|
+
collection = sortable_attribute_collection_for_base(bases.first)
|
62
52
|
@template.collection_select(
|
63
53
|
@object_name, :name, collection, :first, :last,
|
64
54
|
objectify_options(options), @default_options.merge(html_options)
|
@@ -171,17 +161,29 @@ module Ransack
|
|
171
161
|
[base, column].reject {|v| v.blank?}.join('_')
|
172
162
|
end
|
173
163
|
|
174
|
-
def
|
164
|
+
def attribute_collection_for_base(attributes, base=nil)
|
165
|
+
attributes.map do |c|
|
166
|
+
[
|
167
|
+
attr_from_base_and_column(base, c),
|
168
|
+
Translate.attribute(attr_from_base_and_column(base, c), :context => object.context)
|
169
|
+
]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def sortable_attribute_collection_for_base(base=nil)
|
174
|
+
attribute_collection_for_base(object.context.sortable_attributes(base), base)
|
175
|
+
end
|
176
|
+
|
177
|
+
def searchable_attribute_collection_for_base(base=nil)
|
178
|
+
attribute_collection_for_base(object.context.searchable_attributes(base), base)
|
179
|
+
end
|
180
|
+
|
181
|
+
def sortable_attribute_collection_for_bases(bases)
|
175
182
|
bases.map do |base|
|
176
183
|
begin
|
177
184
|
[
|
178
185
|
Translate.association(base, :context => object.context),
|
179
|
-
|
180
|
-
[
|
181
|
-
attr_from_base_and_column(base, c),
|
182
|
-
Translate.attribute(attr_from_base_and_column(base, c), :context => object.context)
|
183
|
-
]
|
184
|
-
end
|
186
|
+
sortable_attribute_collection_for_base(base)
|
185
187
|
]
|
186
188
|
rescue UntraversableAssociationError => e
|
187
189
|
nil
|
@@ -189,6 +191,18 @@ module Ransack
|
|
189
191
|
end.compact
|
190
192
|
end
|
191
193
|
|
194
|
+
def searchable_attribute_collection_for_bases(bases)
|
195
|
+
bases.map do |base|
|
196
|
+
begin
|
197
|
+
[
|
198
|
+
Translate.association(base, :context => object.context),
|
199
|
+
searchable_attribute_collection_for_base(base)
|
200
|
+
]
|
201
|
+
rescue UntraversableAssociationError => e
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
end.compact
|
205
|
+
end
|
192
206
|
end
|
193
207
|
end
|
194
208
|
end
|
data/lib/ransack/nodes/sort.rb
CHANGED
data/lib/ransack/search.rb
CHANGED
@@ -14,7 +14,7 @@ module Ransack
|
|
14
14
|
:translate, :to => :base
|
15
15
|
|
16
16
|
def initialize(object, params = {}, options = {})
|
17
|
-
params ||= {}
|
17
|
+
(params ||= {}).delete_if { |k, v| v.blank? && v != false }
|
18
18
|
@context = Context.for(object, options)
|
19
19
|
@context.auth_object = options[:auth_object]
|
20
20
|
@base = Nodes::Grouping.new(@context, 'and')
|
data/lib/ransack/version.rb
CHANGED
data/ransack.gemspec
CHANGED
@@ -21,6 +21,9 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_development_dependency 'machinist', '~> 1.0.6'
|
22
22
|
s.add_development_dependency 'faker', '~> 0.9.5'
|
23
23
|
s.add_development_dependency 'sqlite3', '~> 1.3.3'
|
24
|
+
s.add_development_dependency 'pg', '0.17.0'
|
25
|
+
s.add_development_dependency 'mysql2', '0.3.13'
|
26
|
+
s.add_development_dependency 'pry', '0.9.12.2'
|
24
27
|
|
25
28
|
s.files = `git ls-files`.split("\n")
|
26
29
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/spec/blueprints/people.rb
CHANGED
@@ -20,6 +20,15 @@ module Ransack
|
|
20
20
|
end
|
21
21
|
|
22
22
|
describe '#ransacker' do
|
23
|
+
# For infix tests
|
24
|
+
def self.sane_adapter?
|
25
|
+
case ::ActiveRecord::Base.connection.adapter_name
|
26
|
+
when "SQLite3" || "PostgreSQL"
|
27
|
+
true
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
23
32
|
# in schema.rb, class Person:
|
24
33
|
# ransacker :reversed_name, :formatter => proc {|v| v.reverse} do |parent|
|
25
34
|
# parent.table[:name]
|
@@ -36,18 +45,36 @@ module Ransack
|
|
36
45
|
|
37
46
|
it 'can be accessed through associations' do
|
38
47
|
s = Person.search(:children_reversed_name_eq => 'htimS cirA')
|
39
|
-
|
48
|
+
|
49
|
+
s.result.to_sql.should match /#{quote_table_name("children_people")}.#{quote_column_name("name")} = 'Aric Smith'/
|
40
50
|
end
|
41
51
|
|
42
52
|
it 'allows an "attribute" to be an InfixOperation' do
|
43
53
|
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
44
54
|
s.result.first.should eq Person.find_by_name('Aric Smith')
|
45
|
-
end if defined?(Arel::Nodes::InfixOperation)
|
55
|
+
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
46
56
|
|
47
57
|
it "doesn't break #count if using InfixOperations" do
|
48
58
|
s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
|
49
59
|
s.result.count.should eq 1
|
50
|
-
end if defined?(Arel::Nodes::InfixOperation)
|
60
|
+
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
61
|
+
|
62
|
+
it "should function correctly when using fields with dots in them" do
|
63
|
+
s = Person.search(email_cont: "example.com")
|
64
|
+
s.result.exists?.should be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should function correctly when using fields with % in them" do
|
68
|
+
Person.create!(name: "110%-er")
|
69
|
+
s = Person.search(name_cont: "10%")
|
70
|
+
s.result.exists?.should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should function correctly when using fields with backslashes in them" do
|
74
|
+
Person.create!(name: "\\WINNER\\")
|
75
|
+
s = Person.search(name_cont: "\\WINNER\\")
|
76
|
+
s.result.exists?.should be_true
|
77
|
+
end
|
51
78
|
end
|
52
79
|
|
53
80
|
describe '#ransackable_attributes' do
|
@@ -58,6 +85,14 @@ module Ransack
|
|
58
85
|
it { should include 'doubled_name' }
|
59
86
|
end
|
60
87
|
|
88
|
+
describe '#ransortable_attributes' do
|
89
|
+
subject { Person.ransortable_attributes }
|
90
|
+
|
91
|
+
it { should include 'name' }
|
92
|
+
it { should include 'reversed_name' }
|
93
|
+
it { should include 'doubled_name' }
|
94
|
+
end
|
95
|
+
|
61
96
|
describe '#ransackable_associations' do
|
62
97
|
subject { Person.ransackable_associations }
|
63
98
|
|
@@ -13,12 +13,12 @@ module Ransack
|
|
13
13
|
end
|
14
14
|
|
15
15
|
describe '#evaluate' do
|
16
|
-
it 'evaluates search
|
16
|
+
it 'evaluates search objects' do
|
17
17
|
search = Search.new(Person, :name_eq => 'Joe Blow')
|
18
18
|
result = subject.evaluate(search)
|
19
19
|
|
20
20
|
result.should be_an ::ActiveRecord::Relation
|
21
|
-
result.to_sql.should match
|
21
|
+
result.to_sql.should match /#{quote_column_name("name")} = 'Joe Blow'/
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'SELECTs DISTINCT when :distinct => true' do
|
@@ -13,13 +13,7 @@ module Ransack
|
|
13
13
|
expect { subject.result }.to_not raise_error
|
14
14
|
end
|
15
15
|
|
16
|
-
it
|
17
|
-
if ActiveRecord::VERSION::MAJOR == 3
|
18
|
-
"escapes '%', '.' and '\\\\' in value"
|
19
|
-
else
|
20
|
-
"escapes % and \\ in value"
|
21
|
-
end
|
22
|
-
) do
|
16
|
+
it "escapes '%', '.' and '\\\\' in value" do
|
23
17
|
subject.send(:"#{method}=", '%._\\')
|
24
18
|
subject.result.to_sql.should match(regexp)
|
25
19
|
end
|
@@ -28,12 +22,14 @@ module Ransack
|
|
28
22
|
describe 'eq' do
|
29
23
|
it 'generates an equality condition for boolean true' do
|
30
24
|
@s.awesome_eq = true
|
31
|
-
|
25
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}"
|
26
|
+
@s.result.to_sql.should match /#{field} = #{ActiveRecord::Base.connection.quoted_true}/
|
32
27
|
end
|
33
28
|
|
34
29
|
it 'generates an equality condition for boolean false' do
|
35
30
|
@s.awesome_eq = false
|
36
|
-
|
31
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("awesome")}"
|
32
|
+
@s.result.to_sql.should match /#{field} = #{ActiveRecord::Base.connection.quoted_false}/
|
37
33
|
end
|
38
34
|
|
39
35
|
it 'does not generate a condition for nil' do
|
@@ -44,48 +40,56 @@ module Ransack
|
|
44
40
|
|
45
41
|
describe 'cont' do
|
46
42
|
|
47
|
-
it_has_behavior 'wildcard escaping', :name_cont,
|
48
|
-
if ActiveRecord::
|
49
|
-
/"people"."name"
|
43
|
+
it_has_behavior 'wildcard escaping', :name_cont,
|
44
|
+
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
45
|
+
/"people"."name" ILIKE '%\\%\\._\\\\%'/
|
46
|
+
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
47
|
+
/`people`.`name` LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
50
48
|
else
|
51
|
-
|
49
|
+
/"people"."name" LIKE '%%._\\%'/
|
52
50
|
end) do
|
53
51
|
subject { @s }
|
54
52
|
end
|
55
53
|
|
56
54
|
it 'generates a LIKE query with value surrounded by %' do
|
57
55
|
@s.name_cont = 'ric'
|
58
|
-
|
56
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
57
|
+
@s.result.to_sql.should match /#{field} I?LIKE '%ric%'/
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
62
61
|
describe 'not_cont' do
|
63
|
-
it_has_behavior 'wildcard escaping', :name_not_cont,
|
64
|
-
if ActiveRecord::
|
65
|
-
/"people"."name" NOT
|
62
|
+
it_has_behavior 'wildcard escaping', :name_not_cont,
|
63
|
+
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
64
|
+
/"people"."name" NOT ILIKE '%\\%\\._\\\\%'/
|
65
|
+
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
66
|
+
/`people`.`name` NOT LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
66
67
|
else
|
67
|
-
|
68
|
+
/"people"."name" NOT LIKE '%%._\\%'/
|
68
69
|
end) do
|
69
70
|
subject { @s }
|
70
71
|
end
|
71
72
|
|
72
73
|
it 'generates a NOT LIKE query with value surrounded by %' do
|
73
74
|
@s.name_not_cont = 'ric'
|
74
|
-
|
75
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
76
|
+
@s.result.to_sql.should match /#{field} NOT I?LIKE '%ric%'/
|
75
77
|
end
|
76
78
|
end
|
77
79
|
|
78
80
|
describe 'null' do
|
79
81
|
it 'generates a value IS NULL query' do
|
80
82
|
@s.name_null = true
|
81
|
-
|
83
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
84
|
+
@s.result.to_sql.should match /#{field} IS NULL/
|
82
85
|
end
|
83
86
|
end
|
84
87
|
|
85
88
|
describe 'not_null' do
|
86
89
|
it 'generates a value IS NOT NULL query' do
|
87
90
|
@s.name_not_null = true
|
88
|
-
|
91
|
+
field = "#{quote_table_name("people")}.#{quote_column_name("name")}"
|
92
|
+
@s.result.to_sql.should match /#{field} IS NOT NULL/
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
data/spec/ransack/search_spec.rb
CHANGED
@@ -113,25 +113,27 @@ module Ransack
|
|
113
113
|
end
|
114
114
|
|
115
115
|
describe '#result' do
|
116
|
+
let(:people_name_field) { "#{quote_table_name("people")}.#{quote_column_name("name")}" }
|
117
|
+
let(:children_people_name_field) { "#{quote_table_name("children_people")}.#{quote_column_name("name")}" }
|
116
118
|
it 'evaluates conditions contextually' do
|
117
119
|
search = Search.new(Person, :children_name_eq => 'Ernie')
|
118
120
|
search.result.should be_an ActiveRecord::Relation
|
119
121
|
where = search.result.where_values.first
|
120
|
-
where.to_sql.should match
|
122
|
+
where.to_sql.should match /#{children_people_name_field} = 'Ernie'/
|
121
123
|
end
|
122
124
|
|
123
125
|
it 'evaluates compound conditions contextually' do
|
124
126
|
search = Search.new(Person, :children_name_or_name_eq => 'Ernie')
|
125
127
|
search.result.should be_an ActiveRecord::Relation
|
126
128
|
where = search.result.where_values.first
|
127
|
-
where.to_sql.should match
|
129
|
+
where.to_sql.should match /#{children_people_name_field} = 'Ernie' OR #{people_name_field} = 'Ernie'/
|
128
130
|
end
|
129
131
|
|
130
132
|
it 'evaluates polymorphic belongs_to association conditions contextually' do
|
131
133
|
search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie')
|
132
134
|
search.result.should be_an ActiveRecord::Relation
|
133
135
|
where = search.result.where_values.first
|
134
|
-
where.to_sql.should match
|
136
|
+
where.to_sql.should match /#{people_name_field} = 'Ernie'/
|
135
137
|
end
|
136
138
|
|
137
139
|
it 'evaluates nested conditions' do
|
@@ -145,9 +147,9 @@ module Ransack
|
|
145
147
|
)
|
146
148
|
search.result.should be_an ActiveRecord::Relation
|
147
149
|
where = search.result.where_values.first
|
148
|
-
where.to_sql.should match
|
149
|
-
where.to_sql.should match
|
150
|
-
where.to_sql.should match
|
150
|
+
where.to_sql.should match /#{children_people_name_field} = 'Ernie'/
|
151
|
+
where.to_sql.should match /#{people_name_field} = 'Ernie'/
|
152
|
+
where.to_sql.should match /#{quote_table_name("children_people_2")}.#{quote_column_name("name")} = 'Ernie'/
|
151
153
|
end
|
152
154
|
|
153
155
|
it 'evaluates arrays of groupings' do
|
@@ -161,10 +163,10 @@ module Ransack
|
|
161
163
|
where = search.result.where_values.first
|
162
164
|
sql = where.to_sql
|
163
165
|
first, second = sql.split(/ AND /)
|
164
|
-
first.should match
|
165
|
-
first.should match
|
166
|
-
second.should match
|
167
|
-
second.should match
|
166
|
+
first.should match /#{people_name_field} = 'Ernie'/
|
167
|
+
first.should match /#{children_people_name_field} = 'Ernie'/
|
168
|
+
second.should match /#{people_name_field} = 'Bert'/
|
169
|
+
second.should match /#{children_people_name_field} = 'Bert'/
|
168
170
|
end
|
169
171
|
|
170
172
|
it 'returns distinct records when passed :distinct => true' do
|
@@ -204,6 +206,24 @@ module Ransack
|
|
204
206
|
sort.dir.should eq 'desc'
|
205
207
|
end
|
206
208
|
|
209
|
+
it 'creates sorts based on a single attribute and uppercase direction' do
|
210
|
+
@s.sorts = 'id DESC'
|
211
|
+
@s.sorts.should have(1).item
|
212
|
+
sort = @s.sorts.first
|
213
|
+
sort.should be_a Nodes::Sort
|
214
|
+
sort.name.should eq 'id'
|
215
|
+
sort.dir.should eq 'desc'
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'creates sorts based on a single attribute and without direction' do
|
219
|
+
@s.sorts = 'id'
|
220
|
+
@s.sorts.should have(1).item
|
221
|
+
sort = @s.sorts.first
|
222
|
+
sort.should be_a Nodes::Sort
|
223
|
+
sort.name.should eq 'id'
|
224
|
+
sort.dir.should eq 'asc'
|
225
|
+
end
|
226
|
+
|
207
227
|
it 'creates sorts based on multiple attributes/directions in array format' do
|
208
228
|
@s.sorts = ['id desc', 'name asc']
|
209
229
|
@s.sorts.should have(2).items
|
@@ -216,6 +236,30 @@ module Ransack
|
|
216
236
|
sort2.dir.should eq 'asc'
|
217
237
|
end
|
218
238
|
|
239
|
+
it 'creates sorts based on multiple attributes and uppercase directions in array format' do
|
240
|
+
@s.sorts = ['id DESC', 'name ASC']
|
241
|
+
@s.sorts.should have(2).items
|
242
|
+
sort1, sort2 = @s.sorts
|
243
|
+
sort1.should be_a Nodes::Sort
|
244
|
+
sort1.name.should eq 'id'
|
245
|
+
sort1.dir.should eq 'desc'
|
246
|
+
sort2.should be_a Nodes::Sort
|
247
|
+
sort2.name.should eq 'name'
|
248
|
+
sort2.dir.should eq 'asc'
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'creates sorts based on multiple attributes and different directions in array format' do
|
252
|
+
@s.sorts = ['id DESC', 'name']
|
253
|
+
@s.sorts.should have(2).items
|
254
|
+
sort1, sort2 = @s.sorts
|
255
|
+
sort1.should be_a Nodes::Sort
|
256
|
+
sort1.name.should eq 'id'
|
257
|
+
sort1.dir.should eq 'desc'
|
258
|
+
sort2.should be_a Nodes::Sort
|
259
|
+
sort2.name.should eq 'name'
|
260
|
+
sort2.dir.should eq 'asc'
|
261
|
+
end
|
262
|
+
|
219
263
|
it 'creates sorts based on multiple attributes/directions in hash format' do
|
220
264
|
@s.sorts = {
|
221
265
|
'0' => { :name => 'id', :dir => 'desc' },
|
@@ -229,6 +273,32 @@ module Ransack
|
|
229
273
|
name_sort.dir.should eq 'asc'
|
230
274
|
end
|
231
275
|
|
276
|
+
it 'creates sorts based on multiple attributes and uppercase directions in hash format' do
|
277
|
+
@s.sorts = {
|
278
|
+
'0' => { :name => 'id', :dir => 'DESC' },
|
279
|
+
'1' => { :name => 'name', :dir => 'ASC' }
|
280
|
+
}
|
281
|
+
@s.sorts.should have(2).items
|
282
|
+
@s.sorts.should be_all { |s| Nodes::Sort === s }
|
283
|
+
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
284
|
+
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
285
|
+
id_sort.dir.should eq 'desc'
|
286
|
+
name_sort.dir.should eq 'asc'
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'creates sorts based on multiple attributes and different directions in hash format' do
|
290
|
+
@s.sorts = {
|
291
|
+
'0' => { :name => 'id', :dir => 'DESC' },
|
292
|
+
'1' => { :name => 'name', :dir => nil }
|
293
|
+
}
|
294
|
+
@s.sorts.should have(2).items
|
295
|
+
@s.sorts.should be_all { |s| Nodes::Sort === s }
|
296
|
+
id_sort = @s.sorts.detect { |s| s.name == 'id' }
|
297
|
+
name_sort = @s.sorts.detect { |s| s.name == 'name' }
|
298
|
+
id_sort.dir.should eq 'desc'
|
299
|
+
name_sort.dir.should eq 'asc'
|
300
|
+
end
|
301
|
+
|
232
302
|
it 'overrides existing sort' do
|
233
303
|
@s.sorts = 'id asc'
|
234
304
|
@s.result.first.id.should eq 1
|
data/spec/spec_helper.rb
CHANGED
@@ -24,7 +24,8 @@ RSpec.configure do |config|
|
|
24
24
|
|
25
25
|
config.before(:suite) do
|
26
26
|
puts '=' * 80
|
27
|
-
|
27
|
+
connection_name = ActiveRecord::Base.connection.adapter_name
|
28
|
+
puts "Running specs against #{connection_name}, ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
|
28
29
|
puts '=' * 80
|
29
30
|
Schema.create
|
30
31
|
end
|
data/spec/support/schema.rb
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
case ENV['DB']
|
4
|
+
when "mysql"
|
5
|
+
ActiveRecord::Base.establish_connection(
|
6
|
+
adapter: 'mysql2',
|
7
|
+
database: 'ransack',
|
8
|
+
encoding: 'utf8'
|
9
|
+
)
|
10
|
+
when "postgres"
|
11
|
+
ActiveRecord::Base.establish_connection(
|
12
|
+
adapter: 'postgresql',
|
13
|
+
database: 'ransack',
|
14
|
+
username: 'postgres',
|
15
|
+
min_messages: 'warning'
|
16
|
+
)
|
17
|
+
else
|
18
|
+
# Assume SQLite3
|
19
|
+
ActiveRecord::Base.establish_connection(
|
20
|
+
adapter: 'sqlite3',
|
21
|
+
database: ':memory:'
|
22
|
+
)
|
23
|
+
end
|
7
24
|
|
8
25
|
class Person < ActiveRecord::Base
|
9
26
|
if ActiveRecord::VERSION::MAJOR == 3
|
@@ -58,6 +75,7 @@ module Schema
|
|
58
75
|
create_table :people, :force => true do |t|
|
59
76
|
t.integer :parent_id
|
60
77
|
t.string :name
|
78
|
+
t.string :email
|
61
79
|
t.integer :salary
|
62
80
|
t.boolean :awesome, :default => false
|
63
81
|
t.timestamps
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ransack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ernie Miller
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -109,6 +109,48 @@ dependencies:
|
|
109
109
|
- - ~>
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: 1.3.3
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: pg
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - '='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 0.17.0
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.17.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: mysql2
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - '='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: 0.3.13
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - '='
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 0.3.13
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: pry
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - '='
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 0.9.12.2
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - '='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: 0.9.12.2
|
112
154
|
description: Ransack is the successor to the MetaSearch gem. It improves and expands
|
113
155
|
upon MetaSearch's functionality, but does not have a 100%-compatible API.
|
114
156
|
email:
|
@@ -140,10 +182,7 @@ files:
|
|
140
182
|
- lib/ransack/helpers.rb
|
141
183
|
- lib/ransack/helpers/form_builder.rb
|
142
184
|
- lib/ransack/helpers/form_helper.rb
|
143
|
-
- lib/ransack/locale/cs.yml
|
144
185
|
- lib/ransack/locale/en.yml
|
145
|
-
- lib/ransack/locale/es.yml
|
146
|
-
- lib/ransack/locale/fr.yml
|
147
186
|
- lib/ransack/naming.rb
|
148
187
|
- lib/ransack/nodes.rb
|
149
188
|
- lib/ransack/nodes/attribute.rb
|
@@ -198,7 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
237
|
version: '0'
|
199
238
|
requirements: []
|
200
239
|
rubyforge_project: ransack
|
201
|
-
rubygems_version: 2.
|
240
|
+
rubygems_version: 2.1.0
|
202
241
|
signing_key:
|
203
242
|
specification_version: 4
|
204
243
|
summary: Object-based searching for ActiveRecord (currently).
|
@@ -222,4 +261,3 @@ test_files:
|
|
222
261
|
- spec/spec_helper.rb
|
223
262
|
- spec/support/en.yml
|
224
263
|
- spec/support/schema.rb
|
225
|
-
has_rdoc:
|
data/lib/ransack/locale/cs.yml
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
cs:
|
2
|
-
ransack:
|
3
|
-
search: "vyhledávání"
|
4
|
-
predicate: "predikát"
|
5
|
-
and: "a"
|
6
|
-
or: "nebo"
|
7
|
-
any: "kteroukoliv"
|
8
|
-
all: "každou"
|
9
|
-
combinator: "kombinátor"
|
10
|
-
attribute: "atribut"
|
11
|
-
value: "hodnota"
|
12
|
-
condition: "podmínka"
|
13
|
-
sort: "řazení"
|
14
|
-
asc: "vzestupné"
|
15
|
-
desc: "sestupné"
|
16
|
-
predicates:
|
17
|
-
eq: "rovno"
|
18
|
-
eq_any: "rovno kterékoliv"
|
19
|
-
eq_all: "rovno všem"
|
20
|
-
not_eq: "nerovno"
|
21
|
-
not_eq_any: "nerovno kterékoliv"
|
22
|
-
not_eq_all: "nerovno všem"
|
23
|
-
matches: "odpovídá"
|
24
|
-
matches_any: "odpovídá kterékoliv"
|
25
|
-
matches_all: "odpovídá všem"
|
26
|
-
does_not_match: "neodpovídá"
|
27
|
-
does_not_match_any: "neodpovídá kterékoliv"
|
28
|
-
does_not_match_all: "neodpovídá všem"
|
29
|
-
lt: "menší než"
|
30
|
-
lt_any: "menší než kterákoliv"
|
31
|
-
lt_all: "menší než všechny"
|
32
|
-
lteq: "menší nebo rovno než"
|
33
|
-
lteq_any: "menší nebo rovno než kterákoliv"
|
34
|
-
lteq_all: "menší nebo rovno než všechny"
|
35
|
-
gt: "větší než"
|
36
|
-
gt_any: "větší než kterákoliv"
|
37
|
-
gt_all: "větší než všechny"
|
38
|
-
gteq: "větší nebo rovno než"
|
39
|
-
gteq_any: "větší nebo rovno než kterákoliv"
|
40
|
-
gteq_all: "větší nebo rovno než všechny"
|
41
|
-
in: "v"
|
42
|
-
in_any: "v kterékoliv"
|
43
|
-
in_all: "ve všech"
|
44
|
-
not_in: "není v"
|
45
|
-
not_in_any: "není v kterékoliv"
|
46
|
-
not_in_all: "není ve všech"
|
47
|
-
cont: "obsahuje"
|
48
|
-
cont_any: "obsahuje kterékoliv"
|
49
|
-
cont_all: "obsahuje všechny"
|
50
|
-
not_cont: "neobsahuje"
|
51
|
-
not_cont_any: "neobsahuje kteroukoliv"
|
52
|
-
not_cont_all: "neobsahuje všechny"
|
53
|
-
start: "začíná s"
|
54
|
-
start_any: "začíná s kteroukoliv"
|
55
|
-
start_all: "začíná se všemi"
|
56
|
-
not_start: "nezačíná s"
|
57
|
-
not_start_any: "nezačíná s kteroukoliv"
|
58
|
-
not_start_all: "nezačíná se všemi"
|
59
|
-
end: "končí s"
|
60
|
-
end_any: "končí s kteroukoliv"
|
61
|
-
end_all: "končí se všemi"
|
62
|
-
not_end: "nekončí s"
|
63
|
-
not_end_any: "nekončí s kteroukoliv"
|
64
|
-
not_end_all: "nekončí se všemi"
|
65
|
-
'true': "je pravdivé"
|
66
|
-
'false': "není pravdivé"
|
67
|
-
present: "je vyplněné"
|
68
|
-
blank: "je prázdné"
|
69
|
-
'null': "je null"
|
70
|
-
not_null: "není null"
|
data/lib/ransack/locale/es.yml
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
es:
|
2
|
-
ransack:
|
3
|
-
search: "buscar"
|
4
|
-
predicate: "predicado"
|
5
|
-
and: "y"
|
6
|
-
or: "o"
|
7
|
-
any: "cualquier"
|
8
|
-
all: "todos"
|
9
|
-
combinator: "combinado"
|
10
|
-
attribute: "atributo"
|
11
|
-
value: "valor"
|
12
|
-
condition: "condición"
|
13
|
-
sort: "ordernar"
|
14
|
-
asc: "ascendente"
|
15
|
-
desc: "descendente"
|
16
|
-
predicates:
|
17
|
-
eq: "es igual a"
|
18
|
-
eq_any: "es igual a cualquier"
|
19
|
-
eq_all: "es igual a todos"
|
20
|
-
not_eq: "no es igual a"
|
21
|
-
not_eq_any: "no es igual a cualquier"
|
22
|
-
not_eq_all: "no es iguala todos"
|
23
|
-
matches: "coincidir"
|
24
|
-
matches_any: "coincidir a cualquier"
|
25
|
-
matches_all: "coincidir a todos"
|
26
|
-
does_not_match: "no coincide"
|
27
|
-
does_not_match_any: "no coincide con ninguna"
|
28
|
-
does_not_match_all: "no coincide con todos"
|
29
|
-
lt: "menor que"
|
30
|
-
lt_any: "menor que cualquier"
|
31
|
-
lt_all: "menor o igual a"
|
32
|
-
lteq: "menor que o igual a"
|
33
|
-
lteq_any: "menor o igual a cualquier"
|
34
|
-
lteq_all: "menor o igual a todos"
|
35
|
-
gt: "mayor que"
|
36
|
-
gt_any: "mayor que cualquier"
|
37
|
-
gt_all: "mayor que todos"
|
38
|
-
gteq: "mayor que o igual a"
|
39
|
-
gteq_any: "mayor que o igual a cualquier"
|
40
|
-
gteq_all: "mayor que o igual a todos"
|
41
|
-
in: "en"
|
42
|
-
in_any: "en cualquier"
|
43
|
-
in_all: "en todos"
|
44
|
-
not_in: "no en"
|
45
|
-
not_in_any: "no en cualquier"
|
46
|
-
not_in_all: "no en todos"
|
47
|
-
cont: "contiene"
|
48
|
-
cont_any: "contiene cualquier"
|
49
|
-
cont_all: "contiene todos"
|
50
|
-
not_cont: "no contiene"
|
51
|
-
not_cont_any: "no contiene ninguna"
|
52
|
-
not_cont_all: "no contiene toda"
|
53
|
-
start: "comienza con"
|
54
|
-
start_any: "comienza con cualquier"
|
55
|
-
start_all: "comienza con toda"
|
56
|
-
not_start: "no inicia con"
|
57
|
-
not_start_any: "no comienza con cualquier"
|
58
|
-
not_start_all: "no inicia con toda"
|
59
|
-
end: "termina con"
|
60
|
-
end_any: "termina con cualquier"
|
61
|
-
end_all: "termina con todo"
|
62
|
-
not_end: "no termina con"
|
63
|
-
not_end_any: "no termina con cualquier"
|
64
|
-
not_end_all: "no termina con todo"
|
65
|
-
'true': "es verdadero"
|
66
|
-
'false': "es falso"
|
67
|
-
present: "es presente"
|
68
|
-
blank: "está en blanco"
|
69
|
-
'null': "es nula"
|
70
|
-
not_null: "no es nula"
|
data/lib/ransack/locale/fr.yml
DELETED
@@ -1,70 +0,0 @@
|
|
1
|
-
fr:
|
2
|
-
ransack:
|
3
|
-
search: "recherche"
|
4
|
-
predicate: "prédicat"
|
5
|
-
and: "et"
|
6
|
-
or: "ou"
|
7
|
-
any: "au moins un"
|
8
|
-
all: "tous"
|
9
|
-
combinator: "combinateur"
|
10
|
-
attribute: "attribut"
|
11
|
-
value: "valeur"
|
12
|
-
condition: "condition"
|
13
|
-
sort: "tri"
|
14
|
-
asc: "ascendant"
|
15
|
-
desc: "descendant"
|
16
|
-
predicates:
|
17
|
-
eq: "égal à"
|
18
|
-
eq_any: "égal à au moins un"
|
19
|
-
eq_all: "égal à tous"
|
20
|
-
not_eq: "différent de"
|
21
|
-
not_eq_any: "différent d'au moins un"
|
22
|
-
not_eq_all: "différent de tous"
|
23
|
-
matches: "correspond à"
|
24
|
-
matches_any: "correspond à au moins un"
|
25
|
-
matches_all: "correspond à tous"
|
26
|
-
does_not_match: "ne correspond pas à"
|
27
|
-
does_not_match_any: "ne correspond pas à au moins un"
|
28
|
-
does_not_match_all: "ne correspond à aucun"
|
29
|
-
lt: "inférieur à"
|
30
|
-
lt_any: "inférieur à au moins un"
|
31
|
-
lt_all: "inférieur à tous"
|
32
|
-
lteq: "inférieur ou égal à"
|
33
|
-
lteq_any: "inférieur ou égal à au moins un"
|
34
|
-
lteq_all: "inférieur ou égal à tous"
|
35
|
-
gt: "supérieur à"
|
36
|
-
gt_any: "supérieur à au moins un"
|
37
|
-
gt_all: "supérieur à tous"
|
38
|
-
gteq: "supérieur ou égal à"
|
39
|
-
gteq_any: "supérieur ou égal à au moins un"
|
40
|
-
gteq_all: "supérieur ou égal à tous"
|
41
|
-
in: "inclus dans"
|
42
|
-
in_any: "inclus dans au moins un"
|
43
|
-
in_all: "inclus dans tous"
|
44
|
-
not_in: "non inclus dans"
|
45
|
-
not_in_any: "non inclus dans au moins un"
|
46
|
-
not_in_all: "non inclus dans tous"
|
47
|
-
cont: "contient"
|
48
|
-
cont_any: "contient au moins un"
|
49
|
-
cont_all: "contient tous"
|
50
|
-
not_cont: "ne contient pas"
|
51
|
-
not_cont_any: "ne contient pas au moins un"
|
52
|
-
not_cont_all: "ne contient pas tous"
|
53
|
-
start: "commence par"
|
54
|
-
start_any: "commence par au moins un"
|
55
|
-
start_all: "commence par tous"
|
56
|
-
not_start: "ne commence pas par"
|
57
|
-
not_start_any: "ne commence pas par au moins un"
|
58
|
-
not_start_all: "ne commence pas par tous"
|
59
|
-
end: "finit par"
|
60
|
-
end_any: "finit par au moins un"
|
61
|
-
end_all: "finit par tous"
|
62
|
-
not_end: "ne finit pas par"
|
63
|
-
not_end_any: "ne finit pas par au moins un"
|
64
|
-
not_end_all: "ne finit pas par tous"
|
65
|
-
'true': "est vrai"
|
66
|
-
'false': "est faux"
|
67
|
-
present: "est présent"
|
68
|
-
blank: "est blanc"
|
69
|
-
'null': "est null"
|
70
|
-
not_null: "n'est pas null"
|