candy 0.0.1 → 0.0.2
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/VERSION +1 -1
- data/candy.gemspec +75 -0
- data/lib/candy.rb +71 -2
- data/spec/candy_spec.rb +101 -0
- metadata +2 -1
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
data/candy.gemspec
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{candy}
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Stephen Eley"]
|
12
|
+
s.date = %q{2010-01-20}
|
13
|
+
s.description = %q{Candy is a lightweight ORM for the MongoDB database. If MongoMapper is Rails, Candy is Sinatra.
|
14
|
+
It provides a module you mix into any class, enabling the class to connect to Mongo on its own
|
15
|
+
and push its objects into a collection. Candied objects act like OpenStructs, allowing attributes
|
16
|
+
to be defined and updated in Mongo immediately without having to be declared in the class.
|
17
|
+
Mongo's atomic operators are used whenever possible, and a smart serializer (Candy::Wrapper)
|
18
|
+
converts almost any object for assignment to any attribute.
|
19
|
+
}
|
20
|
+
s.email = %q{sfeley@gmail.com}
|
21
|
+
s.extra_rdoc_files = [
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc"
|
24
|
+
]
|
25
|
+
s.files = [
|
26
|
+
".document",
|
27
|
+
".gitignore",
|
28
|
+
"LICENSE",
|
29
|
+
"README.rdoc",
|
30
|
+
"Rakefile",
|
31
|
+
"VERSION",
|
32
|
+
"candy.gemspec",
|
33
|
+
"lib/candy.rb",
|
34
|
+
"lib/candy/crunch.rb",
|
35
|
+
"lib/candy/exceptions.rb",
|
36
|
+
"spec/candy/crunch_spec.rb",
|
37
|
+
"spec/candy_spec.rb",
|
38
|
+
"spec/spec.opts",
|
39
|
+
"spec/spec.watchr",
|
40
|
+
"spec/spec_helper.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/SFEley/candy}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{The simplest MongoDB ORM}
|
47
|
+
s.test_files = [
|
48
|
+
"spec/candy/crunch_spec.rb",
|
49
|
+
"spec/candy_spec.rb",
|
50
|
+
"spec/spec_helper.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 3
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<mongo>, [">= 0.18"])
|
59
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
60
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
61
|
+
s.add_development_dependency(%q<mocha>, [">= 0.9.8"])
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<mongo>, [">= 0.18"])
|
64
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
65
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
66
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<mongo>, [">= 0.18"])
|
70
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
71
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
72
|
+
s.add_dependency(%q<mocha>, [">= 0.9.8"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/lib/candy.rb
CHANGED
@@ -3,18 +3,87 @@ require 'candy/crunch'
|
|
3
3
|
|
4
4
|
# Mix me into your classes and Mongo will like them!
|
5
5
|
module Candy
|
6
|
+
|
6
7
|
module ClassMethods
|
8
|
+
include Crunch::ClassMethods
|
9
|
+
|
10
|
+
# Retrieves an object from Mongo by its ID and returns it. Returns nil if the ID isn't found in Mongo.
|
11
|
+
def find(id)
|
12
|
+
if collection.find_one(id)
|
13
|
+
self.new({:_candy => id})
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Retrieves a single object from Mongo by its search attributes, or nil if it can't be found.
|
18
|
+
def first(conditions={})
|
19
|
+
options = extract_options(conditions)
|
20
|
+
if record = collection.find_one(conditions, options)
|
21
|
+
find(record["_id"])
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieves all objects matching the search attributes as an Enumerator (even if it's an empty one).
|
28
|
+
# The option fields from Mongo::Collection#find can be passed as well, and will be extracted from the
|
29
|
+
# condition set if they're found.
|
30
|
+
def all(conditions={})
|
31
|
+
options = extract_options(conditions)
|
32
|
+
cursor = collection.find(conditions, options)
|
33
|
+
Enumerator.new do |yielder|
|
34
|
+
while record = cursor.next_document do
|
35
|
+
yielder.yield find(record["_id"])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
# Returns a hash of options matching those enabled in Mongo::Collection#find, if any of them exist
|
42
|
+
# in the set of search conditions.
|
43
|
+
def extract_options(conditions)
|
44
|
+
options = {:fields => []}
|
45
|
+
[:fields, :skip, :limit, :sort, :hint, :snapshot, :timeout].each do |option|
|
46
|
+
options[option] = conditions.delete(option) if conditions[option]
|
47
|
+
end
|
48
|
+
options
|
49
|
+
end
|
7
50
|
|
8
51
|
end
|
9
52
|
|
10
53
|
module InstanceMethods
|
54
|
+
include Crunch::InstanceMethods
|
55
|
+
|
56
|
+
# We push ourselves into the DB before going on with our day.
|
11
57
|
def initialize(*args, &block)
|
12
|
-
@__candy = self.class.collection.insert({})
|
58
|
+
@__candy = check_for_candy(args) || self.class.collection.insert({})
|
13
59
|
super
|
14
60
|
end
|
15
61
|
|
62
|
+
# Shortcut to the document ID.
|
16
63
|
def id
|
17
|
-
@
|
64
|
+
@__candy
|
65
|
+
end
|
66
|
+
|
67
|
+
# Candy's magic ingredient. Assigning to any unknown attribute will push that value into the Mongo collection.
|
68
|
+
# Retrieving any unknown attribute will return that value from this record in the Mongo collection.
|
69
|
+
def method_missing(name, *args, &block)
|
70
|
+
if name =~ /(.*)=$/ # We're assigning
|
71
|
+
self.class.collection.update({"_id" => @__candy}, {"$set" => {$1 => args[0]}})
|
72
|
+
else
|
73
|
+
self.class.collection.find_one(@__candy, :fields => [name.to_s])[name.to_s]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# Returns the secret decoder ring buried in the arguments to "new"
|
80
|
+
def check_for_candy(args)
|
81
|
+
if (candidate = args.pop).is_a?(Hash) and candidate[:_candy]
|
82
|
+
candidate[:_candy]
|
83
|
+
else # This must not be for us, so put it back
|
84
|
+
args.push candidate if candidate
|
85
|
+
nil
|
86
|
+
end
|
18
87
|
end
|
19
88
|
|
20
89
|
end
|
data/spec/candy_spec.rb
CHANGED
@@ -6,12 +6,113 @@ describe "Candy" do
|
|
6
6
|
include Candy
|
7
7
|
end
|
8
8
|
|
9
|
+
before(:all) do
|
10
|
+
@verifier = Zagnut.collection
|
11
|
+
end
|
12
|
+
|
9
13
|
before(:each) do
|
10
14
|
@this = Zagnut.new
|
11
15
|
end
|
12
16
|
|
17
|
+
|
13
18
|
it "inserts a document immediately" do
|
14
19
|
@this.id.should be_a(Mongo::ObjectID)
|
15
20
|
end
|
21
|
+
|
22
|
+
it "saves any attribute it doesn't already handle to the database" do
|
23
|
+
@this.bite = "Tasty!"
|
24
|
+
@verifier.find_one["bite"].should == "Tasty!"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "retrieves any attribute it doesn't already know about from the database" do
|
28
|
+
@verifier.update({:_id => @this.id}, {:chew => "Yummy!", :bite => "Ouch."})
|
29
|
+
@this.chew.should == "Yummy!"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can roundtrip effectively" do
|
33
|
+
@this.swallow = "Gulp."
|
34
|
+
@this.swallow.should == "Gulp."
|
35
|
+
end
|
36
|
+
|
37
|
+
it "handles missing attributes gracefully" do
|
38
|
+
@this.licks.should == nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "allows multiple attributes to be set" do
|
42
|
+
@this.licks = 7
|
43
|
+
@this.center = 0.5
|
44
|
+
@this.licks.should == 7
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "retrieval" do
|
48
|
+
it "can find a record by its ID" do
|
49
|
+
@this.licks = 10
|
50
|
+
that = Zagnut.find(@this.id)
|
51
|
+
that.licks.should == 10
|
52
|
+
end
|
53
|
+
|
54
|
+
it "roundtrips across identical objects" do
|
55
|
+
that = Zagnut.find(@this.id)
|
56
|
+
@this.calories = 7500
|
57
|
+
that.calories.should == 7500
|
58
|
+
end
|
59
|
+
|
60
|
+
it "returns nil on an object that can't be found" do
|
61
|
+
id = Mongo::ObjectID.new
|
62
|
+
Zagnut.find(id).should be_nil
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can get a single object by attributes" do
|
66
|
+
@this.pieces = 7.5
|
67
|
+
@this.color = "red"
|
68
|
+
that = Zagnut.first("pieces" => 7.5)
|
69
|
+
that.color.should == "red"
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns nil if a first object can't be found" do
|
73
|
+
@this.pieces = 11
|
74
|
+
Zagnut.first("pieces" => 5).should be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "collections" do
|
79
|
+
before(:each) do
|
80
|
+
@this.color = "red"
|
81
|
+
@this.weight = 11.8
|
82
|
+
@that = Zagnut.new
|
83
|
+
@that.color = "red"
|
84
|
+
@that.pieces = 6
|
85
|
+
@that.weight = -5
|
86
|
+
@the_other = Zagnut.new
|
87
|
+
@the_other.color = "blue"
|
88
|
+
@the_other.pieces = 7
|
89
|
+
@the_other.weight = 0
|
90
|
+
end
|
91
|
+
|
92
|
+
it "can get all objects in a collection" do
|
93
|
+
those = Zagnut.all
|
94
|
+
those.count.should == 3
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can get all objects matching a search condition" do
|
98
|
+
those = Zagnut.all(:color => "red")
|
99
|
+
those.count.should == 2
|
100
|
+
end
|
101
|
+
|
102
|
+
it "still returns if nothing matches" do
|
103
|
+
Zagnut.all(:color => "green").to_a.should == []
|
104
|
+
end
|
105
|
+
|
106
|
+
it "can take options" do
|
107
|
+
those = Zagnut.all(:color => "red", :sort => ["weight", :asc])
|
108
|
+
those.collect{|z| z.weight}.should == [-5, 11.8]
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
after(:each) do
|
115
|
+
Zagnut.collection.remove
|
116
|
+
end
|
16
117
|
|
17
118
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: candy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Eley
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- README.rdoc
|
76
76
|
- Rakefile
|
77
77
|
- VERSION
|
78
|
+
- candy.gemspec
|
78
79
|
- lib/candy.rb
|
79
80
|
- lib/candy/crunch.rb
|
80
81
|
- lib/candy/exceptions.rb
|