mongo_odm 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -0
- data/LICENSE +20 -0
- data/README.rdoc +251 -0
- data/Rakefile +70 -0
- data/lib/mongo_odm.rb +48 -0
- data/lib/mongo_odm/collection.rb +42 -0
- data/lib/mongo_odm/core_ext/conversions.rb +262 -0
- data/lib/mongo_odm/cursor.rb +11 -0
- data/lib/mongo_odm/document.rb +34 -0
- data/lib/mongo_odm/document/associations.rb +39 -0
- data/lib/mongo_odm/document/associations/has_many.rb +19 -0
- data/lib/mongo_odm/document/associations/has_one.rb +19 -0
- data/lib/mongo_odm/document/attribute_methods.rb +103 -0
- data/lib/mongo_odm/document/attribute_methods/dirty.rb +51 -0
- data/lib/mongo_odm/document/attribute_methods/localization.rb +67 -0
- data/lib/mongo_odm/document/attribute_methods/query.rb +35 -0
- data/lib/mongo_odm/document/attribute_methods/read.rb +39 -0
- data/lib/mongo_odm/document/attribute_methods/write.rb +37 -0
- data/lib/mongo_odm/document/callbacks.rb +29 -0
- data/lib/mongo_odm/document/fields.rb +66 -0
- data/lib/mongo_odm/document/inspect.rb +28 -0
- data/lib/mongo_odm/document/persistence.rb +96 -0
- data/lib/mongo_odm/document/validations.rb +46 -0
- data/lib/mongo_odm/errors.rb +18 -0
- data/lib/mongo_odm/version.rb +7 -0
- data/spec/models/00-blank_slate.rb +4 -0
- data/spec/models/01-shape.rb +8 -0
- data/spec/models/02-circle.rb +7 -0
- data/spec/models/03-big_red_circle.rb +8 -0
- data/spec/mongo_odm/core_ext/conversions_spec.rb +423 -0
- data/spec/mongo_odm/document/fields_spec.rb +187 -0
- data/spec/mongo_odm/document/inspect_spec.rb +19 -0
- data/spec/mongo_odm/document_spec.rb +12 -0
- data/spec/mongo_odm/mongo_odm_spec.rb +84 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +12 -0
- metadata +185 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'active_support/core_ext/big_decimal'
|
3
|
+
require 'active_support/core_ext/date/conversions'
|
4
|
+
require 'active_support/core_ext/date_time/calculations'
|
5
|
+
require 'active_support/core_ext/date_time/conversions'
|
6
|
+
require 'active_support/core_ext/string/conversions'
|
7
|
+
require 'active_support/core_ext/time/conversions'
|
8
|
+
|
9
|
+
# The type_cast class method is called when:
|
10
|
+
# * An attribute of the same class is assigned
|
11
|
+
# * An attribute of the same class is readed from the Mongo driver
|
12
|
+
#
|
13
|
+
# The to_mongo instance method is called when:
|
14
|
+
# * An attribute of the same class is sent to the Mongo driver
|
15
|
+
|
16
|
+
# @private
|
17
|
+
class Mongo::ObjectID
|
18
|
+
def self.type_cast(value)
|
19
|
+
return nil if value.nil?
|
20
|
+
return value if value.is_a?(Mongo::ObjectID)
|
21
|
+
value.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_mongo
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @private
|
30
|
+
class BSON::ObjectID
|
31
|
+
def self.type_cast(value)
|
32
|
+
return nil if value.nil?
|
33
|
+
return value if value.is_a?(BSON::ObjectID)
|
34
|
+
value.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_mongo
|
38
|
+
self
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
class Array
|
44
|
+
def self.type_cast(value)
|
45
|
+
return nil if value.nil?
|
46
|
+
value.to_a.map {|elem| MongoODM.instanciate(elem)}
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_mongo
|
50
|
+
self.map {|elem| elem.to_mongo}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @private
|
55
|
+
class Class
|
56
|
+
def self.type_cast(value)
|
57
|
+
return nil if value.nil?
|
58
|
+
value.to_s.constantize
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_mongo
|
62
|
+
self.name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @private
|
67
|
+
class Symbol
|
68
|
+
def self.type_cast(value)
|
69
|
+
return nil if value.nil?
|
70
|
+
value.to_s.intern
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_mongo
|
74
|
+
self
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @private
|
79
|
+
class Integer
|
80
|
+
def self.type_cast(value)
|
81
|
+
return nil if value.nil?
|
82
|
+
value.to_i
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_mongo
|
86
|
+
self
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @private
|
91
|
+
class Float
|
92
|
+
def self.type_cast(value)
|
93
|
+
return nil if value.nil?
|
94
|
+
value.to_f
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_mongo
|
98
|
+
self
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @private
|
103
|
+
class BigDecimal
|
104
|
+
def self.type_cast(value)
|
105
|
+
return nil if value.nil?
|
106
|
+
value.is_a?(BigDecimal) ? value : new(value.to_s)
|
107
|
+
end
|
108
|
+
|
109
|
+
def to_mongo
|
110
|
+
self.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# @private
|
115
|
+
class String
|
116
|
+
def self.type_cast(value)
|
117
|
+
return nil if value.nil?
|
118
|
+
value.to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
def to_mongo
|
122
|
+
self
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# @private
|
127
|
+
class Date
|
128
|
+
def self.type_cast(value)
|
129
|
+
return nil if value.nil?
|
130
|
+
value.to_date
|
131
|
+
end
|
132
|
+
|
133
|
+
def to_mongo
|
134
|
+
Time.utc(self.year, self.month, self.day)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# @private
|
139
|
+
class DateTime
|
140
|
+
def self.type_cast(value)
|
141
|
+
return nil if value.nil?
|
142
|
+
value.to_datetime
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_mongo
|
146
|
+
datetime = self.utc
|
147
|
+
Time.utc(datetime.year, datetime.month, datetime.day, datetime.hour, datetime.min, datetime.sec)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @private
|
152
|
+
class TrueClass
|
153
|
+
def self.type_cast(value)
|
154
|
+
return nil if value.nil?
|
155
|
+
true
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_mongo
|
159
|
+
self
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# @private
|
164
|
+
class FalseClass
|
165
|
+
def self.type_cast(value)
|
166
|
+
return nil if value.nil?
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_mongo
|
171
|
+
self
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# @private
|
176
|
+
class Time
|
177
|
+
def self.type_cast(value)
|
178
|
+
return nil if value.nil?
|
179
|
+
value.to_time
|
180
|
+
end
|
181
|
+
|
182
|
+
def to_mongo
|
183
|
+
self.utc
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Stand-in for true/false property types.
|
188
|
+
# @private
|
189
|
+
module Boolean
|
190
|
+
def self.type_cast(value)
|
191
|
+
case value
|
192
|
+
when NilClass
|
193
|
+
nil
|
194
|
+
when Numeric
|
195
|
+
!value.zero?
|
196
|
+
when TrueClass, FalseClass
|
197
|
+
value
|
198
|
+
when /^\s*t/i
|
199
|
+
true
|
200
|
+
when /^\s*f/i
|
201
|
+
false
|
202
|
+
else
|
203
|
+
value.present?
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# @private
|
209
|
+
class Numeric
|
210
|
+
def self.type_cast(value)
|
211
|
+
return nil if value.nil?
|
212
|
+
float_value = value.to_f
|
213
|
+
int_value = value.to_i
|
214
|
+
float_value == int_value ? int_value : float_value
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# @private
|
219
|
+
class Hash
|
220
|
+
def self.type_cast(value)
|
221
|
+
return nil if value.nil?
|
222
|
+
Hash[value.to_hash.map{|k,v| [MongoODM.instanciate(k), MongoODM.instanciate(v)]}]
|
223
|
+
end
|
224
|
+
|
225
|
+
def to_mongo
|
226
|
+
Hash[self.map{|k,v| [k.to_mongo, v.to_mongo]}]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# @private
|
231
|
+
class HashWithIndifferentAccess
|
232
|
+
def self.type_cast(value)
|
233
|
+
Hash.type_cast(value).with_indifferent_access
|
234
|
+
end
|
235
|
+
|
236
|
+
def to_mongo
|
237
|
+
Hash[self.map{|k,v| [k.to_mongo, v.to_mongo]}]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# @private
|
242
|
+
class Regexp
|
243
|
+
def self.type_cast(value)
|
244
|
+
return nil if value.nil?
|
245
|
+
new(value)
|
246
|
+
end
|
247
|
+
|
248
|
+
def to_mongo
|
249
|
+
self
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
# @private
|
254
|
+
class NilClass
|
255
|
+
def self.type_cast(value)
|
256
|
+
nil
|
257
|
+
end
|
258
|
+
|
259
|
+
def to_mongo
|
260
|
+
nil
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'mongo_odm'
|
3
|
+
|
4
|
+
module MongoODM
|
5
|
+
module Document
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
autoload :Associations
|
11
|
+
autoload :AttributeMethods
|
12
|
+
autoload :Callbacks
|
13
|
+
autoload :Fields
|
14
|
+
autoload :Inspect
|
15
|
+
autoload :Persistence
|
16
|
+
autoload :Validations
|
17
|
+
|
18
|
+
included do
|
19
|
+
include ActiveModel::Conversion
|
20
|
+
include ActiveModel::Serializers::JSON
|
21
|
+
include ActiveModel::Serializers::Xml
|
22
|
+
include ActiveModel::Observing
|
23
|
+
include ActiveModel::Translation
|
24
|
+
include MongoODM::Document::Persistence
|
25
|
+
include MongoODM::Document::AttributeMethods
|
26
|
+
include MongoODM::Document::Fields
|
27
|
+
include MongoODM::Document::Inspect
|
28
|
+
include MongoODM::Document::Associations
|
29
|
+
include MongoODM::Document::Callbacks
|
30
|
+
include MongoODM::Document::Validations
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module MongoODM
|
4
|
+
module Document
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
autoload :HasOne
|
11
|
+
autoload :HasMany
|
12
|
+
|
13
|
+
included do
|
14
|
+
class_inheritable_accessor :associations
|
15
|
+
self.associations = {}
|
16
|
+
|
17
|
+
include MongoODM::Document::Associations::HasOne
|
18
|
+
include MongoODM::Document::Associations::HasMany
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
def associations
|
23
|
+
self.class.associations
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def associate(type, options)
|
29
|
+
name = options.name.to_s
|
30
|
+
associations[name] = MetaData.new(type, options)
|
31
|
+
define_method(name) { memoized(name) { type.instantiate(self, options) } }
|
32
|
+
define_method("#{name}=") { |object| reset(name) { type.update(object, self, options) } }
|
33
|
+
end
|
34
|
+
protected :associate
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
|
4
|
+
module MongoODM
|
5
|
+
module Document
|
6
|
+
module AttributeMethods
|
7
|
+
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :Read
|
12
|
+
autoload :Write
|
13
|
+
autoload :Query
|
14
|
+
autoload :Dirty
|
15
|
+
autoload :Localization
|
16
|
+
|
17
|
+
included do
|
18
|
+
include ActiveModel::AttributeMethods
|
19
|
+
include MongoODM::Document::AttributeMethods::Read
|
20
|
+
include MongoODM::Document::AttributeMethods::Write
|
21
|
+
include MongoODM::Document::AttributeMethods::Query
|
22
|
+
include MongoODM::Document::AttributeMethods::Dirty
|
23
|
+
include MongoODM::Document::AttributeMethods::Localization
|
24
|
+
end
|
25
|
+
|
26
|
+
module InstanceMethods
|
27
|
+
attr_reader :attributes
|
28
|
+
|
29
|
+
def initialize(attrs = {})
|
30
|
+
@attributes = {:_class => self.class.name}.with_indifferent_access
|
31
|
+
load_attributes_or_defaults(attrs)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def force_attributes=(new_attributes)
|
36
|
+
send(:attributes=, new_attributes, true)
|
37
|
+
end
|
38
|
+
|
39
|
+
def attributes=(new_attributes, auto_generate_attributes = false)
|
40
|
+
return if new_attributes.blank?
|
41
|
+
new_attributes.each do |name, value|
|
42
|
+
if respond_to?(:"#{name}=")
|
43
|
+
send(:"#{name}=", value)
|
44
|
+
else
|
45
|
+
auto_generate_attributes ? write_attribute(name, value) : raise(MongoODM::Errors::UnknownFieldError, "unknown field: #{name}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def freeze
|
51
|
+
@attributes.freeze; super
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_attribute?(name)
|
55
|
+
@attributes.has_key?(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_attributes_or_defaults(attrs)
|
59
|
+
attrs = self.class.default_attributes.merge(attrs)
|
60
|
+
self.force_attributes = attrs
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_attribute(name)
|
64
|
+
@attributes.delete(name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(method_id, *args, &block)
|
68
|
+
# If we haven't generated any methods yet, generate them, then
|
69
|
+
# see if we've created the method we're looking for.
|
70
|
+
if !self.class.attribute_methods_generated?
|
71
|
+
self.class.define_attribute_methods_for_fields
|
72
|
+
method_name = method_id.to_s
|
73
|
+
guard_private_attribute_method!(method_name, args)
|
74
|
+
send(method_id, *args, &block)
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond_to?(*args)
|
81
|
+
self.class.define_attribute_methods_for_fields
|
82
|
+
super
|
83
|
+
end
|
84
|
+
|
85
|
+
def attribute_method?(attr_name)
|
86
|
+
attr_name == '_id' || attributes.include?(attr_name)
|
87
|
+
end
|
88
|
+
protected :attribute_method?
|
89
|
+
end
|
90
|
+
|
91
|
+
module ClassMethods
|
92
|
+
def default_attributes
|
93
|
+
HashWithIndifferentAccess[fields.values.map{|field| [field.name, field.default]}]
|
94
|
+
end
|
95
|
+
|
96
|
+
def define_attribute_methods_for_fields
|
97
|
+
define_attribute_methods(fields.keys + [:_id, :_class])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|