meta_where 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ class Note < ActiveRecord::Base
2
+ belongs_to :notable, :polymorphic => true
3
+ end
@@ -0,0 +1,79 @@
1
+ peter:
2
+ notable_type: Developer
3
+ notable_id : 1
4
+ note : A straight shooter with upper management written all over him.
5
+
6
+ michael:
7
+ notable_type: Developer
8
+ notable_id : 2
9
+ note : Doesn't like the singer of the same name. The nerve!
10
+
11
+ samir:
12
+ notable_type: Developer
13
+ notable_id : 3
14
+ note : Naga.... Naga..... Not gonna work here anymore anyway.
15
+
16
+ herb:
17
+ notable_type: Developer
18
+ notable_id : 4
19
+ note : Will show you what he's doing.
20
+
21
+ dude:
22
+ notable_type: Developer
23
+ notable_id : 5
24
+ note : Nothing of note.
25
+
26
+ ernie:
27
+ notable_type: Developer
28
+ notable_id : 6
29
+ note : Complete slacker. Should probably be fired.
30
+
31
+ someone:
32
+ notable_type: Developer
33
+ notable_id : 7
34
+ note : Just another developer.
35
+
36
+ another:
37
+ notable_type: Developer
38
+ notable_id : 8
39
+ note : Placing a note in this guy's file for insubordination.
40
+
41
+ initech:
42
+ notable_type: Company
43
+ notable_id : 1
44
+ note : Innovation + Technology!
45
+
46
+ aos:
47
+ notable_type: Company
48
+ notable_id : 2
49
+ note : Advanced solutions of an optical nature.
50
+
51
+ mission_data:
52
+ notable_type: Company
53
+ notable_id : 3
54
+ note : Best design + development shop in the 'ville.
55
+
56
+ y2k:
57
+ notable_type: Project
58
+ notable_id : 1
59
+ note : It may have already passed but that's no excuse to be unprepared!
60
+
61
+ virus:
62
+ notable_type: Project
63
+ notable_id : 2
64
+ note : It could bring the company to its knees.
65
+
66
+ awesome:
67
+ notable_type: Project
68
+ notable_id : 3
69
+ note : This note is AWESOME!!!
70
+
71
+ metasearch:
72
+ notable_type: Project
73
+ notable_id : 4
74
+ note : A complete waste of the developer's time.
75
+
76
+ another:
77
+ notable_type: Project
78
+ notable_id : 5
79
+ note : This is another project note.
@@ -0,0 +1,14 @@
1
+ grandpa:
2
+ name : Abraham
3
+ id : 1
4
+ parent_id: nil
5
+
6
+ father:
7
+ name : Isaac
8
+ id : 2
9
+ parent_id: 1
10
+
11
+ son:
12
+ name : Jacob
13
+ id : 3
14
+ parent_id: 2
@@ -0,0 +1,4 @@
1
+ class Person < ActiveRecord::Base
2
+ belongs_to :parent, :class_name => 'Person', :foreign_key => :parent_id
3
+ has_many :children, :class_name => 'Person', :foreign_key => :parent_id
4
+ end
@@ -0,0 +1,4 @@
1
+ class Project < ActiveRecord::Base
2
+ has_and_belongs_to_many :developers
3
+ has_many :notes, :as => :notable
4
+ end
@@ -0,0 +1,24 @@
1
+ y2k:
2
+ estimated_hours: 1000
3
+ name : Y2K Software Updates
4
+ id : 1
5
+
6
+ virus:
7
+ estimated_hours: 80
8
+ name : Virus
9
+ id : 2
10
+
11
+ awesome:
12
+ estimated_hours: 100
13
+ name : Do something awesome
14
+ id : 3
15
+
16
+ metasearch:
17
+ estimated_hours: 100
18
+ name : MetaSearch Development
19
+ id : 4
20
+
21
+ another:
22
+ estimated_hours: 120
23
+ name : Another Project
24
+ id : 5
@@ -0,0 +1,52 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table "companies", :force => true do |t|
4
+ t.string "name"
5
+ t.datetime "created_at"
6
+ t.datetime "updated_at"
7
+ end
8
+
9
+ create_table "developers", :force => true do |t|
10
+ t.integer "company_id"
11
+ t.string "name"
12
+ t.integer "salary"
13
+ t.boolean "slacker"
14
+ end
15
+
16
+ create_table "projects", :force => true do |t|
17
+ t.string "name"
18
+ t.float "estimated_hours"
19
+ end
20
+
21
+ create_table "developers_projects", :id => false, :force => true do |t|
22
+ t.integer "developer_id"
23
+ t.integer "project_id"
24
+ end
25
+
26
+ create_table "notes", :force => true do |t|
27
+ t.string "notable_type"
28
+ t.integer "notable_id"
29
+ t.string "note"
30
+ end
31
+
32
+ create_table "data_types", :force => true do |t|
33
+ t.integer "company_id"
34
+ t.string "str"
35
+ t.text "txt"
36
+ t.integer "int"
37
+ t.float "flt"
38
+ t.decimal "dec"
39
+ t.datetime "dtm"
40
+ t.timestamp "tms"
41
+ t.time "tim"
42
+ t.date "dat"
43
+ t.binary "bin"
44
+ t.boolean "bln"
45
+ end
46
+
47
+ create_table "people", :force => true do |t|
48
+ t.integer "parent_id"
49
+ t.string "name"
50
+ end
51
+
52
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'active_record'
5
+ require 'active_record/fixtures'
6
+ require 'active_support/time'
7
+
8
+ FIXTURES_PATH = File.join(File.dirname(__FILE__), 'fixtures')
9
+
10
+ Time.zone = 'Eastern Time (US & Canada)'
11
+
12
+ ActiveRecord::Base.establish_connection(
13
+ :adapter => defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3',
14
+ :database => ':memory:'
15
+ )
16
+
17
+ dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
18
+ dep.autoload_paths.unshift FIXTURES_PATH
19
+
20
+ ActiveRecord::Base.silence do
21
+ ActiveRecord::Migration.verbose = false
22
+ load File.join(FIXTURES_PATH, 'schema.rb')
23
+ end
24
+
25
+ Fixtures.create_fixtures(FIXTURES_PATH, ActiveRecord::Base.connection.tables)
26
+
27
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
28
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
29
+ require 'meta_where'
30
+ MetaWhere.operator_overload!
31
+
32
+ class Test::Unit::TestCase
33
+ end
@@ -0,0 +1,225 @@
1
+ require 'helper'
2
+
3
+ class TestRelations < Test::Unit::TestCase
4
+ context "A company relation" do
5
+ setup do
6
+ @r = Company.scoped
7
+ end
8
+
9
+ should "behave as expected with one-level hash params" do
10
+ results = @r.where(:name => 'Initech')
11
+ assert_equal 1, results.size
12
+ assert_equal results.first, Company.find_by_name('Initech')
13
+ end
14
+
15
+ should "behave as expected with nested hash params" do
16
+ results = @r.where(
17
+ :developers => {
18
+ :name => 'Peter Gibbons',
19
+ :notes => {
20
+ :note => 'A straight shooter with upper management written all over him.'
21
+ }
22
+ }
23
+ )
24
+ assert_raises ActiveRecord::StatementInvalid do
25
+ results.all
26
+ end
27
+ results = results.joins(:developers => :notes)
28
+ assert_equal 1, results.size
29
+ assert_equal results.first, Company.find_by_name('Initech')
30
+ end
31
+
32
+ should "create new records with values from equality predicates" do
33
+ assert_equal "New Company",
34
+ @r.where(:name => 'New Company').new.name
35
+ assert_equal "New Company",
36
+ @r.where(:name.eq => 'New Company').new.name
37
+ assert_equal "New Company",
38
+ @r.where(:name.eq % 'New Company').new.name
39
+ end
40
+
41
+ should "create new records with values from equality predicates using last supplied predicate" do
42
+ assert_equal "Newer Company",
43
+ @r.where(:name => 'New Company').where(:name => 'Newer Company').new.name
44
+ assert_equal "Newer Company",
45
+ @r.where(:name.eq => 'New Company').where(:name.eq => 'Newer Company').new.name
46
+ assert_equal "Newer Company",
47
+ @r.where(:name.eq % 'New Company').where(:name.eq % 'Newer Company').new.name
48
+ end
49
+
50
+ should "behave as expected with SQL interpolation" do
51
+ results = @r.where('name like ?', '%tech')
52
+ assert_equal 1, results.size
53
+ assert_equal results.first, Company.find_by_name('Initech')
54
+ end
55
+
56
+ should "behave as expected with mixed hash and SQL interpolation" do
57
+ results = @r.where('name like ?', '%tech').where(:created_at => 100.years.ago..Time.now)
58
+ assert_equal 1, results.size
59
+ assert_equal results.first, Company.find_by_name('Initech')
60
+ end
61
+
62
+ should "allow multiple condition params in a single where" do
63
+ results = @r.where(['name like ?', '%tech'], :created_at => 100.years.ago..Time.now)
64
+ assert_equal 1, results.size
65
+ assert_equal results.first, Company.find_by_name('Initech')
66
+ end
67
+
68
+ should "allow predicate method selection on hash keys" do
69
+ assert_equal @r.where(:name.eq => 'Initech').all, @r.where(:name => 'Initech').all
70
+ assert_equal @r.where(:name.matches => 'Mission%').all, @r.where('name LIKE ?', 'Mission%').all
71
+ end
72
+
73
+ should "allow operators to select predicate methods" do
74
+ assert_equal @r.where(:name ^ 'Initech').all, @r.where('name != ?', 'Initech').all
75
+ assert_equal @r.where(:id + [1,3]).all, @r.where('id IN (?)', [1,3]).all
76
+ assert_equal @r.where(:name =~ 'Advanced%').all, @r.where('name LIKE ?', 'Advanced%').all
77
+ end
78
+
79
+ should "use % 'substitution' for hash key predicate methods" do
80
+ assert_equal @r.where(:name.like % 'Advanced%').all, @r.where('name LIKE ?', 'Advanced%').all
81
+ end
82
+
83
+ should "allow | and & for compound predicates" do
84
+ assert_equal @r.where(:name.like % 'Advanced%' | :name.like % 'Init%').all,
85
+ @r.where('name LIKE ? OR name LIKE ?', 'Advanced%', 'Init%').all
86
+ assert_equal @r.where(:name.like % 'Mission%' & :name.like % '%Data').all,
87
+ @r.where('name LIKE ? AND name LIKE ?', 'Mission%', '%Data').all
88
+ end
89
+
90
+ should "allow nested conditions hashes to have array values" do
91
+ assert_equal @r.joins(:data_types).where(:data_types => {:dec => 2..5}).all,
92
+ @r.joins(:data_types).where(:data_types => [:dec >= 2, :dec <= 5]).all
93
+ end
94
+
95
+ should "allow combinations of options that no sane developer would ever try to use" do
96
+ assert_equal @r.find_all_by_name('Initech'),
97
+ @r.joins(:data_types, :developers => [:projects, :notes]).
98
+ where(
99
+ {
100
+ :data_types => [:dec > 3, {:bln.eq => true}]
101
+ } &
102
+ {
103
+ :developers => {
104
+ :name.like => 'Peter Gibbons'
105
+ }
106
+ } &
107
+ {
108
+ :developers => {
109
+ :projects => {
110
+ :estimated_hours.gteq => 1000
111
+ },
112
+ :notes => [:note.matches % '%straight shooter%']
113
+ }
114
+ }
115
+ ).uniq
116
+ end
117
+
118
+ should "allow ordering by attributes in ascending order" do
119
+ last_created = @r.all.sort {|a, b| a.created_at <=> b.created_at}.last
120
+ assert_equal last_created, @r.order(:created_at.asc).last
121
+ end
122
+
123
+ should "allow ordering by attributes in descending order" do
124
+ last_created = @r.all.sort {|a, b| a.created_at <=> b.created_at}.last
125
+ assert_equal last_created, @r.order(:created_at.desc).first
126
+ end
127
+
128
+ should "allow ordering by attributes on nested associations" do
129
+ highest_paying = Developer.order(:salary.desc).first.company
130
+ assert_equal highest_paying, @r.joins(:developers).order(:developers => :salary.desc).first
131
+ end
132
+
133
+ context "with eager-loaded developers" do
134
+ setup do
135
+ @r = @r.includes(:developers).where(:developers => {:name => 'Ernie Miller'})
136
+ end
137
+
138
+ should "return the expected result" do
139
+ assert_equal Company.where(:name => 'Mission Data'), @r.all
140
+ end
141
+
142
+ should "generate debug SQL with the joins in place" do
143
+ assert_match /LEFT OUTER JOIN "developers"/, @r.debug_sql
144
+ end
145
+ end
146
+ end
147
+
148
+ context "A merged relation" do
149
+ setup do
150
+ @r = Developer.where(:salary.gteq % 70000) & Company.where(:name.matches % 'Initech')
151
+ end
152
+
153
+ should "keep the table of the second relation intact in the query" do
154
+ assert_match /#{Company.quoted_table_name}."name"/, @r.to_sql
155
+ end
156
+
157
+ should "return expected results" do
158
+ assert_equal ['Peter Gibbons', 'Michael Bolton'], @r.all.map(&:name)
159
+ end
160
+ end
161
+
162
+ context "A merged relation with an alternate association" do
163
+ setup do
164
+ @r = Company.scoped.merge(Developer.where(:salary.gt => 70000), :slackers)
165
+ end
166
+
167
+ should "use the proper association" do
168
+ assert_match Company.joins(:slackers).where(:slackers => {:salary.gt => 70000}).to_sql,
169
+ @r.to_sql
170
+ end
171
+
172
+ should "return expected results" do
173
+ assert_equal ['Initech', 'Advanced Optical Solutions'], @r.all.map(&:name)
174
+ end
175
+ end
176
+
177
+ context "A Person relation" do
178
+ setup do
179
+ @r = Person.scoped
180
+ end
181
+
182
+ context "with self-referencing joins" do
183
+ setup do
184
+ @r = @r.where(:children => {:children => {:name => 'Jacob'}}).joins(:children => :children)
185
+ end
186
+
187
+ should "join the table multiple times with aliases" do
188
+ assert_equal 2, @r.to_sql.scan('INNER JOIN').size
189
+ assert_match /INNER JOIN "people" "children_people"/, @r.to_sql
190
+ assert_match /INNER JOIN "people" "children_people_2"/, @r.to_sql
191
+ end
192
+
193
+ should "place the condition on the correct join" do
194
+ assert_match /"children_people_2"."name" = 'Jacob'/, @r.to_sql
195
+ end
196
+
197
+ should "return the expected result" do
198
+ assert_equal Person.where(:name => 'Abraham'), @r.all
199
+ end
200
+ end
201
+
202
+ context "with self-referencing joins on parent and children" do
203
+ setup do
204
+ @r = @r.where(:children => {:children => {:parent => {:parent => {:name => 'Abraham'}}}})
205
+ .joins(:children => {:children => {:parent => :parent}})
206
+ end
207
+
208
+ should "join the table multiple times with aliases" do
209
+ assert_equal 4, @r.to_sql.scan('INNER JOIN').size
210
+ assert_match /INNER JOIN "people" "children_people"/, @r.to_sql
211
+ assert_match /INNER JOIN "people" "children_people_2"/, @r.to_sql
212
+ assert_match /INNER JOIN "people" "parents_people"/, @r.to_sql
213
+ assert_match /INNER JOIN "people" "parents_people_2"/, @r.to_sql
214
+ end
215
+
216
+ should "place the condition on the correct join" do
217
+ assert_match /"parents_people_2"."name" = 'Abraham'/, @r.to_sql
218
+ end
219
+
220
+ should "return the expected result" do
221
+ assert_equal Person.where(:name => 'Abraham'), @r.all
222
+ end
223
+ end
224
+ end
225
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meta_where
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 9
8
+ - 0
9
+ version: 0.9.0
10
+ platform: ruby
11
+ authors:
12
+ - Ernie Miller
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-24 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: activerecord
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 3
43
+ - 0
44
+ - 0
45
+ - rc2
46
+ version: 3.0.0.rc2
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: activesupport
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 3
59
+ - 0
60
+ - 0
61
+ - rc2
62
+ version: 3.0.0.rc2
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: arel
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ segments:
74
+ - 1
75
+ - 0
76
+ - 0
77
+ - rc1
78
+ version: 1.0.0.rc1
79
+ type: :runtime
80
+ version_requirements: *id004
81
+ description: "\n MetaWhere offers the ability to call any Arel predicate methods\n (with a few convenient aliases) on your Model's attributes instead\n of the ones normally offered by ActiveRecord's hash parameters. It also\n adds convenient syntax for order clauses, smarter mapping of nested hash\n conditions, and a debug_sql method to see the real SQL your code is\n generating without running it against the database. If you like the new\n AR 3.0 query interface, you'll love it with MetaWhere.\n "
82
+ email: ernie@metautonomo.us
83
+ executables: []
84
+
85
+ extensions: []
86
+
87
+ extra_rdoc_files:
88
+ - LICENSE
89
+ - README.rdoc
90
+ files:
91
+ - .document
92
+ - .gitignore
93
+ - .gitmodules
94
+ - CHANGELOG
95
+ - Gemfile
96
+ - LICENSE
97
+ - README.rdoc
98
+ - Rakefile
99
+ - VERSION
100
+ - lib/core_ext/hash.rb
101
+ - lib/core_ext/symbol.rb
102
+ - lib/core_ext/symbol_operators.rb
103
+ - lib/meta_where.rb
104
+ - lib/meta_where/builder.rb
105
+ - lib/meta_where/column.rb
106
+ - lib/meta_where/compound.rb
107
+ - lib/meta_where/condition.rb
108
+ - lib/meta_where/join_dependency.rb
109
+ - lib/meta_where/relation.rb
110
+ - lib/meta_where/utility.rb
111
+ - meta_where.gemspec
112
+ - test/fixtures/companies.yml
113
+ - test/fixtures/company.rb
114
+ - test/fixtures/data_type.rb
115
+ - test/fixtures/data_types.yml
116
+ - test/fixtures/developer.rb
117
+ - test/fixtures/developers.yml
118
+ - test/fixtures/developers_projects.yml
119
+ - test/fixtures/note.rb
120
+ - test/fixtures/notes.yml
121
+ - test/fixtures/people.yml
122
+ - test/fixtures/person.rb
123
+ - test/fixtures/project.rb
124
+ - test/fixtures/projects.yml
125
+ - test/fixtures/schema.rb
126
+ - test/helper.rb
127
+ - test/test_relations.rb
128
+ has_rdoc: true
129
+ homepage: http://metautonomo.us/projects/metawhere/
130
+ licenses: []
131
+
132
+ post_install_message:
133
+ rdoc_options:
134
+ - --charset=UTF-8
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ segments:
143
+ - 0
144
+ version: "0"
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ none: false
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ segments:
151
+ - 0
152
+ version: "0"
153
+ requirements: []
154
+
155
+ rubyforge_project:
156
+ rubygems_version: 1.3.7
157
+ signing_key:
158
+ specification_version: 3
159
+ summary: Add a dash of Arel awesomeness to your condition hashes.
160
+ test_files:
161
+ - test/fixtures/company.rb
162
+ - test/fixtures/data_type.rb
163
+ - test/fixtures/developer.rb
164
+ - test/fixtures/note.rb
165
+ - test/fixtures/person.rb
166
+ - test/fixtures/project.rb
167
+ - test/fixtures/schema.rb
168
+ - test/helper.rb
169
+ - test/test_relations.rb