mongo_persist 0.0.2 → 0.1.0
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/Rakefile +2 -0
- data/VERSION +1 -1
- data/examples/only.rb +57 -0
- data/lib/mongo_persist.rb +2 -131
- data/lib/mongo_persist/array_ext.rb +14 -0
- data/lib/mongo_persist/base.rb +83 -0
- data/lib/mongo_persist/core_ext.rb +57 -0
- data/lib/mongo_persist/hash_ext.rb +34 -0
- data/lib/mongo_persist/mongo_ext.rb +23 -0
- data/lib/mongo_persist/util.rb +8 -0
- data/mongo_persist.gemspec +18 -4
- data/spec/mocks.rb +48 -0
- data/spec/mongo_persist_spec.rb +101 -2
- data/spec/spec_helper.rb +11 -1
- metadata +30 -2
data/Rakefile
CHANGED
@@ -11,8 +11,10 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/mharris717/mongo_persist"
|
12
12
|
gem.authors = ["Mike Harris"]
|
13
13
|
gem.add_development_dependency "rspec"
|
14
|
+
gem.add_development_dependency 'rr'
|
14
15
|
gem.add_dependency 'fattr'
|
15
16
|
gem.add_dependency 'activesupport'
|
17
|
+
gem.add_dependency 'mharris_ext'
|
16
18
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
19
|
end
|
18
20
|
Jeweler::GemcutterTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
data/examples/only.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'mongo'
|
3
|
+
require 'mongo_persist'
|
4
|
+
|
5
|
+
def db
|
6
|
+
Mongo::Connection.new.db('test-db')
|
7
|
+
end
|
8
|
+
|
9
|
+
class Order
|
10
|
+
include MongoPersist
|
11
|
+
attr_accessor :po_number
|
12
|
+
fattr(:order_products) { [] }
|
13
|
+
def products
|
14
|
+
order_products.map { |x| x.product }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class OrderProduct
|
19
|
+
include MongoPersist
|
20
|
+
attr_accessor :unit_price, :quantity, :product
|
21
|
+
|
22
|
+
# Store a reference to objects for this attribute, not the entire object.
|
23
|
+
mongo_reference_attributes ['product']
|
24
|
+
end
|
25
|
+
|
26
|
+
class Product
|
27
|
+
include MongoPersist
|
28
|
+
attr_accessor :name
|
29
|
+
end
|
30
|
+
|
31
|
+
products = [Product.new(:name => 'Leather Couch'),Product.new(:name => 'Maroon Chair')].each { |x| x.mongo.save! }
|
32
|
+
|
33
|
+
orders = []
|
34
|
+
orders << Order.new(:po_number => 1234,
|
35
|
+
:order_products => [OrderProduct.new(:unit_price => 1000, :quantity => 1, :product => products[0])]).mongo.save!
|
36
|
+
orders << Order.new(:po_number => 1235,
|
37
|
+
:order_products => [OrderProduct.new(:unit_price => 200, :quantity => 2, :product => products[1])]).mongo.save!
|
38
|
+
|
39
|
+
# objects are saved to MongoDB as JSON objects
|
40
|
+
|
41
|
+
# get all order objects back from Mongo
|
42
|
+
# you get back the ruby objects you put in, not raw JSON objects
|
43
|
+
Order.collection.find_objects
|
44
|
+
|
45
|
+
# Since on OrderProduct, the product attribute was marked as a reference attribute,
|
46
|
+
# the product is stored in MongoDB only as a reference to the product obj
|
47
|
+
#
|
48
|
+
# When you read the Order/OrderProduct back out, MongoPersist takes care of
|
49
|
+
# fetching the Product object again. You don't have to do anything.
|
50
|
+
Order.collection.find_one_object.products.first # An object of class Product
|
51
|
+
Order.collection.find_one_object.products.first.name # Leather Couch
|
52
|
+
|
53
|
+
# Because the product is stored as a reference, if you update that product
|
54
|
+
# elsewhere and save to Mongo, later reads of Orders with that product will be correctly updated
|
55
|
+
products[0].name = 'White Leather Couch'
|
56
|
+
products[0].mongo.save!
|
57
|
+
Order.collection.find_one_object.products.first.name # White Leather Couch
|
data/lib/mongo_persist.rb
CHANGED
@@ -1,137 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'mongo'
|
3
|
-
require '
|
3
|
+
require 'active_support'
|
4
4
|
require 'fattr'
|
5
|
-
require File.dirname(__FILE__) + "/mongo_persist
|
5
|
+
%w(util core_ext array_ext hash_ext base mongo_ext).each { |x| require File.dirname(__FILE__) + "/mongo_persist/#{x}" }
|
6
6
|
require 'andand'
|
7
7
|
|
8
|
-
class Object
|
9
|
-
def safe_to_mongo_hash
|
10
|
-
sos(:to_mongo_hash)
|
11
|
-
end
|
12
|
-
def safe_to_mongo_object
|
13
|
-
sos(:to_mongo_object)
|
14
|
-
end
|
15
|
-
end
|
16
8
|
|
17
|
-
class Array
|
18
|
-
def to_mongo_hash
|
19
|
-
map { |x| x.safe_to_mongo_hash }
|
20
|
-
end
|
21
|
-
def to_mongo_object
|
22
|
-
map { |x| x.safe_to_mongo_object }
|
23
|
-
end
|
24
|
-
def to_mongo_ref_hash
|
25
|
-
map { |x| x.to_mongo_ref_hash }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Hash
|
30
|
-
def get_mongo_id
|
31
|
-
['_id','mongo_id'].each { |x| return self[x] if self[x] }
|
32
|
-
nil
|
33
|
-
end
|
34
|
-
def naked_reference?
|
35
|
-
(keys - ['_id','_mongo_class','mongo_id']).empty? && !!get_mongo_id
|
36
|
-
end
|
37
|
-
def mongo_class
|
38
|
-
self['_mongo_class'] ? eval(self['_mongo_class']) : nil
|
39
|
-
end
|
40
|
-
def to_mongo_hash_for_obj
|
41
|
-
h = reject { |k,v| k == '_mongo_class' }
|
42
|
-
h['mongo_id'] = h['_id'] if h['_id']
|
43
|
-
h.reject { |k,v| k == '_id' }
|
44
|
-
end
|
45
|
-
def to_mongo_object
|
46
|
-
return map_value { |v| v.safe_to_mongo_object } unless mongo_class
|
47
|
-
if naked_reference?
|
48
|
-
mongo_class.collection.find_one_object('_id' => get_mongo_id)
|
49
|
-
else
|
50
|
-
mongo_class.from_mongo_hash(to_mongo_hash_for_obj)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
def to_mongo_hash
|
54
|
-
res = {}
|
55
|
-
each { |k,v| res[k.safe_to_mongo_hash] = v.safe_to_mongo_hash }
|
56
|
-
res
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class MongoWrapper
|
61
|
-
attr_accessor :obj
|
62
|
-
include FromHash
|
63
|
-
def save!
|
64
|
-
if obj.mongo_id
|
65
|
-
obj.klass.collection.update({'_id' => obj.mongo_id},obj.to_mongo_hash)
|
66
|
-
else
|
67
|
-
obj.mongo_id = obj.class.collection.save(obj.to_mongo_hash)
|
68
|
-
end
|
69
|
-
obj
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
module MongoPersist
|
74
|
-
attr_accessor :mongo_id
|
75
|
-
#can be overriden by class. If not, assumes that all instance variables should be saved.
|
76
|
-
def mongo_attributes
|
77
|
-
instance_variables.map { |x| x[1..-1] } - ['mongo']
|
78
|
-
end
|
79
|
-
def mongo_child_attributes
|
80
|
-
mongo_attributes - self.class.mongo_reference_attributes
|
81
|
-
end
|
82
|
-
def to_mongo_ref_hash
|
83
|
-
{'_mongo_class' => klass.to_s, '_id' => mongo_id}
|
84
|
-
end
|
85
|
-
def to_mongo_hash
|
86
|
-
res = mongo_child_attributes.inject({}) { |h,attr| h.merge(attr => send(attr).safe_to_mongo_hash) }.merge("_mongo_class" => self.class.to_s)
|
87
|
-
klass.mongo_reference_attributes.each do |attr|
|
88
|
-
val = send(attr)
|
89
|
-
res[attr] = val.to_mongo_ref_hash if val
|
90
|
-
end
|
91
|
-
res
|
92
|
-
end
|
93
|
-
def from_mongo_hash(h)
|
94
|
-
h = h.map_value { |v| v.safe_to_mongo_object }
|
95
|
-
from_hash(h)
|
96
|
-
end
|
97
|
-
fattr(:mongo) { MongoWrapper.new(:obj => self) }
|
98
|
-
|
99
|
-
module ClassMethods
|
100
|
-
dsl_method(:mongo_reference_attributes) { [] }
|
101
|
-
def default_collection_name
|
102
|
-
to_s.downcase.pluralize
|
103
|
-
end
|
104
|
-
fattr(:collection) { db.collection(default_collection_name) }
|
105
|
-
def from_mongo_hash(h)
|
106
|
-
new.tap { |x| x.from_mongo_hash(h) }
|
107
|
-
end
|
108
|
-
def mongo_connection(ops)
|
109
|
-
ops.each { |k,v| send("#{k}=",v) }
|
110
|
-
end
|
111
|
-
end
|
112
|
-
def self.included(mod)
|
113
|
-
super(mod)
|
114
|
-
mod.send(:include,FromHash)
|
115
|
-
mod.send(:extend,ClassMethods)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
class ObjCursor
|
120
|
-
attr_accessor :cursor
|
121
|
-
include FromHash
|
122
|
-
def each
|
123
|
-
cursor.each { |x| yield(x.to_mongo_object) }
|
124
|
-
end
|
125
|
-
def count
|
126
|
-
cursor.count
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
class Mongo::Collection
|
131
|
-
def find_objects(*args)
|
132
|
-
ObjCursor.new(:cursor => find(*args))
|
133
|
-
end
|
134
|
-
def find_one_object(*args)
|
135
|
-
find_one(*args).to_mongo_object
|
136
|
-
end
|
137
|
-
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Array
|
2
|
+
def to_mongo_hash
|
3
|
+
map { |x| x.safe_to_mongo_hash }
|
4
|
+
end
|
5
|
+
def to_mongo_object
|
6
|
+
map { |x| x.safe_to_mongo_object }
|
7
|
+
end
|
8
|
+
def to_mongo_ref_hash
|
9
|
+
map { |x| x.to_mongo_ref_hash }
|
10
|
+
end
|
11
|
+
def can_mongo_convert?
|
12
|
+
all? { |x| x.can_mongo_convert? && x }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class MongoWrapper
|
2
|
+
attr_accessor :obj
|
3
|
+
include FromHash
|
4
|
+
def save!
|
5
|
+
if obj.mongo_id
|
6
|
+
obj.klass.collection.update({'_id' => obj.mongo_id},obj.to_mongo_hash)
|
7
|
+
else
|
8
|
+
obj.mongo_id = obj.class.collection.save(obj.to_mongo_hash)
|
9
|
+
end
|
10
|
+
obj
|
11
|
+
rescue => exp
|
12
|
+
require 'pp'
|
13
|
+
pp obj.to_mongo_hash
|
14
|
+
raise exp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module MongoPersist
|
19
|
+
NIL_OBJ = 99999
|
20
|
+
attr_accessor :mongo_id
|
21
|
+
#can be overriden by class. If not, assumes that all instance variables should be saved.
|
22
|
+
def mongo_attributes
|
23
|
+
(instance_variables.map { |x| x[1..-1] } - ['mongo','mongo_id']).select { |x| respond_to?(x) }
|
24
|
+
end
|
25
|
+
def mongo_addl_attributes
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
def mongo_child_attributes
|
29
|
+
(mongo_attributes - self.class.mongo_reference_attributes + mongo_addl_attributes).uniq
|
30
|
+
end
|
31
|
+
def to_mongo_ref_hash
|
32
|
+
{'_mongo_class' => klass.to_s, '_id' => mongo_id}
|
33
|
+
end
|
34
|
+
def new_hashx(attr,h,obj)
|
35
|
+
if obj.can_mongo_convert?
|
36
|
+
h.merge(attr => obj.to_mongo_hash)
|
37
|
+
else
|
38
|
+
h
|
39
|
+
end
|
40
|
+
rescue
|
41
|
+
return h
|
42
|
+
end
|
43
|
+
def to_mongo_hash
|
44
|
+
res = mongo_child_attributes.inject({}) do |h,attr|
|
45
|
+
obj = send(attr)
|
46
|
+
raise "#{attr} is nil" unless obj
|
47
|
+
new_hashx(attr,h,obj)
|
48
|
+
end.merge("_mongo_class" => self.class.to_s)
|
49
|
+
klass.mongo_reference_attributes.each do |attr|
|
50
|
+
val = send(attr)
|
51
|
+
res[attr] = val.to_mongo_ref_hash if val
|
52
|
+
end
|
53
|
+
res
|
54
|
+
end
|
55
|
+
def from_mongo_hash(h)
|
56
|
+
h = h.map_value { |v| v.safe_to_mongo_object }
|
57
|
+
from_hash(h)
|
58
|
+
end
|
59
|
+
fattr(:mongo) { MongoWrapper.new(:obj => self) }
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
dsl_method(:mongo_reference_attributes) { [] }
|
63
|
+
def default_collection_name
|
64
|
+
to_s.downcase.pluralize
|
65
|
+
end
|
66
|
+
fattr(:collection) { db.collection(default_collection_name) }
|
67
|
+
def new_with_nil_args
|
68
|
+
args = (1..(instance_method(:initialize).arity)).map { |x| nil }
|
69
|
+
new(*args)
|
70
|
+
end
|
71
|
+
def from_mongo_hash(h)
|
72
|
+
new_with_nil_args.tap { |x| x.from_mongo_hash(h) }
|
73
|
+
end
|
74
|
+
def mongo_connection(ops)
|
75
|
+
ops.each { |k,v| send("#{k}=",v) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
def self.included(mod)
|
79
|
+
super(mod)
|
80
|
+
mod.send(:include,FromHash)
|
81
|
+
mod.send(:extend,ClassMethods)
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class Object
|
2
|
+
def safe_to_mongo_hash
|
3
|
+
respond_to?(:to_mongo_hash) ? to_mongo_hash : nil
|
4
|
+
end
|
5
|
+
def safe_to_mongo_object
|
6
|
+
to_mongo_object
|
7
|
+
end
|
8
|
+
def ngil_obj?
|
9
|
+
self == MongoPersist::NIL_OBJ
|
10
|
+
end
|
11
|
+
def can_mongo_convert?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module BaseObjects
|
17
|
+
def to_mongo_hash
|
18
|
+
self
|
19
|
+
end
|
20
|
+
def to_mongo_object
|
21
|
+
self
|
22
|
+
end
|
23
|
+
def can_mongo_convert?
|
24
|
+
true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[Numeric,Symbol,String,Mongo::ObjectID,TrueClass,FalseClass].each do |cls|
|
29
|
+
cls.send(:include,BaseObjects)
|
30
|
+
end
|
31
|
+
|
32
|
+
class NilClass
|
33
|
+
def to_mongo_object
|
34
|
+
self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Object
|
39
|
+
def to_mongo_key
|
40
|
+
self
|
41
|
+
end
|
42
|
+
def from_mongo_key
|
43
|
+
self
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Fixnum
|
48
|
+
def to_mongo_key
|
49
|
+
"#{self}-NUM"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class String
|
54
|
+
def from_mongo_key
|
55
|
+
(self =~ /^(.*)-NUM$/) ? $1.to_i : self
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module MongoHash
|
2
|
+
def get_mongo_id
|
3
|
+
['_id','mongo_id'].each { |x| return self[x] if self[x] }
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
def naked_reference?
|
7
|
+
(keys - ['_id','_mongo_class','mongo_id']).empty? && !!get_mongo_id
|
8
|
+
end
|
9
|
+
def mongo_class
|
10
|
+
self['_mongo_class'] ? eval(self['_mongo_class']) : nil
|
11
|
+
end
|
12
|
+
def to_mongo_hash_for_obj
|
13
|
+
h = reject { |k,v| k == '_mongo_class' }
|
14
|
+
h['mongo_id'] = h['_id'] if h['_id']
|
15
|
+
h.reject { |k,v| k == '_id' }
|
16
|
+
end
|
17
|
+
def to_mongo_object
|
18
|
+
return map_value { |v| v.safe_to_mongo_object }.map_key { |k| k.from_mongo_key } unless mongo_class
|
19
|
+
if naked_reference?
|
20
|
+
mongo_class.collection.find_one_object('_id' => get_mongo_id)
|
21
|
+
else
|
22
|
+
mongo_class.from_mongo_hash(to_mongo_hash_for_obj)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
def to_mongo_hash
|
26
|
+
res = {}
|
27
|
+
each { |k,v| res[k.safe_to_mongo_hash.to_mongo_key] = v.safe_to_mongo_hash }
|
28
|
+
res
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Hash
|
33
|
+
include MongoHash
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class ObjCursor
|
2
|
+
attr_accessor :cursor
|
3
|
+
include FromHash
|
4
|
+
def each
|
5
|
+
cursor.each { |x| yield(x.to_mongo_object) }
|
6
|
+
end
|
7
|
+
def count
|
8
|
+
cursor.count
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module MongoPersistCollection
|
13
|
+
def find_objects(*args)
|
14
|
+
ObjCursor.new(:cursor => find(*args))
|
15
|
+
end
|
16
|
+
def find_one_object(*args)
|
17
|
+
find_one(*args).to_mongo_object
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mongo::Collection
|
22
|
+
include MongoPersistCollection
|
23
|
+
end
|
data/lib/mongo_persist/util.rb
CHANGED
@@ -44,6 +44,9 @@ module FromHash
|
|
44
44
|
end
|
45
45
|
def initialize(ops={})
|
46
46
|
from_hash(ops)
|
47
|
+
after_initialize
|
48
|
+
end
|
49
|
+
def after_initialize
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -53,4 +56,9 @@ class Hash
|
|
53
56
|
each { |k,v| res[k] = yield(v) }
|
54
57
|
res
|
55
58
|
end
|
59
|
+
def map_key
|
60
|
+
res = {}
|
61
|
+
each { |k,v| res[yield(k)] = v }
|
62
|
+
res
|
63
|
+
end
|
56
64
|
end
|
data/mongo_persist.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mongo_persist}
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Mike Harris"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-12-17}
|
13
13
|
s.description = %q{Library to add MongoDB Persistance to normal Ruby objects}
|
14
14
|
s.email = %q{mharris717@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,9 +24,15 @@ Gem::Specification.new do |s|
|
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
26
|
"lib/mongo_persist.rb",
|
27
|
+
"lib/mongo_persist/array_ext.rb",
|
28
|
+
"lib/mongo_persist/base.rb",
|
29
|
+
"lib/mongo_persist/core_ext.rb",
|
30
|
+
"lib/mongo_persist/hash_ext.rb",
|
31
|
+
"lib/mongo_persist/mongo_ext.rb",
|
27
32
|
"lib/mongo_persist/sandbox.rb",
|
28
33
|
"lib/mongo_persist/util.rb",
|
29
34
|
"mongo_persist.gemspec",
|
35
|
+
"spec/mocks.rb",
|
30
36
|
"spec/mongo_persist_spec.rb",
|
31
37
|
"spec/spec_helper.rb"
|
32
38
|
]
|
@@ -36,8 +42,10 @@ Gem::Specification.new do |s|
|
|
36
42
|
s.rubygems_version = %q{1.3.5}
|
37
43
|
s.summary = %q{Library to add MongoDB Persistance to normal Ruby objects}
|
38
44
|
s.test_files = [
|
39
|
-
"spec/
|
40
|
-
"spec/
|
45
|
+
"spec/mocks.rb",
|
46
|
+
"spec/mongo_persist_spec.rb",
|
47
|
+
"spec/spec_helper.rb",
|
48
|
+
"examples/only.rb"
|
41
49
|
]
|
42
50
|
|
43
51
|
if s.respond_to? :specification_version then
|
@@ -46,16 +54,22 @@ Gem::Specification.new do |s|
|
|
46
54
|
|
47
55
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
48
56
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
57
|
+
s.add_development_dependency(%q<rr>, [">= 0"])
|
49
58
|
s.add_runtime_dependency(%q<fattr>, [">= 0"])
|
50
59
|
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<mharris_ext>, [">= 0"])
|
51
61
|
else
|
52
62
|
s.add_dependency(%q<rspec>, [">= 0"])
|
63
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
53
64
|
s.add_dependency(%q<fattr>, [">= 0"])
|
54
65
|
s.add_dependency(%q<activesupport>, [">= 0"])
|
66
|
+
s.add_dependency(%q<mharris_ext>, [">= 0"])
|
55
67
|
end
|
56
68
|
else
|
57
69
|
s.add_dependency(%q<rspec>, [">= 0"])
|
70
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
58
71
|
s.add_dependency(%q<fattr>, [">= 0"])
|
59
72
|
s.add_dependency(%q<activesupport>, [">= 0"])
|
73
|
+
s.add_dependency(%q<mharris_ext>, [">= 0"])
|
60
74
|
end
|
61
75
|
end
|
data/spec/mocks.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
class MockDB
|
2
|
+
def collection(name)
|
3
|
+
MockCollection.new(:name => name)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class MockCursor
|
8
|
+
include Enumerable
|
9
|
+
attr_accessor :objs
|
10
|
+
include FromHash
|
11
|
+
def each(&b)
|
12
|
+
objs.each(&b)
|
13
|
+
end
|
14
|
+
def count
|
15
|
+
objs.size
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class MockCollection
|
20
|
+
include MongoPersistCollection
|
21
|
+
attr_accessor :name
|
22
|
+
include FromHash
|
23
|
+
fattr(:objs) { {} }
|
24
|
+
def remove
|
25
|
+
objs!
|
26
|
+
end
|
27
|
+
def save(doc)
|
28
|
+
doc['_id'] = rand(10000000000)
|
29
|
+
objs[doc['_id']] = doc
|
30
|
+
doc['_id']
|
31
|
+
end
|
32
|
+
def update(ops,new_doc)
|
33
|
+
find_raw(ops).each do |doc|
|
34
|
+
objs[doc['_id']] = new_doc.merge('_id' => doc['_id'])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
def find_raw(ops={})
|
38
|
+
objs.values.select do |h|
|
39
|
+
ops.all? { |k,v| v === h[k.to_s] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
def find(ops={})
|
43
|
+
MockCursor.new(:objs => find_raw(ops))
|
44
|
+
end
|
45
|
+
def find_one(ops={})
|
46
|
+
find_raw(ops).first
|
47
|
+
end
|
48
|
+
end
|
data/spec/mongo_persist_spec.rb
CHANGED
@@ -1,7 +1,20 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
+
require_lib "/code/mongo_scope/lib/mongo_scope"
|
4
|
+
require 'mharris_ext'
|
5
|
+
|
6
|
+
def on_rcr?
|
7
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
8
|
+
!!(dir =~ /\/mnt\/repos/)#.tap { |x| puts "Dir #{dir} rcr? #{x}" }
|
9
|
+
end
|
10
|
+
|
3
11
|
def db
|
4
|
-
|
12
|
+
if on_rcr?
|
13
|
+
require File.dirname(__FILE__) + "/mocks"
|
14
|
+
MockDB.new
|
15
|
+
else
|
16
|
+
Mongo::Connection.new('98.129.37.34',27017).db('test-db')
|
17
|
+
end
|
5
18
|
end
|
6
19
|
|
7
20
|
class Order
|
@@ -36,6 +49,17 @@ class Customer
|
|
36
49
|
attr_accessor :email
|
37
50
|
end
|
38
51
|
|
52
|
+
class Foo
|
53
|
+
include MongoPersist
|
54
|
+
attr_accessor :bar
|
55
|
+
def initialize(b)
|
56
|
+
@bar = b
|
57
|
+
end
|
58
|
+
def self.fgrom_mongo_hash(ops)
|
59
|
+
new(nil).from_hash(ops)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
if true
|
39
63
|
# too many assertions per test, too many "this is how things should be before i check the point of this test" assertions
|
40
64
|
describe MongoPersist do
|
41
65
|
before do
|
@@ -84,6 +108,81 @@ describe MongoPersist do
|
|
84
108
|
o = Order.new(:po_number => 1, :some_hash => {'1' => Product.new(:name => 'Chair')})
|
85
109
|
o.mongo.save!
|
86
110
|
Order.collection.find_one_object(:po_number => 1).some_hash['1'].name.should == 'Chair'
|
87
|
-
Order.collection.find_one_object.subtotal.should == 1000
|
111
|
+
Order.collection.find_one_object(:po_number => 1234).subtotal.should == 1000
|
112
|
+
end
|
113
|
+
it 'saving reference hashes' do
|
114
|
+
p = Product.new(:name => 'Chair')
|
115
|
+
p.mongo.save!
|
116
|
+
o = Order.new(:po_number => 1, :some_hash => {'1' => p})
|
117
|
+
o.mongo.save!
|
118
|
+
Order.collection.find_one_object(:po_number => 1).some_hash['1'].name.should == 'Chair'
|
119
|
+
Order.collection.find_one_object(:po_number => 1234).subtotal.should == 1000
|
120
|
+
end
|
121
|
+
it 'obj with own constructor' do
|
122
|
+
Foo.collection.remove
|
123
|
+
Foo.new(14).mongo.save!
|
124
|
+
Foo.collection.find_one_object.bar.should == 14
|
125
|
+
end
|
126
|
+
it 'hash with number key' do
|
127
|
+
Foo.collection.remove
|
128
|
+
f = Foo.new(14)
|
129
|
+
f.bar = {1 => 2}
|
130
|
+
f.mongo.save!
|
131
|
+
Foo.collection.find_one_object.bar.keys.should == [1]
|
132
|
+
end
|
133
|
+
it 'grouping' do
|
134
|
+
if false; coll = db.collection('abc')
|
135
|
+
coll.remove
|
136
|
+
coll.save('a' => 'a', 'b' => 1)
|
137
|
+
coll.save("a" => 'a', 'b' => 3)
|
138
|
+
coll.save('a' => 'b', 'b' => 2)
|
139
|
+
|
140
|
+
# reduce_function = "function (obj, prev) { prev.count += obj.b; }"
|
141
|
+
# code = Mongo::Code.new(reduce_function)
|
142
|
+
# res = coll.group(['a'], {}, {"count" => 0},code)
|
143
|
+
|
144
|
+
res = coll.sum_by_raw(:key => 'a', :sum_field => 'b')
|
145
|
+
|
146
|
+
res.find { |x| x['a'] == 'a'}['count'].should == 4
|
147
|
+
res.find { |x| x['a'] == 'b'}['count'].should == 2
|
148
|
+
|
149
|
+
res = coll.sum_by(:key => 'a', :sum_field => 'b').should == {'a' => 4, 'b' => 2}; end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
describe "n+1" do
|
154
|
+
before do
|
155
|
+
$proxy = true
|
156
|
+
[Order,Product].each { |x| x.collection.remove }
|
157
|
+
@products = [Product.new(:name => 'Leather Couch'),Product.new(:name => 'Maroon Chair')].each { |x| x.mongo.save! }
|
158
|
+
#@products = (1..5000).map { |x| Product.new(:name => x.to_s) }
|
159
|
+
@products.each { |x| x.mongo.save! }
|
160
|
+
@order_products = @products.map { |x| OrderProduct.new(:product => x) }
|
161
|
+
@order = Order.new(:order_products => @order_products).mongo.save!
|
162
|
+
end
|
163
|
+
it 'loading products should only do 1 lookup' do
|
164
|
+
mock.proxy(Product.collection).find()
|
165
|
+
Order.collection.find_one_object.products.map { |x| x.name }.should == @products.map { |x| x.name }
|
166
|
+
end
|
167
|
+
it 'speed test' do
|
168
|
+
tm('with proxy') do
|
169
|
+
Order.collection.find_one_object.products.each { |x| x.name }
|
170
|
+
end
|
171
|
+
tm('without proxy') do
|
172
|
+
$proxy = false
|
173
|
+
Order.collection.find_one_object.products.each { |x| x.name }
|
174
|
+
end
|
88
175
|
end
|
89
176
|
end
|
177
|
+
|
178
|
+
class Mongo::Collection
|
179
|
+
def sum_by_raw(ops)
|
180
|
+
reduce_function = "function (obj, prev) { prev.count += (obj.#{ops[:sum_field]} ? obj.#{ops[:sum_field]} : 0); }"
|
181
|
+
code = Mongo::Code.new(reduce_function)
|
182
|
+
group([ops[:key]].flatten, {'a' => 'a'}, {"count" => 0},code)
|
183
|
+
end
|
184
|
+
def sum_by(ops)
|
185
|
+
sum_by_raw(ops).inject({}) { |h,a| k = ops[:key]; h.merge(a[k] => a['count'])}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -5,5 +5,15 @@ require 'spec'
|
|
5
5
|
require 'spec/autorun'
|
6
6
|
|
7
7
|
Spec::Runner.configure do |config|
|
8
|
-
|
8
|
+
config.mock_with :rr
|
9
9
|
end
|
10
|
+
|
11
|
+
def require_lib(path,name=nil)
|
12
|
+
name ||= path.split("/")[-1]
|
13
|
+
file = "#{path}.rb"
|
14
|
+
if FileTest.exists?(file)
|
15
|
+
require path
|
16
|
+
else
|
17
|
+
require name
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo_persist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Harris
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-17 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,6 +22,16 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: "0"
|
24
24
|
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rr
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: fattr
|
27
37
|
type: :runtime
|
@@ -42,6 +52,16 @@ dependencies:
|
|
42
52
|
- !ruby/object:Gem::Version
|
43
53
|
version: "0"
|
44
54
|
version:
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mharris_ext
|
57
|
+
type: :runtime
|
58
|
+
version_requirement:
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
45
65
|
description: Library to add MongoDB Persistance to normal Ruby objects
|
46
66
|
email: mharris717@gmail.com
|
47
67
|
executables: []
|
@@ -59,9 +79,15 @@ files:
|
|
59
79
|
- Rakefile
|
60
80
|
- VERSION
|
61
81
|
- lib/mongo_persist.rb
|
82
|
+
- lib/mongo_persist/array_ext.rb
|
83
|
+
- lib/mongo_persist/base.rb
|
84
|
+
- lib/mongo_persist/core_ext.rb
|
85
|
+
- lib/mongo_persist/hash_ext.rb
|
86
|
+
- lib/mongo_persist/mongo_ext.rb
|
62
87
|
- lib/mongo_persist/sandbox.rb
|
63
88
|
- lib/mongo_persist/util.rb
|
64
89
|
- mongo_persist.gemspec
|
90
|
+
- spec/mocks.rb
|
65
91
|
- spec/mongo_persist_spec.rb
|
66
92
|
- spec/spec_helper.rb
|
67
93
|
has_rdoc: true
|
@@ -93,5 +119,7 @@ signing_key:
|
|
93
119
|
specification_version: 3
|
94
120
|
summary: Library to add MongoDB Persistance to normal Ruby objects
|
95
121
|
test_files:
|
122
|
+
- spec/mocks.rb
|
96
123
|
- spec/mongo_persist_spec.rb
|
97
124
|
- spec/spec_helper.rb
|
125
|
+
- examples/only.rb
|