ransack 1.0.0 → 1.1.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 +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"
|