plucky 0.6.3 → 0.8.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.
- checksums.yaml +7 -0
- data/.gitignore +1 -3
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -4
- data/Gemfile +6 -5
- data/Gemfile.lock +84 -0
- data/LICENSE +1 -1
- data/README.md +17 -75
- data/Rakefile +0 -3
- data/examples/query.rb +8 -7
- data/lib/plucky.rb +1 -0
- data/lib/plucky/criteria_hash.rb +78 -62
- data/lib/plucky/extensions/symbol.rb +8 -0
- data/lib/plucky/normalizers/criteria_hash_value.rb +3 -1
- data/lib/plucky/normalizers/fields_value.rb +3 -3
- data/lib/plucky/normalizers/hash_key.rb +19 -0
- data/lib/plucky/normalizers/options_hash_value.rb +5 -7
- data/lib/plucky/normalizers/sort_value.rb +8 -6
- data/lib/plucky/options_hash.rb +9 -3
- data/lib/plucky/pagination.rb +1 -1
- data/lib/plucky/pagination/{decorator.rb → collection.rb} +10 -1
- data/lib/plucky/query.rb +56 -21
- data/lib/plucky/transformer.rb +14 -0
- data/lib/plucky/version.rb +1 -1
- data/plucky.gemspec +4 -5
- data/script/bootstrap +21 -0
- data/script/release +42 -0
- data/script/test +20 -0
- data/spec/functional/options_hash_spec.rb +41 -0
- data/spec/helper.rb +12 -4
- data/spec/plucky/criteria_hash_spec.rb +68 -4
- data/spec/plucky/normalizers/criteria_hash_value_spec.rb +1 -1
- data/spec/plucky/normalizers/fields_value_spec.rb +5 -5
- data/spec/plucky/normalizers/hash_key_spec.rb +15 -0
- data/spec/plucky/normalizers/options_hash_value_spec.rb +2 -2
- data/spec/plucky/normalizers/sort_value_spec.rb +24 -20
- data/spec/plucky/options_hash_spec.rb +2 -2
- data/spec/plucky/pagination/{decorator_spec.rb → collection_spec.rb} +8 -5
- data/spec/plucky/query_spec.rb +92 -35
- data/spec/plucky_spec.rb +5 -5
- data/spec/symbol_operator_spec.rb +18 -1
- metadata +37 -36
- data/lib/plucky/normalizers/options_hash_key.rb +0 -23
- data/script/criteria_hash.rb +0 -21
- data/spec/plucky/normalizers/options_hash_key_spec.rb +0 -23
@@ -73,6 +73,14 @@ class SymbolOperator
|
|
73
73
|
field.to_s <=> other.field.to_s
|
74
74
|
end
|
75
75
|
end
|
76
|
+
|
77
|
+
def hash
|
78
|
+
field.hash + operator.hash
|
79
|
+
end
|
80
|
+
|
81
|
+
def eql?(other)
|
82
|
+
self == other
|
83
|
+
end
|
76
84
|
|
77
85
|
def ==(other)
|
78
86
|
other.class == self.class && field == other.field && operator == other.operator
|
@@ -1,10 +1,12 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Plucky
|
2
4
|
module Normalizers
|
3
5
|
class CriteriaHashValue
|
4
6
|
|
5
7
|
# Internal: Used by normalized_value to determine if we need to run the
|
6
8
|
# value through another criteria hash to normalize it.
|
7
|
-
NestingOperators = [:$or, :$and, :$nor]
|
9
|
+
NestingOperators = Set[:$or, :$and, :$nor]
|
8
10
|
|
9
11
|
def initialize(criteria_hash)
|
10
12
|
@criteria_hash = criteria_hash
|
@@ -11,12 +11,12 @@ module Plucky
|
|
11
11
|
if value.size == 1 && value.first.is_a?(Hash)
|
12
12
|
value.first
|
13
13
|
else
|
14
|
-
value.flatten
|
14
|
+
value.flatten.inject({}) {|acc, field| acc.merge(field => 1)}
|
15
15
|
end
|
16
16
|
when Symbol
|
17
|
-
|
17
|
+
{value => 1}
|
18
18
|
when String
|
19
|
-
value.split(',').
|
19
|
+
value.split(',').inject({}) { |acc, v| acc.merge(v.strip => 1) }
|
20
20
|
else
|
21
21
|
value
|
22
22
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Plucky
|
2
|
+
module Normalizers
|
3
|
+
class HashKey
|
4
|
+
|
5
|
+
def initialize(keys)
|
6
|
+
@keys = keys
|
7
|
+
end
|
8
|
+
|
9
|
+
# Public: Normalizes an options hash key
|
10
|
+
#
|
11
|
+
# key - The key to normalize
|
12
|
+
#
|
13
|
+
# Returns a Symbol.
|
14
|
+
def call(key)
|
15
|
+
@keys.fetch key.to_sym, key
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -5,9 +5,9 @@ require 'plucky/normalizers/sort_value'
|
|
5
5
|
module Plucky
|
6
6
|
module Normalizers
|
7
7
|
class OptionsHashValue
|
8
|
-
|
9
|
-
# Public: Initialize an OptionsHashValue.
|
10
|
-
#
|
8
|
+
|
9
|
+
# Public: Initialize an OptionsHashValue.
|
10
|
+
#
|
11
11
|
# args - The hash of arguments (default: {})
|
12
12
|
# :key_normalizer - The key normalizer to use, must respond to call
|
13
13
|
# :value_normalizers - Hash where key is name of options hash key
|
@@ -34,7 +34,7 @@ module Plucky
|
|
34
34
|
}
|
35
35
|
|
36
36
|
@value_normalizers = {
|
37
|
-
:
|
37
|
+
:projection => default_fields_value_normalizer,
|
38
38
|
:sort => default_sort_value_normalizer,
|
39
39
|
:limit => default_limit_value_normalizer,
|
40
40
|
:skip => default_skip_value_normalizer,
|
@@ -66,9 +66,7 @@ module Plucky
|
|
66
66
|
|
67
67
|
# Private
|
68
68
|
def default_sort_value_normalizer
|
69
|
-
Normalizers::SortValue.new({
|
70
|
-
:key_normalizer => @key_normalizer,
|
71
|
-
})
|
69
|
+
Normalizers::SortValue.new(:key_normalizer => Normalizers::HashKey.new({:id => :_id}))
|
72
70
|
end
|
73
71
|
|
74
72
|
# Private
|
@@ -21,7 +21,7 @@ module Plucky
|
|
21
21
|
if value.size == 1 && value[0].is_a?(String)
|
22
22
|
normalized_sort_piece(value[0])
|
23
23
|
else
|
24
|
-
value.compact.
|
24
|
+
value.compact.inject({}) { |acc, v| acc.merge(normalized_sort_piece(v)) }
|
25
25
|
end
|
26
26
|
else
|
27
27
|
normalized_sort_piece(value)
|
@@ -32,13 +32,15 @@ module Plucky
|
|
32
32
|
def normalized_sort_piece(value)
|
33
33
|
case value
|
34
34
|
when SymbolOperator
|
35
|
-
|
35
|
+
normalized_direction(value.field, value.operator)
|
36
36
|
when String
|
37
|
-
value.split(',').
|
38
|
-
normalized_direction(*piece.split(' '))
|
37
|
+
value.split(',').inject({}) do |acc, piece|
|
38
|
+
acc.merge(normalized_direction(*piece.split(' ')))
|
39
39
|
end
|
40
40
|
when Symbol
|
41
|
-
|
41
|
+
normalized_direction(value)
|
42
|
+
when Array
|
43
|
+
Hash[*value]
|
42
44
|
else
|
43
45
|
value
|
44
46
|
end
|
@@ -48,7 +50,7 @@ module Plucky
|
|
48
50
|
def normalized_direction(field, direction=nil)
|
49
51
|
direction ||= 'ASC'
|
50
52
|
direction = direction.upcase == 'ASC' ? 1 : -1
|
51
|
-
|
53
|
+
{@key_normalizer.call(field).to_s => direction}
|
52
54
|
end
|
53
55
|
end
|
54
56
|
end
|
data/lib/plucky/options_hash.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
require 'plucky/normalizers/
|
3
|
+
require 'plucky/normalizers/hash_key'
|
4
4
|
require 'plucky/normalizers/options_hash_value'
|
5
5
|
|
6
6
|
module Plucky
|
@@ -55,7 +55,7 @@ module Plucky
|
|
55
55
|
|
56
56
|
# Public
|
57
57
|
def fields?
|
58
|
-
!self[:
|
58
|
+
!self[:projection].nil?
|
59
59
|
end
|
60
60
|
|
61
61
|
# Public
|
@@ -82,7 +82,13 @@ module Plucky
|
|
82
82
|
# Private
|
83
83
|
def key_normalizer
|
84
84
|
@key_normalizer ||= @options.fetch(:key_normalizer) {
|
85
|
-
Normalizers::
|
85
|
+
Normalizers::HashKey.new({
|
86
|
+
:order => :sort,
|
87
|
+
:select => :projection,
|
88
|
+
:fields => :projection,
|
89
|
+
:offset => :skip,
|
90
|
+
:id => :_id,
|
91
|
+
})
|
86
92
|
}
|
87
93
|
end
|
88
94
|
|
data/lib/plucky/pagination.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
module Plucky
|
3
3
|
module Pagination
|
4
|
-
|
4
|
+
class Collection < Array
|
5
5
|
extend Forwardable
|
6
6
|
|
7
7
|
def_delegators :@paginator,
|
@@ -11,6 +11,15 @@ module Plucky
|
|
11
11
|
:skip, :limit,
|
12
12
|
:offset, :out_of_bounds?
|
13
13
|
|
14
|
+
def initialize(records, paginator)
|
15
|
+
replace records
|
16
|
+
@paginator = paginator
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method, *args)
|
20
|
+
@paginator.send method, *args
|
21
|
+
end
|
22
|
+
|
14
23
|
# Public
|
15
24
|
def paginator(p=nil)
|
16
25
|
return @paginator if p.nil?
|
data/lib/plucky/query.rb
CHANGED
@@ -9,11 +9,12 @@ module Plucky
|
|
9
9
|
|
10
10
|
# Private
|
11
11
|
OptionKeys = Set[
|
12
|
-
:select, :offset, :order,
|
13
|
-
:fields, :skip, :limit, :sort, :hint, :snapshot, # Ruby Driver
|
12
|
+
:select, :offset, :order, :transformer, # MM
|
13
|
+
:projection, :fields, :skip, :limit, :sort, :hint, :snapshot, # Ruby Driver
|
14
14
|
:batch_size, :timeout, :max_scan, :return_key, # Ruby Driver
|
15
|
-
:
|
15
|
+
:show_disk_loc, :comment, :read, # Ruby Driver
|
16
16
|
:tag_sets, :acceptable_latency, # Ruby Driver
|
17
|
+
:max_time_ms, :no_cursor_timeout, :collation, :modifiers
|
17
18
|
]
|
18
19
|
|
19
20
|
attr_reader :criteria, :options, :collection
|
@@ -61,26 +62,26 @@ module Plucky
|
|
61
62
|
:skip => paginator.skip,
|
62
63
|
}).all
|
63
64
|
|
64
|
-
|
65
|
-
docs.paginator(paginator)
|
66
|
-
docs
|
65
|
+
Pagination::Collection.new(docs, paginator)
|
67
66
|
end
|
68
67
|
|
69
68
|
def find_each(opts={})
|
70
69
|
query = clone.amend(opts)
|
71
|
-
cursor = query.cursor
|
72
70
|
|
73
71
|
if block_given?
|
74
|
-
|
75
|
-
|
72
|
+
enumerator = query.enumerator
|
73
|
+
enumerator.each do |doc|
|
74
|
+
yield doc
|
75
|
+
end
|
76
|
+
enumerator
|
77
|
+
else
|
78
|
+
query.enumerator
|
76
79
|
end
|
77
|
-
|
78
|
-
cursor
|
79
80
|
end
|
80
81
|
|
81
82
|
def find_one(opts={})
|
82
|
-
query = clone.amend(opts)
|
83
|
-
query.
|
83
|
+
query = clone.amend(opts.merge(limit: -1))
|
84
|
+
query.enumerator.first
|
84
85
|
end
|
85
86
|
|
86
87
|
def find(*ids)
|
@@ -107,12 +108,12 @@ module Plucky
|
|
107
108
|
|
108
109
|
def remove(opts={}, driver_opts={})
|
109
110
|
query = clone.amend(opts)
|
110
|
-
query.collection.
|
111
|
+
query.collection.find(query.criteria_hash, driver_opts).delete_many
|
111
112
|
end
|
112
113
|
|
113
114
|
def count(opts={})
|
114
115
|
query = clone.amend(opts)
|
115
|
-
cursor = query.
|
116
|
+
cursor = query.view
|
116
117
|
cursor.count
|
117
118
|
end
|
118
119
|
|
@@ -121,8 +122,8 @@ module Plucky
|
|
121
122
|
query.collection.distinct(key, query.criteria_hash)
|
122
123
|
end
|
123
124
|
|
124
|
-
def
|
125
|
-
clone.tap { |query| query.options[:
|
125
|
+
def projection(*args)
|
126
|
+
clone.tap { |query| query.options[:projection] = *args }
|
126
127
|
end
|
127
128
|
|
128
129
|
def ignore(*args)
|
@@ -176,12 +177,27 @@ module Plucky
|
|
176
177
|
alias_method :exist?, :exists?
|
177
178
|
alias_method :filter, :where
|
178
179
|
alias_method :to_a, :all
|
180
|
+
alias_method :fields, :projection
|
179
181
|
end
|
180
182
|
include DSL
|
181
183
|
|
182
184
|
def update(document, driver_opts={})
|
183
185
|
query = clone
|
184
|
-
|
186
|
+
if driver_opts[:multi]
|
187
|
+
query.collection.find(query.criteria_hash).update_many(document, driver_opts)
|
188
|
+
else
|
189
|
+
query.collection.find(query.criteria_hash).update_one(document, driver_opts)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def insert(document_or_array, driver_opts={})
|
194
|
+
query = clone
|
195
|
+
|
196
|
+
if document_or_array.is_a?(Array)
|
197
|
+
query.collection.insert_many(document_or_array, driver_opts)
|
198
|
+
else
|
199
|
+
query.collection.insert_one(document_or_array, driver_opts)
|
200
|
+
end
|
185
201
|
end
|
186
202
|
|
187
203
|
def amend(opts={})
|
@@ -230,8 +246,27 @@ module Plucky
|
|
230
246
|
@options.to_hash
|
231
247
|
end
|
232
248
|
|
233
|
-
def
|
234
|
-
|
249
|
+
def view
|
250
|
+
driver_opts = options_hash.dup
|
251
|
+
driver_opts.delete :transformer
|
252
|
+
case driver_opts[:read]
|
253
|
+
when Hash
|
254
|
+
driver_opts[:read] = Mongo::ServerSelector.get(driver_opts[:read])
|
255
|
+
when Symbol
|
256
|
+
driver_opts[:read] = Mongo::ServerSelector.get(mode: driver_opts[:read])
|
257
|
+
else
|
258
|
+
raise "Unexpected read options: #{driver_opts[:read]} - expected hash or symbol"
|
259
|
+
end if driver_opts.has_key?(:read)
|
260
|
+
|
261
|
+
@collection.find(criteria_hash, driver_opts)
|
262
|
+
end
|
263
|
+
|
264
|
+
def enumerator
|
265
|
+
if transformer = options_hash[:transformer]
|
266
|
+
Transformer.new(view, transformer).to_enum
|
267
|
+
else
|
268
|
+
view.to_enum
|
269
|
+
end
|
235
270
|
end
|
236
271
|
|
237
272
|
private
|
@@ -259,7 +294,7 @@ module Plucky
|
|
259
294
|
def set_field_inclusion(fields, value)
|
260
295
|
fields_option = {}
|
261
296
|
fields.each { |field| fields_option[symbolized_key(field)] = value }
|
262
|
-
clone.tap { |query| query.options[:
|
297
|
+
clone.tap { |query| query.options[:projection] = fields_option }
|
263
298
|
end
|
264
299
|
end
|
265
300
|
end
|
data/lib/plucky/version.rb
CHANGED
data/plucky.gemspec
CHANGED
@@ -3,12 +3,11 @@ require File.expand_path('../lib/plucky/version', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'plucky'
|
6
|
-
s.homepage = 'http://github.com/
|
6
|
+
s.homepage = 'http://github.com/mongomapper/plucky'
|
7
7
|
s.summary = 'Thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).'
|
8
8
|
s.require_path = 'lib'
|
9
|
-
s.
|
10
|
-
s.
|
11
|
-
s.email = ['nunemaker@gmail.com']
|
9
|
+
s.authors = ['John Nunemaker', 'Chris Heald', 'Scott Taylor']
|
10
|
+
s.email = ['nunemaker@gmail.com', 'cheald@gmail.com', 'scott@railsnewbie.com']
|
12
11
|
s.version = Plucky::Version
|
13
12
|
s.platform = Gem::Platform::RUBY
|
14
13
|
|
@@ -17,5 +16,5 @@ Gem::Specification.new do |s|
|
|
17
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
17
|
s.require_paths = ["lib"]
|
19
18
|
|
20
|
-
s.add_dependency 'mongo', '~>
|
19
|
+
s.add_dependency 'mongo', '~> 2.0'
|
21
20
|
end
|
data/script/bootstrap
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: bootstrap [bundle options]
|
3
|
+
#/
|
4
|
+
#/ Bundle install the dependencies.
|
5
|
+
#/
|
6
|
+
#/ Examples:
|
7
|
+
#/
|
8
|
+
#/ bootstrap
|
9
|
+
#/ bootstrap --local
|
10
|
+
#/
|
11
|
+
|
12
|
+
set -e
|
13
|
+
cd $(dirname "$0")/..
|
14
|
+
|
15
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
16
|
+
grep '^#/' <"$0"| cut -c4-
|
17
|
+
exit 0
|
18
|
+
}
|
19
|
+
|
20
|
+
rm -rf .bundle/{binstubs,config}
|
21
|
+
bundle install --binstubs .bundle/binstubs --path .bundle --quiet "$@"
|
data/script/release
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: release
|
3
|
+
#/
|
4
|
+
#/ Tag the version in the repo and push the gem.
|
5
|
+
#/
|
6
|
+
|
7
|
+
set -e
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
|
10
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
11
|
+
grep '^#/' <"$0"| cut -c4-
|
12
|
+
exit 0
|
13
|
+
}
|
14
|
+
|
15
|
+
gem_name=plucky
|
16
|
+
|
17
|
+
# Build a new gem archive.
|
18
|
+
rm -rf $gem_name-*.gem
|
19
|
+
gem build -q $gem_name.gemspec
|
20
|
+
|
21
|
+
# Make sure we're on the master branch.
|
22
|
+
(git branch | grep -q '* master') || {
|
23
|
+
echo "Only release from the master branch."
|
24
|
+
exit 1
|
25
|
+
}
|
26
|
+
|
27
|
+
# Figure out what version we're releasing.
|
28
|
+
tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
|
29
|
+
|
30
|
+
echo "Releasing $tag"
|
31
|
+
|
32
|
+
# Make sure we haven't released this version before.
|
33
|
+
git fetch -t origin
|
34
|
+
|
35
|
+
(git tag -l | grep -q "$tag") && {
|
36
|
+
echo "Whoops, there's already a '${tag}' tag."
|
37
|
+
exit 1
|
38
|
+
}
|
39
|
+
|
40
|
+
# Tag it and bag it.
|
41
|
+
gem push $gem_name-*.gem && git tag "$tag" &&
|
42
|
+
git push origin master && git push origin "$tag"
|