candy 0.1.0 → 0.2.1
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.
- 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
|