djsun-mongomapper 0.3.5.5 → 0.4.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +38 -38
- data/Rakefile +87 -73
- data/VERSION +1 -1
- data/lib/mongomapper.rb +67 -71
- data/lib/mongomapper/associations.rb +86 -84
- data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -34
- data/lib/mongomapper/associations/many_embedded_proxy.rb +67 -17
- data/lib/mongomapper/associations/proxy.rb +74 -73
- data/lib/mongomapper/document.rb +342 -348
- data/lib/mongomapper/embedded_document.rb +354 -274
- data/lib/mongomapper/finder_options.rb +84 -84
- data/lib/mongomapper/key.rb +32 -76
- data/lib/mongomapper/rails_compatibility/document.rb +14 -14
- data/lib/mongomapper/rails_compatibility/embedded_document.rb +26 -24
- data/lib/mongomapper/support.rb +156 -29
- data/lib/mongomapper/validations.rb +69 -47
- data/test/custom_matchers.rb +48 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -56
- data/test/functional/associations/test_belongs_to_proxy.rb +48 -49
- data/test/functional/associations/test_many_documents_as_proxy.rb +208 -253
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +130 -130
- data/test/functional/associations/test_many_embedded_proxy.rb +168 -106
- data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -262
- data/test/functional/test_binary.rb +21 -0
- data/test/functional/test_document.rb +946 -952
- data/test/functional/test_embedded_document.rb +98 -0
- data/test/functional/test_pagination.rb +87 -80
- data/test/functional/test_rails_compatibility.rb +29 -29
- data/test/functional/test_validations.rb +262 -172
- data/test/models.rb +169 -169
- data/test/test_helper.rb +28 -66
- data/test/unit/serializers/test_json_serializer.rb +193 -193
- data/test/unit/test_document.rb +161 -123
- data/test/unit/test_embedded_document.rb +643 -547
- data/test/unit/test_finder_options.rb +183 -183
- data/test/unit/test_key.rb +175 -247
- data/test/unit/test_rails_compatibility.rb +38 -33
- data/test/unit/test_serializations.rb +52 -52
- data/test/unit/test_support.rb +268 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +499 -258
- metadata +22 -12
- data/History +0 -76
- data/mongomapper.gemspec +0 -145
@@ -1,85 +1,85 @@
|
|
1
|
-
module MongoMapper
|
2
|
-
class FinderOptions
|
3
|
-
attr_reader :options
|
4
|
-
|
5
|
-
def self.to_mongo_criteria(conditions, parent_key=nil)
|
6
|
-
criteria = {}
|
7
|
-
conditions.each_pair do |field, value|
|
8
|
-
case value
|
9
|
-
when Array
|
10
|
-
operator_present = field.to_s =~ /^\$/
|
11
|
-
criteria[field] = if operator_present
|
12
|
-
value
|
13
|
-
else
|
14
|
-
{'$in' => value}
|
15
|
-
end
|
16
|
-
when Hash
|
17
|
-
criteria[field] = to_mongo_criteria(value, field)
|
18
|
-
else
|
19
|
-
criteria[field] = value
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
criteria
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.to_mongo_options(options)
|
27
|
-
options = options.dup
|
28
|
-
{
|
29
|
-
:fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
|
30
|
-
:
|
31
|
-
:limit => (options.delete(:limit) || 0).to_i,
|
32
|
-
:sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
|
33
|
-
}
|
34
|
-
end
|
35
|
-
|
36
|
-
def initialize(options)
|
37
|
-
raise ArgumentError, "FinderOptions must be a hash" unless options.is_a?(Hash)
|
38
|
-
@options = options.symbolize_keys
|
39
|
-
@conditions = @options.delete(:conditions) || {}
|
40
|
-
end
|
41
|
-
|
42
|
-
def criteria
|
43
|
-
self.class.to_mongo_criteria(@conditions)
|
44
|
-
end
|
45
|
-
|
46
|
-
def options
|
47
|
-
self.class.to_mongo_options(@options)
|
48
|
-
end
|
49
|
-
|
50
|
-
def to_a
|
51
|
-
[criteria, options]
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
def self.to_mongo_fields(fields)
|
56
|
-
return if fields.blank?
|
57
|
-
|
58
|
-
if fields.is_a?(String)
|
59
|
-
fields.split(',').map { |field| field.strip }
|
60
|
-
else
|
61
|
-
fields.flatten.compact
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def self.to_mongo_sort(sort)
|
66
|
-
return if sort.blank?
|
67
|
-
pieces = sort.split(',')
|
68
|
-
pairs = pieces.map { |s| to_mongo_sort_piece(s) }
|
69
|
-
|
70
|
-
hash = OrderedHash.new
|
71
|
-
pairs.each do |pair|
|
72
|
-
field, sort_direction = pair
|
73
|
-
hash[field] = sort_direction
|
74
|
-
end
|
75
|
-
hash.symbolize_keys
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.to_mongo_sort_piece(str)
|
79
|
-
field, direction = str.strip.split(' ')
|
80
|
-
direction ||= 'ASC'
|
81
|
-
direction = direction.upcase == 'ASC' ? 1 : -1
|
82
|
-
[field, direction]
|
83
|
-
end
|
84
|
-
end
|
1
|
+
module MongoMapper
|
2
|
+
class FinderOptions
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def self.to_mongo_criteria(conditions, parent_key=nil)
|
6
|
+
criteria = {}
|
7
|
+
conditions.each_pair do |field, value|
|
8
|
+
case value
|
9
|
+
when Array
|
10
|
+
operator_present = field.to_s =~ /^\$/
|
11
|
+
criteria[field] = if operator_present
|
12
|
+
value
|
13
|
+
else
|
14
|
+
{'$in' => value}
|
15
|
+
end
|
16
|
+
when Hash
|
17
|
+
criteria[field] = to_mongo_criteria(value, field)
|
18
|
+
else
|
19
|
+
criteria[field] = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
criteria
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.to_mongo_options(options)
|
27
|
+
options = options.dup
|
28
|
+
{
|
29
|
+
:fields => to_mongo_fields(options.delete(:fields) || options.delete(:select)),
|
30
|
+
:skip => (options.delete(:skip) || 0).to_i,
|
31
|
+
:limit => (options.delete(:limit) || 0).to_i,
|
32
|
+
:sort => options.delete(:sort) || to_mongo_sort(options.delete(:order))
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(options)
|
37
|
+
raise ArgumentError, "FinderOptions must be a hash" unless options.is_a?(Hash)
|
38
|
+
@options = options.symbolize_keys
|
39
|
+
@conditions = @options.delete(:conditions) || {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def criteria
|
43
|
+
self.class.to_mongo_criteria(@conditions)
|
44
|
+
end
|
45
|
+
|
46
|
+
def options
|
47
|
+
self.class.to_mongo_options(@options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_a
|
51
|
+
[criteria, options]
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def self.to_mongo_fields(fields)
|
56
|
+
return if fields.blank?
|
57
|
+
|
58
|
+
if fields.is_a?(String)
|
59
|
+
fields.split(',').map { |field| field.strip }
|
60
|
+
else
|
61
|
+
fields.flatten.compact
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.to_mongo_sort(sort)
|
66
|
+
return if sort.blank?
|
67
|
+
pieces = sort.split(',')
|
68
|
+
pairs = pieces.map { |s| to_mongo_sort_piece(s) }
|
69
|
+
|
70
|
+
hash = OrderedHash.new
|
71
|
+
pairs.each do |pair|
|
72
|
+
field, sort_direction = pair
|
73
|
+
hash[field] = sort_direction
|
74
|
+
end
|
75
|
+
hash.symbolize_keys
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.to_mongo_sort_piece(str)
|
79
|
+
field, direction = str.strip.split(' ')
|
80
|
+
direction ||= 'ASC'
|
81
|
+
direction = direction.upcase == 'ASC' ? 1 : -1
|
82
|
+
[field, direction]
|
83
|
+
end
|
84
|
+
end
|
85
85
|
end
|
data/lib/mongomapper/key.rb
CHANGED
@@ -1,76 +1,32 @@
|
|
1
|
-
module MongoMapper
|
2
|
-
class Key
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
if type == Array
|
34
|
-
value || []
|
35
|
-
elsif type == Hash
|
36
|
-
HashWithIndifferentAccess.new(value || {})
|
37
|
-
else
|
38
|
-
value
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
def typecast(value)
|
44
|
-
return value if type.nil?
|
45
|
-
return HashWithIndifferentAccess.new(value) if value.is_a?(Hash) && type == Hash
|
46
|
-
return value.utc if type == Time && value.kind_of?(type)
|
47
|
-
return value if value.kind_of?(type) || value.nil?
|
48
|
-
begin
|
49
|
-
if type == String then value.to_s
|
50
|
-
elsif type == Float then value.to_f
|
51
|
-
elsif type == Array then value.to_a
|
52
|
-
elsif type == Time then Time.parse(value.to_s).utc
|
53
|
-
elsif type == Boolean then Boolean.mm_typecast(value)
|
54
|
-
elsif type == Integer
|
55
|
-
# ganked from datamapper
|
56
|
-
value_to_i = value.to_i
|
57
|
-
if value_to_i == 0
|
58
|
-
value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
|
59
|
-
else
|
60
|
-
value_to_i
|
61
|
-
end
|
62
|
-
elsif embedded_document?
|
63
|
-
typecast_embedded_document(value)
|
64
|
-
else
|
65
|
-
value
|
66
|
-
end
|
67
|
-
rescue
|
68
|
-
value
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def typecast_embedded_document(value)
|
73
|
-
value.is_a?(type) ? value : type.new(value)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
1
|
+
module MongoMapper
|
2
|
+
class Key
|
3
|
+
attr_accessor :name, :type, :options, :default_value
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
options = args.extract_options!
|
7
|
+
@name, @type = args.shift.to_s, args.shift
|
8
|
+
self.options = (options || {}).symbolize_keys
|
9
|
+
self.default_value = self.options.delete(:default)
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other)
|
13
|
+
@name == other.name && @type == other.type
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(value)
|
17
|
+
type.to_mongo(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def embeddable?
|
21
|
+
type.respond_to?(:embeddable?) && type.embeddable? ? true : false
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(value)
|
25
|
+
if value.nil? && !default_value.nil?
|
26
|
+
return default_value
|
27
|
+
end
|
28
|
+
|
29
|
+
type.from_mongo(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,15 +1,15 @@
|
|
1
|
-
module MongoMapper
|
2
|
-
module RailsCompatibility
|
3
|
-
module Document
|
4
|
-
def self.included(model)
|
5
|
-
model.class_eval do
|
6
|
-
alias_method :new_record?, :new?
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_param
|
11
|
-
id
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
1
|
+
module MongoMapper
|
2
|
+
module RailsCompatibility
|
3
|
+
module Document
|
4
|
+
def self.included(model)
|
5
|
+
model.class_eval do
|
6
|
+
alias_method :new_record?, :new?
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_param
|
11
|
+
id
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
15
|
end
|
@@ -1,25 +1,27 @@
|
|
1
|
-
module MongoMapper
|
2
|
-
module RailsCompatibility
|
3
|
-
module EmbeddedDocument
|
4
|
-
def self.included(model)
|
5
|
-
model.class_eval do
|
6
|
-
extend ClassMethods
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
1
|
+
module MongoMapper
|
2
|
+
module RailsCompatibility
|
3
|
+
module EmbeddedDocument
|
4
|
+
def self.included(model)
|
5
|
+
model.class_eval do
|
6
|
+
extend ClassMethods
|
7
|
+
|
8
|
+
alias_method :new_record?, :new?
|
9
|
+
end
|
10
|
+
|
11
|
+
class << model
|
12
|
+
alias has_many many
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def column_names
|
18
|
+
keys.keys
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_param
|
23
|
+
raise "Missing to_param method in #{self.class}. You should implement it to return the unique identifier of this embedded document within a document."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
25
27
|
end
|
data/lib/mongomapper/support.rb
CHANGED
@@ -1,30 +1,157 @@
|
|
1
|
-
class BasicObject #:nodoc:
|
2
|
-
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|instance_eval|proxy_|^object_id$)/ }
|
3
|
-
end unless defined?(BasicObject)
|
4
|
-
|
5
|
-
class
|
6
|
-
def self.
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
1
|
+
class BasicObject #:nodoc:
|
2
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^methods$|instance_eval|proxy_|^object_id$)/ }
|
3
|
+
end unless defined?(BasicObject)
|
4
|
+
|
5
|
+
class Array
|
6
|
+
def self.to_mongo(value)
|
7
|
+
value.to_a
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.from_mongo(value)
|
11
|
+
value || []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Binary
|
16
|
+
def self.to_mongo(value)
|
17
|
+
if value.is_a?(ByteBuffer)
|
18
|
+
value
|
19
|
+
else
|
20
|
+
ByteBuffer.new(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_mongo(value)
|
25
|
+
value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Boolean
|
30
|
+
def self.to_mongo(value)
|
31
|
+
if value.is_a?(Boolean)
|
32
|
+
value
|
33
|
+
else
|
34
|
+
['true', 't', '1'].include?(value.to_s.downcase)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.from_mongo(value)
|
39
|
+
!!value
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Date
|
44
|
+
def self.to_mongo(value)
|
45
|
+
date = Date.parse(value.to_s)
|
46
|
+
Time.utc(date.year, date.month, date.day)
|
47
|
+
rescue
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.from_mongo(value)
|
52
|
+
value.to_date if value.present?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Float
|
57
|
+
def self.to_mongo(value)
|
58
|
+
value.to_f
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Hash
|
63
|
+
def self.from_mongo(value)
|
64
|
+
HashWithIndifferentAccess.new(value || {})
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_mongo
|
68
|
+
self
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Integer
|
73
|
+
def self.to_mongo(value)
|
74
|
+
value_to_i = value.to_i
|
75
|
+
if value_to_i == 0
|
76
|
+
value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
|
77
|
+
else
|
78
|
+
value_to_i
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class NilClass
|
84
|
+
def to_mongo(value)
|
85
|
+
value
|
86
|
+
end
|
87
|
+
|
88
|
+
def from_mongo(value)
|
89
|
+
value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Object
|
94
|
+
# The hidden singleton lurks behind everyone
|
95
|
+
def metaclass
|
96
|
+
class << self; self end
|
97
|
+
end
|
98
|
+
|
99
|
+
def meta_eval(&blk)
|
100
|
+
metaclass.instance_eval(&blk)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Adds methods to a metaclass
|
104
|
+
def meta_def(name, &blk)
|
105
|
+
meta_eval { define_method(name, &blk) }
|
106
|
+
end
|
107
|
+
|
108
|
+
# Defines an instance method within a class
|
109
|
+
def class_def(name, &blk)
|
110
|
+
class_eval { define_method(name, &blk) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.to_mongo(value)
|
114
|
+
value
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.from_mongo(value)
|
118
|
+
value
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class String
|
123
|
+
def self.to_mongo(value)
|
124
|
+
value.nil? ? nil : value.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.from_mongo(value)
|
128
|
+
value.present? ? value : nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Time
|
133
|
+
def self.to_mongo(value)
|
134
|
+
to_utc_time(value)
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.from_mongo(value)
|
138
|
+
if Time.respond_to?(:zone) && Time.zone && value.present?
|
139
|
+
value.in_time_zone(Time.zone)
|
140
|
+
else
|
141
|
+
value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.to_utc_time(value)
|
146
|
+
to_local_time(value).try(:utc)
|
147
|
+
end
|
148
|
+
|
149
|
+
# make sure we have a time and that it is local
|
150
|
+
def self.to_local_time(value)
|
151
|
+
if Time.respond_to?(:zone) && Time.zone
|
152
|
+
Time.zone.parse(value.to_s)
|
153
|
+
else
|
154
|
+
Time.parse(value.to_s)
|
155
|
+
end
|
156
|
+
end
|
30
157
|
end
|