ransack_ffcrm 0.6.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.
Files changed (56) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +9 -0
  3. data/Gemfile +40 -0
  4. data/LICENSE +20 -0
  5. data/README.md +137 -0
  6. data/Rakefile +19 -0
  7. data/lib/ransack/adapters/active_record/3.0/compat.rb +166 -0
  8. data/lib/ransack/adapters/active_record/3.0/context.rb +161 -0
  9. data/lib/ransack/adapters/active_record/3.1/context.rb +166 -0
  10. data/lib/ransack/adapters/active_record/base.rb +33 -0
  11. data/lib/ransack/adapters/active_record/context.rb +41 -0
  12. data/lib/ransack/adapters/active_record.rb +12 -0
  13. data/lib/ransack/configuration.rb +35 -0
  14. data/lib/ransack/constants.rb +23 -0
  15. data/lib/ransack/context.rb +124 -0
  16. data/lib/ransack/helpers/form_builder.rb +203 -0
  17. data/lib/ransack/helpers/form_helper.rb +75 -0
  18. data/lib/ransack/helpers.rb +2 -0
  19. data/lib/ransack/locale/en.yml +70 -0
  20. data/lib/ransack/naming.rb +53 -0
  21. data/lib/ransack/nodes/attribute.rb +49 -0
  22. data/lib/ransack/nodes/bindable.rb +30 -0
  23. data/lib/ransack/nodes/condition.rb +212 -0
  24. data/lib/ransack/nodes/grouping.rb +183 -0
  25. data/lib/ransack/nodes/node.rb +34 -0
  26. data/lib/ransack/nodes/sort.rb +41 -0
  27. data/lib/ransack/nodes/value.rb +108 -0
  28. data/lib/ransack/nodes.rb +7 -0
  29. data/lib/ransack/predicate.rb +70 -0
  30. data/lib/ransack/ransacker.rb +24 -0
  31. data/lib/ransack/search.rb +123 -0
  32. data/lib/ransack/translate.rb +92 -0
  33. data/lib/ransack/version.rb +3 -0
  34. data/lib/ransack/visitor.rb +68 -0
  35. data/lib/ransack.rb +27 -0
  36. data/ransack_ffcrm.gemspec +30 -0
  37. data/spec/blueprints/articles.rb +5 -0
  38. data/spec/blueprints/comments.rb +5 -0
  39. data/spec/blueprints/notes.rb +3 -0
  40. data/spec/blueprints/people.rb +4 -0
  41. data/spec/blueprints/tags.rb +3 -0
  42. data/spec/console.rb +21 -0
  43. data/spec/helpers/ransack_helper.rb +2 -0
  44. data/spec/ransack/adapters/active_record/base_spec.rb +67 -0
  45. data/spec/ransack/adapters/active_record/context_spec.rb +45 -0
  46. data/spec/ransack/configuration_spec.rb +31 -0
  47. data/spec/ransack/helpers/form_builder_spec.rb +137 -0
  48. data/spec/ransack/helpers/form_helper_spec.rb +38 -0
  49. data/spec/ransack/nodes/condition_spec.rb +15 -0
  50. data/spec/ransack/nodes/grouping_spec.rb +13 -0
  51. data/spec/ransack/predicate_spec.rb +55 -0
  52. data/spec/ransack/search_spec.rb +225 -0
  53. data/spec/spec_helper.rb +47 -0
  54. data/spec/support/en.yml +5 -0
  55. data/spec/support/schema.rb +111 -0
  56. metadata +229 -0
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Adapters
5
+ module ActiveRecord
6
+ describe Base do
7
+
8
+ subject { ::ActiveRecord::Base }
9
+
10
+ it { should respond_to :ransack }
11
+ it { should respond_to :search }
12
+
13
+ describe '#search' do
14
+ subject { Person.search }
15
+
16
+ it { should be_a Search }
17
+ it 'has a Relation as its object' do
18
+ subject.object.should be_an ::ActiveRecord::Relation
19
+ end
20
+ end
21
+
22
+ describe '#ransacker' do
23
+ # in schema.rb, class Person:
24
+ # ransacker :reversed_name, :formatter => proc {|v| v.reverse} do |parent|
25
+ # parent.table[:name]
26
+ # end
27
+ #
28
+ # ransacker :doubled_name do |parent|
29
+ # Arel::Nodes::InfixOperation.new('||', parent.table[:name], parent.table[:name])
30
+ # end
31
+ it 'creates ransack attributes' do
32
+ s = Person.search(:reversed_name_eq => 'htimS cirA')
33
+ s.result.should have(1).person
34
+ s.result.first.should eq Person.find_by_name('Aric Smith')
35
+ end
36
+
37
+ it 'can be accessed through associations' do
38
+ s = Person.search(:children_reversed_name_eq => 'htimS cirA')
39
+ s.result.to_sql.should match /"children_people"."name" = 'Aric Smith'/
40
+ end
41
+
42
+ it 'allows an "attribute" to be an InfixOperation' do
43
+ s = Person.search(:doubled_name_eq => 'Aric SmithAric Smith')
44
+ s.result.first.should eq Person.find_by_name('Aric Smith')
45
+ end if defined?(Arel::Nodes::InfixOperation)
46
+ end
47
+
48
+ describe '#ransackable_attributes' do
49
+ subject { Person.ransackable_attributes }
50
+
51
+ it { should include 'name' }
52
+ it { should include 'reversed_name' }
53
+ it { should include 'doubled_name' }
54
+ end
55
+
56
+ describe '#ransackable_associations' do
57
+ subject { Person.ransackable_associations }
58
+
59
+ it { should include 'parent' }
60
+ it { should include 'children' }
61
+ it { should include 'articles' }
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Adapters
5
+ module ActiveRecord
6
+ describe Context do
7
+ subject { Context.new(Person) }
8
+
9
+ describe '#evaluate' do
10
+ it 'evaluates search obects' do
11
+ search = Search.new(Person, :name_eq => 'Joe Blow')
12
+ result = subject.evaluate(search)
13
+
14
+ result.should be_an ::ActiveRecord::Relation
15
+ result.to_sql.should match /"name" = 'Joe Blow'/
16
+ end
17
+
18
+ it 'SELECTs DISTINCT when :distinct => true' do
19
+ search = Search.new(Person, :name_eq => 'Joe Blow')
20
+ result = subject.evaluate(search, :distinct => true)
21
+
22
+ result.should be_an ::ActiveRecord::Relation
23
+ result.to_sql.should match /SELECT DISTINCT/
24
+ end
25
+ end
26
+
27
+ it 'contextualizes strings to attributes' do
28
+ attribute = subject.contextualize 'children_children_parent_name'
29
+ attribute.should be_a Arel::Attributes::Attribute
30
+ attribute.name.to_s.should eq 'name'
31
+ attribute.relation.table_alias.should eq 'parents_people'
32
+ end
33
+
34
+ it 'builds new associations if not yet built' do
35
+ attribute = subject.contextualize 'children_articles_title'
36
+ attribute.should be_a Arel::Attributes::Attribute
37
+ attribute.name.to_s.should eq 'title'
38
+ attribute.relation.name.should eq 'articles'
39
+ attribute.relation.table_alias.should be_nil
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ describe Configuration do
5
+ it 'yields Ransack on configure' do
6
+ Ransack.configure do |config|
7
+ config.should eq Ransack
8
+ end
9
+ end
10
+
11
+ it 'adds predicates' do
12
+ Ransack.configure do |config|
13
+ config.add_predicate :test_predicate
14
+ end
15
+
16
+ Ransack.predicates.should have_key 'test_predicate'
17
+ Ransack.predicates.should have_key 'test_predicate_any'
18
+ Ransack.predicates.should have_key 'test_predicate_all'
19
+ end
20
+
21
+ it 'avoids creating compound predicates if :compounds => false' do
22
+ Ransack.configure do |config|
23
+ config.add_predicate :test_predicate_without_compound, :compounds => false
24
+ end
25
+
26
+ Ransack.predicates.should have_key 'test_predicate_without_compound'
27
+ Ransack.predicates.should_not have_key 'test_predicate_without_compound_any'
28
+ Ransack.predicates.should_not have_key 'test_predicate_without_compound_all'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,137 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Helpers
5
+ describe FormBuilder do
6
+
7
+ router = ActionDispatch::Routing::RouteSet.new
8
+ router.draw do
9
+ resources :people
10
+ match ':controller(/:action(/:id(.:format)))'
11
+ end
12
+
13
+ include router.url_helpers
14
+
15
+ # FIXME: figure out a cleaner way to get this behavior
16
+ before do
17
+ @controller = ActionView::TestCase::TestController.new
18
+ @controller.instance_variable_set(:@_routes, router)
19
+ @controller.class_eval do
20
+ include router.url_helpers
21
+ end
22
+
23
+ @controller.view_context_class.class_eval do
24
+ include router.url_helpers
25
+ end
26
+
27
+ @s = Person.search
28
+ @controller.view_context.search_form_for @s do |f|
29
+ @f = f
30
+ end
31
+ end
32
+
33
+ it 'selects previously-entered time values with datetime_select' do
34
+ @s.created_at_eq = [2011, 1, 2, 3, 4, 5]
35
+ html = @f.datetime_select :created_at_eq, :use_month_numbers => true, :include_seconds => true
36
+ %w(2011 1 2 03 04 05).each do |val|
37
+ html.should match /<option selected="selected" value="#{val}">#{val}<\/option>/
38
+ end
39
+ end
40
+
41
+ describe '#label' do
42
+
43
+ it 'localizes attribute names' do
44
+ html = @f.label :name_cont
45
+ html.should match /Full Name contains/
46
+ end
47
+
48
+ end
49
+
50
+ describe '#sort_link' do
51
+ subject { @f.sort_link :name, :controller => 'people' }
52
+
53
+ it { should match /people\?q%5Bs%5D=name\+asc/}
54
+ it { should match /sort_link/}
55
+ it { should match /Full Name<\/a>/}
56
+ end
57
+
58
+ describe '#submit' do
59
+
60
+ it 'localizes :search when no default value given' do
61
+ html = @f.submit
62
+ html.should match /"Search"/
63
+ end
64
+
65
+ end
66
+
67
+ describe '#attribute_select' do
68
+
69
+ it 'returns ransackable attributes' do
70
+ html = @f.attribute_select
71
+ html.split(/\n/).should have(Person.ransackable_attributes.size + 1).lines
72
+ Person.ransackable_attributes.each do |attribute|
73
+ html.should match /<option value="#{attribute}">/
74
+ end
75
+ end
76
+
77
+ it 'returns ransackable attributes for associations with :associations' do
78
+ attributes = Person.ransackable_attributes + Article.ransackable_attributes.map {|a| "articles_#{a}"}
79
+ html = @f.attribute_select :associations => ['articles']
80
+ html.split(/\n/).should have(attributes.size).lines
81
+ attributes.each do |attribute|
82
+ html.should match /<option value="#{attribute}">/
83
+ end
84
+ end
85
+
86
+ it 'returns option groups for base and associations with :associations' do
87
+ html = @f.attribute_select :associations => ['articles']
88
+ [Person, Article].each do |model|
89
+ html.should match /<optgroup label="#{model}">/
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ describe '#predicate_select' do
96
+
97
+ it 'returns predicates with predicate_select' do
98
+ html = @f.predicate_select
99
+ Predicate.names.each do |key|
100
+ html.should match /<option value="#{key}">/
101
+ end
102
+ end
103
+
104
+ it 'filters predicates with single-value :only' do
105
+ html = @f.predicate_select :only => 'eq'
106
+ Predicate.names.reject {|k| k =~ /^eq/}.each do |key|
107
+ html.should_not match /<option value="#{key}">/
108
+ end
109
+ end
110
+
111
+ it 'filters predicates with multi-value :only' do
112
+ html = @f.predicate_select :only => [:eq, :lt]
113
+ Predicate.names.reject {|k| k =~ /^(eq|lt)/}.each do |key|
114
+ html.should_not match /<option value="#{key}">/
115
+ end
116
+ end
117
+
118
+ it 'sorts predicate keys based on order of :only array, with :compounds => false' do
119
+ ordered_keys = @f.send(:predicate_keys, :compounds => false, :only => [:cont, :blank, :eq, :unknown])
120
+ ordered_keys.should == %w(cont blank eq)
121
+ end
122
+
123
+ it 'sorts predicate keys based on order of :only array, with :compounds => true' do
124
+ ordered_keys = @f.send(:predicate_keys, :compounds => true, :only => [:cont, :blank, :eq, :unknown])
125
+ ordered_keys.should == %w(cont cont_any cont_all blank eq eq_any eq_all)
126
+ end
127
+
128
+ it 'excludes compounds when :compounds => false' do
129
+ html = @f.predicate_select :compounds => false
130
+ Predicate.names.select {|k| k =~ /_(any|all)$/}.each do |key|
131
+ html.should_not match /<option value="#{key}">/
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Helpers
5
+ describe FormHelper do
6
+
7
+ router = ActionDispatch::Routing::RouteSet.new
8
+ router.draw do
9
+ resources :people
10
+ match ':controller(/:action(/:id(.:format)))'
11
+ end
12
+
13
+ include router.url_helpers
14
+
15
+ # FIXME: figure out a cleaner way to get this behavior
16
+ before do
17
+ @controller = ActionView::TestCase::TestController.new
18
+ @controller.instance_variable_set(:@_routes, router)
19
+ @controller.class_eval do
20
+ include router.url_helpers
21
+ end
22
+
23
+ @controller.view_context_class.class_eval do
24
+ include router.url_helpers
25
+ end
26
+ end
27
+
28
+ describe '#sort_link' do
29
+ subject { @controller.view_context.sort_link(Person.search(:sorts => ['name desc']), :name, :controller => 'people') }
30
+
31
+ it { should match /people\?q%5Bs%5D=name\+asc/}
32
+ it { should match /sort_link desc/}
33
+ it { should match /Full Name &#9660;/}
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Nodes
5
+ describe Condition do
6
+
7
+ context 'with multiple values and an _any predicate' do
8
+ subject { Condition.extract(Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)) }
9
+
10
+ specify { subject.values.should have(2).values }
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ module Nodes
5
+ describe Grouping do
6
+ before do
7
+ @g = 1
8
+ end
9
+
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ describe Predicate do
5
+
6
+ before do
7
+ @s = Search.new(Person)
8
+ end
9
+
10
+ describe 'eq' do
11
+ it 'generates an equality condition for boolean true' do
12
+ @s.awesome_eq = true
13
+ @s.result.to_sql.should match /"people"."awesome" = 't'/
14
+ end
15
+
16
+ it 'generates an equality condition for boolean false' do
17
+ @s.awesome_eq = false
18
+ @s.result.to_sql.should match /"people"."awesome" = 'f'/
19
+ end
20
+
21
+ it 'does not generate a condition for nil' do
22
+ @s.awesome_eq = nil
23
+ @s.result.to_sql.should_not match /WHERE/
24
+ end
25
+ end
26
+
27
+ describe 'cont' do
28
+ it 'generates a LIKE query with value surrounded by %' do
29
+ @s.name_cont = 'ric'
30
+ @s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
31
+ end
32
+ end
33
+
34
+ describe 'not_cont' do
35
+ it 'generates a NOT LIKE query with value surrounded by %' do
36
+ @s.name_not_cont = 'ric'
37
+ @s.result.to_sql.should match /"people"."name" NOT LIKE '%ric%'/
38
+ end
39
+ end
40
+
41
+ describe 'null' do
42
+ it 'generates a value IS NULL query' do
43
+ @s.name_null = true
44
+ @s.result.to_sql.should match /"people"."name" IS NULL/
45
+ end
46
+ end
47
+
48
+ describe 'not_null' do
49
+ it 'generates a value IS NOT NULL query' do
50
+ @s.name_not_null = true
51
+ @s.result.to_sql.should match /"people"."name" IS NOT NULL/
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,225 @@
1
+ require 'spec_helper'
2
+
3
+ module Ransack
4
+ describe Search do
5
+
6
+ describe '#build' do
7
+ it 'creates Conditions for top-level attributes' do
8
+ search = Search.new(Person, :name_eq => 'Ernie')
9
+ condition = search.base[:name_eq]
10
+ condition.should be_a Nodes::Condition
11
+ condition.predicate.name.should eq 'eq'
12
+ condition.attributes.first.name.should eq 'name'
13
+ condition.value.should eq 'Ernie'
14
+ end
15
+
16
+ it 'creates Conditions for association attributes' do
17
+ search = Search.new(Person, :children_name_eq => 'Ernie')
18
+ condition = search.base[:children_name_eq]
19
+ condition.should be_a Nodes::Condition
20
+ condition.predicate.name.should eq 'eq'
21
+ condition.attributes.first.name.should eq 'children_name'
22
+ condition.value.should eq 'Ernie'
23
+ end
24
+
25
+ it 'creates Conditions for polymorphic belongs_to association attributes' do
26
+ search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie')
27
+ condition = search.base[:notable_of_Person_type_name_eq]
28
+ condition.should be_a Nodes::Condition
29
+ condition.predicate.name.should eq 'eq'
30
+ condition.attributes.first.name.should eq 'notable_of_Person_type_name'
31
+ condition.value.should eq 'Ernie'
32
+ end
33
+
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]
37
+ condition.should be_a Nodes::Condition
38
+ condition.predicate.name.should eq 'eq'
39
+ condition.attributes.first.name.should eq 'notable_of_Person_type_name'
40
+ condition.attributes.last.name.should eq 'notable_of_Article_type_title'
41
+ condition.value.should eq 'Ernie'
42
+ end
43
+
44
+ it 'discards empty conditions' do
45
+ search = Search.new(Person, :children_name_eq => '')
46
+ condition = search.base[:children_name_eq]
47
+ condition.should be_nil
48
+ end
49
+
50
+ it 'accepts arrays of groupings' do
51
+ search = Search.new(Person,
52
+ :g => [
53
+ {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
54
+ {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
55
+ ]
56
+ )
57
+ ors = search.groupings
58
+ ors.should have(2).items
59
+ or1, or2 = ors
60
+ or1.should be_a Nodes::Grouping
61
+ or1.combinator.should eq 'or'
62
+ or2.should be_a Nodes::Grouping
63
+ or2.combinator.should eq 'or'
64
+ end
65
+
66
+ it 'accepts "attributes" hashes for groupings' do
67
+ search = Search.new(Person,
68
+ :g => {
69
+ '0' => {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
70
+ '1' => {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
71
+ }
72
+ )
73
+ ors = search.groupings
74
+ ors.should have(2).items
75
+ or1, or2 = ors
76
+ or1.should be_a Nodes::Grouping
77
+ or1.combinator.should eq 'or'
78
+ or2.should be_a Nodes::Grouping
79
+ or2.combinator.should eq 'or'
80
+ end
81
+
82
+ it 'accepts "attributes" hashes for conditions' do
83
+ search = Search.new(Person,
84
+ :c => {
85
+ '0' => {:a => ['name'], :p => 'eq', :v => ['Ernie']},
86
+ '1' => {:a => ['children_name', 'parent_name'], :p => 'eq', :v => ['Ernie'], :m => 'or'}
87
+ }
88
+ )
89
+ conditions = search.base.conditions
90
+ conditions.should have(2).items
91
+ conditions.map {|c| c.class}.should eq [Nodes::Condition, Nodes::Condition]
92
+ end
93
+ end
94
+
95
+ describe '#result' do
96
+ it 'evaluates conditions contextually' do
97
+ search = Search.new(Person, :children_name_eq => 'Ernie')
98
+ search.result.should be_an ActiveRecord::Relation
99
+ where = search.result.where_values.first
100
+ where.to_sql.should match /"children_people"\."name" = 'Ernie'/
101
+ end
102
+
103
+ it 'evaluates compound conditions contextually' do
104
+ search = Search.new(Person, :children_name_or_name_eq => 'Ernie')
105
+ search.result.should be_an ActiveRecord::Relation
106
+ where = search.result.where_values.first
107
+ where.to_sql.should match /"children_people"\."name" = 'Ernie' OR "people"\."name" = 'Ernie'/
108
+ end
109
+
110
+ it 'evaluates polymorphic belongs_to association conditions contextually' do
111
+ search = Search.new(Note, :notable_of_Person_type_name_eq => 'Ernie')
112
+ search.result.should be_an ActiveRecord::Relation
113
+ where = search.result.where_values.first
114
+ where.to_sql.should match /"people"."name" = 'Ernie'/
115
+ end
116
+
117
+ it 'evaluates nested conditions' do
118
+ search = Search.new(Person, :children_name_eq => 'Ernie',
119
+ :g => [{
120
+ :m => 'or',
121
+ :name_eq => 'Ernie',
122
+ :children_children_name_eq => 'Ernie'
123
+ }]
124
+ )
125
+ search.result.should be_an ActiveRecord::Relation
126
+ where = search.result.where_values.first
127
+ where.to_sql.should match /"children_people"."name" = 'Ernie'/
128
+ where.to_sql.should match /"people"."name" = 'Ernie'/
129
+ where.to_sql.should match /"children_people_2"."name" = 'Ernie'/
130
+ end
131
+
132
+ it 'evaluates arrays of groupings' do
133
+ search = Search.new(Person,
134
+ :g => [
135
+ {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
136
+ {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
137
+ ]
138
+ )
139
+ search.result.should be_an ActiveRecord::Relation
140
+ where = search.result.where_values.first
141
+ sql = where.to_sql
142
+ first, second = sql.split(/ AND /)
143
+ first.should match /"people"."name" = 'Ernie'/
144
+ first.should match /"children_people"."name" = 'Ernie'/
145
+ second.should match /"people"."name" = 'Bert'/
146
+ second.should match /"children_people"."name" = 'Bert'/
147
+ end
148
+
149
+ it 'returns distinct records when passed :distinct => true' do
150
+ search = Search.new(Person, :g => [{:m => 'or', :comments_body_cont => 'e', :articles_comments_body_cont => 'e'}])
151
+ search.result.all.should have(920).items
152
+ search.result(:distinct => true).should have(330).items
153
+ search.result.all.uniq.should eq search.result(:distinct => true).all
154
+ end
155
+ end
156
+
157
+ describe '#sorts=' do
158
+ before do
159
+ @s = Search.new(Person)
160
+ end
161
+
162
+ it 'creates sorts based on a single attribute/direction' do
163
+ @s.sorts = 'id desc'
164
+ @s.sorts.should have(1).item
165
+ sort = @s.sorts.first
166
+ sort.should be_a Nodes::Sort
167
+ sort.name.should eq 'id'
168
+ sort.dir.should eq 'desc'
169
+ end
170
+
171
+ it 'creates sorts based on multiple attributes/directions in array format' do
172
+ @s.sorts = ['id desc', 'name asc']
173
+ @s.sorts.should have(2).items
174
+ sort1, sort2 = @s.sorts
175
+ sort1.should be_a Nodes::Sort
176
+ sort1.name.should eq 'id'
177
+ sort1.dir.should eq 'desc'
178
+ sort2.should be_a Nodes::Sort
179
+ sort2.name.should eq 'name'
180
+ sort2.dir.should eq 'asc'
181
+ end
182
+
183
+ it 'creates sorts based on multiple attributes/directions in hash format' do
184
+ @s.sorts = {
185
+ '0' => {
186
+ :name => 'id',
187
+ :dir => 'desc'
188
+ },
189
+ '1' => {
190
+ :name => 'name',
191
+ :dir => 'asc'
192
+ }
193
+ }
194
+ @s.sorts.should have(2).items
195
+ @s.sorts.should be_all {|s| Nodes::Sort === s}
196
+ id_sort = @s.sorts.detect {|s| s.name == 'id'}
197
+ name_sort = @s.sorts.detect {|s| s.name == 'name'}
198
+ id_sort.dir.should eq 'desc'
199
+ name_sort.dir.should eq 'asc'
200
+ end
201
+ end
202
+
203
+ describe '#method_missing' do
204
+ before do
205
+ @s = Search.new(Person)
206
+ end
207
+
208
+ it 'raises NoMethodError when sent an invalid attribute' do
209
+ expect {@s.blah}.to raise_error NoMethodError
210
+ end
211
+
212
+ it 'sets condition attributes when sent valid attributes' do
213
+ @s.name_eq = 'Ernie'
214
+ @s.name_eq.should eq 'Ernie'
215
+ end
216
+
217
+ it 'allows chaining to access nested conditions' do
218
+ @s.groupings = [{:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'}]
219
+ @s.groupings.first.name_eq.should eq 'Ernie'
220
+ @s.groupings.first.children_name_eq.should eq 'Ernie'
221
+ end
222
+ end
223
+
224
+ end
225
+ end
@@ -0,0 +1,47 @@
1
+ require 'machinist/active_record'
2
+ require 'sham'
3
+ require 'faker'
4
+ require 'ransack'
5
+
6
+ Time.zone = 'Eastern Time (US & Canada)'
7
+ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'support', '*.yml')]
8
+
9
+ Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
10
+ require f
11
+ end
12
+
13
+ Sham.define do
14
+ name { Faker::Name.name }
15
+ title { Faker::Lorem.sentence }
16
+ body { Faker::Lorem.paragraph }
17
+ salary {|index| 30000 + (index * 1000)}
18
+ tag_name { Faker::Lorem.words(3).join(' ') }
19
+ note { Faker::Lorem.words(7).join(' ') }
20
+ end
21
+
22
+ RSpec.configure do |config|
23
+ config.before(:suite) do
24
+ puts '=' * 80
25
+ puts "Running specs against ActiveRecord #{ActiveRecord::VERSION::STRING} and ARel #{Arel::VERSION}..."
26
+ puts '=' * 80
27
+ Schema.create
28
+ end
29
+
30
+ config.before(:all) { Sham.reset(:before_all) }
31
+ config.before(:each) { Sham.reset(:before_each) }
32
+
33
+ config.include RansackHelper
34
+ end
35
+
36
+ RSpec::Matchers.define :be_like do |expected|
37
+ match do |actual|
38
+ actual.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip ==
39
+ expected.gsub(/^\s+|\s+$/, '').gsub(/\s+/, ' ').strip
40
+ end
41
+ end
42
+
43
+ RSpec::Matchers.define :have_attribute_method do |expected|
44
+ match do |actual|
45
+ actual.attribute_method?(expected)
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ en:
2
+ ransack:
3
+ attributes:
4
+ person:
5
+ name: Full Name