ransack 0.7.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -23,7 +23,12 @@ module Ransack
23
23
  module_function
24
24
  # replace % \ to \% \\
25
25
  def escape_wildcards(unescaped)
26
- unescaped.gsub(/\\/){ "\\\\" }.gsub(/%/, "\\%")
26
+ if ActiveRecord::VERSION::MAJOR == 3
27
+ unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
28
+ else
29
+ unescaped.to_s.gsub(/\\/){ "\\\\" }.gsub(/%/, "\\%")
30
+ end
27
31
  end
32
+
28
33
  end
29
34
  end
@@ -28,7 +28,7 @@ module Ransack
28
28
  end
29
29
 
30
30
  def initialize(object, options = {})
31
- @object = object.scoped
31
+ @object = relation_for(object)
32
32
  @klass = @object.klass
33
33
  @join_dependency = join_dependency(@object)
34
34
  @join_type = options[:join_type] || Arel::OuterJoin
@@ -44,6 +44,20 @@ module Ransack
44
44
  end
45
45
  end
46
46
 
47
+ def klassify(obj)
48
+ if Class === obj && ::ActiveRecord::Base > obj
49
+ obj
50
+ elsif obj.respond_to? :klass
51
+ obj.klass
52
+ elsif obj.respond_to? :active_record # Rails 3
53
+ obj.active_record
54
+ elsif obj.respond_to? :base_klass # Rails 4
55
+ obj.base_klass
56
+ else
57
+ raise ArgumentError, "Don't know how to klassify #{obj}"
58
+ end
59
+ end
60
+
47
61
  # Convert a string representing a chain of associations and an attribute
48
62
  # into the attribute itself
49
63
  def contextualize(str)
@@ -163,7 +163,8 @@ module Ransack
163
163
  end
164
164
  else
165
165
  [obj]
166
- end).compact.flatten.map {|v| [prefix, v].compact.join('_')}
166
+ end).
167
+ compact.flatten.map { |v| [prefix, v].compact.join('_') }
167
168
  end
168
169
 
169
170
  def attr_from_base_and_column(base, column)
@@ -59,11 +59,12 @@ module Ransack
59
59
  query_hash = {}
60
60
  query_hash[search.context.search_key] = search_params.merge(:s => "#{attr_name} #{new_dir}")
61
61
  options.merge!(query_hash)
62
+ options_for_url = params.merge options
62
63
 
63
64
  url = if routing_proxy && respond_to?(routing_proxy)
64
- send(routing_proxy).url_for(options)
65
+ send(routing_proxy).url_for(options_for_url)
65
66
  else
66
- url_for(options)
67
+ url_for(options_for_url)
67
68
  end
68
69
 
69
70
  link_to [ERB::Util.h(name), order_indicator_for(current_dir)].compact.join(' ').html_safe,
@@ -29,7 +29,7 @@ es:
29
29
  lt: "menor que"
30
30
  lt_any: "menor que cualquier"
31
31
  lt_all: "menor o igual a"
32
- lteq: "less than or equal to"
32
+ lteq: "menor que o igual a"
33
33
  lteq_any: "menor o igual a cualquier"
34
34
  lteq_all: "menor o igual a todos"
35
35
  gt: "mayor que"
@@ -0,0 +1,70 @@
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"
@@ -23,7 +23,7 @@ module Ransack
23
23
  attribute_names = attributes_str.split(/_and_|_or_/)
24
24
  combinator = attributes_str.match(/_and_/) ? :and : :or
25
25
  defaults = base_ancestors.map do |klass|
26
- :"ransack.attributes.#{klass.model_name.underscore}.#{original_name}"
26
+ :"ransack.attributes.#{klass.model_name.singular}.#{original_name}"
27
27
  end
28
28
 
29
29
  translated_names = attribute_names.map do |attr|
@@ -50,7 +50,7 @@ module Ransack
50
50
  raise ArgumentError, "A context is required to translate associations"
51
51
  end
52
52
 
53
- defaults = key.blank? ? [:"#{context.klass.i18n_scope}.models.#{context.klass.model_name.underscore}"] : [:"ransack.associations.#{context.klass.model_name.underscore}.#{key}"]
53
+ defaults = key.blank? ? [:"#{context.klass.i18n_scope}.models.#{context.klass.model_name.singular}"] : [:"ransack.associations.#{context.klass.model_name.singular}.#{key}"]
54
54
  defaults << context.traverse(key).model_name.human
55
55
  options = {:count => 1, :default => defaults}
56
56
  I18n.translate(defaults.shift, options)
@@ -65,19 +65,20 @@ module Ransack
65
65
  interpolations = {}
66
66
  interpolations[:attr_fallback_name] = I18n.translate(
67
67
  (associated_class ?
68
- :"ransack.attributes.#{associated_class.model_name.underscore}.#{attr_name}" :
69
- :"ransack.attributes.#{context.klass.model_name.underscore}.#{attr_name}"
68
+ :"ransack.attributes.#{associated_class.model_name.singular}.#{attr_name}" :
69
+ :"ransack.attributes.#{context.klass.model_name.singular}.#{attr_name}"
70
70
  ),
71
71
  :default => [
72
72
  (associated_class ?
73
- :"#{associated_class.i18n_scope}.attributes.#{associated_class.model_name.underscore}.#{attr_name}" :
74
- :"#{context.klass.i18n_scope}.attributes.#{context.klass.model_name.underscore}.#{attr_name}"
73
+ :"#{associated_class.i18n_scope}.attributes.#{associated_class.model_name.singular}.#{attr_name}" :
74
+ :"#{context.klass.i18n_scope}.attributes.#{context.klass.model_name.singular}.#{attr_name}"
75
75
  ),
76
+ :".attributes.#{attr_name}",
76
77
  attr_name.humanize
77
78
  ]
78
79
  )
79
80
  defaults = [
80
- :"ransack.attributes.#{context.klass.model_name.underscore}.#{name}"
81
+ :"ransack.attributes.#{context.klass.model_name.singular}.#{name}"
81
82
  ]
82
83
  if include_associations && associated_class
83
84
  defaults << '%{association_name} %{attr_fallback_name}'
@@ -89,4 +90,4 @@ module Ransack
89
90
  I18n.translate(defaults.shift, options.merge(interpolations))
90
91
  end
91
92
  end
92
- end
93
+ end
@@ -1,3 +1,3 @@
1
1
  module Ransack
2
- VERSION = "0.7.2"
2
+ VERSION = "1.0.0"
3
3
  end
data/ransack.gemspec CHANGED
@@ -14,9 +14,9 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "ransack"
16
16
 
17
- s.add_dependency 'activerecord', '~> 3.0'
18
- s.add_dependency 'actionpack', '~> 3.0'
19
- s.add_dependency 'polyamorous', '~> 0.5.0'
17
+ s.add_dependency 'activerecord', '>= 3.0'
18
+ s.add_dependency 'actionpack', '>= 3.0'
19
+ s.add_dependency 'polyamorous', '~> 0.6.0'
20
20
  s.add_development_dependency 'rspec', '~> 2.8.0'
21
21
  s.add_development_dependency 'machinist', '~> 1.0.6'
22
22
  s.add_development_dependency 'faker', '~> 0.9.5'
@@ -6,6 +6,12 @@ module Ransack
6
6
  describe Context do
7
7
  subject { Context.new(Person) }
8
8
 
9
+ describe '#relation_for' do
10
+ it 'returns relation for given object' do
11
+ subject.object.should be_an ::ActiveRecord::Relation
12
+ end
13
+ end
14
+
9
15
  describe '#evaluate' do
10
16
  it 'evaluates search obects' do
11
17
  search = Search.new(Person, :name_eq => 'Joe Blow')
@@ -7,7 +7,7 @@ module Ransack
7
7
  router = ActionDispatch::Routing::RouteSet.new
8
8
  router.draw do
9
9
  resources :people
10
- match ':controller(/:action(/:id(.:format)))'
10
+ get ':controller(/:action(/:id(.:format)))'
11
11
  end
12
12
 
13
13
  include router.url_helpers
@@ -48,11 +48,21 @@ module Ransack
48
48
  end
49
49
 
50
50
  describe '#sort_link' do
51
- subject { @f.sort_link :name, :controller => 'people' }
51
+ it 'sort_link for ransack attribute' do
52
+ sort_link = @f.sort_link :name, :controller => 'people'
53
+ if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
54
+ sort_link.should match /people\?q%5Bs%5D=name\+asc/
55
+ else
56
+ sort_link.should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
57
+ end
58
+ sort_link.should match /sort_link/
59
+ sort_link.should match /Full Name<\/a>/
60
+ end
52
61
 
53
- it { should match /people\?q%5Bs%5D=name\+asc/}
54
- it { should match /sort_link/}
55
- it { should match /Full Name<\/a>/}
62
+ it 'sort_link for common attribute' do
63
+ sort_link = @f.sort_link :id, :controller => 'people'
64
+ sort_link.should match /id<\/a>/
65
+ end
56
66
  end
57
67
 
58
68
  describe '#submit' do
@@ -124,4 +134,4 @@ module Ransack
124
134
  end
125
135
  end
126
136
  end
127
- end
137
+ end
@@ -7,7 +7,7 @@ module Ransack
7
7
  router = ActionDispatch::Routing::RouteSet.new
8
8
  router.draw do
9
9
  resources :people
10
- match ':controller(/:action(/:id(.:format)))'
10
+ get ':controller(/:action(/:id(.:format)))'
11
11
  end
12
12
 
13
13
  include router.url_helpers
@@ -26,24 +26,71 @@ module Ransack
26
26
  end
27
27
 
28
28
  describe '#sort_link with default search_key' do
29
- subject { @controller.view_context.sort_link([:main_app, Person.search(:sorts => ['name desc'])], :name, :controller => 'people') }
30
- it { should match /people\?q%5Bs%5D=name\+asc/ }
29
+ subject {
30
+ @controller.view_context.sort_link(
31
+ [:main_app, Person.search(:sorts => ['name desc'])],
32
+ :name, :controller => 'people'
33
+ )
34
+ }
35
+ it { should match(
36
+ if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
37
+ /people\?q%5Bs%5D=name\+asc/
38
+ else
39
+ /people\?q(%5B|\[)s(%5D|\])=name\+asc/
40
+ end)
41
+ }
31
42
  it { should match /sort_link desc/ }
32
43
  it { should match /Full Name &#9660;/ }
33
44
  end
34
45
 
35
46
  describe '#sort_link with default search_key defined as symbol' do
36
- subject { @controller.view_context.sort_link(Person.search({:sorts => ['name desc']}, :search_key => :people_search),
37
- :name, :controller => 'people') }
38
-
39
- it { should match /people\?people_search%5Bs%5D=name\+asc/ }
47
+ subject { @controller.
48
+ view_context.sort_link(
49
+ Person.search({ :sorts => ['name desc'] }, :search_key => :people_search),
50
+ :name, :controller => 'people'
51
+ )
52
+ }
53
+ it { should match(
54
+ if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
55
+ /people\?people_search%5Bs%5D=name\+asc/
56
+ else
57
+ /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
58
+ end)
59
+ }
40
60
  end
41
61
 
42
62
  describe '#sort_link with default search_key defined as string' do
43
- subject { @controller.view_context.sort_link(Person.search({:sorts => ['name desc']}, :search_key => 'people_search'),
44
- :name, :controller => 'people') }
63
+ subject {
64
+ @controller.view_context.sort_link(
65
+ Person.search({ :sorts => ['name desc'] }, :search_key => 'people_search'),
66
+ :name, :controller => 'people'
67
+ )
68
+ }
69
+ it { should match(
70
+ if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
71
+ /people\?people_search%5Bs%5D=name\+asc/
72
+ else
73
+ /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
74
+ end)
75
+ }
76
+ end
77
+
45
78
 
46
- it { should match /people\?people_search%5Bs%5D=name\+asc/ }
79
+ context 'view has existing parameters' do
80
+ before do
81
+ @controller.view_context.params.merge!({ :exist => 'existing' })
82
+ end
83
+ describe '#sort_link should not remove existing params' do
84
+ subject {
85
+ @controller.view_context.sort_link(
86
+ Person.search({ :sorts => ['name desc'] }, :search_key => 'people_search'),
87
+ :name, :controller => 'people'
88
+ )
89
+ }
90
+ it {
91
+ should match /exist\=existing/
92
+ }
93
+ end
47
94
  end
48
95
  end
49
96
  end
@@ -7,6 +7,24 @@ module Ransack
7
7
  @s = Search.new(Person)
8
8
  end
9
9
 
10
+ shared_examples 'wildcard escaping' do |method, regexp|
11
+ it 'automatically converts integers to strings' do
12
+ subject.parent_id_cont = 1
13
+ expect { subject.result }.to_not raise_error
14
+ end
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
23
+ subject.send(:"#{method}=", '%._\\')
24
+ subject.result.to_sql.should match(regexp)
25
+ end
26
+ end
27
+
10
28
  describe 'eq' do
11
29
  it 'generates an equality condition for boolean true' do
12
30
  @s.awesome_eq = true
@@ -25,17 +43,32 @@ module Ransack
25
43
  end
26
44
 
27
45
  describe 'cont' do
46
+
47
+ it_has_behavior 'wildcard escaping', :name_cont, (
48
+ if ActiveRecord::VERSION::MAJOR == 3
49
+ /"people"."name" LIKE '%\\%\\._\\\\%'/
50
+ else
51
+ /"people"."name" LIKE '%\\%._\\\\%'/
52
+ end) do
53
+ subject { @s }
54
+ end
55
+
28
56
  it 'generates a LIKE query with value surrounded by %' do
29
57
  @s.name_cont = 'ric'
30
58
  @s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
31
59
  end
32
- it 'escapes %, _ and \\ in value' do
33
- @s.name_cont = '%_\\'
34
- @s.result.to_sql.should match /"people"."name" LIKE '%\\%\\_\\\\%'/
35
- end
36
60
  end
37
61
 
38
62
  describe 'not_cont' do
63
+ it_has_behavior 'wildcard escaping', :name_not_cont, (
64
+ if ActiveRecord::VERSION::MAJOR == 3
65
+ /"people"."name" NOT LIKE '%\\%\\._\\\\%'/
66
+ else
67
+ /"people"."name" NOT LIKE '%\\%._\\\\%'/
68
+ end) do
69
+ subject { @s }
70
+ end
71
+
39
72
  it 'generates a NOT LIKE query with value surrounded by %' do
40
73
  @s.name_not_cont = 'ric'
41
74
  @s.result.to_sql.should match /"people"."name" NOT LIKE '%ric%'/
@@ -32,8 +32,10 @@ module Ransack
32
32
  end
33
33
 
34
34
  it 'creates Conditions for multiple polymorphic belongs_to association attributes' do
35
- search = Search.new(Note, :notable_of_Person_type_name_or_notable_of_Article_type_title_eq => 'Ernie')
36
- condition = search.base[:notable_of_Person_type_name_or_notable_of_Article_type_title_eq]
35
+ search = Search.new(Note,
36
+ :notable_of_Person_type_name_or_notable_of_Article_type_title_eq => 'Ernie')
37
+ condition = search.
38
+ base[:notable_of_Person_type_name_or_notable_of_Article_type_title_eq]
37
39
  condition.should be_a Nodes::Condition
38
40
  condition.predicate.name.should eq 'eq'
39
41
  condition.attributes.first.name.should eq 'notable_of_Person_type_name'
@@ -50,8 +52,8 @@ module Ransack
50
52
  it 'accepts arrays of groupings' do
51
53
  search = Search.new(Person,
52
54
  :g => [
53
- {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
54
- {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
55
+ { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' },
56
+ { :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' },
55
57
  ]
56
58
  )
57
59
  ors = search.groupings
@@ -66,8 +68,8 @@ module Ransack
66
68
  it 'accepts "attributes" hashes for groupings' do
67
69
  search = Search.new(Person,
68
70
  :g => {
69
- '0' => {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
70
- '1' => {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
71
+ '0' => { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' },
72
+ '1' => { :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' },
71
73
  }
72
74
  )
73
75
  ors = search.groupings
@@ -82,8 +84,8 @@ module Ransack
82
84
  it 'accepts "attributes" hashes for conditions' do
83
85
  search = Search.new(Person,
84
86
  :c => {
85
- '0' => {:a => ['name'], :p => 'eq', :v => ['Ernie']},
86
- '1' => {:a => ['children_name', 'parent_name'], :p => 'eq', :v => ['Ernie'], :m => 'or'}
87
+ '0' => { :a => ['name'], :p => 'eq', :v => ['Ernie'] },
88
+ '1' => { :a => ['children_name', 'parent_name'], :p => 'eq', :v => ['Ernie'], :m => 'or' }
87
89
  }
88
90
  )
89
91
  conditions = search.base.conditions
@@ -93,8 +95,7 @@ module Ransack
93
95
 
94
96
  it 'creates Conditions for custom predicates that take arrays' do
95
97
  Ransack.configure do |config|
96
- config.add_predicate 'ary_pred',
97
- :wants_array => true
98
+ config.add_predicate 'ary_pred', :wants_array => true
98
99
  end
99
100
 
100
101
  search = Search.new(Person, :name_ary_pred => ['Ernie', 'Bert'])
@@ -135,11 +136,12 @@ module Ransack
135
136
 
136
137
  it 'evaluates nested conditions' do
137
138
  search = Search.new(Person, :children_name_eq => 'Ernie',
138
- :g => [{
139
- :m => 'or',
140
- :name_eq => 'Ernie',
141
- :children_children_name_eq => 'Ernie'
142
- }]
139
+ :g => [
140
+ { :m => 'or',
141
+ :name_eq => 'Ernie',
142
+ :children_children_name_eq => 'Ernie'
143
+ }
144
+ ]
143
145
  )
144
146
  search.result.should be_an ActiveRecord::Relation
145
147
  where = search.result.where_values.first
@@ -151,8 +153,8 @@ module Ransack
151
153
  it 'evaluates arrays of groupings' do
152
154
  search = Search.new(Person,
153
155
  :g => [
154
- {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
155
- {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
156
+ { :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' },
157
+ { :m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert' },
156
158
  ]
157
159
  )
158
160
  search.result.should be_an ActiveRecord::Relation
@@ -166,10 +168,25 @@ module Ransack
166
168
  end
167
169
 
168
170
  it 'returns distinct records when passed :distinct => true' do
169
- search = Search.new(Person, :g => [{:m => 'or', :comments_body_cont => 'e', :articles_comments_body_cont => 'e'}])
170
- search.result.all.should have(920).items
171
- search.result(:distinct => true).should have(330).items
172
- search.result.all.uniq.should eq search.result(:distinct => true).all
171
+ search = Search.new(
172
+ Person, :g => [
173
+ { :m => 'or',
174
+ :comments_body_cont => 'e',
175
+ :articles_comments_body_cont => 'e'
176
+ }
177
+ ]
178
+ )
179
+ if ActiveRecord::VERSION::MAJOR == 3
180
+ all_or_load, uniq_or_distinct = :all, :uniq
181
+ else
182
+ all_or_load, uniq_or_distinct = :load, :distinct
183
+ end
184
+ search.result.send(all_or_load).
185
+ should have(920).items
186
+ search.result(:distinct => true).
187
+ should have(330).items
188
+ search.result.send(all_or_load).send(uniq_or_distinct).
189
+ should eq search.result(:distinct => true).send(all_or_load)
173
190
  end
174
191
  end
175
192
 
@@ -201,22 +218,21 @@ module Ransack
201
218
 
202
219
  it 'creates sorts based on multiple attributes/directions in hash format' do
203
220
  @s.sorts = {
204
- '0' => {
205
- :name => 'id',
206
- :dir => 'desc'
207
- },
208
- '1' => {
209
- :name => 'name',
210
- :dir => 'asc'
211
- }
221
+ '0' => { :name => 'id', :dir => 'desc' },
222
+ '1' => { :name => 'name', :dir => 'asc' }
212
223
  }
213
224
  @s.sorts.should have(2).items
214
- @s.sorts.should be_all {|s| Nodes::Sort === s}
215
- id_sort = @s.sorts.detect {|s| s.name == 'id'}
216
- name_sort = @s.sorts.detect {|s| s.name == 'name'}
225
+ @s.sorts.should be_all { |s| Nodes::Sort === s }
226
+ id_sort = @s.sorts.detect { |s| s.name == 'id' }
227
+ name_sort = @s.sorts.detect { |s| s.name == 'name' }
217
228
  id_sort.dir.should eq 'desc'
218
229
  name_sort.dir.should eq 'asc'
219
230
  end
231
+
232
+ it 'overrides existing sort' do
233
+ @s.sorts = 'id asc'
234
+ @s.result.first.id.should eq 1
235
+ end
220
236
  end
221
237
 
222
238
  describe '#method_missing' do
@@ -225,7 +241,7 @@ module Ransack
225
241
  end
226
242
 
227
243
  it 'raises NoMethodError when sent an invalid attribute' do
228
- expect {@s.blah}.to raise_error NoMethodError
244
+ expect { @s.blah }.to raise_error NoMethodError
229
245
  end
230
246
 
231
247
  it 'sets condition attributes when sent valid attributes' do
@@ -234,8 +250,7 @@ module Ransack
234
250
  end
235
251
 
236
252
  it 'allows chaining to access nested conditions' do
237
- @s.groupings = [{:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'}]
238
- @s.groupings.first.name_eq.should eq 'Ernie'
253
+ @s.groupings = [{ :m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie' }]
239
254
  @s.groupings.first.children_name_eq.should eq 'Ernie'
240
255
  end
241
256
  end