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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -3
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +7 -4
  6. data/Gemfile +6 -5
  7. data/Gemfile.lock +84 -0
  8. data/LICENSE +1 -1
  9. data/README.md +17 -75
  10. data/Rakefile +0 -3
  11. data/examples/query.rb +8 -7
  12. data/lib/plucky.rb +1 -0
  13. data/lib/plucky/criteria_hash.rb +78 -62
  14. data/lib/plucky/extensions/symbol.rb +8 -0
  15. data/lib/plucky/normalizers/criteria_hash_value.rb +3 -1
  16. data/lib/plucky/normalizers/fields_value.rb +3 -3
  17. data/lib/plucky/normalizers/hash_key.rb +19 -0
  18. data/lib/plucky/normalizers/options_hash_value.rb +5 -7
  19. data/lib/plucky/normalizers/sort_value.rb +8 -6
  20. data/lib/plucky/options_hash.rb +9 -3
  21. data/lib/plucky/pagination.rb +1 -1
  22. data/lib/plucky/pagination/{decorator.rb → collection.rb} +10 -1
  23. data/lib/plucky/query.rb +56 -21
  24. data/lib/plucky/transformer.rb +14 -0
  25. data/lib/plucky/version.rb +1 -1
  26. data/plucky.gemspec +4 -5
  27. data/script/bootstrap +21 -0
  28. data/script/release +42 -0
  29. data/script/test +20 -0
  30. data/spec/functional/options_hash_spec.rb +41 -0
  31. data/spec/helper.rb +12 -4
  32. data/spec/plucky/criteria_hash_spec.rb +68 -4
  33. data/spec/plucky/normalizers/criteria_hash_value_spec.rb +1 -1
  34. data/spec/plucky/normalizers/fields_value_spec.rb +5 -5
  35. data/spec/plucky/normalizers/hash_key_spec.rb +15 -0
  36. data/spec/plucky/normalizers/options_hash_value_spec.rb +2 -2
  37. data/spec/plucky/normalizers/sort_value_spec.rb +24 -20
  38. data/spec/plucky/options_hash_spec.rb +2 -2
  39. data/spec/plucky/pagination/{decorator_spec.rb → collection_spec.rb} +8 -5
  40. data/spec/plucky/query_spec.rb +92 -35
  41. data/spec/plucky_spec.rb +5 -5
  42. data/spec/symbol_operator_spec.rb +18 -1
  43. metadata +37 -36
  44. data/lib/plucky/normalizers/options_hash_key.rb +0 -23
  45. data/script/criteria_hash.rb +0 -21
  46. 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
- [value]
17
+ {value => 1}
18
18
  when String
19
- value.split(',').map { |v| v.strip }
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
- :fields => default_fields_value_normalizer,
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.map { |v| normalized_sort_piece(v).flatten }
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
- [normalized_direction(value.field, value.operator)]
35
+ normalized_direction(value.field, value.operator)
36
36
  when String
37
- value.split(',').map do |piece|
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
- [normalized_direction(value)]
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
- [@key_normalizer.call(field).to_s, direction]
53
+ {@key_normalizer.call(field).to_s => direction}
52
54
  end
53
55
  end
54
56
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'plucky/normalizers/options_hash_key'
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[:fields].nil?
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::OptionsHashKey.new
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
 
@@ -1,4 +1,4 @@
1
- require 'plucky/pagination/decorator'
1
+ require 'plucky/pagination/collection'
2
2
  require 'plucky/pagination/paginator'
3
3
 
4
4
  module Plucky
@@ -1,7 +1,7 @@
1
1
  require 'forwardable'
2
2
  module Plucky
3
3
  module Pagination
4
- module Decorator
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?
@@ -9,11 +9,12 @@ module Plucky
9
9
 
10
10
  # Private
11
11
  OptionKeys = Set[
12
- :select, :offset, :order, # MM
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
- :transformer, :show_disk_loc, :comment, :read, # Ruby Driver
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
- docs.extend(Pagination::Decorator)
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
- cursor.each { |doc| yield doc }
75
- cursor.rewind!
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.collection.find_one(query.criteria_hash, query.options_hash)
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.remove(query.criteria_hash, driver_opts)
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.cursor
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 fields(*args)
125
- clone.tap { |query| query.options[:fields] = *args }
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
- query.collection.update(query.criteria_hash, document, driver_opts)
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 cursor
234
- @collection.find(criteria_hash, options_hash)
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[:fields] = fields_option }
297
+ clone.tap { |query| query.options[:projection] = fields_option }
263
298
  end
264
299
  end
265
300
  end
@@ -0,0 +1,14 @@
1
+ module Plucky
2
+ class Transformer
3
+ def initialize(view, transformer)
4
+ @view = view
5
+ @transformer = transformer
6
+ end
7
+
8
+ def each
9
+ @view.each do |doc|
10
+ yield @transformer.call(doc)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module Plucky
3
- Version = '0.6.3'
3
+ Version = '0.8.0'
4
4
  end
@@ -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/jnunemaker/plucky'
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.homepage = 'http://jnunemaker.github.com/plucky/'
10
- s.authors = ['John Nunemaker']
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', '~> 1.5'
19
+ s.add_dependency 'mongo', '~> 2.0'
21
20
  end
@@ -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 "$@"
@@ -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"