mongoid-pre 2.0.0.beta1

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 (246) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +24 -0
  3. data/MIT_LICENSE +20 -0
  4. data/README.rdoc +49 -0
  5. data/Rakefile +52 -0
  6. data/VERSION +1 -0
  7. data/caliper.yml +4 -0
  8. data/lib/mongoid.rb +135 -0
  9. data/lib/mongoid/associations.rb +263 -0
  10. data/lib/mongoid/associations/belongs_to_related.rb +59 -0
  11. data/lib/mongoid/associations/embedded_in.rb +64 -0
  12. data/lib/mongoid/associations/embeds_many.rb +193 -0
  13. data/lib/mongoid/associations/embeds_one.rb +95 -0
  14. data/lib/mongoid/associations/has_many_related.rb +133 -0
  15. data/lib/mongoid/associations/has_one_related.rb +81 -0
  16. data/lib/mongoid/associations/meta_data.rb +28 -0
  17. data/lib/mongoid/associations/options.rb +52 -0
  18. data/lib/mongoid/associations/proxy.rb +31 -0
  19. data/lib/mongoid/attributes.rb +185 -0
  20. data/lib/mongoid/callbacks.rb +18 -0
  21. data/lib/mongoid/collection.rb +119 -0
  22. data/lib/mongoid/collections/cyclic_iterator.rb +34 -0
  23. data/lib/mongoid/collections/master.rb +28 -0
  24. data/lib/mongoid/collections/mimic.rb +46 -0
  25. data/lib/mongoid/collections/operations.rb +41 -0
  26. data/lib/mongoid/collections/slaves.rb +44 -0
  27. data/lib/mongoid/commands.rb +161 -0
  28. data/lib/mongoid/commands/create.rb +19 -0
  29. data/lib/mongoid/commands/delete.rb +16 -0
  30. data/lib/mongoid/commands/delete_all.rb +25 -0
  31. data/lib/mongoid/commands/deletion.rb +18 -0
  32. data/lib/mongoid/commands/destroy.rb +17 -0
  33. data/lib/mongoid/commands/destroy_all.rb +25 -0
  34. data/lib/mongoid/commands/save.rb +30 -0
  35. data/lib/mongoid/components.rb +31 -0
  36. data/lib/mongoid/config.rb +86 -0
  37. data/lib/mongoid/contexts.rb +25 -0
  38. data/lib/mongoid/contexts/enumerable.rb +151 -0
  39. data/lib/mongoid/contexts/ids.rb +25 -0
  40. data/lib/mongoid/contexts/mongo.rb +285 -0
  41. data/lib/mongoid/contexts/paging.rb +42 -0
  42. data/lib/mongoid/criteria.rb +239 -0
  43. data/lib/mongoid/criterion/complex.rb +21 -0
  44. data/lib/mongoid/criterion/exclusion.rb +65 -0
  45. data/lib/mongoid/criterion/inclusion.rb +93 -0
  46. data/lib/mongoid/criterion/optional.rb +136 -0
  47. data/lib/mongoid/cursor.rb +82 -0
  48. data/lib/mongoid/deprecation.rb +22 -0
  49. data/lib/mongoid/dirty.rb +203 -0
  50. data/lib/mongoid/document.rb +306 -0
  51. data/lib/mongoid/errors.rb +77 -0
  52. data/lib/mongoid/extensions.rb +99 -0
  53. data/lib/mongoid/extensions/array/accessors.rb +17 -0
  54. data/lib/mongoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/mongoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/mongoid/extensions/array/conversions.rb +27 -0
  57. data/lib/mongoid/extensions/array/parentization.rb +13 -0
  58. data/lib/mongoid/extensions/big_decimal/conversions.rb +19 -0
  59. data/lib/mongoid/extensions/binary/conversions.rb +17 -0
  60. data/lib/mongoid/extensions/boolean/conversions.rb +16 -0
  61. data/lib/mongoid/extensions/date/conversions.rb +15 -0
  62. data/lib/mongoid/extensions/datetime/conversions.rb +17 -0
  63. data/lib/mongoid/extensions/float/conversions.rb +16 -0
  64. data/lib/mongoid/extensions/hash/accessors.rb +38 -0
  65. data/lib/mongoid/extensions/hash/assimilation.rb +30 -0
  66. data/lib/mongoid/extensions/hash/conversions.rb +15 -0
  67. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  68. data/lib/mongoid/extensions/hash/scoping.rb +12 -0
  69. data/lib/mongoid/extensions/integer/conversions.rb +16 -0
  70. data/lib/mongoid/extensions/nil/assimilation.rb +13 -0
  71. data/lib/mongoid/extensions/object/conversions.rb +27 -0
  72. data/lib/mongoid/extensions/objectid/conversions.rb +15 -0
  73. data/lib/mongoid/extensions/proc/scoping.rb +12 -0
  74. data/lib/mongoid/extensions/string/conversions.rb +15 -0
  75. data/lib/mongoid/extensions/string/inflections.rb +97 -0
  76. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  77. data/lib/mongoid/extensions/time/conversions.rb +18 -0
  78. data/lib/mongoid/extras.rb +61 -0
  79. data/lib/mongoid/factory.rb +19 -0
  80. data/lib/mongoid/field.rb +52 -0
  81. data/lib/mongoid/fields.rb +62 -0
  82. data/lib/mongoid/finders.rb +136 -0
  83. data/lib/mongoid/identity.rb +39 -0
  84. data/lib/mongoid/indexes.rb +27 -0
  85. data/lib/mongoid/javascript.rb +21 -0
  86. data/lib/mongoid/javascript/functions.yml +37 -0
  87. data/lib/mongoid/matchers.rb +36 -0
  88. data/lib/mongoid/matchers/all.rb +11 -0
  89. data/lib/mongoid/matchers/default.rb +26 -0
  90. data/lib/mongoid/matchers/exists.rb +13 -0
  91. data/lib/mongoid/matchers/gt.rb +11 -0
  92. data/lib/mongoid/matchers/gte.rb +11 -0
  93. data/lib/mongoid/matchers/in.rb +11 -0
  94. data/lib/mongoid/matchers/lt.rb +11 -0
  95. data/lib/mongoid/matchers/lte.rb +11 -0
  96. data/lib/mongoid/matchers/ne.rb +11 -0
  97. data/lib/mongoid/matchers/nin.rb +11 -0
  98. data/lib/mongoid/matchers/size.rb +11 -0
  99. data/lib/mongoid/memoization.rb +27 -0
  100. data/lib/mongoid/named_scope.rb +42 -0
  101. data/lib/mongoid/observable.rb +30 -0
  102. data/lib/mongoid/paths.rb +54 -0
  103. data/lib/mongoid/persistence.rb +27 -0
  104. data/lib/mongoid/persistence/command.rb +20 -0
  105. data/lib/mongoid/persistence/insert.rb +71 -0
  106. data/lib/mongoid/persistence/update.rb +78 -0
  107. data/lib/mongoid/scope.rb +75 -0
  108. data/lib/mongoid/state.rb +32 -0
  109. data/lib/mongoid/timestamps.rb +27 -0
  110. data/lib/mongoid/validations.rb +51 -0
  111. data/lib/mongoid/validations/associated.rb +32 -0
  112. data/lib/mongoid/validations/locale/en.yml +4 -0
  113. data/lib/mongoid/validations/uniqueness.rb +22 -0
  114. data/lib/mongoid/versioning.rb +26 -0
  115. data/mongoid.gemspec +413 -0
  116. data/perf/benchmark.rb +77 -0
  117. data/spec/integration/mongoid/associations_spec.rb +340 -0
  118. data/spec/integration/mongoid/attributes_spec.rb +22 -0
  119. data/spec/integration/mongoid/commands_spec.rb +230 -0
  120. data/spec/integration/mongoid/contexts/enumerable_spec.rb +33 -0
  121. data/spec/integration/mongoid/criteria_spec.rb +272 -0
  122. data/spec/integration/mongoid/dirty_spec.rb +70 -0
  123. data/spec/integration/mongoid/document_spec.rb +650 -0
  124. data/spec/integration/mongoid/extensions_spec.rb +22 -0
  125. data/spec/integration/mongoid/finders_spec.rb +119 -0
  126. data/spec/integration/mongoid/inheritance_spec.rb +137 -0
  127. data/spec/integration/mongoid/named_scope_spec.rb +46 -0
  128. data/spec/integration/mongoid/persistence/update_spec.rb +46 -0
  129. data/spec/models/address.rb +39 -0
  130. data/spec/models/animal.rb +6 -0
  131. data/spec/models/callbacks.rb +18 -0
  132. data/spec/models/comment.rb +8 -0
  133. data/spec/models/country_code.rb +6 -0
  134. data/spec/models/employer.rb +5 -0
  135. data/spec/models/game.rb +7 -0
  136. data/spec/models/inheritance.rb +56 -0
  137. data/spec/models/location.rb +5 -0
  138. data/spec/models/mixed_drink.rb +4 -0
  139. data/spec/models/name.rb +13 -0
  140. data/spec/models/namespacing.rb +11 -0
  141. data/spec/models/patient.rb +6 -0
  142. data/spec/models/person.rb +99 -0
  143. data/spec/models/pet.rb +7 -0
  144. data/spec/models/pet_owner.rb +6 -0
  145. data/spec/models/phone.rb +7 -0
  146. data/spec/models/post.rb +15 -0
  147. data/spec/models/translation.rb +5 -0
  148. data/spec/models/vet_visit.rb +5 -0
  149. data/spec/spec.opts +3 -0
  150. data/spec/spec_helper.rb +31 -0
  151. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +145 -0
  152. data/spec/unit/mongoid/associations/embedded_in_spec.rb +193 -0
  153. data/spec/unit/mongoid/associations/embeds_many_spec.rb +516 -0
  154. data/spec/unit/mongoid/associations/embeds_one_spec.rb +282 -0
  155. data/spec/unit/mongoid/associations/has_many_related_spec.rb +418 -0
  156. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  157. data/spec/unit/mongoid/associations/meta_data_spec.rb +88 -0
  158. data/spec/unit/mongoid/associations/options_spec.rb +192 -0
  159. data/spec/unit/mongoid/associations_spec.rb +595 -0
  160. data/spec/unit/mongoid/attributes_spec.rb +507 -0
  161. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  162. data/spec/unit/mongoid/collection_spec.rb +187 -0
  163. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  164. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  165. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  166. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  167. data/spec/unit/mongoid/commands/create_spec.rb +31 -0
  168. data/spec/unit/mongoid/commands/delete_all_spec.rb +59 -0
  169. data/spec/unit/mongoid/commands/delete_spec.rb +38 -0
  170. data/spec/unit/mongoid/commands/destroy_all_spec.rb +21 -0
  171. data/spec/unit/mongoid/commands/destroy_spec.rb +51 -0
  172. data/spec/unit/mongoid/commands/save_spec.rb +107 -0
  173. data/spec/unit/mongoid/commands_spec.rb +270 -0
  174. data/spec/unit/mongoid/config_spec.rb +176 -0
  175. data/spec/unit/mongoid/contexts/enumerable_spec.rb +421 -0
  176. data/spec/unit/mongoid/contexts/mongo_spec.rb +682 -0
  177. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  178. data/spec/unit/mongoid/criteria_spec.rb +824 -0
  179. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  180. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  181. data/spec/unit/mongoid/criterion/inclusion_spec.rb +219 -0
  182. data/spec/unit/mongoid/criterion/optional_spec.rb +319 -0
  183. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  184. data/spec/unit/mongoid/deprecation_spec.rb +24 -0
  185. data/spec/unit/mongoid/dirty_spec.rb +286 -0
  186. data/spec/unit/mongoid/document_spec.rb +818 -0
  187. data/spec/unit/mongoid/errors_spec.rb +103 -0
  188. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  189. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  190. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  191. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  192. data/spec/unit/mongoid/extensions/big_decimal/conversions_spec.rb +22 -0
  193. data/spec/unit/mongoid/extensions/binary/conversions_spec.rb +22 -0
  194. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  195. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  196. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +67 -0
  197. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  198. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  199. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  200. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  201. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  202. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  203. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  204. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  205. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +57 -0
  206. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  207. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  208. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  209. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  210. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  211. data/spec/unit/mongoid/extras_spec.rb +102 -0
  212. data/spec/unit/mongoid/factory_spec.rb +31 -0
  213. data/spec/unit/mongoid/field_spec.rb +143 -0
  214. data/spec/unit/mongoid/fields_spec.rb +181 -0
  215. data/spec/unit/mongoid/finders_spec.rb +404 -0
  216. data/spec/unit/mongoid/identity_spec.rb +109 -0
  217. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  218. data/spec/unit/mongoid/javascript_spec.rb +48 -0
  219. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  220. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  221. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  222. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  223. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  224. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  225. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  226. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  227. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  228. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  229. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  230. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  231. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  232. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  233. data/spec/unit/mongoid/observable_spec.rb +46 -0
  234. data/spec/unit/mongoid/paths_spec.rb +124 -0
  235. data/spec/unit/mongoid/persistence/insert_spec.rb +175 -0
  236. data/spec/unit/mongoid/persistence/update_spec.rb +148 -0
  237. data/spec/unit/mongoid/persistence_spec.rb +40 -0
  238. data/spec/unit/mongoid/scope_spec.rb +240 -0
  239. data/spec/unit/mongoid/state_spec.rb +83 -0
  240. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  241. data/spec/unit/mongoid/validations/associated_spec.rb +103 -0
  242. data/spec/unit/mongoid/validations/uniqueness_spec.rb +47 -0
  243. data/spec/unit/mongoid/validations_spec.rb +190 -0
  244. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  245. data/spec/unit/mongoid_spec.rb +46 -0
  246. metadata +476 -0
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Criterion #:nodoc:
4
+ module Optional
5
+ # Tells the criteria that the cursor that gets returned needs to be
6
+ # cached. This is so multiple iterations don't hit the database multiple
7
+ # times, however this is not advisable when working with large data sets
8
+ # as the entire results will get stored in memory.
9
+ #
10
+ # Example:
11
+ #
12
+ # <tt>criteria.cache</tt>
13
+ def cache
14
+ @options.merge!(:cache => true); self
15
+ end
16
+
17
+ # Will return true if the cache option has been set.
18
+ #
19
+ # Example:
20
+ #
21
+ # <tt>criteria.cached?</tt>
22
+ def cached?
23
+ @options[:cache] == true
24
+ end
25
+
26
+ # Flags the criteria to execute against a read-only slave in the pool
27
+ # instead of master.
28
+ #
29
+ # Example:
30
+ #
31
+ # <tt>criteria.enslave</tt>
32
+ def enslave
33
+ @options.merge!(:enslave => true); self
34
+ end
35
+
36
+ # Will return true if the criteria is enslaved.
37
+ #
38
+ # Example:
39
+ #
40
+ # <tt>criteria.enslaved?</tt>
41
+ def enslaved?
42
+ @options[:enslave] == true
43
+ end
44
+
45
+ # Adds a criterion to the +Criteria+ that specifies additional options
46
+ # to be passed to the Ruby driver, in the exact format for the driver.
47
+ #
48
+ # Options:
49
+ #
50
+ # extras: A +Hash+ that gets set to the driver options.
51
+ #
52
+ # Example:
53
+ #
54
+ # <tt>criteria.extras(:limit => 20, :skip => 40)</tt>
55
+ #
56
+ # Returns: <tt>self</tt>
57
+ def extras(extras)
58
+ @options.merge!(extras); filter_options; self
59
+ end
60
+
61
+ # Adds a criterion to the +Criteria+ that specifies an id that must be matched.
62
+ #
63
+ # Options:
64
+ #
65
+ # object_id: A +String+ representation of a <tt>Mongo::ObjectID</tt>
66
+ #
67
+ # Example:
68
+ #
69
+ # <tt>criteria.id("4ab2bc4b8ad548971900005c")</tt>
70
+ #
71
+ # Returns: <tt>self</tt>
72
+ def id(*args)
73
+ (args.flatten.size > 1) ? self.in(:_id => args.flatten) : (@selector[:_id] = args.first)
74
+ self
75
+ end
76
+
77
+ # Adds a criterion to the +Criteria+ that specifies the maximum number of
78
+ # results to return. This is mostly used in conjunction with <tt>skip()</tt>
79
+ # to handle paginated results.
80
+ #
81
+ # Options:
82
+ #
83
+ # value: An +Integer+ specifying the max number of results. Defaults to 20.
84
+ #
85
+ # Example:
86
+ #
87
+ # <tt>criteria.limit(100)</tt>
88
+ #
89
+ # Returns: <tt>self</tt>
90
+ def limit(value = 20)
91
+ @options[:limit] = value; self
92
+ end
93
+
94
+ # Returns the offset option. If a per_page option is in the list then it
95
+ # will replace it with a skip parameter and return the same value. Defaults
96
+ # to 20 if nothing was provided.
97
+ def offset(*args)
98
+ args.size > 0 ? skip(args.first) : @options[:skip]
99
+ end
100
+
101
+ # Adds a criterion to the +Criteria+ that specifies the sort order of
102
+ # the returned documents in the database. Similar to a SQL "ORDER BY".
103
+ #
104
+ # Options:
105
+ #
106
+ # params: An +Array+ of [field, direction] sorting pairs.
107
+ #
108
+ # Example:
109
+ #
110
+ # <tt>criteria.order_by([[:field1, :asc], [:field2, :desc]])</tt>
111
+ #
112
+ # Returns: <tt>self</tt>
113
+ def order_by(params = [])
114
+ @options[:sort] = params; self
115
+ end
116
+
117
+ # Adds a criterion to the +Criteria+ that specifies how many results to skip
118
+ # when returning Documents. This is mostly used in conjunction with
119
+ # <tt>limit()</tt> to handle paginated results, and is similar to the
120
+ # traditional "offset" parameter.
121
+ #
122
+ # Options:
123
+ #
124
+ # value: An +Integer+ specifying the number of results to skip. Defaults to 0.
125
+ #
126
+ # Example:
127
+ #
128
+ # <tt>criteria.skip(20)</tt>
129
+ #
130
+ # Returns: <tt>self</tt>
131
+ def skip(value = 0)
132
+ @options[:skip] = value; self
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ class Cursor
4
+ include Enumerable
5
+ # Operations on the Mongo::Cursor object that will not get overriden by the
6
+ # Mongoid::Cursor are defined here.
7
+ OPERATIONS = [
8
+ :admin,
9
+ :close,
10
+ :closed?,
11
+ :count,
12
+ :explain,
13
+ :fields,
14
+ :full_collection_name,
15
+ :hint,
16
+ :limit,
17
+ :order,
18
+ :query_options_hash,
19
+ :query_opts,
20
+ :selector,
21
+ :skip,
22
+ :snapshot,
23
+ :sort,
24
+ :timeout
25
+ ]
26
+
27
+ attr_reader :collection
28
+
29
+ # The operations above will all delegate to the proxied Mongo::Cursor.
30
+ #
31
+ # Example:
32
+ #
33
+ # <tt>cursor.close</tt>
34
+ OPERATIONS.each do |name|
35
+ define_method(name) { |*args| @cursor.send(name, *args) }
36
+ end
37
+
38
+ # Iterate over each document in the cursor and yield to it.
39
+ #
40
+ # Example:
41
+ #
42
+ # <tt>cursor.each { |doc| p doc.title }</tt>
43
+ def each
44
+ @cursor.each do |document|
45
+ yield Mongoid::Factory.build(@klass, document)
46
+ end
47
+ end
48
+
49
+ # Create the new +Mongoid::Cursor+.
50
+ #
51
+ # Options:
52
+ #
53
+ # collection: The Mongoid::Collection instance.
54
+ # cursor: The Mongo::Cursor to be proxied.
55
+ #
56
+ # Example:
57
+ #
58
+ # <tt>Mongoid::Cursor.new(Person, cursor)</tt>
59
+ def initialize(klass, collection, cursor)
60
+ @klass, @collection, @cursor = klass, collection, cursor
61
+ end
62
+
63
+ # Return the next document in the cursor. Will instantiate a new Mongoid
64
+ # document with the attributes.
65
+ #
66
+ # Example:
67
+ #
68
+ # <tt>cursor.next_document</tt>
69
+ def next_document
70
+ Mongoid::Factory.build(@klass, @cursor.next_document)
71
+ end
72
+
73
+ # Returns an array of all the documents in the cursor.
74
+ #
75
+ # Example:
76
+ #
77
+ # <tt>cursor.to_a</tt>
78
+ def to_a
79
+ @cursor.to_a.collect { |attrs| Mongoid::Factory.build(@klass, attrs) }
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ class Deprecation #:nodoc
4
+ include Singleton
5
+
6
+ # Alert of a deprecation. This will delegate to the logger and call warn on
7
+ # it.
8
+ #
9
+ # Example:
10
+ #
11
+ # <tt>deprecation.alert("Method no longer used")</tt>
12
+ def alert(message)
13
+ @logger.warn("Deprecation: #{message}")
14
+ end
15
+
16
+ protected
17
+ # Instantiate a new logger to stdout or a rails logger if available.
18
+ def initialize
19
+ @logger = defined?(Rails) ? Rails.logger : Logger.new($stdout)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,203 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Dirty #:nodoc:
4
+ extend ActiveSupport::Concern
5
+ module InstanceMethods #:nodoc:
6
+ # Gets the changes for a specific field.
7
+ #
8
+ # Example:
9
+ #
10
+ # person = Person.new(:title => "Sir")
11
+ # person.title = "Madam"
12
+ # person.attribute_change("title") # [ "Sir", "Madam" ]
13
+ #
14
+ # Returns:
15
+ #
16
+ # An +Array+ containing the old and new values.
17
+ def attribute_change(name)
18
+ @modifications[name]
19
+ end
20
+
21
+ # Determines if a specific field has chaged.
22
+ #
23
+ # Example:
24
+ #
25
+ # person = Person.new(:title => "Sir")
26
+ # person.title = "Madam"
27
+ # person.attribute_changed?("title") # true
28
+ #
29
+ # Returns:
30
+ #
31
+ # +true+ if changed, +false+ if not.
32
+ def attribute_changed?(name)
33
+ @modifications.include?(name)
34
+ end
35
+
36
+ # Gets the old value for a specific field.
37
+ #
38
+ # Example:
39
+ #
40
+ # person = Person.new(:title => "Sir")
41
+ # person.title = "Madam"
42
+ # person.attribute_was("title") # "Sir"
43
+ #
44
+ # Returns:
45
+ #
46
+ # The old field value.
47
+ def attribute_was(name)
48
+ change = @modifications[name]
49
+ change ? change[0] : nil
50
+ end
51
+
52
+ # Gets the names of all the fields that have changed in the document.
53
+ #
54
+ # Example:
55
+ #
56
+ # person = Person.new(:title => "Sir")
57
+ # person.title = "Madam"
58
+ # person.changed # returns [ "title" ]
59
+ #
60
+ # Returns:
61
+ #
62
+ # An +Array+ of changed field names.
63
+ def changed
64
+ @modifications.keys
65
+ end
66
+
67
+ # Alerts to whether the document has been modified or not.
68
+ #
69
+ # Example:
70
+ #
71
+ # person = Person.new(:title => "Sir")
72
+ # person.title = "Madam"
73
+ # person.changed? # returns true
74
+ #
75
+ # Returns:
76
+ #
77
+ # +true+ if changed, +false+ if not.
78
+ def changed?
79
+ !@modifications.empty?
80
+ end
81
+
82
+ # Gets all the modifications that have happened to the object as a +Hash+
83
+ # with the keys being the names of the fields, and the values being an
84
+ # +Array+ with the old value and new value.
85
+ #
86
+ # Example:
87
+ #
88
+ # person = Person.new(:title => "Sir")
89
+ # person.title = "Madam"
90
+ # person.changes # returns { "title" => [ "Sir", "Madam" ] }
91
+ #
92
+ # Returns:
93
+ #
94
+ # A +Hash+ of changes.
95
+ def changes
96
+ @modifications
97
+ end
98
+
99
+ # Call this method after save, so the changes can be properly switched.
100
+ #
101
+ # Example:
102
+ #
103
+ # <tt>person.move_changes</tt>
104
+ def move_changes
105
+ @previous_modifications = @modifications.dup
106
+ @modifications = {}
107
+ end
108
+
109
+ # Gets all the new values for each of the changed fields, to be passed to
110
+ # a MongoDB $set modifier.
111
+ #
112
+ # Example:
113
+ #
114
+ # person = Person.new(:title => "Sir")
115
+ # person.title = "Madam"
116
+ # person.setters # returns { "title" => "Madam" }
117
+ #
118
+ # Returns:
119
+ #
120
+ # A +Hash+ of new values.
121
+ def setters
122
+ @modifications.inject({}) do |sets, (field, changes)|
123
+ key = embedded ? "#{position}.#{field}" : field
124
+ sets[key] = changes[1]; sets
125
+ end
126
+ end
127
+
128
+ # Gets all the modifications that have happened to the object before the
129
+ # object was saved.
130
+ #
131
+ # Example:
132
+ #
133
+ # person = Person.new(:title => "Sir")
134
+ # person.title = "Madam"
135
+ # person.save!
136
+ # person.previous_changes # returns { "title" => [ "Sir", "Madam" ] }
137
+ #
138
+ # Returns:
139
+ #
140
+ # A +Hash+ of changes before save.
141
+ def previous_changes
142
+ @previous_modifications
143
+ end
144
+
145
+ # Resets a changed field back to its old value.
146
+ #
147
+ # Example:
148
+ #
149
+ # person = Person.new(:title => "Sir")
150
+ # person.title = "Madam"
151
+ # person.reset_attribute!("title")
152
+ # person.title # "Sir"
153
+ #
154
+ # Returns:
155
+ #
156
+ # The old field value.
157
+ def reset_attribute!(name)
158
+ value = attribute_was(name)
159
+ if value
160
+ @attributes[name] = value
161
+ @modifications.delete(name)
162
+ end
163
+ end
164
+
165
+ # Sets up the modifications hash. This occurs just after the document is
166
+ # instantiated.
167
+ #
168
+ # Example:
169
+ #
170
+ # <tt>document.setup_notifications</tt>
171
+ def setup_modifications
172
+ @modifications ||= {}
173
+ @previous_modifications ||= {}
174
+ end
175
+
176
+ protected
177
+ # Audit the change of a field's value.
178
+ def modify(name, old_value, new_value)
179
+ @attributes[name] = new_value
180
+ @modifications[name] = [ old_value, new_value ] if @modifications
181
+ end
182
+ end
183
+ module ClassMethods #:nodoc:
184
+ # Add the dynamic dirty methods. These are custom methods defined on a
185
+ # field by field basis that wrap the dirty attribute methods.
186
+ #
187
+ # Example:
188
+ #
189
+ # person = Person.new(:title => "Sir")
190
+ # person.title = "Madam"
191
+ # person.title_change # [ "Sir", "Madam" ]
192
+ # person.title_changed? # true
193
+ # person.title_was # "Sir"
194
+ # person.reset_title!
195
+ def add_dirty_methods(name)
196
+ define_method("#{name}_change") { attribute_change(name) }
197
+ define_method("#{name}_changed?") { attribute_changed?(name) }
198
+ define_method("#{name}_was") { attribute_was(name) }
199
+ define_method("reset_#{name}!") { reset_attribute!(name) }
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,306 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Document
4
+ extend ActiveSupport::Concern
5
+ included do
6
+ include Mongoid::Components
7
+
8
+ cattr_accessor :_collection, :collection_name, :embedded, :primary_key, :hereditary
9
+
10
+ self.embedded = false
11
+ self.hereditary = false
12
+ self.collection_name = self.name.collectionize
13
+
14
+ attr_accessor :association_name, :_parent
15
+ attr_reader :new_record
16
+
17
+ delegate :collection, :db, :embedded, :primary_key, :to => "self.class"
18
+ end
19
+
20
+ module ClassMethods
21
+ # Return the database associated with this class.
22
+ def db
23
+ collection.db
24
+ end
25
+
26
+ # Returns the collection associated with this +Document+. If the
27
+ # document is embedded, there will be no collection associated
28
+ # with it.
29
+ #
30
+ # Returns: <tt>Mongo::Collection</tt>
31
+ def collection
32
+ raise Errors::InvalidCollection.new(self) if embedded
33
+ self._collection ||= Mongoid::Collection.new(self, self.collection_name)
34
+ add_indexes; self._collection
35
+ end
36
+
37
+ # Perform default behavior but mark the hierarchy as being hereditary.
38
+ def inherited(subclass)
39
+ super(subclass)
40
+ self.hereditary = true
41
+ end
42
+
43
+ # Instantiate a new object, only when loaded from the database or when
44
+ # the attributes have already been typecast.
45
+ #
46
+ # Example:
47
+ #
48
+ # <tt>Person.instantiate(:title => "Sir", :age => 30)</tt>
49
+ def instantiate(attrs = nil, allocating = false)
50
+ attributes = attrs || {}
51
+ if attributes["_id"] || allocating
52
+ document = allocate
53
+ document.instance_variable_set(:@attributes, attributes)
54
+ document.setup_modifications
55
+ return document
56
+ else
57
+ return new(attrs)
58
+ end
59
+ end
60
+
61
+ # Defines the field that will be used for the id of this +Document+. This
62
+ # set the id of this +Document+ before save to a parameterized version of
63
+ # the field that was supplied. This is good for use for readable URLS in
64
+ # web applications.
65
+ #
66
+ # Example:
67
+ #
68
+ # class Person
69
+ # include Mongoid::Document
70
+ # key :first_name, :last_name
71
+ # end
72
+ def key(*fields)
73
+ self.primary_key = fields
74
+ before_save :identify
75
+ end
76
+
77
+ # Macro for setting the collection name to store in.
78
+ #
79
+ # Example:
80
+ #
81
+ # <tt>Person.store_in :populdation</tt>
82
+ def store_in(name)
83
+ self.collection_name = name.to_s
84
+ self._collection = Mongoid::Collection.new(self, name.to_s)
85
+ end
86
+
87
+ # Returns all types to query for when using this class as the base.
88
+ def _types
89
+ @_type ||= (subclasses_of(self).map { |o| o.to_s } + [ self.name ])
90
+ end
91
+
92
+ # return the list of subclassses for an object
93
+ def subclasses_of(*superclasses) #:nodoc:
94
+ subclasses = []
95
+ superclasses.each do |sup|
96
+ ObjectSpace.each_object(class << sup; self; end) do |k|
97
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
98
+ subclasses << k
99
+ end
100
+ end
101
+ end
102
+ subclasses
103
+ end
104
+ end
105
+
106
+ module InstanceMethods
107
+ # Performs equality checking on the attributes. For now we chack against
108
+ # all attributes excluding timestamps on the object.
109
+ def ==(other)
110
+ return false unless other.is_a?(Document)
111
+ attributes.except(:modified_at).except(:created_at) ==
112
+ other.attributes.except(:modified_at).except(:created_at)
113
+ end
114
+
115
+ # Delegates to ==
116
+ def eql?(comparison_object)
117
+ self == (comparison_object)
118
+ end
119
+
120
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
121
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
122
+ def hash
123
+ id.hash
124
+ end
125
+
126
+ # Introduces a child object into the +Document+ object graph. This will
127
+ # set up the relationships between the parent and child and update the
128
+ # attributes of the parent +Document+.
129
+ #
130
+ # Options:
131
+ #
132
+ # parent: The +Document+ to assimilate with.
133
+ # options: The association +Options+ for the child.
134
+ def assimilate(parent, options)
135
+ parentize(parent, options.name); notify; self
136
+ end
137
+
138
+ # Return the attributes hash with indifferent access.
139
+ def attributes
140
+ @attributes.with_indifferent_access
141
+ end
142
+
143
+ # Clone the current +Document+. This will return all attributes with the
144
+ # exception of the document's id and versions.
145
+ def clone
146
+ self.class.instantiate(@attributes.except("_id").except("versions").dup, true)
147
+ end
148
+
149
+ # Generate an id for this +Document+.
150
+ def identify
151
+ Identity.create(self)
152
+ end
153
+
154
+ # Instantiate a new +Document+, setting the Document's attributes if
155
+ # given. If no attributes are provided, they will be initialized with
156
+ # an empty +Hash+.
157
+ #
158
+ # If a primary key is defined, the document's id will be set to that key,
159
+ # otherwise it will be set to a fresh +Mongo::ObjectID+ string.
160
+ #
161
+ # Options:
162
+ #
163
+ # attrs: The attributes +Hash+ to set up the document with.
164
+ def initialize(attrs = nil)
165
+ @attributes = {}
166
+ process(attrs)
167
+ @attributes = attributes_with_defaults(@attributes)
168
+ @new_record = true if id.nil?
169
+ document = yield self if block_given?
170
+ identify
171
+ end
172
+
173
+ # Returns the class name plus its attributes.
174
+ def inspect
175
+ attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" } * ", "
176
+ "#<#{self.class.name} _id: #{id}, #{attrs}>"
177
+ end
178
+
179
+ # Notify observers of an update.
180
+ #
181
+ # Example:
182
+ #
183
+ # <tt>person.notify</tt>
184
+ def notify
185
+ notify_observers(self)
186
+ end
187
+
188
+ # Sets up a child/parent association. This is used for newly created
189
+ # objects so they can be properly added to the graph and have the parent
190
+ # observers set up properly.
191
+ #
192
+ # Options:
193
+ #
194
+ # abject: The parent object that needs to be set for the child.
195
+ # association_name: The name of the association for the child.
196
+ #
197
+ # Example:
198
+ #
199
+ # <tt>address.parentize(person, :addresses)</tt>
200
+ def parentize(object, association_name)
201
+ self._parent = object
202
+ self.association_name = association_name.to_s
203
+ add_observer(object)
204
+ end
205
+
206
+ # Return the attributes hash.
207
+ def raw_attributes
208
+ @attributes
209
+ end
210
+
211
+ # Reloads the +Document+ attributes from the database.
212
+ def reload
213
+ @attributes = collection.find_one(:_id => id)
214
+ self
215
+ end
216
+
217
+ # Remove a child document from this parent +Document+. Will reset the
218
+ # memoized association and notify the parent of the change.
219
+ def remove(child)
220
+ name = child.association_name
221
+ reset(name) { @attributes.remove(name, child.raw_attributes) }
222
+ notify
223
+ end
224
+
225
+ # Return the root +Document+ in the object graph. If the current +Document+
226
+ # is the root object in the graph it will return self.
227
+ def _root
228
+ object = self
229
+ while (object._parent) do object = object._parent; end
230
+ object || self
231
+ end
232
+
233
+ # Return an array with this +Document+ only in it.
234
+ def to_a
235
+ [ self ]
236
+ end
237
+
238
+ # Return this document as a JSON string. Nothing special is required here
239
+ # since Mongoid bubbles up all the child associations to the parent
240
+ # attribute +Hash+ using observers throughout the +Document+ lifecycle.
241
+ #
242
+ # Example:
243
+ #
244
+ # <tt>person.to_json</tt>
245
+ def to_json(options = nil)
246
+ attributes.to_json(options)
247
+ end
248
+
249
+ # Return an object to be encoded into a JSON string.
250
+ # Used by Rails 3's object->JSON chain to create JSON
251
+ # in a backend-agnostic way
252
+ #
253
+ # Example:
254
+ #
255
+ # <tt>person.as_json</tt>
256
+ def as_json(options = nil)
257
+ attributes
258
+ end
259
+
260
+ # Return this document as an object to be encoded as JSON,
261
+ # with any particular items modified on a per-encoder basis.
262
+ # Nothing special is required here since Mongoid bubbles up
263
+ # all the child associations to the parent attribute +Hash+
264
+ # using observers throughout the +Document+ lifecycle.
265
+ #
266
+ # Example:
267
+ #
268
+ # <tt>person.encode_json(encoder)</tt>
269
+ def encode_json(encoder)
270
+ attributes
271
+ end
272
+
273
+ # Returns the id of the Document, used in Rails compatibility.
274
+ def to_param
275
+ id
276
+ end
277
+
278
+ # Observe a notify call from a child +Document+. This will either update
279
+ # existing attributes on the +Document+ or clear them out for the child if
280
+ # the clear boolean is provided.
281
+ #
282
+ # Options:
283
+ #
284
+ # child: The child +Document+ that sent the notification.
285
+ # clear: Will clear out the child's attributes if set to true.
286
+ #
287
+ # This will also cause the observing +Document+ to notify it's parent if
288
+ # there is any.
289
+ def observe(child, clear = false)
290
+ name = child.association_name
291
+ attrs = child.instance_variable_get(:@attributes)
292
+ clear ? @attributes.delete(name) : @attributes.insert(name, attrs)
293
+ notify
294
+ end
295
+
296
+ protected
297
+ # apply default values to attributes - calling procs as required
298
+ def attributes_with_defaults(attributes = {})
299
+ default_values = defaults.merge(attributes)
300
+ default_values.each_pair do |key, val|
301
+ default_values[key] = val.call if val.respond_to?(:call)
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end