plucky 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.travis.yml +9 -0
- data/Gemfile +17 -0
- data/README.md +101 -0
- data/Rakefile +21 -0
- data/examples/query.rb +80 -0
- data/lib/plucky.rb +2 -10
- data/lib/plucky/criteria_hash.rb +12 -3
- data/lib/plucky/query.rb +111 -101
- data/lib/plucky/version.rb +2 -2
- data/plucky.gemspec +21 -0
- data/specs.watchr +54 -0
- data/test/helper.rb +5 -10
- data/test/plucky/test_criteria_hash.rb +57 -2
- data/test/plucky/test_options_hash.rb +3 -3
- data/test/plucky/test_query.rb +24 -0
- data/test/test_plucky.rb +7 -5
- metadata +31 -16
- data/README.rdoc +0 -19
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source :rubygems
|
2
|
+
gemspec
|
3
|
+
|
4
|
+
gem 'bson_ext', '~> 1.5'
|
5
|
+
gem 'rake'
|
6
|
+
|
7
|
+
group(:debug) do
|
8
|
+
platforms(:mri_18) { gem 'ruby-debug' }
|
9
|
+
platforms(:mri_19) { gem 'ruby-debug19' }
|
10
|
+
end
|
11
|
+
|
12
|
+
group(:test) do
|
13
|
+
gem 'shoulda', '~> 2.11'
|
14
|
+
gem 'jnunemaker-matchy', '~> 0.4.0', :require => 'matchy'
|
15
|
+
gem 'mocha', '~> 0.9.8'
|
16
|
+
gem 'log_buddy'
|
17
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Plucky
|
2
|
+
|
3
|
+
Thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
```
|
8
|
+
gem install plucky
|
9
|
+
```
|
10
|
+
|
11
|
+
## Examples
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
connection = Mongo::Connection.new
|
15
|
+
db = connection.db('test')
|
16
|
+
collection = db['users']
|
17
|
+
collection.remove # clear out the collection
|
18
|
+
|
19
|
+
collection.insert({'_id' => 'chris', 'age' => 26, 'name' => 'Chris'})
|
20
|
+
collection.insert({'_id' => 'steve', 'age' => 29, 'name' => 'Steve'})
|
21
|
+
collection.insert({'_id' => 'john', 'age' => 28, 'name' => 'John'})
|
22
|
+
|
23
|
+
# initialize query with collection
|
24
|
+
query = Plucky::Query.new(collection)
|
25
|
+
|
26
|
+
puts 'Querying'
|
27
|
+
pp query.where(:name => 'John').first
|
28
|
+
pp query.first(:name => 'John')
|
29
|
+
pp query.where(:name => 'John').all
|
30
|
+
pp query.all(:name => 'John')
|
31
|
+
|
32
|
+
puts 'Find by _id'
|
33
|
+
pp query.find('chris')
|
34
|
+
pp query.find('chris', 'steve')
|
35
|
+
pp query.find(['chris', 'steve'])
|
36
|
+
|
37
|
+
puts 'Sort'
|
38
|
+
pp query.sort(:age).all
|
39
|
+
pp query.sort(:age.asc).all # same as above
|
40
|
+
pp query.sort(:age.desc).all
|
41
|
+
pp query.sort(:age).last # steve
|
42
|
+
|
43
|
+
puts 'Counting'
|
44
|
+
pp query.count # 3
|
45
|
+
pp query.size # 3
|
46
|
+
pp query.count(:name => 'John') # 1
|
47
|
+
pp query.where(:name => 'John').count # 1
|
48
|
+
pp query.where(:name => 'John').size # 1
|
49
|
+
|
50
|
+
puts 'Distinct'
|
51
|
+
pp query.distinct(:age) # [26, 29, 28]
|
52
|
+
|
53
|
+
puts 'Select only certain fields'
|
54
|
+
pp query.fields(:age).find('chris') # {"_id"=>"chris", "age"=>26}
|
55
|
+
pp query.only(:age).find('chris') # {"_id"=>"chris", "age"=>26}
|
56
|
+
pp query.ignore(:name).find('chris') # {"_id"=>"chris", "age"=>26}
|
57
|
+
|
58
|
+
puts 'Pagination, yeah we got that'
|
59
|
+
pp query.sort(:age).paginate(:per_page => 1, :page => 2)
|
60
|
+
pp query.sort(:age).per_page(1).paginate(:page => 2)
|
61
|
+
|
62
|
+
pp query.sort(:age).limit(2).to_a # [chris, john]
|
63
|
+
pp query.sort(:age).skip(1).limit(2).to_a # [john, steve]
|
64
|
+
pp query.sort(:age).offset(1).limit(2).to_a # [john, steve]
|
65
|
+
|
66
|
+
puts 'Using a cursor'
|
67
|
+
cursor = query.find_each(:sort => :age) do |doc|
|
68
|
+
pp doc
|
69
|
+
end
|
70
|
+
pp cursor
|
71
|
+
|
72
|
+
puts 'Symbol Operators'
|
73
|
+
pp query.where(:age.gt => 28).count # 1 (steve)
|
74
|
+
pp query.where(:age.lt => 28).count # 1 (chris)
|
75
|
+
pp query.where(:age.in => [26, 28]).to_a # [chris, john]
|
76
|
+
pp query.where(:age.nin => [26, 28]).to_a # [steve]
|
77
|
+
|
78
|
+
puts 'Removing'
|
79
|
+
query.remove(:name => 'John')
|
80
|
+
pp query.count # 2
|
81
|
+
query.where(:name => 'Chris').remove
|
82
|
+
pp query.count # 1
|
83
|
+
query.remove
|
84
|
+
pp query.count # 0
|
85
|
+
```
|
86
|
+
|
87
|
+
## Help
|
88
|
+
|
89
|
+
https://groups.google.com/forum/#!forum/mongomapper
|
90
|
+
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
* Fork the project.
|
94
|
+
* Make your feature addition or bug fix.
|
95
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
96
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
97
|
+
* Send me a pull request. Bonus points for topic branches.
|
98
|
+
|
99
|
+
## Copyright
|
100
|
+
|
101
|
+
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require File.expand_path('../lib/plucky/version', __FILE__)
|
5
|
+
|
6
|
+
require 'bundler'
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
namespace :test do
|
10
|
+
Rake::TestTask.new(:all) do |test|
|
11
|
+
test.libs << 'lib' << 'test'
|
12
|
+
test.pattern = 'test/**/test_*.rb'
|
13
|
+
test.verbose = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
task :test do
|
18
|
+
Rake::Task['test:all'].invoke
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :test
|
data/examples/query.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'pathname'
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
6
|
+
lib_path = root_path.join('lib')
|
7
|
+
$:.unshift(lib_path)
|
8
|
+
require 'plucky'
|
9
|
+
|
10
|
+
connection = Mongo::Connection.new
|
11
|
+
db = connection.db('test')
|
12
|
+
collection = db['users']
|
13
|
+
collection.remove # clear out the collection
|
14
|
+
|
15
|
+
collection.insert({'_id' => 'chris', 'age' => 26, 'name' => 'Chris'})
|
16
|
+
collection.insert({'_id' => 'steve', 'age' => 29, 'name' => 'Steve'})
|
17
|
+
collection.insert({'_id' => 'john', 'age' => 28, 'name' => 'John'})
|
18
|
+
|
19
|
+
# initialize query with collection
|
20
|
+
query = Plucky::Query.new(collection)
|
21
|
+
|
22
|
+
puts 'Querying'
|
23
|
+
pp query.where(:name => 'John').first
|
24
|
+
pp query.first(:name => 'John')
|
25
|
+
pp query.where(:name => 'John').all
|
26
|
+
pp query.all(:name => 'John')
|
27
|
+
|
28
|
+
puts 'Find by _id'
|
29
|
+
pp query.find('chris')
|
30
|
+
pp query.find('chris', 'steve')
|
31
|
+
pp query.find(['chris', 'steve'])
|
32
|
+
|
33
|
+
puts 'Sort'
|
34
|
+
pp query.sort(:age).all
|
35
|
+
pp query.sort(:age.asc).all # same as above
|
36
|
+
pp query.sort(:age.desc).all
|
37
|
+
pp query.sort(:age).last # steve
|
38
|
+
|
39
|
+
puts 'Counting'
|
40
|
+
pp query.count # 3
|
41
|
+
pp query.size # 3
|
42
|
+
pp query.count(:name => 'John') # 1
|
43
|
+
pp query.where(:name => 'John').count # 1
|
44
|
+
pp query.where(:name => 'John').size # 1
|
45
|
+
|
46
|
+
puts 'Distinct'
|
47
|
+
pp query.distinct(:age) # [26, 29, 28]
|
48
|
+
|
49
|
+
puts 'Select only certain fields'
|
50
|
+
pp query.fields(:age).find('chris') # {"_id"=>"chris", "age"=>26}
|
51
|
+
pp query.only(:age).find('chris') # {"_id"=>"chris", "age"=>26}
|
52
|
+
pp query.ignore(:name).find('chris') # {"_id"=>"chris", "age"=>26}
|
53
|
+
|
54
|
+
puts 'Pagination, yeah we got that'
|
55
|
+
pp query.sort(:age).paginate(:per_page => 1, :page => 2)
|
56
|
+
pp query.sort(:age).per_page(1).paginate(:page => 2)
|
57
|
+
|
58
|
+
pp query.sort(:age).limit(2).to_a # [chris, john]
|
59
|
+
pp query.sort(:age).skip(1).limit(2).to_a # [john, steve]
|
60
|
+
pp query.sort(:age).offset(1).limit(2).to_a # [john, steve]
|
61
|
+
|
62
|
+
puts 'Using a cursor'
|
63
|
+
cursor = query.find_each(:sort => :age) do |doc|
|
64
|
+
pp doc
|
65
|
+
end
|
66
|
+
pp cursor
|
67
|
+
|
68
|
+
puts 'Symbol Operators'
|
69
|
+
pp query.where(:age.gt => 28).count # 1 (steve)
|
70
|
+
pp query.where(:age.lt => 28).count # 1 (chris)
|
71
|
+
pp query.where(:age.in => [26, 28]).to_a # [chris, john]
|
72
|
+
pp query.where(:age.nin => [26, 28]).to_a # [steve]
|
73
|
+
|
74
|
+
puts 'Removing'
|
75
|
+
query.remove(:name => 'John')
|
76
|
+
pp query.count # 2
|
77
|
+
query.where(:name => 'Chris').remove
|
78
|
+
pp query.count # 1
|
79
|
+
query.remove
|
80
|
+
pp query.count # 0
|
data/lib/plucky.rb
CHANGED
@@ -10,16 +10,8 @@ require 'plucky/pagination'
|
|
10
10
|
module Plucky
|
11
11
|
autoload :Version, 'plucky/version'
|
12
12
|
|
13
|
-
# Array of
|
14
|
-
Methods =
|
15
|
-
:where, :filter, :limit, :skip, :offset, :sort, :order,
|
16
|
-
:fields, :ignore, :only,
|
17
|
-
:each, :find_each,
|
18
|
-
:count, :size, :distinct,
|
19
|
-
:last, :first, :all, :paginate,
|
20
|
-
:exists?, :exist?, :empty?,
|
21
|
-
:to_a, :remove,
|
22
|
-
]
|
13
|
+
# Array of finder DSL methods to delegate
|
14
|
+
Methods = Plucky::Query::DSL.instance_methods.sort.map(&:to_sym)
|
23
15
|
|
24
16
|
def self.to_object_id(value)
|
25
17
|
return value if value.is_a?(BSON::ObjectId)
|
data/lib/plucky/criteria_hash.rb
CHANGED
@@ -3,6 +3,8 @@ module Plucky
|
|
3
3
|
class CriteriaHash
|
4
4
|
attr_reader :source, :options
|
5
5
|
|
6
|
+
NestingOperators = [:$or, :$and, :$nor]
|
7
|
+
|
6
8
|
def initialize(hash={}, options={})
|
7
9
|
@source, @options = {}, options
|
8
10
|
hash.each { |key, value| self[key] = value }
|
@@ -119,8 +121,15 @@ module Plucky
|
|
119
121
|
def normalized_value(parent_key, key, value)
|
120
122
|
case value
|
121
123
|
when Array, Set
|
122
|
-
|
123
|
-
|
124
|
+
if object_id?(parent_key)
|
125
|
+
value.map { |v| Plucky.to_object_id(v) }
|
126
|
+
elsif NestingOperators.include?(key)
|
127
|
+
value.map { |v| CriteriaHash.new(v, options).to_hash }
|
128
|
+
elsif parent_key == key
|
129
|
+
{'$in' => value.to_a}
|
130
|
+
else
|
131
|
+
value.to_a
|
132
|
+
end
|
124
133
|
when Time
|
125
134
|
value.utc
|
126
135
|
when String
|
@@ -136,4 +145,4 @@ module Plucky
|
|
136
145
|
end
|
137
146
|
end
|
138
147
|
end
|
139
|
-
end
|
148
|
+
end
|
data/lib/plucky/query.rb
CHANGED
@@ -34,138 +34,148 @@ module Plucky
|
|
34
34
|
self
|
35
35
|
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
# finder DSL methods to delegate to your model if you're building an ODM
|
38
|
+
# e.g. MyModel.last needs to be equivalent to MyModel.query.last
|
39
|
+
module DSL
|
40
|
+
def per_page(limit=nil)
|
41
|
+
return @per_page || 25 if limit.nil?
|
42
|
+
@per_page = limit
|
43
|
+
self
|
44
|
+
end
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
def paginate(opts={})
|
47
|
+
page = opts.delete(:page)
|
48
|
+
limit = opts.delete(:per_page) || per_page
|
49
|
+
query = clone.amend(opts)
|
50
|
+
paginator = Pagination::Paginator.new(query.count, page, limit)
|
51
|
+
query.amend(:limit => paginator.limit, :skip => paginator.skip).all.tap do |docs|
|
52
|
+
docs.extend(Pagination::Decorator)
|
53
|
+
docs.paginator(paginator)
|
54
|
+
end
|
51
55
|
end
|
52
|
-
end
|
53
56
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
57
|
+
def find_each(opts={}, &block)
|
58
|
+
query = clone.amend(opts)
|
59
|
+
cursor = query.collection.find(query.criteria.to_hash, query.options.to_hash)
|
60
|
+
if block_given?
|
61
|
+
cursor.each { |doc| yield doc }
|
62
|
+
cursor.rewind!
|
63
|
+
end
|
64
|
+
cursor
|
65
|
+
end
|
58
66
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
def find_one(opts={})
|
68
|
+
query = clone.amend(opts)
|
69
|
+
query.collection.find_one(query.criteria.to_hash, query.options.to_hash)
|
70
|
+
end
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
72
|
+
def find(*ids)
|
73
|
+
return nil if ids.empty?
|
74
|
+
if ids.size == 1 && !ids[0].is_a?(Array)
|
75
|
+
first(:_id => ids[0])
|
76
|
+
else
|
77
|
+
all(:_id => ids.flatten)
|
78
|
+
end
|
70
79
|
end
|
71
|
-
end
|
72
80
|
|
73
|
-
|
74
|
-
|
75
|
-
|
81
|
+
def all(opts={})
|
82
|
+
find_each(opts).to_a
|
83
|
+
end
|
76
84
|
|
77
|
-
|
78
|
-
|
79
|
-
|
85
|
+
def first(opts={})
|
86
|
+
find_one(opts)
|
87
|
+
end
|
80
88
|
|
81
|
-
|
82
|
-
|
83
|
-
|
89
|
+
def last(opts={})
|
90
|
+
clone.amend(opts).reverse.find_one
|
91
|
+
end
|
84
92
|
|
85
|
-
|
86
|
-
|
87
|
-
|
93
|
+
def each(&block)
|
94
|
+
find_each(&block)
|
95
|
+
end
|
88
96
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
97
|
+
def remove(opts={}, driver_opts={})
|
98
|
+
query = clone.amend(opts)
|
99
|
+
query.collection.remove(query.criteria.to_hash, driver_opts)
|
100
|
+
end
|
93
101
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
102
|
+
def count(opts={})
|
103
|
+
find_each(opts).count
|
104
|
+
end
|
98
105
|
|
99
|
-
|
100
|
-
|
101
|
-
|
106
|
+
def size
|
107
|
+
count
|
108
|
+
end
|
102
109
|
|
103
|
-
|
104
|
-
|
105
|
-
|
110
|
+
def distinct(key, opts = {})
|
111
|
+
query = clone.amend(opts)
|
112
|
+
query.collection.distinct(key, query.criteria.to_hash)
|
113
|
+
end
|
106
114
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
115
|
+
def fields(*args)
|
116
|
+
clone.tap { |query| query.options[:fields] = *args }
|
117
|
+
end
|
111
118
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
119
|
+
def ignore(*args)
|
120
|
+
set_fields(args, 0)
|
121
|
+
end
|
116
122
|
|
117
|
-
|
118
|
-
|
119
|
-
|
123
|
+
def only(*args)
|
124
|
+
set_fields(args, 1)
|
125
|
+
end
|
120
126
|
|
121
|
-
|
122
|
-
|
123
|
-
|
127
|
+
def limit(count=nil)
|
128
|
+
clone.tap { |query| query.options[:limit] = count }
|
129
|
+
end
|
124
130
|
|
125
|
-
|
126
|
-
|
127
|
-
|
131
|
+
def reverse
|
132
|
+
clone.tap do |query|
|
133
|
+
query[:sort] = query[:sort].map do |s|
|
134
|
+
[s[0], -s[1]]
|
135
|
+
end unless query.options[:sort].nil?
|
136
|
+
end
|
137
|
+
end
|
128
138
|
|
129
|
-
|
130
|
-
|
131
|
-
|
139
|
+
def skip(count=nil)
|
140
|
+
clone.tap { |query| query.options[:skip] = count }
|
141
|
+
end
|
142
|
+
alias offset skip
|
132
143
|
|
133
|
-
|
134
|
-
|
135
|
-
query[:sort] = query[:sort].map do |s|
|
136
|
-
[s[0], -s[1]]
|
137
|
-
end unless query.options[:sort].nil?
|
144
|
+
def sort(*args)
|
145
|
+
clone.tap { |query| query.options[:sort] = *args }
|
138
146
|
end
|
139
|
-
|
147
|
+
alias order sort
|
140
148
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
149
|
+
def where(hash={})
|
150
|
+
clone.tap do |query|
|
151
|
+
query.criteria.merge!(CriteriaHash.new(hash))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
alias filter where
|
145
155
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
alias order sort
|
156
|
+
def empty?
|
157
|
+
count.zero?
|
158
|
+
end
|
150
159
|
|
151
|
-
|
152
|
-
|
153
|
-
query.criteria.merge!(CriteriaHash.new(hash))
|
160
|
+
def exists?(options={})
|
161
|
+
!count(options).zero?
|
154
162
|
end
|
155
|
-
|
156
|
-
alias filter where
|
163
|
+
alias :exist? :exists?
|
157
164
|
|
158
|
-
|
159
|
-
|
165
|
+
def to_a
|
166
|
+
find_each.to_a
|
167
|
+
end
|
160
168
|
end
|
169
|
+
include DSL
|
161
170
|
|
162
|
-
def
|
163
|
-
|
171
|
+
def update(document, driver_opts={})
|
172
|
+
query = clone
|
173
|
+
query.collection.update(query.criteria.to_hash, document, driver_opts)
|
164
174
|
end
|
165
|
-
alias :exist? :exists?
|
166
175
|
|
167
|
-
def
|
168
|
-
|
176
|
+
def amend(opts={})
|
177
|
+
opts.each { |key, value| self[key] = value }
|
178
|
+
self
|
169
179
|
end
|
170
180
|
|
171
181
|
def [](key)
|
data/lib/plucky/version.rb
CHANGED
data/plucky.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require File.expand_path('../lib/plucky/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'plucky'
|
6
|
+
s.homepage = 'http://github.com/jnunemaker/plucky'
|
7
|
+
s.summary = 'Thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).'
|
8
|
+
s.require_path = 'lib'
|
9
|
+
s.homepage = 'http://jnunemaker.github.com/plucky/'
|
10
|
+
s.authors = ['John Nunemaker']
|
11
|
+
s.email = ['nunemaker@gmail.com']
|
12
|
+
s.version = Plucky::Version
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency 'mongo', '~> 1.5'
|
21
|
+
end
|
data/specs.watchr
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
def growl(title, msg, img)
|
2
|
+
%x{growlnotify -m #{ msg.inspect} -t #{title.inspect} --image ~/.watchr/#{img}.png}
|
3
|
+
end
|
4
|
+
|
5
|
+
def form_growl_message(str)
|
6
|
+
results = str.split("\n").last
|
7
|
+
if results =~ /[1-9]\s(failure|error)s?/
|
8
|
+
growl "Test Results", "#{results}", "fail"
|
9
|
+
elsif results != ""
|
10
|
+
growl "Test Results", "#{results}", "pass"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(cmd)
|
15
|
+
puts(cmd)
|
16
|
+
output = ""
|
17
|
+
IO.popen(cmd) do |com|
|
18
|
+
com.each_char do |c|
|
19
|
+
print c
|
20
|
+
output << c
|
21
|
+
$stdout.flush
|
22
|
+
end
|
23
|
+
end
|
24
|
+
form_growl_message output
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_test_file(file)
|
28
|
+
run %Q(ruby -I"lib:test" -rubygems #{file})
|
29
|
+
end
|
30
|
+
|
31
|
+
def run_all_tests
|
32
|
+
run "rake test"
|
33
|
+
end
|
34
|
+
|
35
|
+
def related_test_files(path)
|
36
|
+
Dir['test/**/*.rb'].select { |file| file =~ /test_#{File.basename(path)}/ }
|
37
|
+
end
|
38
|
+
|
39
|
+
# watch('.*\.rb') { system('clear'); run_all_tests }
|
40
|
+
watch('test/helper\.rb') { system('clear'); run_all_tests }
|
41
|
+
watch('test/.*test_.*\.rb') { |m| system('clear'); run_test_file(m[0]) }
|
42
|
+
watch('lib/.*') { |m| related_test_files(m[0]).each { |file| run_test_file(file) } }
|
43
|
+
|
44
|
+
watch('examples/.*\.rb') { |m| system('clear'); run "bundle exec ruby #{m[0]}" }
|
45
|
+
|
46
|
+
# Ctrl-\
|
47
|
+
Signal.trap('QUIT') do
|
48
|
+
puts " --- Running all tests ---\n\n"
|
49
|
+
run_all_tests
|
50
|
+
end
|
51
|
+
|
52
|
+
# Ctrl-C
|
53
|
+
Signal.trap('INT') { abort("\n") }
|
54
|
+
|
data/test/helper.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
gem 'mocha', '~> 0.9.8'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
Bundler.require(:default, :test)
|
6
5
|
|
7
6
|
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
8
7
|
require 'plucky'
|
@@ -10,11 +9,7 @@ require 'plucky'
|
|
10
9
|
require 'fileutils'
|
11
10
|
require 'logger'
|
12
11
|
require 'pp'
|
13
|
-
|
14
|
-
require 'log_buddy'
|
15
|
-
require 'shoulda'
|
16
|
-
require 'matchy'
|
17
|
-
require 'mocha'
|
12
|
+
require 'set'
|
18
13
|
|
19
14
|
log_dir = File.expand_path('../../log', __FILE__)
|
20
15
|
FileUtils.mkdir_p(log_dir)
|
@@ -42,4 +37,4 @@ end
|
|
42
37
|
|
43
38
|
operators = %w{gt lt gte lte ne in nin mod all size exists}
|
44
39
|
operators.delete('size') if RUBY_VERSION >= '1.9.1'
|
45
|
-
SymbolOperators = operators
|
40
|
+
SymbolOperators = operators
|
@@ -24,6 +24,52 @@ class CriteriaHashTest < Test::Unit::TestCase
|
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
27
|
+
context "nested clauses" do
|
28
|
+
context "::NestingOperators" do
|
29
|
+
should "return array of operators that take nested queries" do
|
30
|
+
CriteriaHash::NestingOperators.should == [:$or, :$and, :$nor]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
CriteriaHash::NestingOperators.each do |operator|
|
35
|
+
context "#{operator}" do
|
36
|
+
should "work with symbol operators" do
|
37
|
+
nested1 = {:age.gt => 12, :age.lt => 20}
|
38
|
+
translated1 = {:age => {'$gt' => 12, '$lt' => 20 }}
|
39
|
+
nested2 = {:type.nin => ['friend', 'enemy']}
|
40
|
+
translated2 = {:type => {'$nin' => ['friend', 'enemy']}}
|
41
|
+
|
42
|
+
given = {operator.to_s => [nested1, nested2]}
|
43
|
+
|
44
|
+
CriteriaHash.new(given)[operator].should == [translated1, translated2]
|
45
|
+
end
|
46
|
+
|
47
|
+
should "honor criteria hash options" do
|
48
|
+
nested = {:post_id => '4f5ead6378fca23a13000001'}
|
49
|
+
translated = {:post_id => BSON::ObjectId.from_string('4f5ead6378fca23a13000001')}
|
50
|
+
given = {operator.to_s => [nested]}
|
51
|
+
|
52
|
+
CriteriaHash.new(given, :object_ids => [:post_id])[operator].should == [translated]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "doubly nested" do
|
58
|
+
should "work with symbol operators" do
|
59
|
+
nested1 = {:age.gt => 12, :age.lt => 20}
|
60
|
+
translated1 = {:age => {'$gt' => 12, '$lt' => 20}}
|
61
|
+
nested2 = {:type.nin => ['friend', 'enemy']}
|
62
|
+
translated2 = {:type => {'$nin' => ['friend', 'enemy']}}
|
63
|
+
nested3 = {'$and' => [nested2]}
|
64
|
+
translated3 = {:$and => [translated2]}
|
65
|
+
|
66
|
+
given = {'$or' => [nested1, nested3]}
|
67
|
+
|
68
|
+
CriteriaHash.new(given)[:$or].should == [translated1, translated3]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
27
73
|
context "#initialize_copy" do
|
28
74
|
setup do
|
29
75
|
@original = CriteriaHash.new({
|
@@ -158,6 +204,10 @@ class CriteriaHashTest < Test::Unit::TestCase
|
|
158
204
|
should "not turn value to $in with $and key" do
|
159
205
|
CriteriaHash.new(:$and => [{:numbers => 1}, {:numbers => 2}] )[:$and].should == [{:numbers=>1}, {:numbers=>2}]
|
160
206
|
end
|
207
|
+
|
208
|
+
should "not turn value to $in with $nor key" do
|
209
|
+
CriteriaHash.new(:$nor => [{:numbers => 1}, {:numbers => 2}] )[:$nor].should == [{:numbers=>1}, {:numbers=>2}]
|
210
|
+
end
|
161
211
|
end
|
162
212
|
|
163
213
|
context "with set value" do
|
@@ -210,12 +260,17 @@ class CriteriaHashTest < Test::Unit::TestCase
|
|
210
260
|
setup do
|
211
261
|
@id1 = BSON::ObjectId.new
|
212
262
|
@id2 = BSON::ObjectId.new
|
213
|
-
@
|
263
|
+
@ids = [@id1.to_s, @id2.to_s]
|
264
|
+
@criteria = CriteriaHash.new({:_id => {'$in' => @ids}}, :object_ids => [:_id])
|
214
265
|
end
|
215
266
|
|
216
267
|
should "convert strings to object ids" do
|
217
268
|
@criteria[:_id].should == {'$in' => [@id1, @id2]}
|
218
269
|
end
|
270
|
+
|
271
|
+
should "not modify original array of string ids" do
|
272
|
+
@ids.should == [@id1.to_s, @id2.to_s]
|
273
|
+
end
|
219
274
|
end
|
220
275
|
|
221
276
|
context "#merge" do
|
@@ -297,4 +352,4 @@ class CriteriaHashTest < Test::Unit::TestCase
|
|
297
352
|
end
|
298
353
|
end
|
299
354
|
end
|
300
|
-
end
|
355
|
+
end
|
@@ -7,9 +7,9 @@ class OptionsHashTest < Test::Unit::TestCase
|
|
7
7
|
should "delegate missing methods to the source hash" do
|
8
8
|
hash = {:limit => 1, :skip => 1}
|
9
9
|
options = OptionsHash.new(hash)
|
10
|
-
options[:skip].should
|
11
|
-
options[:limit].should
|
12
|
-
options.keys.should
|
10
|
+
options[:skip].should == 1
|
11
|
+
options[:limit].should == 1
|
12
|
+
options.keys.to_set.should == [:limit, :skip].to_set
|
13
13
|
end
|
14
14
|
|
15
15
|
context "#initialize_copy" do
|
data/test/plucky/test_query.rb
CHANGED
@@ -72,6 +72,18 @@ class QueryTest < Test::Unit::TestCase
|
|
72
72
|
cursor = Query.new(@collection).find_each(:order => :name.asc)
|
73
73
|
cursor.to_a.should == [@chris, @john, @steve]
|
74
74
|
end
|
75
|
+
|
76
|
+
should "yield elements to a block if given" do
|
77
|
+
yielded_elements = Set.new
|
78
|
+
Query.new(@collection).find_each { |doc| yielded_elements << doc }
|
79
|
+
yielded_elements.should == [@chris, @john, @steve].to_set
|
80
|
+
end
|
81
|
+
|
82
|
+
should "be Ruby-like and return a reset cursor if a block is given" do
|
83
|
+
cursor = Query.new(@collection).find_each {}
|
84
|
+
cursor.should be_instance_of(Mongo::Cursor)
|
85
|
+
cursor.next.should be_instance_of(oh.class)
|
86
|
+
end
|
75
87
|
end
|
76
88
|
|
77
89
|
context "#find_one" do
|
@@ -650,6 +662,18 @@ class QueryTest < Test::Unit::TestCase
|
|
650
662
|
end
|
651
663
|
docs.should == [@chris, @john, @steve]
|
652
664
|
end
|
665
|
+
|
666
|
+
should "return a working enumerator" do
|
667
|
+
query = Query.new(@collection)
|
668
|
+
query.each.methods.map(&:to_sym).include?(:group_by).should be(true)
|
669
|
+
query.each.next.class.should == oh.class
|
670
|
+
end
|
671
|
+
|
672
|
+
should "be fulfilled by #find_each" do
|
673
|
+
query = Query.new(@collection)
|
674
|
+
query.expects(:find_each)
|
675
|
+
query.each
|
676
|
+
end
|
653
677
|
end
|
654
678
|
|
655
679
|
context "enumerables" do
|
data/test/test_plucky.rb
CHANGED
@@ -32,14 +32,16 @@ class PluckyTest < Test::Unit::TestCase
|
|
32
32
|
context "::Methods" do
|
33
33
|
should "return array of methods" do
|
34
34
|
Plucky::Methods.should == [
|
35
|
-
:where, :filter,
|
35
|
+
:where, :filter,
|
36
|
+
:sort, :order, :reverse,
|
37
|
+
:paginate, :per_page, :limit, :skip, :offset,
|
36
38
|
:fields, :ignore, :only,
|
37
|
-
:each, :find_each,
|
39
|
+
:each, :find_each, :find_one, :find,
|
38
40
|
:count, :size, :distinct,
|
39
|
-
:last, :first, :all, :
|
41
|
+
:last, :first, :all, :to_a,
|
40
42
|
:exists?, :exist?, :empty?,
|
41
|
-
:
|
42
|
-
]
|
43
|
+
:remove,
|
44
|
+
].sort_by(&:to_s)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plucky
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- John Nunemaker
|
@@ -15,10 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-04-20 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
21
|
+
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -30,8 +30,8 @@ dependencies:
|
|
30
30
|
- 1
|
31
31
|
- 5
|
32
32
|
version: "1.5"
|
33
|
-
type: :runtime
|
34
33
|
version_requirements: *id001
|
34
|
+
name: mongo
|
35
35
|
description:
|
36
36
|
email:
|
37
37
|
- nunemaker@gmail.com
|
@@ -42,18 +42,28 @@ extensions: []
|
|
42
42
|
extra_rdoc_files: []
|
43
43
|
|
44
44
|
files:
|
45
|
+
- .gitignore
|
46
|
+
- .travis.yml
|
47
|
+
- Gemfile
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- UPGRADES
|
52
|
+
- examples/query.rb
|
53
|
+
- lib/plucky.rb
|
45
54
|
- lib/plucky/criteria_hash.rb
|
55
|
+
- lib/plucky/extensions.rb
|
46
56
|
- lib/plucky/extensions/duplicable.rb
|
47
57
|
- lib/plucky/extensions/symbol.rb
|
48
|
-
- lib/plucky/extensions.rb
|
49
58
|
- lib/plucky/new_relic.rb
|
50
59
|
- lib/plucky/options_hash.rb
|
60
|
+
- lib/plucky/pagination.rb
|
51
61
|
- lib/plucky/pagination/decorator.rb
|
52
62
|
- lib/plucky/pagination/paginator.rb
|
53
|
-
- lib/plucky/pagination.rb
|
54
63
|
- lib/plucky/query.rb
|
55
64
|
- lib/plucky/version.rb
|
56
|
-
-
|
65
|
+
- plucky.gemspec
|
66
|
+
- specs.watchr
|
57
67
|
- test/helper.rb
|
58
68
|
- test/plucky/pagination/test_decorator.rb
|
59
69
|
- test/plucky/pagination/test_paginator.rb
|
@@ -63,10 +73,7 @@ files:
|
|
63
73
|
- test/test_plucky.rb
|
64
74
|
- test/test_symbol.rb
|
65
75
|
- test/test_symbol_operator.rb
|
66
|
-
|
67
|
-
- README.rdoc
|
68
|
-
- UPGRADES
|
69
|
-
homepage: http://github.com/jnunemaker/plucky
|
76
|
+
homepage: http://jnunemaker.github.com/plucky/
|
70
77
|
licenses: []
|
71
78
|
|
72
79
|
post_install_message:
|
@@ -99,5 +106,13 @@ rubygems_version: 1.8.10
|
|
99
106
|
signing_key:
|
100
107
|
specification_version: 3
|
101
108
|
summary: Thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).
|
102
|
-
test_files:
|
103
|
-
|
109
|
+
test_files:
|
110
|
+
- test/helper.rb
|
111
|
+
- test/plucky/pagination/test_decorator.rb
|
112
|
+
- test/plucky/pagination/test_paginator.rb
|
113
|
+
- test/plucky/test_criteria_hash.rb
|
114
|
+
- test/plucky/test_options_hash.rb
|
115
|
+
- test/plucky/test_query.rb
|
116
|
+
- test/test_plucky.rb
|
117
|
+
- test/test_symbol.rb
|
118
|
+
- test/test_symbol_operator.rb
|
data/README.rdoc
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
= Plucky
|
2
|
-
|
3
|
-
Thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).
|
4
|
-
|
5
|
-
== Install
|
6
|
-
|
7
|
-
$ gem install plucky
|
8
|
-
|
9
|
-
== Note on Patches/Pull Requests
|
10
|
-
|
11
|
-
* Fork the project.
|
12
|
-
* Make your feature addition or bug fix.
|
13
|
-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
14
|
-
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
15
|
-
* Send me a pull request. Bonus points for topic branches.
|
16
|
-
|
17
|
-
== Copyright
|
18
|
-
|
19
|
-
Copyright (c) 2010 John Nunemaker. See LICENSE for details.
|