mack-data_mapper 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. data/lib/gems/addressable-2.0.0/lib/addressable/idna.rb +4867 -0
  2. data/lib/gems/addressable-2.0.0/lib/addressable/uri.rb +2469 -0
  3. data/lib/gems/addressable-2.0.0/lib/addressable/version.rb +35 -0
  4. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/adapters/data_objects_adapter.rb +85 -0
  5. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/aggregate_functions.rb +201 -0
  6. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/collection.rb +11 -0
  7. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/model.rb +11 -0
  8. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/repository.rb +7 -0
  9. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/support/symbol.rb +21 -0
  10. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/version.rb +7 -0
  11. data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates.rb +15 -0
  12. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  13. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/data_objects_adapter.rb +709 -0
  14. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  15. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  16. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  17. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  18. data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters.rb +22 -0
  19. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_many.rb +147 -0
  20. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_one.rb +107 -0
  21. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb +318 -0
  22. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_one.rb +61 -0
  23. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship.rb +223 -0
  24. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship_chain.rb +81 -0
  25. data/lib/gems/dm-core-0.9.7/lib/dm-core/associations.rb +200 -0
  26. data/lib/gems/dm-core-0.9.7/lib/dm-core/auto_migrations.rb +105 -0
  27. data/lib/gems/dm-core-0.9.7/lib/dm-core/collection.rb +642 -0
  28. data/lib/gems/dm-core-0.9.7/lib/dm-core/dependency_queue.rb +32 -0
  29. data/lib/gems/dm-core-0.9.7/lib/dm-core/hook.rb +11 -0
  30. data/lib/gems/dm-core-0.9.7/lib/dm-core/identity_map.rb +42 -0
  31. data/lib/gems/dm-core-0.9.7/lib/dm-core/is.rb +16 -0
  32. data/lib/gems/dm-core-0.9.7/lib/dm-core/logger.rb +232 -0
  33. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/gems/dm-core-0.9.7/lib/dm-core/migrator.rb +29 -0
  35. data/lib/gems/dm-core-0.9.7/lib/dm-core/model.rb +488 -0
  36. data/lib/gems/dm-core-0.9.7/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/gems/dm-core-0.9.7/lib/dm-core/property.rb +663 -0
  38. data/lib/gems/dm-core-0.9.7/lib/dm-core/property_set.rb +169 -0
  39. data/lib/gems/dm-core-0.9.7/lib/dm-core/query.rb +628 -0
  40. data/lib/gems/dm-core-0.9.7/lib/dm-core/repository.rb +159 -0
  41. data/lib/gems/dm-core-0.9.7/lib/dm-core/resource.rb +637 -0
  42. data/lib/gems/dm-core-0.9.7/lib/dm-core/scope.rb +58 -0
  43. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/array.rb +13 -0
  44. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/assertions.rb +8 -0
  45. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/errors.rb +23 -0
  46. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/kernel.rb +11 -0
  47. data/lib/gems/dm-core-0.9.7/lib/dm-core/support/symbol.rb +41 -0
  48. data/lib/gems/dm-core-0.9.7/lib/dm-core/support.rb +7 -0
  49. data/lib/gems/dm-core-0.9.7/lib/dm-core/transaction.rb +267 -0
  50. data/lib/gems/dm-core-0.9.7/lib/dm-core/type.rb +160 -0
  51. data/lib/gems/dm-core-0.9.7/lib/dm-core/type_map.rb +80 -0
  52. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/boolean.rb +7 -0
  53. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/discriminator.rb +34 -0
  54. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/object.rb +24 -0
  55. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_boolean.rb +34 -0
  56. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_datetime.rb +33 -0
  57. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/serial.rb +9 -0
  58. data/lib/gems/dm-core-0.9.7/lib/dm-core/types/text.rb +10 -0
  59. data/lib/gems/dm-core-0.9.7/lib/dm-core/types.rb +19 -0
  60. data/lib/gems/dm-core-0.9.7/lib/dm-core/version.rb +3 -0
  61. data/lib/gems/dm-core-0.9.7/lib/dm-core.rb +217 -0
  62. data/lib/gems/dm-core-0.9.7/script/all +5 -0
  63. data/lib/gems/dm-core-0.9.7/script/performance.rb +284 -0
  64. data/lib/gems/dm-core-0.9.7/script/profile.rb +87 -0
  65. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations/version.rb +5 -0
  66. data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations.rb +1 -0
  67. data/lib/gems/dm-migrations-0.9.7/lib/migration.rb +215 -0
  68. data/lib/gems/dm-migrations-0.9.7/lib/migration_runner.rb +88 -0
  69. data/lib/gems/dm-migrations-0.9.7/lib/spec/example/migration_example_group.rb +73 -0
  70. data/lib/gems/dm-migrations-0.9.7/lib/spec/matchers/migration_matchers.rb +107 -0
  71. data/lib/gems/dm-migrations-0.9.7/lib/sql/column.rb +9 -0
  72. data/lib/gems/dm-migrations-0.9.7/lib/sql/mysql.rb +52 -0
  73. data/lib/gems/dm-migrations-0.9.7/lib/sql/postgresql.rb +78 -0
  74. data/lib/gems/dm-migrations-0.9.7/lib/sql/sqlite3.rb +43 -0
  75. data/lib/gems/dm-migrations-0.9.7/lib/sql/table.rb +19 -0
  76. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_creator.rb +81 -0
  77. data/lib/gems/dm-migrations-0.9.7/lib/sql/table_modifier.rb +53 -0
  78. data/lib/gems/dm-migrations-0.9.7/lib/sql.rb +10 -0
  79. data/lib/gems/dm-observer-0.9.7/lib/dm-observer/version.rb +5 -0
  80. data/lib/gems/dm-observer-0.9.7/lib/dm-observer.rb +91 -0
  81. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer/version.rb +5 -0
  82. data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer.rb +183 -0
  83. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps/version.rb +5 -0
  84. data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps.rb +57 -0
  85. data/lib/gems/dm-types-0.9.7/lib/dm-types/bcrypt_hash.rb +31 -0
  86. data/lib/gems/dm-types-0.9.7/lib/dm-types/csv.rb +28 -0
  87. data/lib/gems/dm-types-0.9.7/lib/dm-types/enum.rb +70 -0
  88. data/lib/gems/dm-types-0.9.7/lib/dm-types/epoch_time.rb +27 -0
  89. data/lib/gems/dm-types-0.9.7/lib/dm-types/file_path.rb +27 -0
  90. data/lib/gems/dm-types-0.9.7/lib/dm-types/flag.rb +61 -0
  91. data/lib/gems/dm-types-0.9.7/lib/dm-types/ip_address.rb +30 -0
  92. data/lib/gems/dm-types-0.9.7/lib/dm-types/json.rb +40 -0
  93. data/lib/gems/dm-types-0.9.7/lib/dm-types/regexp.rb +20 -0
  94. data/lib/gems/dm-types-0.9.7/lib/dm-types/serial.rb +8 -0
  95. data/lib/gems/dm-types-0.9.7/lib/dm-types/slug.rb +37 -0
  96. data/lib/gems/dm-types-0.9.7/lib/dm-types/uri.rb +29 -0
  97. data/lib/gems/dm-types-0.9.7/lib/dm-types/uuid.rb +64 -0
  98. data/lib/gems/dm-types-0.9.7/lib/dm-types/version.rb +5 -0
  99. data/lib/gems/dm-types-0.9.7/lib/dm-types/yaml.rb +36 -0
  100. data/lib/gems/dm-types-0.9.7/lib/dm-types.rb +28 -0
  101. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/absent_field_validator.rb +60 -0
  102. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/acceptance_validator.rb +76 -0
  103. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/auto_validate.rb +153 -0
  104. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/block_validator.rb +60 -0
  105. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/confirmation_validator.rb +80 -0
  106. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/contextual_validators.rb +56 -0
  107. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/custom_validator.rb +72 -0
  108. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/format_validator.rb +97 -0
  109. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/email.rb +40 -0
  110. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/url.rb +20 -0
  111. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/generic_validator.rb +100 -0
  112. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/length_validator.rb +113 -0
  113. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/method_validator.rb +68 -0
  114. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/numeric_validator.rb +83 -0
  115. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/primitive_validator.rb +60 -0
  116. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/required_field_validator.rb +88 -0
  117. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/support/object.rb +5 -0
  118. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/uniqueness_validator.rb +64 -0
  119. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/validation_errors.rb +63 -0
  120. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/version.rb +5 -0
  121. data/lib/gems/dm-validations-0.9.7/lib/dm-validations/within_validator.rb +53 -0
  122. data/lib/gems/dm-validations-0.9.7/lib/dm-validations.rb +234 -0
  123. data/lib/gems/json_pure-1.1.3/GPL +340 -0
  124. data/lib/gems/json_pure-1.1.3/VERSION +1 -0
  125. data/lib/gems/json_pure-1.1.3/bin/edit_json.rb +10 -0
  126. data/lib/gems/json_pure-1.1.3/bin/prettify_json.rb +76 -0
  127. data/lib/gems/json_pure-1.1.3/lib/json/Array.xpm +21 -0
  128. data/lib/gems/json_pure-1.1.3/lib/json/FalseClass.xpm +21 -0
  129. data/lib/gems/json_pure-1.1.3/lib/json/Hash.xpm +21 -0
  130. data/lib/gems/json_pure-1.1.3/lib/json/Key.xpm +73 -0
  131. data/lib/gems/json_pure-1.1.3/lib/json/NilClass.xpm +21 -0
  132. data/lib/gems/json_pure-1.1.3/lib/json/Numeric.xpm +28 -0
  133. data/lib/gems/json_pure-1.1.3/lib/json/String.xpm +96 -0
  134. data/lib/gems/json_pure-1.1.3/lib/json/TrueClass.xpm +21 -0
  135. data/lib/gems/json_pure-1.1.3/lib/json/add/core.rb +135 -0
  136. data/lib/gems/json_pure-1.1.3/lib/json/add/rails.rb +58 -0
  137. data/lib/gems/json_pure-1.1.3/lib/json/common.rb +354 -0
  138. data/lib/gems/json_pure-1.1.3/lib/json/editor.rb +1362 -0
  139. data/lib/gems/json_pure-1.1.3/lib/json/ext.rb +13 -0
  140. data/lib/gems/json_pure-1.1.3/lib/json/json.xpm +1499 -0
  141. data/lib/gems/json_pure-1.1.3/lib/json/pure/generator.rb +394 -0
  142. data/lib/gems/json_pure-1.1.3/lib/json/pure/parser.rb +259 -0
  143. data/lib/gems/json_pure-1.1.3/lib/json/pure.rb +75 -0
  144. data/lib/gems/json_pure-1.1.3/lib/json/version.rb +9 -0
  145. data/lib/gems/json_pure-1.1.3/lib/json.rb +235 -0
  146. data/lib/gems/launchy-0.3.2/bin/launchy +12 -0
  147. data/lib/gems/launchy-0.3.2/lib/launchy/application.rb +163 -0
  148. data/lib/gems/launchy-0.3.2/lib/launchy/browser.rb +85 -0
  149. data/lib/gems/launchy-0.3.2/lib/launchy/command_line.rb +48 -0
  150. data/lib/gems/launchy-0.3.2/lib/launchy/gemspec.rb +53 -0
  151. data/lib/gems/launchy-0.3.2/lib/launchy/specification.rb +133 -0
  152. data/lib/gems/launchy-0.3.2/lib/launchy/version.rb +18 -0
  153. data/lib/gems/launchy-0.3.2/lib/launchy.rb +58 -0
  154. data/lib/gems/uuidtools-1.0.3/lib/uuidtools/version.rb +32 -0
  155. data/lib/gems/uuidtools-1.0.3/lib/uuidtools.rb +648 -0
  156. data/lib/gems.rb +13 -0
  157. data/lib/mack-data_mapper/migration_generator/migration_generator.rb +5 -0
  158. data/lib/mack-data_mapper/migration_generator/templates/db/migrations/%=@migration_name%.rb.template +1 -1
  159. data/lib/mack-data_mapper/model_generator/manifest.yml +3 -3
  160. data/lib/mack-data_mapper/model_generator/model_generator.rb +8 -1
  161. data/lib/mack-data_mapper/model_generator/templates/model.rb.template +1 -1
  162. data/lib/mack-data_mapper/model_generator/templates/rspec.rb.template +1 -1
  163. data/lib/mack-data_mapper/model_generator/templates/test_case.rb.template +1 -1
  164. data/lib/mack-data_mapper.rb +3 -2
  165. data/lib/mack-data_mapper_tasks.rb +7 -0
  166. metadata +235 -86
@@ -0,0 +1,35 @@
1
+ #--
2
+ # Addressable, Copyright (c) 2006-2008 Bob Aman
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ # Used to prevent the class/module from being loaded more than once
25
+ if !defined?(Addressable::VERSION)
26
+ module Addressable
27
+ module VERSION #:nodoc:
28
+ MAJOR = 2
29
+ MINOR = 0
30
+ TINY = 0
31
+
32
+ STRING = [MAJOR, MINOR, TINY].join('.')
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,85 @@
1
+ module DataMapper
2
+ module Adapters
3
+ class DataObjectsAdapter
4
+ def aggregate(query)
5
+ with_reader(read_statement(query), query.bind_values) do |reader|
6
+ results = []
7
+
8
+ while(reader.next!) do
9
+ row = query.fields.zip(reader.values).map do |field,value|
10
+ if field.respond_to?(:operator)
11
+ send(field.operator, field.target, value)
12
+ else
13
+ field.typecast(value)
14
+ end
15
+ end
16
+
17
+ results << (query.fields.size > 1 ? row : row[0])
18
+ end
19
+
20
+ results
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def count(property, value)
27
+ value.to_i
28
+ end
29
+
30
+ def min(property, value)
31
+ property.typecast(value)
32
+ end
33
+
34
+ def max(property, value)
35
+ property.typecast(value)
36
+ end
37
+
38
+ def avg(property, value)
39
+ property.type == Integer ? value.to_f : property.typecast(value)
40
+ end
41
+
42
+ def sum(property, value)
43
+ property.typecast(value)
44
+ end
45
+
46
+ module SQL
47
+ private
48
+
49
+ alias original_property_to_column_name property_to_column_name
50
+
51
+ def property_to_column_name(repository, property, qualify)
52
+ case property
53
+ when Query::Operator
54
+ aggregate_field_statement(repository, property.operator, property.target, qualify)
55
+ when Property, Query::Path
56
+ original_property_to_column_name(repository, property, qualify)
57
+ else
58
+ raise ArgumentError, "+property+ must be a DataMapper::Query::Operator, a DataMapper::Property or a Query::Path, but was a #{property.class} (#{property.inspect})"
59
+ end
60
+ end
61
+
62
+ def aggregate_field_statement(repository, aggregate_function, property, qualify)
63
+ column_name = if aggregate_function == :count && property == :all
64
+ '*'
65
+ else
66
+ property_to_column_name(repository, property, qualify)
67
+ end
68
+
69
+ function_name = case aggregate_function
70
+ when :count then 'COUNT'
71
+ when :min then 'MIN'
72
+ when :max then 'MAX'
73
+ when :avg then 'AVG'
74
+ when :sum then 'SUM'
75
+ else raise "Invalid aggregate function: #{aggregate_function.inspect}"
76
+ end
77
+
78
+ "#{function_name}(#{column_name})"
79
+ end
80
+ end # module SQL
81
+
82
+ include SQL
83
+ end # class DataObjectsAdapter
84
+ end # module Adapters
85
+ end # module DataMapper
@@ -0,0 +1,201 @@
1
+ module DataMapper
2
+ module AggregateFunctions
3
+ # Count results (given the conditions)
4
+ #
5
+ # @example the count of all friends
6
+ # Friend.count
7
+ #
8
+ # @example the count of all friends older then 18
9
+ # Friend.count(:age.gt => 18)
10
+ #
11
+ # @example the count of all your female friends
12
+ # Friend.count(:conditions => [ 'gender = ?', 'female' ])
13
+ #
14
+ # @example the count of all friends with an address (NULL values are not included)
15
+ # Friend.count(:address)
16
+ #
17
+ # @example the count of all friends with an address that are older then 18
18
+ # Friend.count(:address, :age.gt => 18)
19
+ #
20
+ # @example the count of all your female friends with an address
21
+ # Friend.count(:address, :conditions => [ 'gender = ?', 'female' ])
22
+ #
23
+ # @param property [Symbol] of the property you with to count (optional)
24
+ # @param opts [Hash, Symbol] the conditions
25
+ #
26
+ # @return [Integer] return the count given the conditions
27
+ #
28
+ # @api public
29
+ def count(*args)
30
+ query = args.last.kind_of?(Hash) ? args.pop : {}
31
+ property_name = args.first
32
+
33
+ if property_name
34
+ assert_kind_of 'property', property_by_name(property_name), Property
35
+ end
36
+
37
+ aggregate(query.merge(:fields => [ property_name ? property_name.count : :all.count ]))
38
+ end
39
+
40
+ # Get the lowest value of a property
41
+ #
42
+ # @example the age of the youngest friend
43
+ # Friend.min(:age)
44
+ #
45
+ # @example the age of the youngest female friend
46
+ # Friend.min(:age, :conditions => [ 'gender = ?', 'female' ])
47
+ #
48
+ # @param property [Symbol] the property you wish to get the lowest value of
49
+ # @param opts [Hash, Symbol] the conditions
50
+ #
51
+ # @return [Integer] return the lowest value of a property given the conditions
52
+ #
53
+ # @api public
54
+ def min(*args)
55
+ query = args.last.kind_of?(Hash) ? args.pop : {}
56
+ property_name = args.first
57
+
58
+ assert_property_type property_name, Integer, Float, BigDecimal, DateTime, Date, Time
59
+
60
+ aggregate(query.merge(:fields => [ property_name.min ]))
61
+ end
62
+
63
+ # Get the highest value of a property
64
+ #
65
+ # @example the age of the oldest friend
66
+ # Friend.max(:age)
67
+ #
68
+ # @example the age of the oldest female friend
69
+ # Friend.max(:age, :conditions => [ 'gender = ?', 'female' ])
70
+ #
71
+ # @param property [Symbol] the property you wish to get the highest value of
72
+ # @param opts [Hash, Symbol] the conditions
73
+ #
74
+ # @return [Integer] return the highest value of a property given the conditions
75
+ #
76
+ # @api public
77
+ def max(*args)
78
+ query = args.last.kind_of?(Hash) ? args.pop : {}
79
+ property_name = args.first
80
+
81
+ assert_property_type property_name, Integer, Float, BigDecimal, DateTime, Date, Time
82
+
83
+ aggregate(query.merge(:fields => [ property_name.max ]))
84
+ end
85
+
86
+ # Get the average value of a property
87
+ #
88
+ # @example the average age of all friends
89
+ # Friend.avg(:age)
90
+ #
91
+ # @example the average age of all female friends
92
+ # Friend.avg(:age, :conditions => [ 'gender = ?', 'female' ])
93
+ #
94
+ # @param property [Symbol] the property you wish to get the average value of
95
+ # @param opts [Hash, Symbol] the conditions
96
+ #
97
+ # @return [Integer] return the average value of a property given the conditions
98
+ #
99
+ # @api public
100
+ def avg(*args)
101
+ query = args.last.kind_of?(Hash) ? args.pop : {}
102
+ property_name = args.first
103
+
104
+ assert_property_type property_name, Integer, Float, BigDecimal
105
+
106
+ aggregate(query.merge(:fields => [ property_name.avg ]))
107
+ end
108
+
109
+ # Get the total value of a property
110
+ #
111
+ # @example the total age of all friends
112
+ # Friend.sum(:age)
113
+ #
114
+ # @example the total age of all female friends
115
+ # Friend.max(:age, :conditions => [ 'gender = ?', 'female' ])
116
+ #
117
+ # @param property [Symbol] the property you wish to get the total value of
118
+ # @param opts [Hash, Symbol] the conditions
119
+ #
120
+ # @return [Integer] return the total value of a property given the conditions
121
+ #
122
+ # @api public
123
+ def sum(*args)
124
+ query = args.last.kind_of?(Hash) ? args.pop : {}
125
+ property_name = args.first
126
+
127
+ assert_property_type property_name, Integer, Float, BigDecimal
128
+
129
+ aggregate(query.merge(:fields => [ property_name.sum ]))
130
+ end
131
+
132
+ # Perform aggregate queries
133
+ #
134
+ # @example the count of friends
135
+ # Friend.aggregate(:all.count)
136
+ #
137
+ # @example the minimum age, the maximum age and the total age of friends
138
+ # Friend.aggregate(:age.min, :age.max, :age.sum)
139
+ #
140
+ # @example the average age, grouped by gender
141
+ # Friend.aggregate(:age.avg, :fields => [ :gender ])
142
+ #
143
+ # @param aggregates [Symbol, ...] operators to aggregate with
144
+ # @params query [Hash] the conditions
145
+ #
146
+ # @return [Array,Numeric,DateTime,Date,Time] the results of the
147
+ # aggregate query
148
+ #
149
+ # @api public
150
+ def aggregate(*args)
151
+ query = args.last.kind_of?(Hash) ? args.pop : {}
152
+
153
+ query[:fields] ||= []
154
+ query[:fields] |= args
155
+ query[:fields].map! { |f| normalize_field(f) }
156
+ query[:order] ||= query[:fields].select { |p| p.kind_of?(Property) }
157
+
158
+ raise ArgumentError, 'query[:fields] must not be empty' if query[:fields].empty?
159
+
160
+ query = scoped_query(query)
161
+
162
+ if query.fields.any? { |p| p.kind_of?(Property) }
163
+ # explicitly specify the fields to circumvent a bug in Query#update
164
+ query.repository.aggregate(query.update(:fields => query.fields, :unique => true))
165
+ else
166
+ query.repository.aggregate(query).first # only return one row
167
+ end
168
+ end
169
+
170
+ private
171
+
172
+ def assert_property_type(name, *types)
173
+ if name.nil?
174
+ raise ArgumentError, 'property name must not be nil'
175
+ end
176
+
177
+ type = property_by_name(name).type
178
+
179
+ unless types.include?(type)
180
+ raise ArgumentError, "#{name} must be #{types * ' or '}, but was #{type}"
181
+ end
182
+ end
183
+
184
+ def normalize_field(field)
185
+ assert_kind_of 'field', field, Query::Operator, Symbol, Property
186
+
187
+ case field
188
+ when Query::Operator
189
+ if field.target == :all
190
+ field
191
+ else
192
+ field.class.new(property_by_name(field.target), field.operator)
193
+ end
194
+ when Symbol
195
+ property_by_name(field)
196
+ when Property
197
+ field
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,11 @@
1
+ module DataMapper
2
+ class Collection
3
+ include AggregateFunctions
4
+
5
+ private
6
+
7
+ def property_by_name(property_name)
8
+ properties[property_name]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module DataMapper
2
+ module Model
3
+ include AggregateFunctions
4
+
5
+ private
6
+
7
+ def property_by_name(property_name)
8
+ properties(repository.name)[property_name]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ class Repository
3
+ def aggregate(query)
4
+ adapter.aggregate(query)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ class Symbol
2
+ def count
3
+ DataMapper::Query::Operator.new(self, :count)
4
+ end
5
+
6
+ def min
7
+ DataMapper::Query::Operator.new(self, :min)
8
+ end
9
+
10
+ def max
11
+ DataMapper::Query::Operator.new(self, :max)
12
+ end
13
+
14
+ def avg
15
+ DataMapper::Query::Operator.new(self, :avg)
16
+ end
17
+
18
+ def sum
19
+ DataMapper::Query::Operator.new(self, :sum)
20
+ end
21
+ end # class Symbol
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ module More
3
+ module Aggregates
4
+ VERSION = "0.9.7"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+
3
+ dir = Pathname(__FILE__).dirname.expand_path + 'dm-aggregates'
4
+
5
+ require dir + 'version'
6
+ gem 'dm-core', DataMapper::More::Aggregates::VERSION
7
+ require 'dm-core'
8
+
9
+
10
+ require dir + 'aggregate_functions'
11
+ require dir + 'model'
12
+ require dir + 'repository'
13
+ require dir + 'collection'
14
+ require dir + 'adapters' + 'data_objects_adapter'
15
+ require dir + 'support' + 'symbol'
@@ -0,0 +1,209 @@
1
+ module DataMapper
2
+ module Adapters
3
+ class AbstractAdapter
4
+ include Assertions
5
+
6
+ attr_reader :name, :uri
7
+ attr_accessor :resource_naming_convention, :field_naming_convention
8
+
9
+ def create(resources)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def read_many(query)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def read_one(query)
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def update(attributes, query)
22
+ raise NotImplementedError
23
+ end
24
+
25
+ def delete(query)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ protected
30
+
31
+ def normalize_uri(uri_or_options)
32
+ uri_or_options
33
+ end
34
+
35
+ private
36
+
37
+ # Instantiate an Adapter by passing it a DataMapper::Repository
38
+ # connection string for configuration.
39
+ def initialize(name, uri_or_options)
40
+ assert_kind_of 'name', name, Symbol
41
+ assert_kind_of 'uri_or_options', uri_or_options, Addressable::URI, DataObjects::URI, Hash, String
42
+
43
+ @name = name
44
+ @uri = normalize_uri(uri_or_options)
45
+
46
+ @resource_naming_convention = NamingConventions::Resource::UnderscoredAndPluralized
47
+ @field_naming_convention = NamingConventions::Field::Underscored
48
+
49
+ @transactions = {}
50
+ end
51
+
52
+ # TODO: move to dm-more/dm-migrations
53
+ module Migration
54
+ #
55
+ # Returns whether the storage_name exists.
56
+ #
57
+ # @param storage_name<String> a String defining the name of a storage,
58
+ # for example a table name.
59
+ #
60
+ # @return <Boolean> true if the storage exists
61
+ #
62
+ # TODO: move to dm-more/dm-migrations (if possible)
63
+ def storage_exists?(storage_name)
64
+ raise NotImplementedError
65
+ end
66
+
67
+ #
68
+ # Returns whether the field exists.
69
+ #
70
+ # @param storage_name<String> a String defining the name of a storage, for example a table name.
71
+ # @param field_name<String> a String defining the name of a field, for example a column name.
72
+ #
73
+ # @return <Boolean> true if the field exists.
74
+ #
75
+ # TODO: move to dm-more/dm-migrations (if possible)
76
+ def field_exists?(storage_name, field_name)
77
+ raise NotImplementedError
78
+ end
79
+
80
+ # TODO: move to dm-more/dm-migrations
81
+ def upgrade_model_storage(repository, model)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # TODO: move to dm-more/dm-migrations
86
+ def create_model_storage(repository, model)
87
+ raise NotImplementedError
88
+ end
89
+
90
+ # TODO: move to dm-more/dm-migrations
91
+ def destroy_model_storage(repository, model)
92
+ raise NotImplementedError
93
+ end
94
+
95
+ # TODO: move to dm-more/dm-migrations
96
+ def alter_model_storage(repository, *args)
97
+ raise NotImplementedError
98
+ end
99
+
100
+ # TODO: move to dm-more/dm-migrations
101
+ def create_property_storage(repository, property)
102
+ raise NotImplementedError
103
+ end
104
+
105
+ # TODO: move to dm-more/dm-migrations
106
+ def destroy_property_storage(repository, property)
107
+ raise NotImplementedError
108
+ end
109
+
110
+ # TODO: move to dm-more/dm-migrations
111
+ def alter_property_storage(repository, *args)
112
+ raise NotImplementedError
113
+ end
114
+
115
+ module ClassMethods
116
+ # Default TypeMap for all adapters.
117
+ #
118
+ # @return <DataMapper::TypeMap> default TypeMap
119
+ #
120
+ # TODO: move to dm-more/dm-migrations
121
+ def type_map
122
+ @type_map ||= TypeMap.new
123
+ end
124
+ end
125
+ end
126
+
127
+ include Migration
128
+ extend Migration::ClassMethods
129
+
130
+ # TODO: move to dm-more/dm-transaction
131
+ module Transaction
132
+ #
133
+ # Pushes the given Transaction onto the per thread Transaction stack so
134
+ # that everything done by this Adapter is done within the context of said
135
+ # Transaction.
136
+ #
137
+ # @param transaction<DataMapper::Transaction> a Transaction to be the
138
+ # 'current' transaction until popped.
139
+ #
140
+ # TODO: move to dm-more/dm-transaction
141
+ def push_transaction(transaction)
142
+ transactions(Thread.current) << transaction
143
+ end
144
+
145
+ #
146
+ # Pop the 'current' Transaction from the per thread Transaction stack so
147
+ # that everything done by this Adapter is no longer necessarily within the
148
+ # context of said Transaction.
149
+ #
150
+ # @return <DataMapper::Transaction> the former 'current' transaction.
151
+ #
152
+ # TODO: move to dm-more/dm-transaction
153
+ def pop_transaction
154
+ transactions(Thread.current).pop
155
+ end
156
+
157
+ #
158
+ # Retrieve the current transaction for this Adapter.
159
+ #
160
+ # Everything done by this Adapter is done within the context of this
161
+ # Transaction.
162
+ #
163
+ # @return <DataMapper::Transaction> the 'current' transaction for this Adapter.
164
+ #
165
+ # TODO: move to dm-more/dm-transaction
166
+ def current_transaction
167
+ transactions(Thread.current).last
168
+ end
169
+
170
+ #
171
+ # Returns whether we are within a Transaction.
172
+ #
173
+ # @return <Boolean> whether we are within a Transaction.
174
+ #
175
+ # TODO: move to dm-more/dm-transaction
176
+ def within_transaction?
177
+ !current_transaction.nil?
178
+ end
179
+
180
+ #
181
+ # Produces a fresh transaction primitive for this Adapter
182
+ #
183
+ # Used by DataMapper::Transaction to perform its various tasks.
184
+ #
185
+ # @return <Object> a new Object that responds to :close, :begin, :commit,
186
+ # :rollback, :rollback_prepared and :prepare
187
+ #
188
+ # TODO: move to dm-more/dm-transaction (if possible)
189
+ def transaction_primitive
190
+ raise NotImplementedError
191
+ end
192
+
193
+ private
194
+ def transactions(thread)
195
+ unless @transactions[thread]
196
+ @transactions.delete_if do |key, value|
197
+ !key.respond_to?(:alive?) || !key.alive?
198
+ end
199
+ @transactions[thread] = []
200
+ end
201
+ @transactions[thread]
202
+ end
203
+
204
+ end
205
+
206
+ include Transaction
207
+ end # class AbstractAdapter
208
+ end # module Adapters
209
+ end # module DataMapper