djsun-mongomapper 0.3.5.5 → 0.4.1.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.
Files changed (44) hide show
  1. data/README.rdoc +38 -38
  2. data/Rakefile +87 -73
  3. data/VERSION +1 -1
  4. data/lib/mongomapper.rb +67 -71
  5. data/lib/mongomapper/associations.rb +86 -84
  6. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -34
  7. data/lib/mongomapper/associations/many_embedded_proxy.rb +67 -17
  8. data/lib/mongomapper/associations/proxy.rb +74 -73
  9. data/lib/mongomapper/document.rb +342 -348
  10. data/lib/mongomapper/embedded_document.rb +354 -274
  11. data/lib/mongomapper/finder_options.rb +84 -84
  12. data/lib/mongomapper/key.rb +32 -76
  13. data/lib/mongomapper/rails_compatibility/document.rb +14 -14
  14. data/lib/mongomapper/rails_compatibility/embedded_document.rb +26 -24
  15. data/lib/mongomapper/support.rb +156 -29
  16. data/lib/mongomapper/validations.rb +69 -47
  17. data/test/custom_matchers.rb +48 -0
  18. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -56
  19. data/test/functional/associations/test_belongs_to_proxy.rb +48 -49
  20. data/test/functional/associations/test_many_documents_as_proxy.rb +208 -253
  21. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +130 -130
  22. data/test/functional/associations/test_many_embedded_proxy.rb +168 -106
  23. data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -262
  24. data/test/functional/test_binary.rb +21 -0
  25. data/test/functional/test_document.rb +946 -952
  26. data/test/functional/test_embedded_document.rb +98 -0
  27. data/test/functional/test_pagination.rb +87 -80
  28. data/test/functional/test_rails_compatibility.rb +29 -29
  29. data/test/functional/test_validations.rb +262 -172
  30. data/test/models.rb +169 -169
  31. data/test/test_helper.rb +28 -66
  32. data/test/unit/serializers/test_json_serializer.rb +193 -193
  33. data/test/unit/test_document.rb +161 -123
  34. data/test/unit/test_embedded_document.rb +643 -547
  35. data/test/unit/test_finder_options.rb +183 -183
  36. data/test/unit/test_key.rb +175 -247
  37. data/test/unit/test_rails_compatibility.rb +38 -33
  38. data/test/unit/test_serializations.rb +52 -52
  39. data/test/unit/test_support.rb +268 -0
  40. data/test/unit/test_time_zones.rb +40 -0
  41. data/test/unit/test_validations.rb +499 -258
  42. metadata +22 -12
  43. data/History +0 -76
  44. 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
- :offset => (options.delete(:offset) || 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
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
@@ -1,76 +1,32 @@
1
- module MongoMapper
2
- class Key
3
- # DateTime and Date are currently not supported by mongo's bson so just use Time
4
- NativeTypes = [String, Float, Time, Integer, Boolean, Array, Hash]
5
-
6
- attr_accessor :name, :type, :options, :default_value
7
-
8
- def initialize(*args)
9
- options = args.extract_options!
10
- @name, @type = args.shift.to_s, args.shift
11
- self.options = (options || {}).symbolize_keys
12
- self.default_value = self.options.delete(:default)
13
- end
14
-
15
- def ==(other)
16
- @name == other.name && @type == other.type
17
- end
18
-
19
- def set(value)
20
- typecast(value)
21
- end
22
-
23
- def native?
24
- @native ||= NativeTypes.include?(type) || type.nil?
25
- end
26
-
27
- def embedded_document?
28
- type.respond_to?(:embeddable?) && type.embeddable?
29
- end
30
-
31
- def get(value)
32
- return default_value if value.nil? && !default_value.nil?
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
- end
8
-
9
- class << model
10
- alias has_many many
11
- end
12
- end
13
-
14
- module ClassMethods
15
- def column_names
16
- keys.keys
17
- end
18
- end
19
-
20
- def to_param
21
- raise "Missing to_param method in #{self.class}. You should implement it to return the unique identifier of this embedded document within a document."
22
- end
23
- end
24
- end
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
@@ -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 Boolean
6
- def self.mm_typecast(value)
7
- ['true', 't', '1'].include?(value.to_s.downcase)
8
- end
9
- end
10
-
11
- class Object
12
- # The hidden singleton lurks behind everyone
13
- def metaclass
14
- class << self; self end
15
- end
16
-
17
- def meta_eval(&blk)
18
- metaclass.instance_eval(&blk)
19
- end
20
-
21
- # Adds methods to a metaclass
22
- def meta_def(name, &blk)
23
- meta_eval { define_method(name, &blk) }
24
- end
25
-
26
- # Defines an instance method within a class
27
- def class_def(name, &blk)
28
- class_eval { define_method(name, &blk) }
29
- end
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