ransack 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +11 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +5 -0
  5. data/Rakefile +19 -0
  6. data/lib/ransack.rb +24 -0
  7. data/lib/ransack/adapters/active_record.rb +2 -0
  8. data/lib/ransack/adapters/active_record/base.rb +17 -0
  9. data/lib/ransack/adapters/active_record/context.rb +153 -0
  10. data/lib/ransack/configuration.rb +39 -0
  11. data/lib/ransack/constants.rb +23 -0
  12. data/lib/ransack/context.rb +152 -0
  13. data/lib/ransack/helpers.rb +2 -0
  14. data/lib/ransack/helpers/form_builder.rb +172 -0
  15. data/lib/ransack/helpers/form_helper.rb +27 -0
  16. data/lib/ransack/locale/en.yml +67 -0
  17. data/lib/ransack/naming.rb +53 -0
  18. data/lib/ransack/nodes.rb +7 -0
  19. data/lib/ransack/nodes/and.rb +8 -0
  20. data/lib/ransack/nodes/attribute.rb +36 -0
  21. data/lib/ransack/nodes/condition.rb +209 -0
  22. data/lib/ransack/nodes/grouping.rb +207 -0
  23. data/lib/ransack/nodes/node.rb +34 -0
  24. data/lib/ransack/nodes/or.rb +8 -0
  25. data/lib/ransack/nodes/sort.rb +39 -0
  26. data/lib/ransack/nodes/value.rb +120 -0
  27. data/lib/ransack/predicate.rb +57 -0
  28. data/lib/ransack/search.rb +114 -0
  29. data/lib/ransack/translate.rb +92 -0
  30. data/lib/ransack/version.rb +3 -0
  31. data/ransack.gemspec +29 -0
  32. data/spec/blueprints/articles.rb +5 -0
  33. data/spec/blueprints/comments.rb +5 -0
  34. data/spec/blueprints/notes.rb +3 -0
  35. data/spec/blueprints/people.rb +4 -0
  36. data/spec/blueprints/tags.rb +3 -0
  37. data/spec/console.rb +22 -0
  38. data/spec/helpers/ransack_helper.rb +2 -0
  39. data/spec/playground.rb +37 -0
  40. data/spec/ransack/adapters/active_record/base_spec.rb +30 -0
  41. data/spec/ransack/adapters/active_record/context_spec.rb +29 -0
  42. data/spec/ransack/configuration_spec.rb +11 -0
  43. data/spec/ransack/helpers/form_builder_spec.rb +39 -0
  44. data/spec/ransack/nodes/compound_condition_spec.rb +0 -0
  45. data/spec/ransack/nodes/condition_spec.rb +0 -0
  46. data/spec/ransack/nodes/grouping_spec.rb +13 -0
  47. data/spec/ransack/predicate_spec.rb +25 -0
  48. data/spec/ransack/search_spec.rb +182 -0
  49. data/spec/spec_helper.rb +28 -0
  50. data/spec/support/schema.rb +102 -0
  51. metadata +200 -0
File without changes
@@ -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,25 @@
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 'cont' do
11
+ it 'generates a LIKE query with value surrounded by %' do
12
+ @s.name_cont = 'ric'
13
+ @s.result.to_sql.should match /"people"."name" LIKE '%ric%'/
14
+ end
15
+ end
16
+
17
+ describe 'not_cont' do
18
+ it 'generates a NOT LIKE query with value surrounded by %' do
19
+ @s.name_not_cont = 'ric'
20
+ @s.result.to_sql.should match /"people"."name" NOT LIKE '%ric%'/
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,182 @@
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 'discards empty conditions' do
26
+ search = Search.new(Person, :children_name_eq => '')
27
+ condition = search.base[:children_name_eq]
28
+ condition.should be_nil
29
+ end
30
+
31
+ it 'accepts arrays of groupings' do
32
+ search = Search.new(Person,
33
+ :o => [
34
+ {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
35
+ {:name_eq => 'Bert', :children_name_eq => 'Bert'},
36
+ ]
37
+ )
38
+ ors = search.ors
39
+ ors.should have(2).items
40
+ or1, or2 = ors
41
+ or1.should be_a Nodes::Or
42
+ or2.should be_a Nodes::Or
43
+ end
44
+
45
+ it 'accepts "attributes" hashes for groupings' do
46
+ search = Search.new(Person,
47
+ :o => {
48
+ '0' => {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
49
+ '1' => {:name_eq => 'Bert', :children_name_eq => 'Bert'},
50
+ }
51
+ )
52
+ ors = search.ors
53
+ ors.should have(2).items
54
+ or1, or2 = ors
55
+ or1.should be_a Nodes::Or
56
+ or2.should be_a Nodes::Or
57
+ end
58
+
59
+ it 'accepts "attributes" hashes for conditions' do
60
+ search = Search.new(Person,
61
+ :c => {
62
+ '0' => {:a => ['name'], :p => 'eq', :v => ['Ernie']},
63
+ '1' => {:a => ['children_name', 'parent_name'], :p => 'eq', :v => ['Ernie'], :m => 'or'}
64
+ }
65
+ )
66
+ conditions = search.base.conditions
67
+ conditions.should have(2).items
68
+ conditions.map {|c| c.class}.should eq [Nodes::Condition, Nodes::Condition]
69
+ end
70
+ end
71
+
72
+ describe '#result' do
73
+ it 'evaluates conditions contextually' do
74
+ search = Search.new(Person, :children_name_eq => 'Ernie')
75
+ search.result.should be_an ActiveRecord::Relation
76
+ where = search.result.where_values.first
77
+ where.to_sql.should match /"children_people"\."name" = 'Ernie'/
78
+ end
79
+
80
+ it 'evaluates compound conditions contextually' do
81
+ search = Search.new(Person, :children_name_or_name_eq => 'Ernie')
82
+ search.result.should be_an ActiveRecord::Relation
83
+ where = search.result.where_values.first
84
+ where.to_sql.should match /"children_people"\."name" = 'Ernie' OR "people"\."name" = 'Ernie'/
85
+ end
86
+
87
+ it 'evaluates nested conditions' do
88
+ search = Search.new(Person, :children_name_eq => 'Ernie',
89
+ :o => [{
90
+ :name_eq => 'Ernie',
91
+ :children_children_name_eq => 'Ernie'
92
+ }]
93
+ )
94
+ search.result.should be_an ActiveRecord::Relation
95
+ where = search.result.where_values.first
96
+ where.to_sql.should match /\("children_people"."name" = 'Ernie' AND \("people"."name" = 'Ernie' OR "children_people_2"."name" = 'Ernie'\)\)/
97
+ end
98
+
99
+ it 'evaluates arrays of groupings' do
100
+ search = Search.new(Person,
101
+ :o => [
102
+ {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
103
+ {:name_eq => 'Bert', :children_name_eq => 'Bert'},
104
+ ]
105
+ )
106
+ search.result.should be_an ActiveRecord::Relation
107
+ where = search.result.where_values.first
108
+ where.to_sql.should match /\(\("people"."name" = 'Ernie' OR "children_people"."name" = 'Ernie'\) AND \("people"."name" = 'Bert' OR "children_people"."name" = 'Bert'\)\)/
109
+ end
110
+ end
111
+
112
+ describe '#sorts=' do
113
+ before do
114
+ @s = Search.new(Person)
115
+ end
116
+
117
+ it 'creates sorts based on a single attribute/direction' do
118
+ @s.sorts = 'id desc'
119
+ @s.sorts.should have(1).item
120
+ sort = @s.sorts.first
121
+ sort.should be_a Nodes::Sort
122
+ sort.name.should eq 'id'
123
+ sort.dir.should eq 'desc'
124
+ end
125
+
126
+ it 'creates sorts based on multiple attributes/directions in array format' do
127
+ @s.sorts = ['id desc', 'name asc']
128
+ @s.sorts.should have(2).items
129
+ sort1, sort2 = @s.sorts
130
+ sort1.should be_a Nodes::Sort
131
+ sort1.name.should eq 'id'
132
+ sort1.dir.should eq 'desc'
133
+ sort2.should be_a Nodes::Sort
134
+ sort2.name.should eq 'name'
135
+ sort2.dir.should eq 'asc'
136
+ end
137
+
138
+ it 'creates sorts based on multiple attributes/directions in hash format' do
139
+ @s.sorts = {
140
+ '0' => {
141
+ :name => 'id',
142
+ :dir => 'desc'
143
+ },
144
+ '1' => {
145
+ :name => 'name',
146
+ :dir => 'asc'
147
+ }
148
+ }
149
+ @s.sorts.should have(2).items
150
+ sort1, sort2 = @s.sorts
151
+ sort1.should be_a Nodes::Sort
152
+ sort1.name.should eq 'id'
153
+ sort1.dir.should eq 'desc'
154
+ sort2.should be_a Nodes::Sort
155
+ sort2.name.should eq 'name'
156
+ sort2.dir.should eq 'asc'
157
+ end
158
+ end
159
+
160
+ describe '#method_missing' do
161
+ before do
162
+ @s = Search.new(Person)
163
+ end
164
+
165
+ it 'raises NoMethodError when sent an invalid attribute' do
166
+ expect {@s.blah}.to raise_error NoMethodError
167
+ end
168
+
169
+ it 'sets condition attributes when sent valid attributes' do
170
+ @s.name_eq = 'Ernie'
171
+ @s.name_eq.should eq 'Ernie'
172
+ end
173
+
174
+ it 'allows chaining to access nested conditions' do
175
+ @s.ors = [{:name_eq => 'Ernie', :children_name_eq => 'Ernie'}]
176
+ @s.ors.first.name_eq.should eq 'Ernie'
177
+ @s.ors.first.children_name_eq.should eq 'Ernie'
178
+ end
179
+ end
180
+
181
+ end
182
+ end
@@ -0,0 +1,28 @@
1
+ require 'machinist/active_record'
2
+ require 'sham'
3
+ require 'faker'
4
+
5
+ Time.zone = 'Eastern Time (US & Canada)'
6
+
7
+ Dir[File.expand_path('../{helpers,support,blueprints}/*.rb', __FILE__)].each do |f|
8
+ require f
9
+ end
10
+
11
+ Sham.define do
12
+ name { Faker::Name.name }
13
+ title { Faker::Lorem.sentence }
14
+ body { Faker::Lorem.paragraph }
15
+ salary {|index| 30000 + (index * 1000)}
16
+ tag_name { Faker::Lorem.words(3).join(' ') }
17
+ note { Faker::Lorem.words(7).join(' ') }
18
+ end
19
+
20
+ RSpec.configure do |config|
21
+ config.before(:suite) { Schema.create }
22
+ config.before(:all) { Sham.reset(:before_all) }
23
+ config.before(:each) { Sham.reset(:before_each) }
24
+
25
+ config.include RansackHelper
26
+ end
27
+
28
+ require 'ransack'
@@ -0,0 +1,102 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ :adapter => 'sqlite3',
5
+ :database => ':memory:'
6
+ )
7
+
8
+ class Person < ActiveRecord::Base
9
+ belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id
10
+ has_many :children, :class_name => 'Person', :foreign_key => :parent_id
11
+ has_many :articles
12
+ has_many :comments
13
+ has_many :authored_article_comments, :through => :articles,
14
+ :class_name => 'Comment', :foreign_key => :person_id
15
+ has_many :notes, :as => :notable
16
+ end
17
+
18
+ class Article < ActiveRecord::Base
19
+ belongs_to :person
20
+ has_many :comments
21
+ has_and_belongs_to_many :tags
22
+ has_many :notes, :as => :notable
23
+ end
24
+
25
+ class Comment < ActiveRecord::Base
26
+ belongs_to :article
27
+ belongs_to :person
28
+ end
29
+
30
+ class Tag < ActiveRecord::Base
31
+ has_and_belongs_to_many :articles
32
+ end
33
+
34
+ class Note < ActiveRecord::Base
35
+ belongs_to :notable, :polymorphic => true
36
+ end
37
+
38
+ module Schema
39
+ def self.create
40
+ ActiveRecord::Base.silence do
41
+ ActiveRecord::Migration.verbose = false
42
+
43
+ ActiveRecord::Schema.define do
44
+ create_table :people, :force => true do |t|
45
+ t.integer :parent_id
46
+ t.string :name
47
+ t.integer :salary
48
+ t.timestamps
49
+ end
50
+
51
+ create_table :articles, :force => true do |t|
52
+ t.integer :person_id
53
+ t.string :title
54
+ t.text :body
55
+ end
56
+
57
+ create_table :comments, :force => true do |t|
58
+ t.integer :article_id
59
+ t.integer :person_id
60
+ t.text :body
61
+ end
62
+
63
+ create_table :tags, :force => true do |t|
64
+ t.string :name
65
+ end
66
+
67
+ create_table :articles_tags, :force => true, :id => false do |t|
68
+ t.integer :article_id
69
+ t.integer :tag_id
70
+ end
71
+
72
+ create_table :notes, :force => true do |t|
73
+ t.integer :notable_id
74
+ t.string :notable_type
75
+ t.string :note
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ 10.times do
82
+ person = Person.make
83
+ Note.make(:notable => person)
84
+ 3.times do
85
+ article = Article.make(:person => person)
86
+ 3.times do
87
+ article.tags = [Tag.make, Tag.make, Tag.make]
88
+ end
89
+ Note.make(:notable => article)
90
+ 10.times do
91
+ Comment.make(:article => article)
92
+ end
93
+ end
94
+ 2.times do
95
+ Comment.make(:person => person)
96
+ end
97
+ end
98
+
99
+ Comment.make(:body => 'First post!', :article => Article.make(:title => 'Hello, world!'))
100
+
101
+ end
102
+ end
metadata ADDED
@@ -0,0 +1,200 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ransack
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Ernie Miller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-30 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activerecord
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 3.1.0.alpha
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 3.1.0.alpha
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: actionpack
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 3.1.0.alpha
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rspec
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 2.5.0
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: machinist
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.6
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: faker
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 0.9.5
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: sqlite3
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 1.3.3
91
+ type: :development
92
+ version_requirements: *id007
93
+ description: Not yet ready for public consumption.
94
+ email:
95
+ - ernie@metautonomo.us
96
+ executables: []
97
+
98
+ extensions: []
99
+
100
+ extra_rdoc_files: []
101
+
102
+ files:
103
+ - .gitignore
104
+ - Gemfile
105
+ - LICENSE
106
+ - README.rdoc
107
+ - Rakefile
108
+ - lib/ransack.rb
109
+ - lib/ransack/adapters/active_record.rb
110
+ - lib/ransack/adapters/active_record/base.rb
111
+ - lib/ransack/adapters/active_record/context.rb
112
+ - lib/ransack/configuration.rb
113
+ - lib/ransack/constants.rb
114
+ - lib/ransack/context.rb
115
+ - lib/ransack/helpers.rb
116
+ - lib/ransack/helpers/form_builder.rb
117
+ - lib/ransack/helpers/form_helper.rb
118
+ - lib/ransack/locale/en.yml
119
+ - lib/ransack/naming.rb
120
+ - lib/ransack/nodes.rb
121
+ - lib/ransack/nodes/and.rb
122
+ - lib/ransack/nodes/attribute.rb
123
+ - lib/ransack/nodes/condition.rb
124
+ - lib/ransack/nodes/grouping.rb
125
+ - lib/ransack/nodes/node.rb
126
+ - lib/ransack/nodes/or.rb
127
+ - lib/ransack/nodes/sort.rb
128
+ - lib/ransack/nodes/value.rb
129
+ - lib/ransack/predicate.rb
130
+ - lib/ransack/search.rb
131
+ - lib/ransack/translate.rb
132
+ - lib/ransack/version.rb
133
+ - ransack.gemspec
134
+ - spec/blueprints/articles.rb
135
+ - spec/blueprints/comments.rb
136
+ - spec/blueprints/notes.rb
137
+ - spec/blueprints/people.rb
138
+ - spec/blueprints/tags.rb
139
+ - spec/console.rb
140
+ - spec/helpers/ransack_helper.rb
141
+ - spec/playground.rb
142
+ - spec/ransack/adapters/active_record/base_spec.rb
143
+ - spec/ransack/adapters/active_record/context_spec.rb
144
+ - spec/ransack/configuration_spec.rb
145
+ - spec/ransack/helpers/form_builder_spec.rb
146
+ - spec/ransack/nodes/compound_condition_spec.rb
147
+ - spec/ransack/nodes/condition_spec.rb
148
+ - spec/ransack/nodes/grouping_spec.rb
149
+ - spec/ransack/predicate_spec.rb
150
+ - spec/ransack/search_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/support/schema.rb
153
+ has_rdoc: true
154
+ homepage: http://metautonomo.us/projects/ransack
155
+ licenses: []
156
+
157
+ post_install_message:
158
+ rdoc_options: []
159
+
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ none: false
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: "0"
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: "0"
174
+ requirements: []
175
+
176
+ rubyforge_project: ransack
177
+ rubygems_version: 1.5.3
178
+ signing_key:
179
+ specification_version: 3
180
+ summary: Object-based searching. Like MetaSearch, but this time, with a better name.
181
+ test_files:
182
+ - spec/blueprints/articles.rb
183
+ - spec/blueprints/comments.rb
184
+ - spec/blueprints/notes.rb
185
+ - spec/blueprints/people.rb
186
+ - spec/blueprints/tags.rb
187
+ - spec/console.rb
188
+ - spec/helpers/ransack_helper.rb
189
+ - spec/playground.rb
190
+ - spec/ransack/adapters/active_record/base_spec.rb
191
+ - spec/ransack/adapters/active_record/context_spec.rb
192
+ - spec/ransack/configuration_spec.rb
193
+ - spec/ransack/helpers/form_builder_spec.rb
194
+ - spec/ransack/nodes/compound_condition_spec.rb
195
+ - spec/ransack/nodes/condition_spec.rb
196
+ - spec/ransack/nodes/grouping_spec.rb
197
+ - spec/ransack/predicate_spec.rb
198
+ - spec/ransack/search_spec.rb
199
+ - spec/spec_helper.rb
200
+ - spec/support/schema.rb