mack-data_mapper 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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