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.
- data/lib/gems/addressable-2.0.0/lib/addressable/idna.rb +4867 -0
- data/lib/gems/addressable-2.0.0/lib/addressable/uri.rb +2469 -0
- data/lib/gems/addressable-2.0.0/lib/addressable/version.rb +35 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/adapters/data_objects_adapter.rb +85 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/aggregate_functions.rb +201 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/collection.rb +11 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/model.rb +11 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/repository.rb +7 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/support/symbol.rb +21 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates/version.rb +7 -0
- data/lib/gems/dm-aggregates-0.9.7/lib/dm-aggregates.rb +15 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/abstract_adapter.rb +209 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/data_objects_adapter.rb +709 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/mysql_adapter.rb +136 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/postgres_adapter.rb +188 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/adapters.rb +22 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_many.rb +147 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/many_to_one.rb +107 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_many.rb +318 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/one_to_one.rb +61 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship.rb +223 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations/relationship_chain.rb +81 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/associations.rb +200 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/auto_migrations.rb +105 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/collection.rb +642 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/dependency_queue.rb +32 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/hook.rb +11 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/identity_map.rb +42 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/is.rb +16 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/logger.rb +232 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/migrations/destructive_migrations.rb +17 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/migrator.rb +29 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/model.rb +488 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/naming_conventions.rb +84 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/property.rb +663 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/property_set.rb +169 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/query.rb +628 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/repository.rb +159 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/resource.rb +637 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/scope.rb +58 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/array.rb +13 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/assertions.rb +8 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/errors.rb +23 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/kernel.rb +11 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support/symbol.rb +41 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/support.rb +7 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/transaction.rb +267 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/type.rb +160 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/type_map.rb +80 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/boolean.rb +7 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/discriminator.rb +34 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/object.rb +24 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_boolean.rb +34 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/paranoid_datetime.rb +33 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/serial.rb +9 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types/text.rb +10 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/types.rb +19 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core/version.rb +3 -0
- data/lib/gems/dm-core-0.9.7/lib/dm-core.rb +217 -0
- data/lib/gems/dm-core-0.9.7/script/all +5 -0
- data/lib/gems/dm-core-0.9.7/script/performance.rb +284 -0
- data/lib/gems/dm-core-0.9.7/script/profile.rb +87 -0
- data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations/version.rb +5 -0
- data/lib/gems/dm-migrations-0.9.7/lib/dm-migrations.rb +1 -0
- data/lib/gems/dm-migrations-0.9.7/lib/migration.rb +215 -0
- data/lib/gems/dm-migrations-0.9.7/lib/migration_runner.rb +88 -0
- data/lib/gems/dm-migrations-0.9.7/lib/spec/example/migration_example_group.rb +73 -0
- data/lib/gems/dm-migrations-0.9.7/lib/spec/matchers/migration_matchers.rb +107 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/column.rb +9 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/mysql.rb +52 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/postgresql.rb +78 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/sqlite3.rb +43 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table.rb +19 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table_creator.rb +81 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql/table_modifier.rb +53 -0
- data/lib/gems/dm-migrations-0.9.7/lib/sql.rb +10 -0
- data/lib/gems/dm-observer-0.9.7/lib/dm-observer/version.rb +5 -0
- data/lib/gems/dm-observer-0.9.7/lib/dm-observer.rb +91 -0
- data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer/version.rb +5 -0
- data/lib/gems/dm-serializer-0.9.7/lib/dm-serializer.rb +183 -0
- data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps/version.rb +5 -0
- data/lib/gems/dm-timestamps-0.9.7/lib/dm-timestamps.rb +57 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/bcrypt_hash.rb +31 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/csv.rb +28 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/enum.rb +70 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/epoch_time.rb +27 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/file_path.rb +27 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/flag.rb +61 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/ip_address.rb +30 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/json.rb +40 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/regexp.rb +20 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/serial.rb +8 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/slug.rb +37 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/uri.rb +29 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/uuid.rb +64 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/version.rb +5 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types/yaml.rb +36 -0
- data/lib/gems/dm-types-0.9.7/lib/dm-types.rb +28 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/absent_field_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/acceptance_validator.rb +76 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/auto_validate.rb +153 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/block_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/confirmation_validator.rb +80 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/contextual_validators.rb +56 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/custom_validator.rb +72 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/format_validator.rb +97 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/email.rb +40 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/formats/url.rb +20 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/generic_validator.rb +100 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/length_validator.rb +113 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/method_validator.rb +68 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/numeric_validator.rb +83 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/primitive_validator.rb +60 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/required_field_validator.rb +88 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/support/object.rb +5 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/uniqueness_validator.rb +64 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/validation_errors.rb +63 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/version.rb +5 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations/within_validator.rb +53 -0
- data/lib/gems/dm-validations-0.9.7/lib/dm-validations.rb +234 -0
- data/lib/gems/json_pure-1.1.3/GPL +340 -0
- data/lib/gems/json_pure-1.1.3/VERSION +1 -0
- data/lib/gems/json_pure-1.1.3/bin/edit_json.rb +10 -0
- data/lib/gems/json_pure-1.1.3/bin/prettify_json.rb +76 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Array.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/FalseClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Hash.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Key.xpm +73 -0
- data/lib/gems/json_pure-1.1.3/lib/json/NilClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/Numeric.xpm +28 -0
- data/lib/gems/json_pure-1.1.3/lib/json/String.xpm +96 -0
- data/lib/gems/json_pure-1.1.3/lib/json/TrueClass.xpm +21 -0
- data/lib/gems/json_pure-1.1.3/lib/json/add/core.rb +135 -0
- data/lib/gems/json_pure-1.1.3/lib/json/add/rails.rb +58 -0
- data/lib/gems/json_pure-1.1.3/lib/json/common.rb +354 -0
- data/lib/gems/json_pure-1.1.3/lib/json/editor.rb +1362 -0
- data/lib/gems/json_pure-1.1.3/lib/json/ext.rb +13 -0
- data/lib/gems/json_pure-1.1.3/lib/json/json.xpm +1499 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure/generator.rb +394 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure/parser.rb +259 -0
- data/lib/gems/json_pure-1.1.3/lib/json/pure.rb +75 -0
- data/lib/gems/json_pure-1.1.3/lib/json/version.rb +9 -0
- data/lib/gems/json_pure-1.1.3/lib/json.rb +235 -0
- data/lib/gems/launchy-0.3.2/bin/launchy +12 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/application.rb +163 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/browser.rb +85 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/command_line.rb +48 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/gemspec.rb +53 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/specification.rb +133 -0
- data/lib/gems/launchy-0.3.2/lib/launchy/version.rb +18 -0
- data/lib/gems/launchy-0.3.2/lib/launchy.rb +58 -0
- data/lib/gems/uuidtools-1.0.3/lib/uuidtools/version.rb +32 -0
- data/lib/gems/uuidtools-1.0.3/lib/uuidtools.rb +648 -0
- data/lib/gems.rb +13 -0
- data/lib/mack-data_mapper/migration_generator/migration_generator.rb +5 -0
- data/lib/mack-data_mapper/migration_generator/templates/db/migrations/%=@migration_name%.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/manifest.yml +3 -3
- data/lib/mack-data_mapper/model_generator/model_generator.rb +8 -1
- data/lib/mack-data_mapper/model_generator/templates/model.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/templates/rspec.rb.template +1 -1
- data/lib/mack-data_mapper/model_generator/templates/test_case.rb.template +1 -1
- data/lib/mack-data_mapper.rb +3 -2
- data/lib/mack-data_mapper_tasks.rb +7 -0
- 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,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,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
|