thorsson-mongo_mapper 0.8.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 (134) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +27 -0
  3. data/bin/mmconsole +60 -0
  4. data/examples/keys.rb +40 -0
  5. data/examples/modifiers/set.rb +25 -0
  6. data/examples/plugins.rb +41 -0
  7. data/examples/querying.rb +35 -0
  8. data/examples/scopes.rb +52 -0
  9. data/lib/mongo_mapper.rb +79 -0
  10. data/lib/mongo_mapper/connection.rb +83 -0
  11. data/lib/mongo_mapper/document.rb +41 -0
  12. data/lib/mongo_mapper/embedded_document.rb +31 -0
  13. data/lib/mongo_mapper/exceptions.rb +27 -0
  14. data/lib/mongo_mapper/extensions/array.rb +19 -0
  15. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  16. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  17. data/lib/mongo_mapper/extensions/date.rb +25 -0
  18. data/lib/mongo_mapper/extensions/float.rb +14 -0
  19. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  20. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  21. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  22. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  23. data/lib/mongo_mapper/extensions/object.rb +27 -0
  24. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  25. data/lib/mongo_mapper/extensions/set.rb +20 -0
  26. data/lib/mongo_mapper/extensions/string.rb +18 -0
  27. data/lib/mongo_mapper/extensions/time.rb +29 -0
  28. data/lib/mongo_mapper/plugins.rb +16 -0
  29. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  30. data/lib/mongo_mapper/plugins/associations.rb +97 -0
  31. data/lib/mongo_mapper/plugins/associations/base.rb +124 -0
  32. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +29 -0
  33. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +24 -0
  34. data/lib/mongo_mapper/plugins/associations/collection.rb +22 -0
  35. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +40 -0
  36. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +127 -0
  37. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  38. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +110 -0
  39. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  40. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  41. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  42. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +40 -0
  43. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +68 -0
  44. data/lib/mongo_mapper/plugins/associations/proxy.rb +126 -0
  45. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  46. data/lib/mongo_mapper/plugins/callbacks.rb +241 -0
  47. data/lib/mongo_mapper/plugins/clone.rb +19 -0
  48. data/lib/mongo_mapper/plugins/descendants.rb +17 -0
  49. data/lib/mongo_mapper/plugins/dirty.rb +120 -0
  50. data/lib/mongo_mapper/plugins/document.rb +41 -0
  51. data/lib/mongo_mapper/plugins/dynamic_querying.rb +43 -0
  52. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  53. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  54. data/lib/mongo_mapper/plugins/equality.rb +17 -0
  55. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  56. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  57. data/lib/mongo_mapper/plugins/inspect.rb +15 -0
  58. data/lib/mongo_mapper/plugins/keys.rb +309 -0
  59. data/lib/mongo_mapper/plugins/keys/key.rb +55 -0
  60. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  61. data/lib/mongo_mapper/plugins/modifiers.rb +112 -0
  62. data/lib/mongo_mapper/plugins/pagination.rb +14 -0
  63. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  64. data/lib/mongo_mapper/plugins/protected.rb +53 -0
  65. data/lib/mongo_mapper/plugins/querying.rb +176 -0
  66. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  67. data/lib/mongo_mapper/plugins/querying/plucky_methods.rb +15 -0
  68. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  69. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  70. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  71. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  72. data/lib/mongo_mapper/plugins/serialization.rb +76 -0
  73. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  74. data/lib/mongo_mapper/plugins/userstamps.rb +15 -0
  75. data/lib/mongo_mapper/plugins/validations.rb +50 -0
  76. data/lib/mongo_mapper/support/descendant_appends.rb +45 -0
  77. data/lib/mongo_mapper/version.rb +4 -0
  78. data/test/_NOTE_ON_TESTING +1 -0
  79. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  80. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  81. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  82. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  83. data/test/functional/associations/test_many_documents_proxy.rb +575 -0
  84. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  85. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  86. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  87. data/test/functional/associations/test_one_embedded_proxy.rb +67 -0
  88. data/test/functional/associations/test_one_proxy.rb +182 -0
  89. data/test/functional/test_accessible.rb +168 -0
  90. data/test/functional/test_associations.rb +44 -0
  91. data/test/functional/test_binary.rb +27 -0
  92. data/test/functional/test_caching.rb +76 -0
  93. data/test/functional/test_callbacks.rb +151 -0
  94. data/test/functional/test_dirty.rb +163 -0
  95. data/test/functional/test_document.rb +253 -0
  96. data/test/functional/test_dynamic_querying.rb +75 -0
  97. data/test/functional/test_embedded_document.rb +210 -0
  98. data/test/functional/test_identity_map.rb +506 -0
  99. data/test/functional/test_indexes.rb +42 -0
  100. data/test/functional/test_logger.rb +20 -0
  101. data/test/functional/test_modifiers.rb +416 -0
  102. data/test/functional/test_pagination.rb +91 -0
  103. data/test/functional/test_protected.rb +175 -0
  104. data/test/functional/test_querying.rb +873 -0
  105. data/test/functional/test_safe.rb +76 -0
  106. data/test/functional/test_sci.rb +230 -0
  107. data/test/functional/test_scopes.rb +171 -0
  108. data/test/functional/test_string_id_compatibility.rb +67 -0
  109. data/test/functional/test_timestamps.rb +62 -0
  110. data/test/functional/test_userstamps.rb +27 -0
  111. data/test/functional/test_validations.rb +342 -0
  112. data/test/models.rb +233 -0
  113. data/test/test_active_model_lint.rb +13 -0
  114. data/test/test_helper.rb +104 -0
  115. data/test/unit/associations/test_base.rb +212 -0
  116. data/test/unit/associations/test_proxy.rb +105 -0
  117. data/test/unit/serializers/test_json_serializer.rb +202 -0
  118. data/test/unit/test_clone.rb +69 -0
  119. data/test/unit/test_descendant_appends.rb +71 -0
  120. data/test/unit/test_document.rb +213 -0
  121. data/test/unit/test_dynamic_finder.rb +125 -0
  122. data/test/unit/test_embedded_document.rb +644 -0
  123. data/test/unit/test_extensions.rb +380 -0
  124. data/test/unit/test_key.rb +185 -0
  125. data/test/unit/test_keys.rb +55 -0
  126. data/test/unit/test_mongo_mapper.rb +110 -0
  127. data/test/unit/test_pagination.rb +11 -0
  128. data/test/unit/test_plugins.rb +50 -0
  129. data/test/unit/test_rails.rb +181 -0
  130. data/test/unit/test_rails_compatibility.rb +52 -0
  131. data/test/unit/test_serialization.rb +51 -0
  132. data/test/unit/test_time_zones.rb +39 -0
  133. data/test/unit/test_validations.rb +544 -0
  134. metadata +316 -0
@@ -0,0 +1,44 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Accessible
4
+ module ClassMethods
5
+ def attr_accessible(*attrs)
6
+ raise AccessibleOrProtected.new(name) if try(:protected_attributes?)
7
+ self.write_inheritable_attribute(:attr_accessible, Set.new(attrs) + (accessible_attributes || []))
8
+ end
9
+
10
+ def accessible_attributes?
11
+ !accessible_attributes.nil?
12
+ end
13
+
14
+ def accessible_attributes
15
+ self.read_inheritable_attribute(:attr_accessible)
16
+ end
17
+ end
18
+
19
+ module InstanceMethods
20
+ def assign(attrs={})
21
+ super(filter_inaccessible_attrs(attrs))
22
+ end
23
+
24
+ def update_attributes(attrs={})
25
+ super(filter_inaccessible_attrs(attrs))
26
+ end
27
+
28
+ def update_attributes!(attrs={})
29
+ super(filter_inaccessible_attrs(attrs))
30
+ end
31
+
32
+ def accessible_attributes
33
+ self.class.accessible_attributes
34
+ end
35
+
36
+ protected
37
+ def filter_inaccessible_attrs(attrs)
38
+ return attrs if accessible_attributes.blank? || attrs.blank?
39
+ attrs.dup.delete_if { |key, val| !accessible_attributes.include?(key.to_sym) }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ module ClassMethods
6
+ def inherited(subclass)
7
+ subclass.associations = associations.dup
8
+ super
9
+ end
10
+
11
+ def belongs_to(association_id, options={}, &extension)
12
+ create_association(:belongs_to, association_id, options, &extension)
13
+ end
14
+
15
+ def many(association_id, options={}, &extension)
16
+ create_association(:many, association_id, options, &extension)
17
+ end
18
+
19
+ def one(association_id, options={}, &extension)
20
+ create_association(:one, association_id, options, &extension)
21
+ end
22
+
23
+ def associations
24
+ @associations ||= HashWithIndifferentAccess.new
25
+ end
26
+
27
+ def associations=(hash)
28
+ @associations = hash
29
+ end
30
+
31
+ private
32
+ def create_association(type, name, options, &extension)
33
+ association = Associations::Base.new(type, name, options, &extension)
34
+ associations[association.name] = association
35
+
36
+ define_method(association.name) do
37
+ get_proxy(association)
38
+ end
39
+
40
+ define_method("#{association.name}=") do |value|
41
+ get_proxy(association).replace(value)
42
+ value
43
+ end
44
+
45
+ if association.one? || association.belongs_to?
46
+ define_method("#{association.name}?") do
47
+ get_proxy(association).present?
48
+ end
49
+ end
50
+
51
+ if association.options[:dependent] && association.many? && !association.embeddable?
52
+ after_destroy do |doc|
53
+ case association.options[:dependent]
54
+ when :destroy
55
+ doc.get_proxy(association).destroy_all
56
+ when :delete_all
57
+ doc.get_proxy(association).delete_all
58
+ when :nullify
59
+ doc.get_proxy(association).nullify
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ module InstanceMethods
67
+ def associations
68
+ self.class.associations
69
+ end
70
+
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
+ proxy
85
+ end
86
+
87
+ def save_to_collection(options={})
88
+ super
89
+ associations.each do |association_name, association|
90
+ proxy = get_proxy(association)
91
+ proxy.save_to_collection(options) if proxy.proxy_respond_to?(:save_to_collection)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,124 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class Base
6
+ attr_reader :type, :name, :options, :query_options
7
+
8
+ # Options that should not be considered MongoDB query options/criteria
9
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic]
10
+
11
+ def initialize(type, name, options={}, &extension)
12
+ @type, @name, @options, @query_options, @original_options = type, name, {}, {}, options
13
+ options.symbolize_keys!
14
+ options[:extend] = modularized_extensions(extension, options[:extend])
15
+ separate_options_and_conditions
16
+ end
17
+
18
+ def class_name
19
+ return @class_name if defined?(@class_name)
20
+
21
+ @class_name =
22
+ if cn = options[:class_name]
23
+ cn
24
+ elsif many?
25
+ name.to_s.singularize.camelize
26
+ else
27
+ name.to_s.camelize
28
+ end
29
+ end
30
+
31
+ def klass
32
+ @klass ||= options[:class] || class_name.constantize
33
+ end
34
+
35
+ def many?
36
+ @type == :many
37
+ end
38
+
39
+ def belongs_to?
40
+ @type == :belongs_to
41
+ end
42
+
43
+ def one?
44
+ @type == :one
45
+ end
46
+
47
+ def polymorphic?
48
+ !!@options[:polymorphic]
49
+ end
50
+
51
+ def as?
52
+ !!@options[:as]
53
+ end
54
+
55
+ def in_array?
56
+ !!@options[:in]
57
+ end
58
+
59
+ def embeddable?
60
+ (one? || many?) && klass.embeddable?
61
+ end
62
+
63
+ def type_key_name
64
+ many? ? '_type' : "#{as}_type"
65
+ end
66
+
67
+ def as
68
+ @options[:as] || self.name
69
+ end
70
+
71
+ def foreign_key
72
+ @options[:foreign_key] || "#{name}_id"
73
+ end
74
+
75
+ def ivar
76
+ @ivar ||= "@_#{name}"
77
+ end
78
+
79
+ # hate this, need to revisit
80
+ def proxy_class
81
+ return @proxy_class if defined?(@proxy_class)
82
+
83
+ @proxy_class =
84
+ if many?
85
+ if klass.embeddable?
86
+ polymorphic? ? ManyEmbeddedPolymorphicProxy : ManyEmbeddedProxy
87
+ else
88
+ if polymorphic?
89
+ ManyPolymorphicProxy
90
+ elsif as?
91
+ ManyDocumentsAsProxy
92
+ elsif in_array?
93
+ InArrayProxy
94
+ else
95
+ ManyDocumentsProxy
96
+ end
97
+ end
98
+ elsif one?
99
+ klass.embeddable? ? OneEmbeddedProxy : OneProxy
100
+ else
101
+ polymorphic? ? BelongsToPolymorphicProxy : BelongsToProxy
102
+ end
103
+ end
104
+
105
+ private
106
+ def separate_options_and_conditions
107
+ @original_options.each_pair do |key, value|
108
+ if AssociationOptions.include?(key)
109
+ @options[key] = value
110
+ else
111
+ @query_options[key] = value
112
+ end
113
+ end
114
+ end
115
+
116
+ def modularized_extensions(*extensions)
117
+ extensions.flatten.compact.map do |extension|
118
+ Proc === extension ? Module.new(&extension) : extension
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class BelongsToPolymorphicProxy < Proxy
6
+ def replace(doc)
7
+ if doc
8
+ doc.save if doc.new?
9
+ id, type = doc.id, doc.class.name
10
+ end
11
+
12
+ proxy_owner[association.foreign_key] = id
13
+ proxy_owner[association.type_key_name] = type
14
+ reset
15
+ end
16
+
17
+ protected
18
+ def find_target
19
+ return nil if association_class.nil? || proxy_owner[association.foreign_key].nil?
20
+ association_class.find_by_id(proxy_owner[association.foreign_key])
21
+ end
22
+
23
+ def association_class
24
+ proxy_owner[association.type_key_name] ? proxy_owner[association.type_key_name].constantize : nil
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class BelongsToProxy < Proxy
6
+ def replace(doc)
7
+ if doc
8
+ doc.save if doc.new?
9
+ id = doc.id
10
+ end
11
+
12
+ proxy_owner[association.foreign_key] = id
13
+ reload
14
+ end
15
+
16
+ protected
17
+ def find_target
18
+ return nil if proxy_owner[association.foreign_key].nil?
19
+ klass.find_by_id(proxy_owner[association.foreign_key])
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class Collection < Proxy
6
+ def to_ary
7
+ load_target
8
+ if target.is_a?(Array)
9
+ target.to_ary
10
+ else
11
+ Array(target)
12
+ end
13
+ end
14
+
15
+ def reset
16
+ super
17
+ target = []
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class EmbeddedCollection < Collection
6
+ def build(attributes={})
7
+ doc = klass.new(attributes)
8
+ assign_references(doc)
9
+ self << doc
10
+ doc
11
+ end
12
+
13
+ def find(id)
14
+ load_target
15
+ target.detect { |item| item.id.to_s == id || item.id == id }
16
+ end
17
+
18
+ def count
19
+ load_target
20
+ target.size
21
+ end
22
+
23
+ def <<(*docs)
24
+ load_target
25
+ docs.each do |doc|
26
+ assign_references(doc)
27
+ target << doc
28
+ end
29
+ end
30
+ alias_method :push, :<<
31
+ alias_method :concat, :<<
32
+
33
+ private
34
+ def assign_references(*docs)
35
+ docs.each { |doc| doc._parent_document = proxy_owner }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: UTF-8
2
+ module MongoMapper
3
+ module Plugins
4
+ module Associations
5
+ class InArrayProxy < Collection
6
+ include DynamicQuerying::ClassMethods
7
+
8
+ def find(*args)
9
+ query.find(*scoped_ids(args))
10
+ end
11
+
12
+ def find!(*args)
13
+ query.find!(*scoped_ids(args))
14
+ end
15
+
16
+ def paginate(options)
17
+ query.paginate(options)
18
+ end
19
+
20
+ def all(options={})
21
+ query(options).all
22
+ end
23
+
24
+ def first(options={})
25
+ query(options).first
26
+ end
27
+
28
+ def last(options={})
29
+ query(options).last
30
+ end
31
+
32
+ def count(options={})
33
+ options.blank? ? ids.size : query(options).count
34
+ end
35
+
36
+ def destroy_all(options={})
37
+ all(options).each do |doc|
38
+ ids.delete(doc.id)
39
+ doc.destroy
40
+ end
41
+ reset
42
+ end
43
+
44
+ def delete_all(options={})
45
+ docs = query(options).fields(:_id).all
46
+ docs.each { |doc| ids.delete(doc.id) }
47
+ klass.delete(docs.map(&:id))
48
+ reset
49
+ end
50
+
51
+ def nullify
52
+ replace([])
53
+ reset
54
+ end
55
+
56
+ def create(attrs={})
57
+ doc = klass.create(attrs)
58
+ unless doc.new?
59
+ ids << doc.id
60
+ proxy_owner.save
61
+ reset
62
+ end
63
+ doc
64
+ end
65
+
66
+ def create!(attrs={})
67
+ doc = klass.create!(attrs)
68
+ unless doc.new?
69
+ ids << doc.id
70
+ proxy_owner.save
71
+ reset
72
+ end
73
+ doc
74
+ end
75
+
76
+ def <<(*docs)
77
+ flatten_deeper(docs).each do |doc|
78
+ doc.save if doc.new?
79
+ unless ids.include?(doc.id)
80
+ ids << doc.id
81
+ end
82
+ end
83
+ reset
84
+ end
85
+ alias_method :push, :<<
86
+ alias_method :concat, :<<
87
+
88
+ def replace(docs)
89
+ doc_ids = docs.map do |doc|
90
+ doc.save if doc.new?
91
+ doc.id
92
+ end
93
+ ids.replace(doc_ids.uniq)
94
+ reset
95
+ end
96
+
97
+ private
98
+ def query(options={})
99
+ klass.
100
+ query(association.query_options).
101
+ update(options).
102
+ update(criteria)
103
+ end
104
+
105
+ def criteria
106
+ {:_id => ids}
107
+ end
108
+
109
+ def scoped_ids(args)
110
+ valid = args.flatten.select do |id|
111
+ id = ObjectId.to_mongo(id) if klass.using_object_id?
112
+ ids.include?(id)
113
+ end
114
+ valid.empty? ? nil : valid
115
+ end
116
+
117
+ def find_target
118
+ ids.blank? ? [] : all
119
+ end
120
+
121
+ def ids
122
+ proxy_owner[options[:in]]
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end