humanoid 1.2.7

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 (210) hide show
  1. data/.gitignore +6 -0
  2. data/.watchr +29 -0
  3. data/HISTORY +342 -0
  4. data/MIT_LICENSE +20 -0
  5. data/README.rdoc +56 -0
  6. data/Rakefile +53 -0
  7. data/VERSION +1 -0
  8. data/caliper.yml +4 -0
  9. data/humanoid.gemspec +374 -0
  10. data/lib/humanoid.rb +111 -0
  11. data/lib/humanoid/associations.rb +258 -0
  12. data/lib/humanoid/associations/belongs_to.rb +64 -0
  13. data/lib/humanoid/associations/belongs_to_related.rb +62 -0
  14. data/lib/humanoid/associations/has_many.rb +180 -0
  15. data/lib/humanoid/associations/has_many_related.rb +109 -0
  16. data/lib/humanoid/associations/has_one.rb +95 -0
  17. data/lib/humanoid/associations/has_one_related.rb +81 -0
  18. data/lib/humanoid/associations/options.rb +57 -0
  19. data/lib/humanoid/associations/proxy.rb +31 -0
  20. data/lib/humanoid/attributes.rb +184 -0
  21. data/lib/humanoid/callbacks.rb +23 -0
  22. data/lib/humanoid/collection.rb +118 -0
  23. data/lib/humanoid/collections/cyclic_iterator.rb +34 -0
  24. data/lib/humanoid/collections/master.rb +28 -0
  25. data/lib/humanoid/collections/mimic.rb +46 -0
  26. data/lib/humanoid/collections/operations.rb +41 -0
  27. data/lib/humanoid/collections/slaves.rb +44 -0
  28. data/lib/humanoid/commands.rb +182 -0
  29. data/lib/humanoid/commands/create.rb +21 -0
  30. data/lib/humanoid/commands/delete.rb +16 -0
  31. data/lib/humanoid/commands/delete_all.rb +23 -0
  32. data/lib/humanoid/commands/deletion.rb +18 -0
  33. data/lib/humanoid/commands/destroy.rb +19 -0
  34. data/lib/humanoid/commands/destroy_all.rb +23 -0
  35. data/lib/humanoid/commands/save.rb +27 -0
  36. data/lib/humanoid/components.rb +24 -0
  37. data/lib/humanoid/config.rb +84 -0
  38. data/lib/humanoid/contexts.rb +25 -0
  39. data/lib/humanoid/contexts/enumerable.rb +117 -0
  40. data/lib/humanoid/contexts/ids.rb +25 -0
  41. data/lib/humanoid/contexts/mongo.rb +224 -0
  42. data/lib/humanoid/contexts/paging.rb +42 -0
  43. data/lib/humanoid/criteria.rb +259 -0
  44. data/lib/humanoid/criterion/complex.rb +21 -0
  45. data/lib/humanoid/criterion/exclusion.rb +65 -0
  46. data/lib/humanoid/criterion/inclusion.rb +91 -0
  47. data/lib/humanoid/criterion/optional.rb +128 -0
  48. data/lib/humanoid/cursor.rb +82 -0
  49. data/lib/humanoid/document.rb +300 -0
  50. data/lib/humanoid/enslavement.rb +38 -0
  51. data/lib/humanoid/errors.rb +77 -0
  52. data/lib/humanoid/extensions.rb +84 -0
  53. data/lib/humanoid/extensions/array/accessors.rb +17 -0
  54. data/lib/humanoid/extensions/array/aliasing.rb +4 -0
  55. data/lib/humanoid/extensions/array/assimilation.rb +26 -0
  56. data/lib/humanoid/extensions/array/conversions.rb +29 -0
  57. data/lib/humanoid/extensions/array/parentization.rb +13 -0
  58. data/lib/humanoid/extensions/boolean/conversions.rb +16 -0
  59. data/lib/humanoid/extensions/date/conversions.rb +15 -0
  60. data/lib/humanoid/extensions/datetime/conversions.rb +17 -0
  61. data/lib/humanoid/extensions/float/conversions.rb +16 -0
  62. data/lib/humanoid/extensions/hash/accessors.rb +38 -0
  63. data/lib/humanoid/extensions/hash/assimilation.rb +30 -0
  64. data/lib/humanoid/extensions/hash/conversions.rb +15 -0
  65. data/lib/humanoid/extensions/hash/criteria_helpers.rb +20 -0
  66. data/lib/humanoid/extensions/hash/scoping.rb +12 -0
  67. data/lib/humanoid/extensions/integer/conversions.rb +16 -0
  68. data/lib/humanoid/extensions/nil/assimilation.rb +13 -0
  69. data/lib/humanoid/extensions/object/conversions.rb +33 -0
  70. data/lib/humanoid/extensions/proc/scoping.rb +12 -0
  71. data/lib/humanoid/extensions/string/conversions.rb +15 -0
  72. data/lib/humanoid/extensions/string/inflections.rb +97 -0
  73. data/lib/humanoid/extensions/symbol/inflections.rb +36 -0
  74. data/lib/humanoid/extensions/time/conversions.rb +18 -0
  75. data/lib/humanoid/factory.rb +19 -0
  76. data/lib/humanoid/field.rb +39 -0
  77. data/lib/humanoid/fields.rb +62 -0
  78. data/lib/humanoid/finders.rb +224 -0
  79. data/lib/humanoid/identity.rb +39 -0
  80. data/lib/humanoid/indexes.rb +30 -0
  81. data/lib/humanoid/matchers.rb +36 -0
  82. data/lib/humanoid/matchers/all.rb +11 -0
  83. data/lib/humanoid/matchers/default.rb +26 -0
  84. data/lib/humanoid/matchers/exists.rb +13 -0
  85. data/lib/humanoid/matchers/gt.rb +11 -0
  86. data/lib/humanoid/matchers/gte.rb +11 -0
  87. data/lib/humanoid/matchers/in.rb +11 -0
  88. data/lib/humanoid/matchers/lt.rb +11 -0
  89. data/lib/humanoid/matchers/lte.rb +11 -0
  90. data/lib/humanoid/matchers/ne.rb +11 -0
  91. data/lib/humanoid/matchers/nin.rb +11 -0
  92. data/lib/humanoid/matchers/size.rb +11 -0
  93. data/lib/humanoid/memoization.rb +27 -0
  94. data/lib/humanoid/named_scope.rb +40 -0
  95. data/lib/humanoid/scope.rb +75 -0
  96. data/lib/humanoid/timestamps.rb +30 -0
  97. data/lib/humanoid/versioning.rb +28 -0
  98. data/perf/benchmark.rb +77 -0
  99. data/spec/integration/humanoid/associations_spec.rb +301 -0
  100. data/spec/integration/humanoid/attributes_spec.rb +22 -0
  101. data/spec/integration/humanoid/commands_spec.rb +216 -0
  102. data/spec/integration/humanoid/contexts/enumerable_spec.rb +33 -0
  103. data/spec/integration/humanoid/criteria_spec.rb +224 -0
  104. data/spec/integration/humanoid/document_spec.rb +587 -0
  105. data/spec/integration/humanoid/extensions_spec.rb +26 -0
  106. data/spec/integration/humanoid/finders_spec.rb +119 -0
  107. data/spec/integration/humanoid/inheritance_spec.rb +137 -0
  108. data/spec/integration/humanoid/named_scope_spec.rb +46 -0
  109. data/spec/models/address.rb +39 -0
  110. data/spec/models/animal.rb +6 -0
  111. data/spec/models/comment.rb +8 -0
  112. data/spec/models/country_code.rb +6 -0
  113. data/spec/models/employer.rb +5 -0
  114. data/spec/models/game.rb +6 -0
  115. data/spec/models/inheritance.rb +56 -0
  116. data/spec/models/location.rb +5 -0
  117. data/spec/models/mixed_drink.rb +4 -0
  118. data/spec/models/name.rb +13 -0
  119. data/spec/models/namespacing.rb +11 -0
  120. data/spec/models/patient.rb +4 -0
  121. data/spec/models/person.rb +98 -0
  122. data/spec/models/pet.rb +7 -0
  123. data/spec/models/pet_owner.rb +6 -0
  124. data/spec/models/phone.rb +7 -0
  125. data/spec/models/post.rb +15 -0
  126. data/spec/models/translation.rb +5 -0
  127. data/spec/models/vet_visit.rb +5 -0
  128. data/spec/spec.opts +3 -0
  129. data/spec/spec_helper.rb +31 -0
  130. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +141 -0
  131. data/spec/unit/mongoid/associations/belongs_to_spec.rb +193 -0
  132. data/spec/unit/mongoid/associations/has_many_related_spec.rb +387 -0
  133. data/spec/unit/mongoid/associations/has_many_spec.rb +471 -0
  134. data/spec/unit/mongoid/associations/has_one_related_spec.rb +179 -0
  135. data/spec/unit/mongoid/associations/has_one_spec.rb +282 -0
  136. data/spec/unit/mongoid/associations/options_spec.rb +191 -0
  137. data/spec/unit/mongoid/associations_spec.rb +545 -0
  138. data/spec/unit/mongoid/attributes_spec.rb +484 -0
  139. data/spec/unit/mongoid/callbacks_spec.rb +55 -0
  140. data/spec/unit/mongoid/collection_spec.rb +171 -0
  141. data/spec/unit/mongoid/collections/cyclic_iterator_spec.rb +75 -0
  142. data/spec/unit/mongoid/collections/master_spec.rb +41 -0
  143. data/spec/unit/mongoid/collections/mimic_spec.rb +43 -0
  144. data/spec/unit/mongoid/collections/slaves_spec.rb +81 -0
  145. data/spec/unit/mongoid/commands/create_spec.rb +30 -0
  146. data/spec/unit/mongoid/commands/delete_all_spec.rb +58 -0
  147. data/spec/unit/mongoid/commands/delete_spec.rb +35 -0
  148. data/spec/unit/mongoid/commands/destroy_all_spec.rb +23 -0
  149. data/spec/unit/mongoid/commands/destroy_spec.rb +44 -0
  150. data/spec/unit/mongoid/commands/save_spec.rb +105 -0
  151. data/spec/unit/mongoid/commands_spec.rb +282 -0
  152. data/spec/unit/mongoid/config_spec.rb +165 -0
  153. data/spec/unit/mongoid/contexts/enumerable_spec.rb +374 -0
  154. data/spec/unit/mongoid/contexts/mongo_spec.rb +505 -0
  155. data/spec/unit/mongoid/contexts_spec.rb +25 -0
  156. data/spec/unit/mongoid/criteria_spec.rb +769 -0
  157. data/spec/unit/mongoid/criterion/complex_spec.rb +19 -0
  158. data/spec/unit/mongoid/criterion/exclusion_spec.rb +91 -0
  159. data/spec/unit/mongoid/criterion/inclusion_spec.rb +211 -0
  160. data/spec/unit/mongoid/criterion/optional_spec.rb +329 -0
  161. data/spec/unit/mongoid/cursor_spec.rb +74 -0
  162. data/spec/unit/mongoid/document_spec.rb +986 -0
  163. data/spec/unit/mongoid/enslavement_spec.rb +63 -0
  164. data/spec/unit/mongoid/errors_spec.rb +103 -0
  165. data/spec/unit/mongoid/extensions/array/accessors_spec.rb +50 -0
  166. data/spec/unit/mongoid/extensions/array/assimilation_spec.rb +24 -0
  167. data/spec/unit/mongoid/extensions/array/conversions_spec.rb +35 -0
  168. data/spec/unit/mongoid/extensions/array/parentization_spec.rb +20 -0
  169. data/spec/unit/mongoid/extensions/boolean/conversions_spec.rb +49 -0
  170. data/spec/unit/mongoid/extensions/date/conversions_spec.rb +102 -0
  171. data/spec/unit/mongoid/extensions/datetime/conversions_spec.rb +70 -0
  172. data/spec/unit/mongoid/extensions/float/conversions_spec.rb +61 -0
  173. data/spec/unit/mongoid/extensions/hash/accessors_spec.rb +184 -0
  174. data/spec/unit/mongoid/extensions/hash/assimilation_spec.rb +46 -0
  175. data/spec/unit/mongoid/extensions/hash/conversions_spec.rb +21 -0
  176. data/spec/unit/mongoid/extensions/hash/criteria_helpers_spec.rb +17 -0
  177. data/spec/unit/mongoid/extensions/hash/scoping_spec.rb +14 -0
  178. data/spec/unit/mongoid/extensions/integer/conversions_spec.rb +61 -0
  179. data/spec/unit/mongoid/extensions/nil/assimilation_spec.rb +24 -0
  180. data/spec/unit/mongoid/extensions/object/conversions_spec.rb +43 -0
  181. data/spec/unit/mongoid/extensions/proc/scoping_spec.rb +34 -0
  182. data/spec/unit/mongoid/extensions/string/conversions_spec.rb +17 -0
  183. data/spec/unit/mongoid/extensions/string/inflections_spec.rb +208 -0
  184. data/spec/unit/mongoid/extensions/symbol/inflections_spec.rb +91 -0
  185. data/spec/unit/mongoid/extensions/time/conversions_spec.rb +70 -0
  186. data/spec/unit/mongoid/factory_spec.rb +31 -0
  187. data/spec/unit/mongoid/field_spec.rb +81 -0
  188. data/spec/unit/mongoid/fields_spec.rb +158 -0
  189. data/spec/unit/mongoid/finders_spec.rb +368 -0
  190. data/spec/unit/mongoid/identity_spec.rb +88 -0
  191. data/spec/unit/mongoid/indexes_spec.rb +93 -0
  192. data/spec/unit/mongoid/matchers/all_spec.rb +27 -0
  193. data/spec/unit/mongoid/matchers/default_spec.rb +27 -0
  194. data/spec/unit/mongoid/matchers/exists_spec.rb +56 -0
  195. data/spec/unit/mongoid/matchers/gt_spec.rb +39 -0
  196. data/spec/unit/mongoid/matchers/gte_spec.rb +49 -0
  197. data/spec/unit/mongoid/matchers/in_spec.rb +27 -0
  198. data/spec/unit/mongoid/matchers/lt_spec.rb +39 -0
  199. data/spec/unit/mongoid/matchers/lte_spec.rb +49 -0
  200. data/spec/unit/mongoid/matchers/ne_spec.rb +27 -0
  201. data/spec/unit/mongoid/matchers/nin_spec.rb +27 -0
  202. data/spec/unit/mongoid/matchers/size_spec.rb +27 -0
  203. data/spec/unit/mongoid/matchers_spec.rb +329 -0
  204. data/spec/unit/mongoid/memoization_spec.rb +75 -0
  205. data/spec/unit/mongoid/named_scope_spec.rb +123 -0
  206. data/spec/unit/mongoid/scope_spec.rb +240 -0
  207. data/spec/unit/mongoid/timestamps_spec.rb +25 -0
  208. data/spec/unit/mongoid/versioning_spec.rb +41 -0
  209. data/spec/unit/mongoid_spec.rb +37 -0
  210. metadata +431 -0
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Hash #:nodoc:
5
+ module CriteriaHelpers #:nodoc:
6
+ def expand_complex_criteria
7
+ hsh = {}
8
+ self.each_pair do |k,v|
9
+ if k.class == Humanoid::Criterion::Complex
10
+ hsh[k.key] = {"$#{k.operator}" => v}
11
+ else
12
+ hsh[k] = v
13
+ end
14
+ end
15
+ hsh
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Hash #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ self
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Integer #:nodoc:
5
+ module Conversions #:nodoc:
6
+ def set(value)
7
+ return nil if value.blank?
8
+ value =~ /\d/ ? value.to_i : value
9
+ end
10
+ def get(value)
11
+ value
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Nil #:nodoc:
5
+ module Assimilation #:nodoc:
6
+ # Will remove the child object from the parent.
7
+ def assimilate(parent, options, type = nil)
8
+ parent.remove_attribute(options.name); self
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Object #:nodoc:
5
+ # This module converts objects into humanoid related objects.
6
+ module Conversions #:nodoc:
7
+ def self.included(base)
8
+ base.class_eval do
9
+ include InstanceMethods
10
+ extend ClassMethods
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+ # Converts this object to a hash of attributes
16
+ def humanoidize
17
+ self.attributes
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ def set(value)
23
+ value.respond_to?(:attributes) ? value.attributes : value
24
+ end
25
+
26
+ def get(value)
27
+ self.new(value)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Proc #:nodoc:
5
+ module Scoping #:nodoc:
6
+ def scoped(*args)
7
+ call(*args).scoped
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module String #:nodoc:
5
+ module Conversions #:nodoc:
6
+ def set(value)
7
+ value.to_s
8
+ end
9
+ def get(value)
10
+ value
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module String #:nodoc:
5
+ module Inflections #:nodoc:
6
+
7
+ ActiveSupport::Inflector.inflections do |inflect|
8
+ inflect.singular("address", "address")
9
+ inflect.singular("addresses", "address")
10
+ inflect.irregular("canvas", "canvases")
11
+ end
12
+
13
+ # Represents how special characters will get converted when creating a
14
+ # composite key that should be unique and part of a url.
15
+ CHAR_CONV = {
16
+ " " => "-",
17
+ "!" => "-excl-",
18
+ "\"" => "-bckslsh-",
19
+ "#" => "-hash-",
20
+ "$" => "-dol-",
21
+ "%" => "-perc-",
22
+ "&" => "-and-",
23
+ "'" => "-quo-",
24
+ "(" => "-oparen-",
25
+ ")" => "-cparen-",
26
+ "*" => "-astx-",
27
+ "+" => "-plus-",
28
+ "," => "-comma-",
29
+ "-" => "-dash-",
30
+ "." => "-period-",
31
+ "/" => "-fwdslsh-",
32
+ ":" => "-colon-",
33
+ ";" => "-semicol-",
34
+ "<" => "-lt-",
35
+ "=" => "-eq-",
36
+ ">" => "-gt-",
37
+ "?" => "-ques-",
38
+ "@" => "-at-",
39
+ "[" => "-obrck-",
40
+ "\\" => "-bckslsh-",
41
+ "]" => "-clbrck-",
42
+ "^" => "-carat-",
43
+ "_" => "-undscr-",
44
+ "`" => "-bcktick-",
45
+ "{" => "-ocurly-",
46
+ "|" => "-pipe-",
47
+ "}" => "-clcurly-",
48
+ "~" => "-tilda-"
49
+ }
50
+
51
+ REVERSALS = {
52
+ "asc" => "desc",
53
+ "ascending" => "descending",
54
+ "desc" => "asc",
55
+ "descending" => "ascending"
56
+ }
57
+
58
+ def collectionize
59
+ tableize.gsub("/", "_")
60
+ end
61
+
62
+ def identify
63
+ if Humanoid.parameterize_keys
64
+ key = ""
65
+ each_char { |c| key += (CHAR_CONV[c] || c.downcase) }; key
66
+ else
67
+ self
68
+ end
69
+ end
70
+
71
+ def labelize
72
+ underscore.humanize
73
+ end
74
+
75
+ def invert
76
+ REVERSALS[self]
77
+ end
78
+
79
+ def singular?
80
+ singularize == self
81
+ end
82
+
83
+ def plural?
84
+ pluralize == self
85
+ end
86
+
87
+ def reader
88
+ writer? ? gsub("=", "") : self
89
+ end
90
+
91
+ def writer?
92
+ include?("=")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Symbol #:nodoc:
5
+ module Inflections #:nodoc:
6
+
7
+ REVERSALS = {
8
+ :asc => :desc,
9
+ :ascending => :descending,
10
+ :desc => :asc,
11
+ :descending => :ascending
12
+ }
13
+
14
+ def invert
15
+ REVERSALS[self]
16
+ end
17
+
18
+ def singular?
19
+ to_s.singular?
20
+ end
21
+
22
+ def plural?
23
+ to_s.plural?
24
+ end
25
+
26
+ ["gt", "lt", "gte", "lte", "ne", "in", "nin", "mod", "all", "size", "exists"].each do |oper|
27
+ class_eval <<-OPERATORS
28
+ def #{oper}
29
+ Criterion::Complex.new(:key => self, :operator => "#{oper}")
30
+ end
31
+ OPERATORS
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Time #:nodoc:
5
+ module Conversions #:nodoc:
6
+ def set(value)
7
+ return nil if value.blank?
8
+ time = ::Time.parse(value.is_a?(::Time) ? value.strftime("%Y-%m-%d %H:%M:%S %Z") : value.to_s)
9
+ time.utc? ? time : time.utc
10
+ end
11
+ def get(value)
12
+ return nil if value.blank?
13
+ ::Time.zone ? value.getlocal : value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ class Factory #:nodoc:
4
+ # Builds a new +Document+ from the supplied attributes.
5
+ #
6
+ # Example:
7
+ #
8
+ # <tt>Humanoid::Factory.build(Person, {})</tt>
9
+ #
10
+ # Options:
11
+ #
12
+ # klass: The class to instantiate from if _type is not present.
13
+ # attributes: The +Document+ attributes.
14
+ def self.build(klass, attrs)
15
+ type = attrs["_type"]
16
+ type ? type.constantize.instantiate(attrs) : klass.instantiate(attrs)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ class Field
4
+
5
+ attr_reader \
6
+ :default,
7
+ :name,
8
+ :type
9
+
10
+ # Create the new field with a name and optional additional options. Valid
11
+ # options are :default
12
+ #
13
+ # Options:
14
+ #
15
+ # name: The name of the field as a +Symbol+.
16
+ # options: A +Hash+ of options for the field.
17
+ #
18
+ # Example:
19
+ #
20
+ # <tt>Field.new(:score, :default => 0)</tt>
21
+ def initialize(name, options = {})
22
+ @name = name
23
+ @default = options[:default]
24
+ @type = options[:type] || String
25
+ end
26
+
27
+ # Used for setting an object in the attributes hash. If nil is provided the
28
+ # default will get returned if it exists.
29
+ def set(object)
30
+ object.nil? ? default : type.set(object)
31
+ end
32
+
33
+ # Used for retrieving the object out of the attributes hash.
34
+ def get(object)
35
+ type.get(object)
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc
3
+ module Fields #:nodoc
4
+ def self.included(base)
5
+ base.class_eval do
6
+ extend ClassMethods
7
+ # Set up the class attributes that must be available to all subclasses.
8
+ # These include defaults, fields
9
+ class_inheritable_accessor :defaults, :fields
10
+
11
+ self.defaults = {}
12
+ self.fields = {}
13
+
14
+ delegate :defaults, :fields, :to => "self.class"
15
+ end
16
+ end
17
+
18
+ module ClassMethods #:nodoc
19
+ # Defines all the fields that are accessable on the Document
20
+ # For each field that is defined, a getter and setter will be
21
+ # added as an instance method to the Document.
22
+ #
23
+ # Options:
24
+ #
25
+ # name: The name of the field, as a +Symbol+.
26
+ # options: A +Hash+ of options to supply to the +Field+.
27
+ #
28
+ # Example:
29
+ #
30
+ # <tt>field :score, :default => 0</tt>
31
+ def field(name, options = {})
32
+ access = name.to_s
33
+ set_field(access, options)
34
+ set_default(access, options)
35
+ end
36
+
37
+ protected
38
+ # Define a field attribute for the +Document+.
39
+ def set_field(name, options = {})
40
+ meth = options.delete(:as) || name
41
+ fields[name] = Field.new(name, options)
42
+ create_accessors(name, meth, options)
43
+ end
44
+
45
+ # Create the field accessors.
46
+ def create_accessors(name, meth, options = {})
47
+ define_method(meth) { read_attribute(name) }
48
+ define_method("#{meth}=") { |value| write_attribute(name, value) }
49
+ define_method("#{meth}?") do
50
+ attr = read_attribute(name)
51
+ (options[:type] == Boolean) ? attr == true : attr.present?
52
+ end
53
+ end
54
+
55
+ # Set up a default value for a field.
56
+ def set_default(name, options = {})
57
+ value = options[:default]
58
+ defaults[name] = value unless value.nil?
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,224 @@
1
+ # encoding: utf-8
2
+ module Humanoid #:nodoc:
3
+ module Finders #:nodoc:
4
+ # Find +Documents+ given the conditions.
5
+ #
6
+ # Options:
7
+ #
8
+ # args: A +Hash+ with a conditions key and other options
9
+ #
10
+ # <tt>Person.all(:conditions => { :attribute => "value" })</tt>
11
+ def all(*args)
12
+ find(:all, *args)
13
+ end
14
+
15
+ # Returns a count of matching records in the database based on the
16
+ # provided arguments.
17
+ #
18
+ # <tt>Person.count(:first, :conditions => { :attribute => "value" })</tt>
19
+ def count(*args)
20
+ Criteria.translate(self, *args).count
21
+ end
22
+
23
+ # Helper to initialize a new +Criteria+ object for this class.
24
+ #
25
+ # Example:
26
+ #
27
+ # <tt>Person.criteria</tt>
28
+ def criteria
29
+ Criteria.new(self)
30
+ end
31
+
32
+ # Find a +Document+ in several different ways.
33
+ #
34
+ # If a +String+ is provided, it will be assumed that it is a
35
+ # representation of a Mongo::ObjectID and will attempt to find a single
36
+ # +Document+ based on that id. If a +Symbol+ and +Hash+ is provided then
37
+ # it will attempt to find either a single +Document+ or multiples based
38
+ # on the conditions provided and the first parameter.
39
+ #
40
+ # <tt>Person.find(:first, :conditions => { :attribute => "value" })</tt>
41
+ #
42
+ # <tt>Person.find(:all, :conditions => { :attribute => "value" })</tt>
43
+ #
44
+ # <tt>Person.find(Mongo::ObjectID.new.to_s)</tt>
45
+ def find(*args)
46
+ raise Errors::InvalidOptions.new("Calling Document#find with nil is invalid") if args[0].nil?
47
+ type = args.delete_at(0) if args[0].is_a?(Symbol)
48
+ criteria = Criteria.translate(self, *args)
49
+ case type
50
+ when :first then return criteria.one
51
+ when :last then return criteria.last
52
+ else
53
+ return criteria
54
+ end
55
+ end
56
+
57
+ # Find the first +Document+ given the conditions, or creates a new document
58
+ # with the conditions that were supplied
59
+ #
60
+ # Options:
61
+ #
62
+ # args: A +Hash+ of attributes
63
+ #
64
+ # <tt>Person.find_or_create_by(:attribute => "value")</tt>
65
+ def find_or_create_by(attrs = {})
66
+ find_or(:create, attrs)
67
+ end
68
+
69
+ # Find the first +Document+ given the conditions, or instantiates a new document
70
+ # with the conditions that were supplied
71
+ #
72
+ # Options:
73
+ #
74
+ # args: A +Hash+ of attributes
75
+ #
76
+ # <tt>Person.find_or_initialize_by(:attribute => "value")</tt>
77
+ def find_or_initialize_by(attrs = {})
78
+ find_or(:new, attrs)
79
+ end
80
+
81
+ # Find the first +Document+ given the conditions.
82
+ #
83
+ # Options:
84
+ #
85
+ # args: A +Hash+ with a conditions key and other options
86
+ #
87
+ # <tt>Person.first(:conditions => { :attribute => "value" })</tt>
88
+ def first(*args)
89
+ find(:first, *args)
90
+ end
91
+
92
+ # Find the last +Document+ given the conditions.
93
+ #
94
+ # Options:
95
+ #
96
+ # args: A +Hash+ with a conditions key and other options
97
+ #
98
+ # <tt>Person.last(:conditions => { :attribute => "value" })</tt>
99
+ def last(*args)
100
+ find(:last, *args)
101
+ end
102
+
103
+ # Convenience method for returning the max value of a field.
104
+ #
105
+ # Options:
106
+ #
107
+ # field: The field to use when calculating the max.
108
+ #
109
+ # Example:
110
+ #
111
+ # <tt>Person.max(:age)</tt>
112
+ #
113
+ # Returns: <tt>Float</tt> max value.
114
+ def max(field)
115
+ Criteria.new(self).max(field)
116
+ end
117
+
118
+ # Will execute a +Criteria+ based on the +DynamicFinder+ that gets
119
+ # generated.
120
+ #
121
+ # Options:
122
+ #
123
+ # name: The finder method name
124
+ # args: The arguments to pass to the method.
125
+ #
126
+ # Example:
127
+ #
128
+ # <tt>Person.find_all_by_title_and_age("Sir", 30)</tt>
129
+ # def method_missing(name, *args)
130
+ # dyna = DynamicFinder.new(name, *args)
131
+ # finder, conditions = dyna.finder, dyna.conditions
132
+ # results = find(finder, :conditions => conditions)
133
+ # results ? results : dyna.create(self)
134
+ # end
135
+
136
+ # Convenience method for returning the min value of a field.
137
+ #
138
+ # Options:
139
+ #
140
+ # field: The field to use when calculating the min.
141
+ #
142
+ # Example:
143
+ #
144
+ # <tt>Person.min(:age)</tt>
145
+ #
146
+ # Returns: <tt>Float</tt> min value.
147
+ def min(field)
148
+ Criteria.new(self).min(field)
149
+ end
150
+
151
+ # Find all documents in paginated fashion given the supplied arguments.
152
+ # If no parameters are passed just default to offset 0 and limit 20.
153
+ #
154
+ # Options:
155
+ #
156
+ # params: A +Hash+ of params to pass to the Criteria API.
157
+ #
158
+ # Example:
159
+ #
160
+ # <tt>Person.paginate(:conditions => { :field => "Test" }, :page => 1,
161
+ # :per_page => 20)</tt>
162
+ #
163
+ # Returns paginated array of docs.
164
+ def paginate(params = {})
165
+ Criteria.translate(self, params).paginate
166
+ end
167
+
168
+ # Entry point for creating a new criteria from a Document. This will
169
+ # instantiate a new +Criteria+ object with the supplied select criterion
170
+ # already added to it.
171
+ #
172
+ # Options:
173
+ #
174
+ # args: A list of field names to retrict the returned fields to.
175
+ #
176
+ # Example:
177
+ #
178
+ # <tt>Person.only(:field1, :field2, :field3)</tt>
179
+ #
180
+ # Returns: <tt>Criteria</tt>
181
+ def only(*args)
182
+ Criteria.new(self).only(*args)
183
+ end
184
+
185
+ # Convenience method for returning the sum of a specified field for all
186
+ # documents in the database.
187
+ #
188
+ # Options:
189
+ #
190
+ # field: The field to use when calculating the sum.
191
+ #
192
+ # Example:
193
+ #
194
+ # <tt>Person.sum(:age)</tt>
195
+ #
196
+ # Returns: <tt>Float</tt> of the sum.
197
+ def sum(field)
198
+ Criteria.new(self).sum(field)
199
+ end
200
+
201
+ # Entry point for creating a new criteria from a Document. This will
202
+ # instantiate a new +Criteria+ object with the supplied select criterion
203
+ # already added to it.
204
+ #
205
+ # Options:
206
+ #
207
+ # selector: A where criteria to initialize.
208
+ #
209
+ # Example:
210
+ #
211
+ # <tt>Person.where(:field1 => "Value")</tt>
212
+ #
213
+ # Returns: <tt>Criteria</tt>
214
+ def where(selector = nil)
215
+ Criteria.new(self).where(selector)
216
+ end
217
+
218
+ protected
219
+ # Find the first object or create/initialize it.
220
+ def find_or(method, attrs = {})
221
+ first(:conditions => attrs) || send(method, attrs)
222
+ end
223
+ end
224
+ end