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 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"