plucky 0.6.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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"