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,55 @@
1
+ module MongoMapper
2
+ module EmbeddedDocument
3
+ extend Support::DescendantAppends
4
+
5
+ def self.included(model)
6
+ model.class_eval do
7
+ include InstanceMethods
8
+ extend ClassMethods
9
+ extend Plugins
10
+
11
+ plugin Plugins::Associations
12
+ plugin Plugins::Clone
13
+ plugin Plugins::Descendants
14
+ plugin Plugins::Equality
15
+ plugin Plugins::Inspect
16
+ plugin Plugins::Keys
17
+ plugin Plugins::Logger
18
+ plugin Plugins::Protected
19
+ plugin Plugins::Rails
20
+ plugin Plugins::Serialization
21
+ plugin Plugins::Validations
22
+
23
+ attr_accessor :_root_document, :_parent_document
24
+ end
25
+
26
+ super
27
+ end
28
+
29
+ module ClassMethods
30
+ def embeddable?
31
+ true
32
+ end
33
+
34
+ def embedded_in(owner_name)
35
+ define_method(owner_name) { _parent_document }
36
+ end
37
+ end
38
+
39
+ module InstanceMethods
40
+ def save(options={})
41
+ if result = _root_document.try(:save, options)
42
+ @new = false
43
+ end
44
+ result
45
+ end
46
+
47
+ def save!(options={})
48
+ if result = _root_document.try(:save!, options)
49
+ @new = false
50
+ end
51
+ result
52
+ end
53
+ end # InstanceMethods
54
+ end # EmbeddedDocument
55
+ end # MongoMapper
@@ -0,0 +1,127 @@
1
+ module MongoMapper
2
+ # = Important Note
3
+ # This class is private to MongoMapper and should not be considered part of
4
+ # MongoMapper's public API.
5
+ #
6
+ class FinderOptions
7
+ OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
8
+
9
+ def self.normalized_field(field)
10
+ field.to_s == 'id' ? :_id : field
11
+ end
12
+
13
+ def self.normalized_order_direction(direction)
14
+ direction ||= 'ASC'
15
+ direction.upcase == 'ASC' ? 1 : -1
16
+ end
17
+
18
+ def initialize(model, options)
19
+ raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
20
+
21
+ @model = model
22
+ @options = {}
23
+ @conditions = {}
24
+
25
+ options.each_pair do |key, value|
26
+ key = key.respond_to?(:to_sym) ? key.to_sym : key
27
+ if OptionKeys.include?(key)
28
+ @options[key] = value
29
+ elsif key == :conditions
30
+ @conditions.merge!(value)
31
+ else
32
+ @conditions[key] = value
33
+ end
34
+ end
35
+
36
+ add_sci_scope
37
+ end
38
+
39
+ def criteria
40
+ to_mongo_criteria(@conditions)
41
+ end
42
+
43
+ def options
44
+ fields = @options[:fields] || @options[:select]
45
+ skip = @options[:skip] || @options[:offset] || 0
46
+ limit = @options[:limit] || 0
47
+ sort = @options[:sort] || convert_order_to_sort(@options[:order])
48
+
49
+ {:fields => to_mongo_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
50
+ end
51
+
52
+ def to_a
53
+ [criteria, options]
54
+ end
55
+
56
+ private
57
+ def to_mongo_criteria(conditions, parent_key=nil)
58
+ criteria = {}
59
+
60
+ conditions.each_pair do |field, value|
61
+ field = self.class.normalized_field(field)
62
+
63
+ if @model.object_id_key?(field) && value.is_a?(String)
64
+ value = Mongo::ObjectID.from_string(value)
65
+ end
66
+
67
+ if field.is_a?(SymbolOperator)
68
+ criteria.update(field.to_mm_criteria(value))
69
+ next
70
+ end
71
+
72
+ case value
73
+ when Array
74
+ criteria[field] = operator?(field) ? value : {'$in' => value}
75
+ when Hash
76
+ criteria[field] = to_mongo_criteria(value, field)
77
+ when Time
78
+ criteria[field] = value.utc
79
+ else
80
+ criteria[field] = value
81
+ end
82
+ end
83
+
84
+ criteria
85
+ end
86
+
87
+ def operator?(field)
88
+ field.to_s =~ /^\$/
89
+ end
90
+
91
+ # adds _type single collection inheritance scope for models that need it
92
+ def add_sci_scope
93
+ if @model.single_collection_inherited?
94
+ @conditions[:_type] = @model.to_s
95
+ end
96
+ end
97
+
98
+ def to_mongo_fields(fields)
99
+ return if fields.blank?
100
+
101
+ if fields.respond_to?(:flatten, :compact)
102
+ fields.flatten.compact
103
+ else
104
+ fields.split(',').map { |field| field.strip }
105
+ end
106
+ end
107
+
108
+ def convert_order_to_sort(sort)
109
+ return if sort.blank?
110
+
111
+ if sort.respond_to?(:all?) && sort.all? { |s| s.respond_to?(:to_mm_order) }
112
+ sort.map { |s| s.to_mm_order }
113
+ elsif sort.respond_to?(:to_mm_order)
114
+ [sort.to_mm_order]
115
+ else
116
+ pieces = sort.split(',')
117
+ pieces.map { |s| to_mongo_sort_piece(s) }
118
+ end
119
+ end
120
+
121
+ def to_mongo_sort_piece(str)
122
+ field, direction = str.strip.split(' ')
123
+ direction = FinderOptions.normalized_order_direction(direction)
124
+ [field, direction]
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,30 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ def plugins
4
+ @plugins ||= []
5
+ end
6
+
7
+ def plugin(mod)
8
+ extend mod::ClassMethods if mod.const_defined?(:ClassMethods)
9
+ include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
10
+ mod.configure(self) if mod.respond_to?(:configure)
11
+ plugins << mod
12
+ end
13
+ end
14
+ end
15
+
16
+ require 'mongo_mapper/plugins/associations'
17
+ require 'mongo_mapper/plugins/callbacks'
18
+ require 'mongo_mapper/plugins/clone'
19
+ require 'mongo_mapper/plugins/descendants'
20
+ require 'mongo_mapper/plugins/dirty'
21
+ require 'mongo_mapper/plugins/equality'
22
+ require 'mongo_mapper/plugins/identity_map'
23
+ require 'mongo_mapper/plugins/inspect'
24
+ require 'mongo_mapper/plugins/keys'
25
+ require 'mongo_mapper/plugins/logger'
26
+ require 'mongo_mapper/plugins/pagination'
27
+ require 'mongo_mapper/plugins/protected'
28
+ require 'mongo_mapper/plugins/rails'
29
+ require 'mongo_mapper/plugins/serialization'
30
+ require 'mongo_mapper/plugins/validations'
@@ -0,0 +1,104 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ module ClassMethods
5
+ def belongs_to(association_id, options={}, &extension)
6
+ create_association(:belongs_to, association_id, options, &extension)
7
+ end
8
+
9
+ def many(association_id, options={}, &extension)
10
+ create_association(:many, association_id, options, &extension)
11
+ end
12
+
13
+ def one(association_id, options={}, &extension)
14
+ create_association(:one, association_id, options, &extension)
15
+ end
16
+
17
+ def associations
18
+ @associations ||= HashWithIndifferentAccess.new
19
+ end
20
+
21
+ def associations=(hash)
22
+ @associations = hash
23
+ end
24
+
25
+ def inherited(subclass)
26
+ subclass.associations = associations.dup
27
+ super
28
+ end
29
+
30
+ private
31
+ def create_association(type, name, options, &extension)
32
+ association = Associations::Base.new(type, name, options, &extension)
33
+ associations[association.name] = association
34
+
35
+ define_method(association.name) do
36
+ get_proxy(association)
37
+ end
38
+
39
+ define_method("#{association.name}=") do |value|
40
+ get_proxy(association).replace(value)
41
+ value
42
+ end
43
+
44
+ if association.one? || association.belongs_to?
45
+ define_method("#{association.name}?") do
46
+ get_proxy(association).present?
47
+ end
48
+ end
49
+
50
+ if association.options[:dependent] && association.many? && !association.embeddable?
51
+ after_destroy do |doc|
52
+ case association.options[:dependent]
53
+ when :destroy
54
+ doc.get_proxy(association).destroy_all
55
+ when :delete_all
56
+ doc.get_proxy(association).delete_all
57
+ when :nullify
58
+ doc.get_proxy(association).nullify
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ module InstanceMethods
66
+ def associations
67
+ self.class.associations
68
+ end
69
+
70
+ # @api private?
71
+ def embedded_associations
72
+ associations.select do |name, association|
73
+ association.embeddable?
74
+ end.map do |name, association|
75
+ association
76
+ end
77
+ end
78
+
79
+ def get_proxy(association)
80
+ unless proxy = self.instance_variable_get(association.ivar)
81
+ proxy = association.proxy_class.new(self, association)
82
+ self.instance_variable_set(association.ivar, proxy)
83
+ end
84
+
85
+ proxy
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ require 'mongo_mapper/plugins/associations/base'
93
+ require 'mongo_mapper/plugins/associations/proxy'
94
+ require 'mongo_mapper/plugins/associations/collection'
95
+ require 'mongo_mapper/plugins/associations/embedded_collection'
96
+ require 'mongo_mapper/plugins/associations/many_documents_proxy'
97
+ require 'mongo_mapper/plugins/associations/belongs_to_proxy'
98
+ require 'mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy'
99
+ require 'mongo_mapper/plugins/associations/many_polymorphic_proxy'
100
+ require 'mongo_mapper/plugins/associations/many_embedded_proxy'
101
+ require 'mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy'
102
+ require 'mongo_mapper/plugins/associations/many_documents_as_proxy'
103
+ require 'mongo_mapper/plugins/associations/one_proxy'
104
+ require 'mongo_mapper/plugins/associations/in_array_proxy'
@@ -0,0 +1,121 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class Base
5
+ attr_reader :type, :name, :options, :finder_options
6
+
7
+ # Options that should not be considered MongoDB query options/criteria
8
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic]
9
+
10
+ def initialize(type, name, options={}, &extension)
11
+ @type, @name, @options, @finder_options = type, name, {}, {}
12
+ options.symbolize_keys!
13
+
14
+ options[:extend] = modulized_extensions(extension, options[:extend])
15
+
16
+ options.each_pair do |key, value|
17
+ if AssociationOptions.include?(key)
18
+ @options[key] = value
19
+ else
20
+ @finder_options[key] = value
21
+ end
22
+ end
23
+ end
24
+
25
+ def class_name
26
+ @class_name ||= begin
27
+ if cn = options[:class_name]
28
+ cn
29
+ elsif many?
30
+ name.to_s.singularize.camelize
31
+ else
32
+ name.to_s.camelize
33
+ end
34
+ end
35
+ end
36
+
37
+ def klass
38
+ @klass ||= options[:class] || class_name.constantize
39
+ end
40
+
41
+ def many?
42
+ @many_type ||= @type == :many
43
+ end
44
+
45
+ def belongs_to?
46
+ @belongs_to_type ||= @type == :belongs_to
47
+ end
48
+
49
+ def one?
50
+ @one_type ||= @type == :one
51
+ end
52
+
53
+ def polymorphic?
54
+ !!@options[:polymorphic]
55
+ end
56
+
57
+ def as?
58
+ !!@options[:as]
59
+ end
60
+
61
+ def in_array?
62
+ !!@options[:in]
63
+ end
64
+
65
+ def embeddable?
66
+ many? && klass.embeddable?
67
+ end
68
+
69
+ def type_key_name
70
+ @type_key_name ||= many? ? '_type' : "#{as}_type"
71
+ end
72
+
73
+ def as
74
+ @options[:as] || self.name
75
+ end
76
+
77
+ def foreign_key
78
+ @options[:foreign_key] || "#{name}_id"
79
+ end
80
+
81
+ def ivar
82
+ @ivar ||= "@_#{name}"
83
+ end
84
+
85
+ def proxy_class
86
+ @proxy_class ||= begin
87
+ if many?
88
+ if self.klass.embeddable?
89
+ polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
90
+ else
91
+ if polymorphic?
92
+ ManyPolymorphicProxy
93
+ elsif as?
94
+ ManyDocumentsAsProxy
95
+ elsif in_array?
96
+ InArrayProxy
97
+ else
98
+ ManyDocumentsProxy
99
+ end
100
+ end
101
+ elsif one?
102
+ OneProxy
103
+ else
104
+ polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
105
+ end
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ # @param [Array<Module, Proc>] extensions a collection of Modules or
112
+ # Procs that extend the behaviour of this association.
113
+ def modulized_extensions(*extensions)
114
+ extensions.flatten.compact.map do |extension|
115
+ Proc === extension ? Module.new(&extension) : extension
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,30 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Associations
4
+ class BelongsToPolymorphicProxy < Proxy
5
+ undef_method :object_id
6
+
7
+ def replace(doc)
8
+ if doc
9
+ doc.save if doc.new?
10
+ id, type = doc.id, doc.class.name
11
+ end
12
+
13
+ owner[reflection.foreign_key] = id
14
+ owner[reflection.type_key_name] = type
15
+ reset
16
+ end
17
+
18
+ protected
19
+ def find_target
20
+ return nil if association_class.nil? || owner[reflection.foreign_key].nil?
21
+ association_class.find_by_id(owner[reflection.foreign_key])
22
+ end
23
+
24
+ def association_class
25
+ proxy_owner[reflection.type_key_name] ? proxy_owner[reflection.type_key_name].constantize : nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end