candy 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.markdown +27 -0
- data/LICENSE.markdown +110 -0
- data/README.markdown +269 -41
- data/Rakefile +7 -9
- data/VERSION +1 -1
- data/candy.gemspec +35 -21
- data/lib/candy.rb +11 -138
- data/lib/candy/array.rb +74 -0
- data/lib/candy/collection.rb +151 -0
- data/lib/candy/crunch.rb +85 -44
- data/lib/candy/embeddable.rb +34 -0
- data/lib/candy/factory.rb +30 -0
- data/lib/candy/hash.rb +30 -0
- data/lib/candy/piece.rb +210 -0
- data/lib/candy/wrapper.rb +39 -13
- data/spec/candy/array_spec.rb +50 -0
- data/spec/candy/collection_spec.rb +102 -0
- data/spec/candy/crunch_spec.rb +17 -36
- data/spec/candy/hash_spec.rb +38 -0
- data/spec/candy/piece_spec.rb +259 -0
- data/spec/candy/wrapper_spec.rb +5 -12
- data/spec/candy_spec.rb +1 -191
- data/spec/spec_helper.rb +5 -1
- data/spec/support/kitkat_fixture.rb +10 -0
- data/spec/support/zagnuts_fixture.rb +10 -0
- metadata +62 -36
- data/LICENSE +0 -20
data/lib/candy/wrapper.rb
CHANGED
@@ -8,6 +8,7 @@ module Candy
|
|
8
8
|
module Wrapper
|
9
9
|
|
10
10
|
BSON_SAFE = [String,
|
11
|
+
Symbol,
|
11
12
|
NilClass,
|
12
13
|
TrueClass,
|
13
14
|
FalseClass,
|
@@ -27,8 +28,8 @@ module Candy
|
|
27
28
|
# Pass the simple cases through
|
28
29
|
return thing if BSON_SAFE.include?(thing.class)
|
29
30
|
case thing
|
30
|
-
when Symbol
|
31
|
-
|
31
|
+
# when Symbol
|
32
|
+
# wrap_symbol(thing)
|
32
33
|
when Array
|
33
34
|
wrap_array(thing)
|
34
35
|
when Hash
|
@@ -47,21 +48,32 @@ module Candy
|
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
|
-
# Takes an array and returns the same array with unsafe objects wrapped
|
51
|
+
# Takes an array and returns the same array with unsafe objects wrapped.
|
51
52
|
def self.wrap_array(array)
|
52
53
|
array.map {|element| wrap(element)}
|
53
54
|
end
|
54
55
|
|
55
|
-
# Takes a hash and returns it with
|
56
|
+
# Takes a hash and returns it with values wrapped. Keys are converted to strings, and
|
57
|
+
# wrapped with single quotes if they were already strings. (So that we can easily tell
|
58
|
+
# hash keys from string keys later on.)
|
56
59
|
def self.wrap_hash(hash)
|
57
60
|
wrapped = {}
|
58
|
-
hash.each
|
61
|
+
hash.each do |key, value|
|
62
|
+
case key
|
63
|
+
when Symbol
|
64
|
+
wrapped[key.to_s] = wrap(value)
|
65
|
+
when String
|
66
|
+
wrapped["'#{key}'"] = wrap(value)
|
67
|
+
else
|
68
|
+
wrapped[wrap(key)] = wrap(value)
|
69
|
+
end
|
70
|
+
end
|
59
71
|
wrapped
|
60
72
|
end
|
61
73
|
|
62
|
-
# Returns a string that's distinctive enough for us to unwrap later and produce the same symbol
|
74
|
+
# Returns a string that's distinctive enough for us to unwrap later and produce the same symbol.
|
63
75
|
def self.wrap_symbol(symbol)
|
64
|
-
"
|
76
|
+
"__:" + symbol.to_s
|
65
77
|
end
|
66
78
|
|
67
79
|
# Returns a nested hash containing the class and instance variables of the object. It's not the
|
@@ -89,19 +101,33 @@ module Candy
|
|
89
101
|
unwrap_hash(thing)
|
90
102
|
end
|
91
103
|
when Array
|
92
|
-
thing.map {|element| unwrap(element)}
|
93
|
-
when /^
|
94
|
-
|
104
|
+
CandyArray.embed(*thing.map {|element| unwrap(element)})
|
105
|
+
# when /^__:(.+)/
|
106
|
+
# $1.to_sym
|
95
107
|
else
|
96
108
|
thing
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
100
|
-
# Traverses the hash, unwrapping
|
112
|
+
# Traverses the hash, unwrapping values and converting keys back to symbols. Returns
|
113
|
+
# the results as a CandyHash object.
|
101
114
|
def self.unwrap_hash(hash)
|
102
115
|
unwrapped = {}
|
103
|
-
hash.each
|
104
|
-
|
116
|
+
hash.each do |key, value|
|
117
|
+
case key
|
118
|
+
when /^'(.+)'$/
|
119
|
+
unwrapped[$1] = unwrap(value)
|
120
|
+
when String
|
121
|
+
unwrapped[key.to_sym] = unwrap(value)
|
122
|
+
else
|
123
|
+
unwrapped[unwrap(key)] = unwrap(value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
if klass = unwrapped.delete(CLASS_KEY)
|
127
|
+
qualified_const_get(klass).embed(unwrapped)
|
128
|
+
else
|
129
|
+
CandyHash.embed(unwrapped)
|
130
|
+
end
|
105
131
|
end
|
106
132
|
|
107
133
|
# Turns a hashed object back into an object of the stated class, setting any captured instance
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Candy::CandyArray do
|
4
|
+
before(:all) do
|
5
|
+
@verifier = Zagnut.collection
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@this = Zagnut.new
|
10
|
+
@this.bits = ['peanut', 'almonds', 'titanium']
|
11
|
+
end
|
12
|
+
|
13
|
+
it "writes the array" do
|
14
|
+
@verifier.find_one['bits'][1].should == 'almonds'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "reads the array" do
|
18
|
+
that = Zagnut(@this.id)
|
19
|
+
that.bits[2].should == 'titanium'
|
20
|
+
end
|
21
|
+
|
22
|
+
it "cascades appends" do
|
23
|
+
@this.bits << 'kryptonite'
|
24
|
+
that = Zagnut(@this.id)
|
25
|
+
that.bits[-1].should == 'kryptonite'
|
26
|
+
end
|
27
|
+
|
28
|
+
it "cascades substitutions" do
|
29
|
+
@this.bits[0] = 'raisins'
|
30
|
+
that = Zagnut(@this.id)
|
31
|
+
that.bits.should == ['raisins', 'almonds', 'titanium']
|
32
|
+
end
|
33
|
+
|
34
|
+
it "cascades deletions" do
|
35
|
+
@this.bits.shift.should == 'peanut'
|
36
|
+
that = Zagnut(@this.id)
|
37
|
+
that.bits.size.should == 2
|
38
|
+
end
|
39
|
+
|
40
|
+
it "cascades deeply" do
|
41
|
+
@this.bits.push [5, 11, {foo: [:bar]}]
|
42
|
+
that = Zagnut(@this.id)
|
43
|
+
that.bits[3][2][:foo][0].should == :bar
|
44
|
+
end
|
45
|
+
|
46
|
+
after(:each) do
|
47
|
+
Zagnut.collection.remove
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Candy::Collection do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@this = Zagnut.new
|
7
|
+
@this.color = "red"
|
8
|
+
@this.weight = 11.8
|
9
|
+
@that = Zagnut.new
|
10
|
+
@that.color = "red"
|
11
|
+
@that.pieces = 6
|
12
|
+
@that.weight = -5
|
13
|
+
@the_other = Zagnut.new
|
14
|
+
@the_other.color = "blue"
|
15
|
+
@the_other.pieces = 7
|
16
|
+
@the_other.weight = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
it "can get all objects matching a search condition" do
|
21
|
+
those = Zagnuts.color("red")
|
22
|
+
those.count.should == 2
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can get all objects in a collection" do
|
26
|
+
those = Zagnuts.all
|
27
|
+
those.count.should == 3
|
28
|
+
end
|
29
|
+
|
30
|
+
it "still returns if nothing matches" do
|
31
|
+
Zagnuts.color("green").to_a.should == []
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can take options" do
|
35
|
+
those = Zagnuts.color("red").sort("weight", :down)
|
36
|
+
those.collect{|z| z.weight}.should == [11.8, -5]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can be iterated" do
|
40
|
+
these = Zagnuts.color("red").sort(:weight)
|
41
|
+
this = these.first
|
42
|
+
this.pieces.should == 6
|
43
|
+
this.weight.should == -5
|
44
|
+
this = these.next
|
45
|
+
this.pieces.should be_nil
|
46
|
+
this.weight.should == 11.8
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can take scoping on a class or instance level" do
|
50
|
+
these = Zagnuts.color("red")
|
51
|
+
these.pieces(6)
|
52
|
+
these.count.should == 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can get the collection by a method at the top level" do
|
56
|
+
these = Zagnuts
|
57
|
+
these.count.should == 3
|
58
|
+
end
|
59
|
+
|
60
|
+
# Test class for scoped magic method generation
|
61
|
+
class BabyRuth
|
62
|
+
include Candy::Piece
|
63
|
+
end
|
64
|
+
|
65
|
+
class BabiesRuth
|
66
|
+
include Candy::Collection
|
67
|
+
collects :baby_ruth
|
68
|
+
end
|
69
|
+
|
70
|
+
it "can get the object by a method within the enclosing namespace" do
|
71
|
+
foo = BabyRuth.new
|
72
|
+
foo.color = 'green'
|
73
|
+
bar = BabiesRuth(color: 'green')
|
74
|
+
bar.count.should == 1
|
75
|
+
end
|
76
|
+
|
77
|
+
# Test class to verify magic method generation doesn't override anything
|
78
|
+
def Jawbreakers(param=nil)
|
79
|
+
"Broken everywhere!"
|
80
|
+
end
|
81
|
+
|
82
|
+
class Jawbreaker
|
83
|
+
include Candy::Piece
|
84
|
+
end
|
85
|
+
|
86
|
+
class Jawbreakers
|
87
|
+
include Candy::Collection
|
88
|
+
collects :jawbreaker
|
89
|
+
end
|
90
|
+
|
91
|
+
it "doesn't create a namespace method if one already exists" do
|
92
|
+
too = Jawbreaker.new
|
93
|
+
too.calories = 55
|
94
|
+
tar = Jawbreakers()
|
95
|
+
tar.should == 'Broken everywhere!'
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
after(:each) do
|
100
|
+
Zagnut.collection.remove
|
101
|
+
end
|
102
|
+
end
|
data/spec/candy/crunch_spec.rb
CHANGED
@@ -11,7 +11,7 @@ describe Candy::Crunch do
|
|
11
11
|
describe "connection" do
|
12
12
|
before(:each) do
|
13
13
|
# Make sure we don't waste time making bogus connections
|
14
|
-
|
14
|
+
Candy.connection_options[:connect] = false
|
15
15
|
end
|
16
16
|
|
17
17
|
it "takes yours if you give it one" do
|
@@ -24,39 +24,22 @@ describe Candy::Crunch do
|
|
24
24
|
PeanutBrittle.connection.nodes.should == [["localhost", 27017]]
|
25
25
|
end
|
26
26
|
|
27
|
-
it "uses the host you provide" do
|
28
|
-
PeanutBrittle.host = 'example.org'
|
29
|
-
PeanutBrittle.connection.nodes.should == [["example.org", 27017]]
|
30
|
-
end
|
31
|
-
|
32
|
-
it "uses the port you provide" do
|
33
|
-
PeanutBrittle.host = 'localhost'
|
34
|
-
PeanutBrittle.port = 3000
|
35
|
-
PeanutBrittle.connection.nodes.should == [["localhost", 3000]]
|
36
|
-
end
|
37
27
|
|
38
|
-
it "uses
|
39
|
-
|
40
|
-
PeanutBrittle.options[:logger] = l
|
41
|
-
PeanutBrittle.connection.logger.should == l
|
42
|
-
end
|
43
|
-
|
44
|
-
it "uses the $MONGO_HOST setting if you don't override it" do
|
45
|
-
$MONGO_HOST = 'example.net'
|
28
|
+
it "uses the Candy.host setting if you don't override it" do
|
29
|
+
Candy.host = 'example.net'
|
46
30
|
PeanutBrittle.connection.nodes.should == [["example.net", 27017]]
|
47
31
|
end
|
48
32
|
|
49
|
-
it "uses the
|
50
|
-
|
51
|
-
|
33
|
+
it "uses the Candy.port setting if you don't override it" do
|
34
|
+
Candy.host = 'localhost'
|
35
|
+
Candy.port = 33333
|
52
36
|
PeanutBrittle.connection.nodes.should == [["localhost", 33333]]
|
53
37
|
end
|
54
38
|
|
55
|
-
it "uses the
|
39
|
+
it "uses the Candy.connection_options setting if you don't override it" do
|
56
40
|
l = Logger.new(STDOUT)
|
57
|
-
|
58
|
-
|
59
|
-
PeanutBrittle.connection.logger.should == $MONGO_OPTIONS[:logger]
|
41
|
+
Candy.connection_options = {:logger => l, :connect => false}
|
42
|
+
PeanutBrittle.connection.logger.should == Candy.connection_options[:logger]
|
60
43
|
end
|
61
44
|
|
62
45
|
it "clears the database when you set it" do
|
@@ -66,18 +49,16 @@ describe Candy::Crunch do
|
|
66
49
|
end
|
67
50
|
|
68
51
|
after(:each) do
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
PeanutBrittle.
|
73
|
-
PeanutBrittle.port = nil
|
74
|
-
PeanutBrittle.options = nil
|
52
|
+
Candy.host = nil
|
53
|
+
Candy.port = nil
|
54
|
+
Candy.connection_options = nil
|
55
|
+
PeanutBrittle.connection = nil
|
75
56
|
end
|
76
57
|
end
|
77
58
|
|
78
59
|
describe "database" do
|
79
60
|
before(:each) do
|
80
|
-
|
61
|
+
Candy.db = nil
|
81
62
|
end
|
82
63
|
|
83
64
|
it "takes yours if you give it one" do
|
@@ -95,8 +76,8 @@ describe Candy::Crunch do
|
|
95
76
|
lambda{PeanutBrittle.db = 5}.should raise_error(Candy::ConnectionError, "The db attribute needs a Mongo::DB object or a name string.")
|
96
77
|
end
|
97
78
|
|
98
|
-
it "uses the
|
99
|
-
|
79
|
+
it "uses the Candy.db setting if you don't override it" do
|
80
|
+
Candy.db = 'foobar'
|
100
81
|
PeanutBrittle.db.name.should == 'foobar'
|
101
82
|
end
|
102
83
|
|
@@ -118,7 +99,7 @@ describe Candy::Crunch do
|
|
118
99
|
end
|
119
100
|
|
120
101
|
after(:all) do
|
121
|
-
|
102
|
+
Candy.db = 'candy_test' # Get back to our starting point
|
122
103
|
end
|
123
104
|
end
|
124
105
|
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Candy::CandyHash do
|
4
|
+
before(:all) do
|
5
|
+
@verifier = Zagnut.collection
|
6
|
+
end
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@this = Zagnut.new
|
10
|
+
@this.filling = {taste: 'caramel', ounces: 0.75}
|
11
|
+
end
|
12
|
+
|
13
|
+
it "writes the hash" do
|
14
|
+
@verifier.find_one['filling']['ounces'].should == 0.75
|
15
|
+
end
|
16
|
+
|
17
|
+
it "reads the hash" do
|
18
|
+
that = Zagnut(@this.id)
|
19
|
+
that.filling.taste.should == 'caramel'
|
20
|
+
that.filling.should be_a(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "cascades changes" do
|
24
|
+
@this.filling[:calories] = 250
|
25
|
+
that = Zagnut(@this.id)
|
26
|
+
that.filling.calories.should == 250
|
27
|
+
end
|
28
|
+
|
29
|
+
it "cascades deeply" do
|
30
|
+
@this.filling.subfilling = {texture: :gravel}
|
31
|
+
that = Zagnut(@this.id)
|
32
|
+
that.filling.subfilling.texture.should == :gravel
|
33
|
+
end
|
34
|
+
|
35
|
+
after(:each) do
|
36
|
+
Zagnut.collection.remove
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe Candy::Piece do
|
4
|
+
|
5
|
+
class Nougat
|
6
|
+
attr_accessor :foo
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:all) do
|
10
|
+
@verifier = Zagnut.collection
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
@this = Zagnut.new
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
it "lazy inserts" do
|
19
|
+
@this.id.should be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "knows its ID after inserting" do
|
23
|
+
@this.name = 'Zagnut'
|
24
|
+
@this.id.should be_a(Mongo::ObjectID)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
it "can be given a hash of data to insert immediately" do
|
29
|
+
that = Zagnut.new({calories: 500, morsels: "chewy"})
|
30
|
+
@verifier.find_one(calories: 500)["morsels"].should == "chewy"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "saves any attribute it doesn't already handle to the database" do
|
34
|
+
@this.bite = "Tasty!"
|
35
|
+
@verifier.find_one["bite"].should == "Tasty!"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "retrieves any attribute it doesn't already know about from the database" do
|
39
|
+
@this.chew = "Munchy!"
|
40
|
+
@verifier.update({:_id => @this.id}, {:chew => "Yummy!"})
|
41
|
+
that = Zagnut(@this.id)
|
42
|
+
that.chew.should == "Yummy!"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "can roundtrip effectively" do
|
46
|
+
@this.swallow = "Gulp."
|
47
|
+
@this.swallow.should == "Gulp."
|
48
|
+
end
|
49
|
+
|
50
|
+
it "handles missing attributes gracefully" do
|
51
|
+
@this.licks.should == nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "allows multiple attributes to be set" do
|
55
|
+
@this.licks = 7
|
56
|
+
@this.center = 0.5
|
57
|
+
@this.licks.should == 7
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
it "wraps objects" do
|
62
|
+
nougat = Nougat.new
|
63
|
+
nougat.foo = 5
|
64
|
+
@this.center = nougat
|
65
|
+
@verifier.find_one["center"]["__object_"]["class"].should == Nougat.name
|
66
|
+
end
|
67
|
+
|
68
|
+
it "unwraps objects" do
|
69
|
+
@this.blank = "" # To force a save
|
70
|
+
center = Nougat.new
|
71
|
+
center.foo = :bar
|
72
|
+
@verifier.update({'_id' => @this.id}, '$set' => {:center => {"__object_" => {:class => Nougat.name, :ivars => {"@foo" => 'bar'}}}})
|
73
|
+
@this.refresh.center.should be_a(Nougat)
|
74
|
+
@this.center.instance_variable_get(:@foo).should == 'bar'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "wraps symbols" do
|
78
|
+
@this.crunch = :chomp
|
79
|
+
@verifier.find_one["crunch"].should == :chomp
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
it "considers objects equal if they point to the same MongoDB ref" do
|
84
|
+
@this.blank = ""
|
85
|
+
that = Zagnut(@this.id)
|
86
|
+
that.should == @this
|
87
|
+
end
|
88
|
+
|
89
|
+
it "considers objects unequal if they don't have the same MongoDB ref" do
|
90
|
+
@this.calories = 5
|
91
|
+
that = Zagnut.new(calories: 5)
|
92
|
+
@this.should_not == that
|
93
|
+
end
|
94
|
+
|
95
|
+
it "considers objects unequal if this one hasn't been saved yet" do
|
96
|
+
that = Zagnut.new
|
97
|
+
@this.should_not == that
|
98
|
+
end
|
99
|
+
|
100
|
+
it "considers objects unequal if compared to something without an id" do
|
101
|
+
that = Object.new
|
102
|
+
@this.should_not == that
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
describe "retrieval" do
|
108
|
+
it "can find a record by its ID" do
|
109
|
+
@this.licks = 10
|
110
|
+
that = Zagnut.first(@this.id)
|
111
|
+
that.licks.should == 10
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns nil on an object that can't be found" do
|
115
|
+
id = Mongo::ObjectID.new
|
116
|
+
Zagnut(id).should be_nil
|
117
|
+
end
|
118
|
+
|
119
|
+
it "can get a single object by attributes" do
|
120
|
+
@this.pieces = 7.5
|
121
|
+
@this.color = "red"
|
122
|
+
that = Zagnut.first("pieces" => 7.5)
|
123
|
+
that.color.should == "red"
|
124
|
+
end
|
125
|
+
|
126
|
+
it "returns nil if a first object can't be found" do
|
127
|
+
@this.pieces = 11
|
128
|
+
Zagnut.first("pieces" => 5).should be_nil
|
129
|
+
end
|
130
|
+
|
131
|
+
it "can get a single object by attribute method" do
|
132
|
+
@this.color = "blue"
|
133
|
+
@this.smushy = true
|
134
|
+
that = Zagnut.color("blue")
|
135
|
+
that.should be_smushy
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can get the object by a method at the top level" do
|
139
|
+
@this.intensity = :Yowza
|
140
|
+
that = Zagnut(@this.id)
|
141
|
+
that.intensity.should == :Yowza
|
142
|
+
end
|
143
|
+
|
144
|
+
# Test class for scoped magic method generation
|
145
|
+
class BabyRuth
|
146
|
+
include Candy::Piece
|
147
|
+
end
|
148
|
+
|
149
|
+
it "can get the object by a method within the enclosing namespace" do
|
150
|
+
foo = BabyRuth.new
|
151
|
+
foo.color = 'green'
|
152
|
+
bar = BabyRuth(foo.id)
|
153
|
+
bar.color.should == 'green'
|
154
|
+
end
|
155
|
+
|
156
|
+
# Test class to verify magic method generation doesn't override anything
|
157
|
+
def Jawbreaker(param=nil)
|
158
|
+
"Broken!"
|
159
|
+
end
|
160
|
+
|
161
|
+
class Jawbreaker
|
162
|
+
include Candy::Piece
|
163
|
+
end
|
164
|
+
|
165
|
+
it "doesn't create a namespace method if one already exists" do
|
166
|
+
too = Jawbreaker.new
|
167
|
+
too.calories = 55
|
168
|
+
tar = Jawbreaker(too.id)
|
169
|
+
tar.should == 'Broken!'
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "updates" do
|
175
|
+
before(:each) do
|
176
|
+
@this.ounces = 17
|
177
|
+
@this.crunchy = :very
|
178
|
+
@that = Zagnut.new
|
179
|
+
@that.ounces = 11
|
180
|
+
@that.crunchy = :very
|
181
|
+
end
|
182
|
+
|
183
|
+
it "will insert a document if the key field's value isn't found" do
|
184
|
+
Zagnut.update(:ounces, {ounces: 15, crunchy: :not_very, flavor: 'butterscotch'})
|
185
|
+
@verifier.count.should == 3
|
186
|
+
Zagnut.ounces(15).flavor.should == 'butterscotch'
|
187
|
+
end
|
188
|
+
|
189
|
+
it "will update a document if the key field's value is found" do
|
190
|
+
Zagnut.update(:ounces, {ounces: 11, crunchy: :barely, salt: 0})
|
191
|
+
@verifier.count.should == 2
|
192
|
+
@that.refresh
|
193
|
+
@that.crunchy.should == :barely
|
194
|
+
end
|
195
|
+
|
196
|
+
it "can match on multiple keys" do
|
197
|
+
Zagnut.update([:crunchy, :ounces], {ounces: 17, crunchy: :very, calories: 715})
|
198
|
+
@verifier.count.should == 2
|
199
|
+
@this.refresh
|
200
|
+
@this.calories.should == 715
|
201
|
+
end
|
202
|
+
|
203
|
+
it "won't match on multiple keys if they aren't all found" do
|
204
|
+
Zagnut.update([:crunchy, :ounces], {ounces: 11, crunchy: :not_quite, color: 'brown'})
|
205
|
+
@verifier.count.should == 3
|
206
|
+
Zagnut.crunchy(:not_quite).color.should == 'brown'
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe "embedding" do
|
211
|
+
describe "Candy objects" do
|
212
|
+
before(:each) do
|
213
|
+
@inner = KitKat.new
|
214
|
+
@inner.crunch = 'wafer'
|
215
|
+
@this.inner = @inner
|
216
|
+
end
|
217
|
+
|
218
|
+
it "writes the object" do
|
219
|
+
@verifier.find_one['inner']['crunch'].should == 'wafer'
|
220
|
+
end
|
221
|
+
|
222
|
+
it "reads the object" do
|
223
|
+
that = Zagnut(@this.id)
|
224
|
+
that.inner.crunch.should == 'wafer'
|
225
|
+
end
|
226
|
+
|
227
|
+
it "maintains the class" do
|
228
|
+
that = Zagnut(@this.id)
|
229
|
+
that.inner.should be_a(KitKat)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "cascades changes" do
|
233
|
+
@this.inner.coating = 'chocolate'
|
234
|
+
@verifier.find_one['inner']['coating'].should == 'chocolate'
|
235
|
+
end
|
236
|
+
|
237
|
+
it "cascades deeply" do
|
238
|
+
@this.inner.inner = Zagnut.embed(beauty: 'recursive!')
|
239
|
+
that = Zagnut(@this.id)
|
240
|
+
that.inner.inner.beauty.should == 'recursive!'
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
after(:each) do
|
255
|
+
KitKat.collection.remove
|
256
|
+
Zagnut.collection.remove
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|