plucky 0.4.4 → 0.5.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.
- 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.
|