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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae3f27c548f10f431a0d5c13b3a9863d7e707e73
4
- data.tar.gz: 83840f65158cb74f04e8bd5e0b63d1251bcd3272
3
+ metadata.gz: aa2e8ee70ef82d95b0bc2c83e0005505252e2b13
4
+ data.tar.gz: ae83094fdd4be127ba5471b48442a6c1f2922680
5
5
  SHA512:
6
- metadata.gz: 1883d781609fbae07044a265169f8960c4416e2836d3c332ad66ffb2ec8a4427bd8f3114d7539bb62f7ed0d132478004751763bb51d96e782b6ca904f0d82f88
7
- data.tar.gz: 8a817a0a03b09622c527a63c24502d3c9da37c46096ccf376a2577cf05d54c6302c9ba13e866dc2789f79e588c2b262a5e799760a15dfeae0c9339da6d016f8a
6
+ metadata.gz: f2d9b0b0be3432472e801b0fdbfc19075a71461bba475c64f0cafa7cc32f9b8606b9136488fd7e1f1ef0dc34b7f2877815d32fc30a626ad74c3c75d21422e1b8
7
+ data.tar.gz: dbb0b40594df15a6e7321d97e9ba134402168facc2de41b9a07cdf58683bb172f61aa01a6f5a61c5f73d6ff7053c597e2ce13b3b942cba2aea1b79f67de614cd
@@ -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
- - RAILS=4-0-stable
9
- - RAILS=3-2-stable
10
- - RAILS=3-1-stable
11
- - RAILS=3-0-stable
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
- matrix:
14
- exclude:
15
- - rvm: 1.8.7
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
 
@@ -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
 
@@ -1,4 +1,3 @@
1
- require 'active_record'
2
1
  require 'ransack/adapters/active_record/base'
3
2
  ActiveRecord::Base.extend Ransack::Adapters::ActiveRecord::Base
4
3
 
@@ -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
@@ -23,10 +23,12 @@ module Ransack
23
23
  module_function
24
24
  # replace % \ to \% \\
25
25
  def escape_wildcards(unescaped)
26
- if ActiveRecord::VERSION::MAJOR == 3
27
- unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
28
- else
29
- unescaped.to_s.gsub(/\\/){ "\\\\" }.gsub(/%/, "\\%")
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
 
@@ -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, attribute_collection_for_bases(bases), :last, :first, :first, :last,
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 = object.context.searchable_attributes(bases.first).map do |c|
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, attribute_collection_for_bases(bases), :last, :first, :first, :last,
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 = object.context.searchable_attributes(bases.first).map do |c|
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 attribute_collection_for_bases(bases)
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
- object.context.searchable_attributes(base).map do |c|
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
@@ -33,6 +33,7 @@ module Ransack
33
33
  end
34
34
 
35
35
  def dir=(dir)
36
+ dir = dir.try(:downcase)
36
37
  @dir = %w(asc desc).include?(dir) ? dir : 'asc'
37
38
  end
38
39
 
@@ -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')
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -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")
@@ -1,4 +1,5 @@
1
1
  Person.blueprint do
2
2
  name
3
+ email { "test@example.com" }
3
4
  salary
4
5
  end
@@ -1,2 +1,9 @@
1
1
  module RansackHelper
2
+ def quote_table_name(table)
3
+ ActiveRecord::Base.connection.quote_table_name(table)
4
+ end
5
+
6
+ def quote_column_name(column)
7
+ ActiveRecord::Base.connection.quote_column_name(column)
8
+ end
2
9
  end
@@ -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
- s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
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 obects' do
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 /"name" = 'Joe Blow'/
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
- @s.result.to_sql.should match /"people"."awesome" = 't'/
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
- @s.result.to_sql.should match /"people"."awesome" = 'f'/
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::VERSION::MAJOR == 3
49
- /"people"."name" LIKE '%\\%\\._\\\\%'/
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
- /"people"."name" LIKE '%\\%._\\\\%'/
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
- @s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
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::VERSION::MAJOR == 3
65
- /"people"."name" NOT LIKE '%\\%\\._\\\\%'/
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
- /"people"."name" NOT LIKE '%\\%._\\\\%'/
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
- @s.result.to_sql.should match /"people"."name" NOT LIKE '%ric%'/
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
- @s.result.to_sql.should match /"people"."name" IS NULL/
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
- @s.result.to_sql.should match /"people"."name" IS NOT NULL/
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
@@ -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 /"children_people"\."name" = 'Ernie'/
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 /"children_people"\."name" = 'Ernie' OR "people"\."name" = 'Ernie'/
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 /"people"."name" = 'Ernie'/
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 /"children_people"."name" = 'Ernie'/
149
- where.to_sql.should match /"people"."name" = 'Ernie'/
150
- where.to_sql.should match /"children_people_2"."name" = 'Ernie'/
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 /"people"."name" = 'Ernie'/
165
- first.should match /"children_people"."name" = 'Ernie'/
166
- second.should match /"people"."name" = 'Bert'/
167
- second.should match /"children_people"."name" = 'Bert'/
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
@@ -24,7 +24,8 @@ RSpec.configure do |config|
24
24
 
25
25
  config.before(:suite) do
26
26
  puts '=' * 80
27
- puts "Running specs against ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
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
@@ -1,9 +1,26 @@
1
1
  require 'active_record'
2
2
 
3
- ActiveRecord::Base.establish_connection(
4
- :adapter => 'sqlite3',
5
- :database => ':memory:'
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.0.0
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-08-09 00:00:00.000000000 Z
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.0.0
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:
@@ -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"
@@ -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"
@@ -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"