candy 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,17 @@ Candy History
3
3
 
4
4
  This document aims to provide only an overview. Further, we've only really been tracking things since **v0.2**. For obsessive detail, just check out the `git log`.
5
5
 
6
+ v0.2.9 - 2010-05-14 (the "+1" release)
7
+ --------------------------------------
8
+ Moved methods around again, placing more of the database update methods into Candy::Crunch. Also began support for two flavors of
9
+ atomic update methods:
10
+
11
+ 1. _Safe_ methods that call the collection in "safe" mode, meaning that it's much slower but the update is verified and exceptions are returned by the Mongo driver. These methods also return the new values. So far supported are **set** and **inc**.
12
+ 2. _Unsafe_ or _bang!_ methods that call the collection in "unsafe" mode, for maximum speed but without verification or new values returned. So far supported are **set!** and **inc!**.
13
+
14
+ * Refactored; added 'bang!' methods for atomic updates
15
+
16
+
6
17
  v0.2.8 - 2010-05-13 (the "Holy crap, that was ten pomodoros" release)
7
18
  ---------------------------------------------------------------------
8
19
  Major refactoring to fix a major bug: embedded documents weren't being loaded properly on document retrieval. This resulted in a lot of code being moved around, and some regrettable circular connascence between Piece and Wrapper that I hope to address later. Overall, though, it's simpler now.
@@ -29,7 +29,7 @@ We got 'em. Candy pieces can contain each other recursively, to any arbitrary d
29
29
  seafood: 'Maryland blue crabs',
30
30
  scotch: ['Glenmorangie Port Wood Finish',
31
31
  'Balvenie Single Barrel']}
32
- me.spouse = Person.embed(first_name: 'Anna', eyes: :blue)
32
+ me.spouse = Person.piece(first_name: 'Anna', eyes: :blue)
33
33
  me.spouse.eyes # => :blue
34
34
  me.favorites.scotch[1] # => 'Balvenie Single Barrel'
35
35
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.8
1
+ 0.2.9
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{candy}
8
- s.version = "0.2.8"
8
+ s.version = "0.2.9"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Stephen Eley"]
12
- s.date = %q{2010-05-13}
12
+ s.date = %q{2010-05-14}
13
13
  s.description = %q{Candy provides simple, transparent object persistence for the MongoDB database. Classes that
14
14
  include Candy modules save all properties to Mongo automatically, can be recursively embedded,
15
15
  and can retrieve records with chainable open-ended class methods, eliminating the need for
@@ -48,7 +48,7 @@ module Candy
48
48
  # (Thus supporting real-time concurrency for queue-like behavior.)
49
49
  def shift(n=1)
50
50
  doc = @__candy_parent.collection.find_and_modify query: {"_id" => @__candy_parent.id}, update: {'$pop' => {@__candy_parent_key => -1}}, new: false
51
- @__candy = doc[@__candy_parent_key.to_s]
51
+ @__candy = from_candy(doc[@__candy_parent_key.to_s])
52
52
  @__candy.shift
53
53
  end
54
54
 
@@ -212,18 +212,75 @@ module Candy
212
212
 
213
213
  end
214
214
 
215
+ # HERE BEGINNETH THE MODULE PROPER.
216
+ # (The above were class methods.)
215
217
 
216
- # We're implementing FindAndModify on Mongo 1.4 until the Ruby driver gets around to being updated...
217
- def findAndModify(query, update, sort={})
218
- command = OrderedHash[
219
- findandmodify: self.collection.name,
220
- query: query,
221
- update: update,
222
- sort: sort
223
- ]
224
- result = self.class.db.command(command)
218
+ # The MongoDB collection object that everything saves to. Defaults to the class's
219
+ # collection, which in turn defaults to the classname.
220
+ def collection
221
+ @__candy_collection ||= self.class.collection
222
+ end
223
+
224
+ # This is normally set at the class level (with a default of the classname) but you
225
+ # can override it on a per-object basis if you need to.
226
+ def collection=(val)
227
+ @__candy_collection = val
228
+ end
229
+
230
+ ### RETRIEVAL METHODS
231
+ # Returns the listed fields of the document. If no fields are given, returns the whole document.
232
+ def retrieve(*fields)
233
+ options = (fields.empty? ? {} : {fields: fields})
234
+ from_candy(collection.find_one({'_id' => id}, options)) if id
235
+ end
236
+
237
+
238
+ # A generic updater that performs the atomic operation specified on a value nested arbitrarily deeply.
239
+ # Operates in "unsafe" mode, meaning that no document errors will be returned and results are not
240
+ # guaranteed. The benefit is that it's very, very fast. Always returns true.
241
+ def operate!(operator, fields)
242
+ operate operator, fields, {safe: false} and true
243
+ end
244
+
245
+ # A generic updater that performs the atomic operation specified on a value nested arbitrarily deeply.
246
+ #
247
+ def operate(operator, fields, options={safe: true})
248
+ if @__candy_parent
249
+ @__candy_parent.operate operator, embedded(fields), options
250
+ else
251
+ @__candy_id = collection.insert({}) unless id # Ensure we have something to update
252
+ collection.update({'_id' => id}, {"$#{operator}" => Wrapper.wrap(fields)}, options)
253
+ end
254
+ end
255
+
256
+ # Given a hash of property/value pairs, sets those values in Mongo using the atomic $set if
257
+ # we have a document ID. Otherwise inserts them and sets the object's ID. Operates in
258
+ # 'unsafe' mode, so database exceptions are not reported but updates are very fast.
259
+ def set!(fields)
260
+ operate! :set, fields
225
261
  end
226
262
 
263
+ # Given a hash of property/value pairs, sets those values in Mongo using the atomic $set if
264
+ # we have a document ID. Otherwise inserts them and sets the object's ID. Returns the
265
+ # values passed to it.
266
+ def set(fields)
267
+ operate :set, fields
268
+ fields
269
+ end
270
+
271
+ # Increments the specified field by the specified amount (defaults to 1). Does not return the
272
+ # new value or any document errors.
273
+ def inc!(field, value=1)
274
+ operate! :inc, field: value
275
+ end
276
+
277
+ # Increments the specified field by the specified amount (defaults to 1) and returns the
278
+ # new value.
279
+ def inc(field, value=1)
280
+ operate :inc, field => value
281
+ retrieve(field)[field]
282
+ end
283
+
227
284
 
228
285
  def self.included(receiver)
229
286
  receiver.extend ClassMethods
@@ -101,16 +101,11 @@ module Candy
101
101
  def id
102
102
  @__candy_id
103
103
  end
104
-
105
- # Pull our document from the database if we know our ID.
106
- def retrieve_document
107
- from_candy(collection.find_one({'_id' => id})) if id
108
- end
109
-
104
+
110
105
 
111
106
  # Returns the hash of memoized values.
112
107
  def candy
113
- @__candy ||= retrieve_document || {}
108
+ @__candy ||= retrieve || {}
114
109
  end
115
110
 
116
111
  # Objects are equal if they point to the same MongoDB record (unless both have IDs of nil, in which case
@@ -153,17 +148,6 @@ module Candy
153
148
  self
154
149
  end
155
150
 
156
- # The MongoDB collection object that everything saves to. Defaults to the class's
157
- # collection, which in turn defaults to the classname.
158
- def collection
159
- @__candy_collection ||= self.class.collection
160
- end
161
-
162
- # This is normally set at the class level (with a default of the classname) but you
163
- # can override it on a per-object basis if you need to.
164
- def collection=(val)
165
- @__candy_collection = val
166
- end
167
151
 
168
152
  # Convenience method for debugging. Shows the class, the Mongo ID, and the saved state hash.
169
153
  def to_s
@@ -189,22 +173,6 @@ module Candy
189
173
  end
190
174
 
191
175
 
192
- # Given a hash of property/value pairs, sets those values in Mongo using the atomic $set if
193
- # we have a document ID. Otherwise inserts them and sets the object's ID.
194
- def set(fields)
195
- operate :set, fields
196
- end
197
-
198
- # A generic updater that performs the atomic operation specified on a value nested arbitrarily deeply.
199
- #
200
- def operate(operator, fields)
201
- if @__candy_parent
202
- @__candy_parent.operate operator, embedded(fields)
203
- else
204
- @__candy_id = collection.insert({}) unless id # Ensure we have something to update
205
- collection.update({'_id' => id}, {"$#{operator}" => Wrapper.wrap(fields)})
206
- end
207
- end
208
176
 
209
177
  private
210
178
 
@@ -141,6 +141,14 @@ describe Candy::Piece do
141
141
  that.intensity.should == :Yowza
142
142
  end
143
143
 
144
+ it "can get particular attributes" do
145
+ @this.smell = "fruity"
146
+ @this.feel = "rough"
147
+ that = Zagnut(@this.id)
148
+ that.retrieve(:smell)[:smell].should == "fruity"
149
+ that.retrieve(:smell).should_not have_key(:feel)
150
+ end
151
+
144
152
  # Test class for scoped magic method generation
145
153
  class BabyRuth
146
154
  include Candy::Piece
@@ -205,6 +213,22 @@ describe Candy::Piece do
205
213
  @verifier.count.should == 3
206
214
  Zagnut.crunchy(:not_quite).color.should == 'brown'
207
215
  end
216
+
217
+ it "can increment a value simply" do
218
+ @this.inc(:ounces).should == 18
219
+ @verifier.find_one(ounces: 18)["crunchy"].should == :very
220
+ end
221
+
222
+ it "can increment a value by a specified positive amount" do
223
+ @this.inc(:ounces, 5).should == 22
224
+ @verifier.find_one(ounces: 22)["crunchy"].should == :very
225
+ end
226
+
227
+ it "can increment a value by a specified negative amount" do
228
+ @this.inc(:ounces, -5).should == 12
229
+ @verifier.find_one(ounces: 12)["crunchy"].should == :very
230
+ end
231
+
208
232
  end
209
233
 
210
234
  describe "embedding" do
@@ -175,7 +175,6 @@ module Candy
175
175
  hash = {"foo" => :bar, "'missile'" => @wrapped}
176
176
  unwrapped = Wrapper.unwrap(hash)
177
177
  unwrapped[:foo].should == :bar
178
- puts unwrapped
179
178
  unwrapped["missile"].should be_a(Missile)
180
179
  end
181
180
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 2
8
- - 8
9
- version: 0.2.8
8
+ - 9
9
+ version: 0.2.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - Stephen Eley
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-05-13 00:00:00 -04:00
17
+ date: 2010-05-14 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency