drogus-mongo_mapper 0.6.10

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 (91) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +29 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper.rb +131 -0
  8. data/lib/mongo_mapper/document.rb +417 -0
  9. data/lib/mongo_mapper/embedded_document.rb +55 -0
  10. data/lib/mongo_mapper/finder_options.rb +127 -0
  11. data/lib/mongo_mapper/plugins.rb +30 -0
  12. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  13. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  14. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +30 -0
  15. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +25 -0
  16. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  17. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +50 -0
  18. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  19. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  20. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  21. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  22. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  23. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  24. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  25. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  26. data/lib/mongo_mapper/plugins/callbacks.rb +134 -0
  27. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  28. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  29. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  30. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  31. data/lib/mongo_mapper/plugins/identity_map.rb +122 -0
  32. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  33. data/lib/mongo_mapper/plugins/keys.rb +324 -0
  34. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  35. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  36. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  37. data/lib/mongo_mapper/plugins/rails.rb +45 -0
  38. data/lib/mongo_mapper/plugins/serialization.rb +105 -0
  39. data/lib/mongo_mapper/plugins/validations.rb +57 -0
  40. data/lib/mongo_mapper/support.rb +217 -0
  41. data/lib/mongo_mapper/support/descendant_appends.rb +46 -0
  42. data/lib/mongo_mapper/support/find.rb +77 -0
  43. data/mongo_mapper.gemspec +195 -0
  44. data/performance/read_write.rb +52 -0
  45. data/specs.watchr +51 -0
  46. data/test/NOTE_ON_TESTING +1 -0
  47. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  48. data/test/functional/associations/test_belongs_to_proxy.rb +101 -0
  49. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  50. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  51. data/test/functional/associations/test_many_documents_proxy.rb +431 -0
  52. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  53. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  54. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  55. data/test/functional/associations/test_one_proxy.rb +161 -0
  56. data/test/functional/test_associations.rb +44 -0
  57. data/test/functional/test_binary.rb +27 -0
  58. data/test/functional/test_callbacks.rb +81 -0
  59. data/test/functional/test_dirty.rb +163 -0
  60. data/test/functional/test_document.rb +1264 -0
  61. data/test/functional/test_embedded_document.rb +125 -0
  62. data/test/functional/test_identity_map.rb +508 -0
  63. data/test/functional/test_logger.rb +20 -0
  64. data/test/functional/test_modifiers.rb +252 -0
  65. data/test/functional/test_pagination.rb +93 -0
  66. data/test/functional/test_protected.rb +155 -0
  67. data/test/functional/test_string_id_compatibility.rb +67 -0
  68. data/test/functional/test_validations.rb +329 -0
  69. data/test/models.rb +232 -0
  70. data/test/support/custom_matchers.rb +55 -0
  71. data/test/support/timing.rb +16 -0
  72. data/test/test_helper.rb +60 -0
  73. data/test/unit/associations/test_base.rb +207 -0
  74. data/test/unit/associations/test_proxy.rb +105 -0
  75. data/test/unit/serializers/test_json_serializer.rb +189 -0
  76. data/test/unit/test_descendant_appends.rb +71 -0
  77. data/test/unit/test_document.rb +231 -0
  78. data/test/unit/test_dynamic_finder.rb +123 -0
  79. data/test/unit/test_embedded_document.rb +663 -0
  80. data/test/unit/test_finder_options.rb +329 -0
  81. data/test/unit/test_keys.rb +169 -0
  82. data/test/unit/test_mongo_mapper.rb +65 -0
  83. data/test/unit/test_pagination.rb +127 -0
  84. data/test/unit/test_plugins.rb +50 -0
  85. data/test/unit/test_rails.rb +123 -0
  86. data/test/unit/test_rails_compatibility.rb +52 -0
  87. data/test/unit/test_serialization.rb +51 -0
  88. data/test/unit/test_support.rb +354 -0
  89. data/test/unit/test_time_zones.rb +39 -0
  90. data/test/unit/test_validations.rb +544 -0
  91. metadata +290 -0
@@ -0,0 +1,17 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Logger
4
+ module ClassMethods
5
+ def logger
6
+ MongoMapper.logger
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+ def logger
12
+ self.class.logger
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,85 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Pagination
4
+ module ClassMethods
5
+ def per_page
6
+ 25
7
+ end
8
+
9
+ def paginate(options)
10
+ per_page = options.delete(:per_page) || self.per_page
11
+ page = options.delete(:page)
12
+ total_entries = count(options)
13
+ pagination = Pagination::PaginationProxy.new(total_entries, page, per_page)
14
+
15
+ options.merge!(:limit => pagination.limit, :skip => pagination.skip)
16
+ pagination.subject = find_many(options)
17
+ pagination
18
+ end
19
+ end
20
+
21
+ class PaginationProxy
22
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
23
+
24
+ attr_accessor :subject
25
+ attr_reader :total_entries, :per_page, :current_page
26
+ alias limit per_page
27
+
28
+ def initialize(total_entries, current_page, per_page=nil)
29
+ @total_entries = total_entries.to_i
30
+ self.per_page = per_page
31
+ self.current_page = current_page
32
+ end
33
+
34
+ def total_pages
35
+ (total_entries / per_page.to_f).ceil
36
+ end
37
+
38
+ def out_of_bounds?
39
+ current_page > total_pages
40
+ end
41
+
42
+ def previous_page
43
+ current_page > 1 ? (current_page - 1) : nil
44
+ end
45
+
46
+ def next_page
47
+ current_page < total_pages ? (current_page + 1) : nil
48
+ end
49
+
50
+ def skip
51
+ (current_page - 1) * per_page
52
+ end
53
+ alias offset skip # for will paginate support
54
+
55
+ def send(method, *args, &block)
56
+ if respond_to?(method)
57
+ super
58
+ else
59
+ subject.send(method, *args, &block)
60
+ end
61
+ end
62
+
63
+ def ===(other)
64
+ other === subject
65
+ end
66
+
67
+ def method_missing(name, *args, &block)
68
+ @subject.send(name, *args, &block)
69
+ end
70
+
71
+ private
72
+ def per_page=(value)
73
+ value = 25 if value.blank?
74
+ @per_page = value.to_i
75
+ end
76
+
77
+ def current_page=(value)
78
+ value = value.to_i
79
+ value = 1 if value < 1
80
+ @current_page = value
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,45 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Protected
4
+ module ClassMethods
5
+ def attr_protected(*attrs)
6
+ self.write_inheritable_attribute(:attr_protected, Set.new(attrs) + (protected_attributes || []))
7
+ end
8
+
9
+ def protected_attributes
10
+ self.read_inheritable_attribute(:attr_protected)
11
+ end
12
+
13
+ def key(*args)
14
+ key = super
15
+ attr_protected key.name.to_sym if key.options[:protected]
16
+ key
17
+ end
18
+ end
19
+
20
+ module InstanceMethods
21
+ def assign(attrs={})
22
+ super(filter_protected_attrs(attrs))
23
+ end
24
+
25
+ def update_attributes(attrs={})
26
+ super(filter_protected_attrs(attrs))
27
+ end
28
+
29
+ def update_attributes!(attrs={})
30
+ super(filter_protected_attrs(attrs))
31
+ end
32
+
33
+ def protected_attributes
34
+ self.class.protected_attributes
35
+ end
36
+
37
+ protected
38
+ def filter_protected_attrs(attrs)
39
+ return attrs if protected_attributes.blank?
40
+ attrs.dup.delete_if { |key, val| protected_attributes.include?(key) }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,45 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Rails
4
+ module InstanceMethods
5
+ def to_param
6
+ id.to_s
7
+ end
8
+
9
+ def new_record?
10
+ new?
11
+ end
12
+
13
+ def read_attribute(name)
14
+ self[name]
15
+ end
16
+
17
+ def read_attribute_before_typecast(name)
18
+ read_key_before_typecast(name)
19
+ end
20
+
21
+ def write_attribute(name, value)
22
+ self[name] = value
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def has_one(*args)
28
+ one(*args)
29
+ end
30
+
31
+ def has_many(*args)
32
+ many(*args)
33
+ end
34
+
35
+ def column_names
36
+ keys.keys
37
+ end
38
+
39
+ def human_name
40
+ self.name.demodulize.titleize
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,105 @@
1
+ require 'active_support/json'
2
+
3
+ module MongoMapper
4
+ module Plugins
5
+ module Serialization
6
+ def self.configure(model)
7
+ model.class_eval { include Json }
8
+ end
9
+
10
+ class Serializer
11
+ attr_reader :options
12
+
13
+ def initialize(record, options={})
14
+ @record, @options = record, options.dup
15
+ end
16
+
17
+ def serializable_key_names
18
+ key_names = @record.attributes.keys
19
+
20
+ if options[:only]
21
+ options.delete(:except)
22
+ key_names = key_names & Array(options[:only]).collect { |n| n.to_s }
23
+ else
24
+ options[:except] = Array(options[:except])
25
+ key_names = key_names - options[:except].collect { |n| n.to_s }
26
+ end
27
+
28
+ key_names
29
+ end
30
+
31
+ def serializable_method_names
32
+ Array(options[:methods]).inject([]) do |method_attributes, name|
33
+ method_attributes << name if @record.respond_to?(name.to_s)
34
+ method_attributes
35
+ end
36
+ end
37
+
38
+ def serializable_names
39
+ serializable_key_names + serializable_method_names
40
+ end
41
+
42
+ def serializable_record
43
+ returning(serializable_record = {}) do
44
+ serializable_names.each { |name| serializable_record[name] = @record.send(name) }
45
+ end
46
+ end
47
+
48
+ def serialize
49
+ # overwrite to implement
50
+ end
51
+
52
+ def to_s(&block)
53
+ serialize(&block)
54
+ end
55
+ end
56
+
57
+ module Json
58
+ def self.included(base)
59
+ base.cattr_accessor :include_root_in_json, :instance_writer => false
60
+ base.extend ClassMethods
61
+ end
62
+
63
+ module ClassMethods
64
+ def json_class_name
65
+ @json_class_name ||= name.demodulize.underscore.inspect
66
+ end
67
+ end
68
+
69
+ def to_json(options={})
70
+ apply_to_json_defaults(options)
71
+
72
+ if include_root_in_json
73
+ "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
74
+ else
75
+ JsonSerializer.new(self, options).to_s
76
+ end
77
+ end
78
+
79
+ def from_json(json)
80
+ self.attributes = ActiveSupport::JSON.decode(json)
81
+ self
82
+ end
83
+
84
+ class JsonSerializer < Serializer
85
+ def serialize
86
+ serializable_record.to_json
87
+ end
88
+ end
89
+
90
+ private
91
+ def apply_to_json_defaults(options)
92
+ unless options[:only]
93
+ methods = [options.delete(:methods)].flatten.compact
94
+ methods << :id
95
+ options[:methods] = methods.uniq
96
+ end
97
+
98
+ except = [options.delete(:except)].flatten.compact
99
+ except << :_id
100
+ options[:except] = except
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,57 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Validations
4
+ def self.configure(model)
5
+ model.class_eval { include Validatable }
6
+
7
+ if defined?(ActiveModel)
8
+ # this methods are not defined when using rails 3
9
+ class << model
10
+ [:validate, :validate_on_update, :validate_on_create].each do |method|
11
+ define_method(method) do |*args, &block|
12
+ set_callback(method, *args, &block)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ module DocumentMacros
20
+ def validates_uniqueness_of(*args)
21
+ add_validations(args, MongoMapper::Plugins::Validations::ValidatesUniquenessOf)
22
+ end
23
+ end
24
+
25
+ class ValidatesUniquenessOf < Validatable::ValidationBase
26
+ option :scope, :case_sensitive
27
+ default :case_sensitive => true
28
+
29
+ def valid?(instance)
30
+ value = instance[attribute]
31
+ return true if allow_blank && value.blank?
32
+ return true if allow_nil && value.nil?
33
+ base_conditions = case_sensitive ? {self.attribute => value} : {}
34
+ doc = instance.class.first(base_conditions.merge(scope_conditions(instance)).merge(where_conditions(instance)))
35
+ doc.nil? || instance._id == doc._id
36
+ end
37
+
38
+ def message(instance)
39
+ super || "has already been taken"
40
+ end
41
+
42
+ def scope_conditions(instance)
43
+ return {} unless scope
44
+ Array(scope).inject({}) do |conditions, key|
45
+ conditions.merge(key => instance[key])
46
+ end
47
+ end
48
+
49
+ def where_conditions(instance)
50
+ conditions = {}
51
+ conditions[attribute] = /#{instance[attribute].to_s}/i unless case_sensitive
52
+ conditions
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,217 @@
1
+ class Array
2
+ def self.to_mongo(value)
3
+ value = value.respond_to?(:lines) ? value.lines : value
4
+ value.to_a
5
+ end
6
+
7
+ def self.from_mongo(value)
8
+ value || []
9
+ end
10
+ end
11
+
12
+ class Binary
13
+ def self.to_mongo(value)
14
+ if value.is_a?(ByteBuffer)
15
+ value
16
+ else
17
+ value.nil? ? nil : ByteBuffer.new(value)
18
+ end
19
+ end
20
+
21
+ def self.from_mongo(value)
22
+ value
23
+ end
24
+ end
25
+
26
+ class Boolean
27
+ BOOLEAN_MAPPING = {
28
+ true => true, 'true' => true, 'TRUE' => true, 'True' => true, 't' => true, 'T' => true, '1' => true, 1 => true, 1.0 => true,
29
+ false => false, 'false' => false, 'FALSE' => false, 'False' => false, 'f' => false, 'F' => false, '0' => false, 0 => false, 0.0 => false, nil => false
30
+ }
31
+
32
+ def self.to_mongo(value)
33
+ if value.is_a?(Boolean)
34
+ value
35
+ else
36
+ v = BOOLEAN_MAPPING[value]
37
+ v = value.to_s.downcase == 'true' if v.nil? # Check all mixed case spellings for true
38
+ v
39
+ end
40
+ end
41
+
42
+ def self.from_mongo(value)
43
+ !!value
44
+ end
45
+ end
46
+
47
+ class Date
48
+ def self.to_mongo(value)
49
+ if value.nil? || value == ''
50
+ nil
51
+ else
52
+ date = value.is_a?(Date) || value.is_a?(Time) ? value : Date.parse(value.to_s)
53
+ Time.utc(date.year, date.month, date.day)
54
+ end
55
+ rescue
56
+ nil
57
+ end
58
+
59
+ def self.from_mongo(value)
60
+ value.to_date if value.present?
61
+ end
62
+ end
63
+
64
+ class Float
65
+ def self.to_mongo(value)
66
+ value.to_f
67
+ end
68
+ end
69
+
70
+ class Hash
71
+ def self.from_mongo(value)
72
+ HashWithIndifferentAccess.new(value || {})
73
+ end
74
+
75
+ def to_mongo
76
+ self
77
+ end
78
+ end
79
+
80
+ class Integer
81
+ def self.to_mongo(value)
82
+ value_to_i = value.to_i
83
+ if value_to_i == 0 && value != value_to_i
84
+ value.to_s =~ /^(0x|0b)?0+/ ? 0 : nil
85
+ else
86
+ value_to_i
87
+ end
88
+ end
89
+ end
90
+
91
+ class NilClass
92
+ def to_mongo(value)
93
+ value
94
+ end
95
+
96
+ def from_mongo(value)
97
+ value
98
+ end
99
+ end
100
+
101
+ class Object
102
+ # The hidden singleton lurks behind everyone
103
+ def metaclass
104
+ class << self; self end
105
+ end
106
+
107
+ def meta_eval(&blk)
108
+ metaclass.instance_eval(&blk)
109
+ end
110
+
111
+ # Adds methods to a metaclass
112
+ def meta_def(name, &blk)
113
+ meta_eval { define_method(name, &blk) }
114
+ end
115
+
116
+ # Defines an instance method within a class
117
+ def class_def(name, &blk)
118
+ class_eval { define_method(name, &blk) }
119
+ end
120
+
121
+ def self.to_mongo(value)
122
+ value
123
+ end
124
+
125
+ def self.from_mongo(value)
126
+ value
127
+ end
128
+ end
129
+
130
+ class ObjectId
131
+ def self.to_mongo(value)
132
+ if value.blank?
133
+ nil
134
+ elsif value.is_a?(Mongo::ObjectID)
135
+ value
136
+ else
137
+ Mongo::ObjectID.from_string(value.to_s)
138
+ end
139
+ end
140
+
141
+ def self.from_mongo(value)
142
+ value
143
+ end
144
+ end
145
+
146
+ class Set
147
+ def self.to_mongo(value)
148
+ value.to_a
149
+ end
150
+
151
+ def self.from_mongo(value)
152
+ Set.new(value || [])
153
+ end
154
+ end
155
+
156
+ class String
157
+ def self.to_mongo(value)
158
+ value.nil? ? nil : value.to_s
159
+ end
160
+
161
+ def self.from_mongo(value)
162
+ value.nil? ? nil : value.to_s
163
+ end
164
+ end
165
+
166
+ class SymbolOperator
167
+ def initialize(field, operator, options={})
168
+ @field, @operator = field, operator
169
+ end unless method_defined?(:initialize)
170
+
171
+ def to_mm_criteria(value)
172
+ {MongoMapper::FinderOptions.normalized_field(@field) => {"$#{@operator}" => value}}
173
+ end
174
+
175
+ def to_mm_order
176
+ [@field.to_s, MongoMapper::FinderOptions.normalized_order_direction(@operator)]
177
+ end
178
+ end
179
+
180
+ class Symbol
181
+ %w(gt lt gte lte ne in nin mod size where exists asc desc).each do |operator|
182
+ define_method(operator) do
183
+ SymbolOperator.new(self, operator)
184
+ end unless method_defined?(operator)
185
+ end
186
+ end
187
+
188
+ class Time
189
+ def self.to_mongo(value)
190
+ if value.nil? || value == ''
191
+ nil
192
+ else
193
+ time = value.is_a?(Time) ? value : MongoMapper.time_class.parse(value.to_s)
194
+ # Convert time to milliseconds since BSON stores dates with that accurracy, but Ruby uses microseconds
195
+ Time.at((time.to_f * 1000).round / 1000.0).utc if time
196
+ end
197
+ end
198
+
199
+ def self.from_mongo(value)
200
+ if MongoMapper.use_time_zone? && value.present?
201
+ value.in_time_zone(Time.zone)
202
+ else
203
+ value
204
+ end
205
+ end
206
+ end
207
+
208
+ class Mongo::ObjectID
209
+ alias_method :original_to_json, :to_json
210
+
211
+ def to_json(options = nil)
212
+ %Q("#{to_s}")
213
+ end
214
+ end
215
+
216
+ require 'mongo_mapper/support/descendant_appends'
217
+ require 'mongo_mapper/support/find'