rpbertp13-dm-core 0.9.11.1

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 (131) hide show
  1. data/.autotest +26 -0
  2. data/.gitignore +18 -0
  3. data/CONTRIBUTING +51 -0
  4. data/FAQ +92 -0
  5. data/History.txt +52 -0
  6. data/MIT-LICENSE +22 -0
  7. data/Manifest.txt +130 -0
  8. data/QUICKLINKS +11 -0
  9. data/README.txt +143 -0
  10. data/Rakefile +32 -0
  11. data/SPECS +62 -0
  12. data/TODO +1 -0
  13. data/dm-core.gemspec +40 -0
  14. data/lib/dm-core.rb +217 -0
  15. data/lib/dm-core/adapters.rb +16 -0
  16. data/lib/dm-core/adapters/abstract_adapter.rb +209 -0
  17. data/lib/dm-core/adapters/data_objects_adapter.rb +716 -0
  18. data/lib/dm-core/adapters/in_memory_adapter.rb +87 -0
  19. data/lib/dm-core/adapters/mysql_adapter.rb +138 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +189 -0
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  22. data/lib/dm-core/associations.rb +207 -0
  23. data/lib/dm-core/associations/many_to_many.rb +147 -0
  24. data/lib/dm-core/associations/many_to_one.rb +107 -0
  25. data/lib/dm-core/associations/one_to_many.rb +315 -0
  26. data/lib/dm-core/associations/one_to_one.rb +61 -0
  27. data/lib/dm-core/associations/relationship.rb +221 -0
  28. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  29. data/lib/dm-core/auto_migrations.rb +105 -0
  30. data/lib/dm-core/collection.rb +670 -0
  31. data/lib/dm-core/dependency_queue.rb +32 -0
  32. data/lib/dm-core/hook.rb +11 -0
  33. data/lib/dm-core/identity_map.rb +42 -0
  34. data/lib/dm-core/is.rb +16 -0
  35. data/lib/dm-core/logger.rb +232 -0
  36. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  37. data/lib/dm-core/migrator.rb +29 -0
  38. data/lib/dm-core/model.rb +526 -0
  39. data/lib/dm-core/naming_conventions.rb +84 -0
  40. data/lib/dm-core/property.rb +676 -0
  41. data/lib/dm-core/property_set.rb +169 -0
  42. data/lib/dm-core/query.rb +676 -0
  43. data/lib/dm-core/repository.rb +167 -0
  44. data/lib/dm-core/resource.rb +671 -0
  45. data/lib/dm-core/scope.rb +58 -0
  46. data/lib/dm-core/support.rb +7 -0
  47. data/lib/dm-core/support/array.rb +13 -0
  48. data/lib/dm-core/support/assertions.rb +8 -0
  49. data/lib/dm-core/support/errors.rb +23 -0
  50. data/lib/dm-core/support/kernel.rb +11 -0
  51. data/lib/dm-core/support/symbol.rb +41 -0
  52. data/lib/dm-core/transaction.rb +252 -0
  53. data/lib/dm-core/type.rb +160 -0
  54. data/lib/dm-core/type_map.rb +80 -0
  55. data/lib/dm-core/types.rb +19 -0
  56. data/lib/dm-core/types/boolean.rb +7 -0
  57. data/lib/dm-core/types/discriminator.rb +34 -0
  58. data/lib/dm-core/types/object.rb +24 -0
  59. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  60. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  61. data/lib/dm-core/types/serial.rb +9 -0
  62. data/lib/dm-core/types/text.rb +10 -0
  63. data/lib/dm-core/version.rb +3 -0
  64. data/script/all +4 -0
  65. data/script/performance.rb +282 -0
  66. data/script/profile.rb +87 -0
  67. data/spec/integration/association_spec.rb +1382 -0
  68. data/spec/integration/association_through_spec.rb +203 -0
  69. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  70. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  71. data/spec/integration/associations/one_to_many_spec.rb +188 -0
  72. data/spec/integration/auto_migrations_spec.rb +413 -0
  73. data/spec/integration/collection_spec.rb +1073 -0
  74. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  75. data/spec/integration/dependency_queue_spec.rb +46 -0
  76. data/spec/integration/model_spec.rb +197 -0
  77. data/spec/integration/mysql_adapter_spec.rb +85 -0
  78. data/spec/integration/postgres_adapter_spec.rb +731 -0
  79. data/spec/integration/property_spec.rb +253 -0
  80. data/spec/integration/query_spec.rb +514 -0
  81. data/spec/integration/repository_spec.rb +61 -0
  82. data/spec/integration/resource_spec.rb +513 -0
  83. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  84. data/spec/integration/sti_spec.rb +273 -0
  85. data/spec/integration/strategic_eager_loading_spec.rb +156 -0
  86. data/spec/integration/transaction_spec.rb +60 -0
  87. data/spec/integration/type_spec.rb +275 -0
  88. data/spec/lib/logging_helper.rb +18 -0
  89. data/spec/lib/mock_adapter.rb +27 -0
  90. data/spec/lib/model_loader.rb +100 -0
  91. data/spec/lib/publicize_methods.rb +28 -0
  92. data/spec/models/content.rb +16 -0
  93. data/spec/models/vehicles.rb +34 -0
  94. data/spec/models/zoo.rb +48 -0
  95. data/spec/spec.opts +3 -0
  96. data/spec/spec_helper.rb +91 -0
  97. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  98. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  99. data/spec/unit/adapters/data_objects_adapter_spec.rb +632 -0
  100. data/spec/unit/adapters/in_memory_adapter_spec.rb +98 -0
  101. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  102. data/spec/unit/associations/many_to_many_spec.rb +32 -0
  103. data/spec/unit/associations/many_to_one_spec.rb +159 -0
  104. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  105. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  106. data/spec/unit/associations/relationship_spec.rb +71 -0
  107. data/spec/unit/associations_spec.rb +242 -0
  108. data/spec/unit/auto_migrations_spec.rb +111 -0
  109. data/spec/unit/collection_spec.rb +182 -0
  110. data/spec/unit/data_mapper_spec.rb +35 -0
  111. data/spec/unit/identity_map_spec.rb +126 -0
  112. data/spec/unit/is_spec.rb +80 -0
  113. data/spec/unit/migrator_spec.rb +33 -0
  114. data/spec/unit/model_spec.rb +321 -0
  115. data/spec/unit/naming_conventions_spec.rb +36 -0
  116. data/spec/unit/property_set_spec.rb +90 -0
  117. data/spec/unit/property_spec.rb +753 -0
  118. data/spec/unit/query_spec.rb +571 -0
  119. data/spec/unit/repository_spec.rb +93 -0
  120. data/spec/unit/resource_spec.rb +649 -0
  121. data/spec/unit/scope_spec.rb +142 -0
  122. data/spec/unit/transaction_spec.rb +469 -0
  123. data/spec/unit/type_map_spec.rb +114 -0
  124. data/spec/unit/type_spec.rb +119 -0
  125. data/tasks/ci.rb +36 -0
  126. data/tasks/dm.rb +63 -0
  127. data/tasks/doc.rb +20 -0
  128. data/tasks/gemspec.rb +23 -0
  129. data/tasks/hoe.rb +46 -0
  130. data/tasks/install.rb +20 -0
  131. metadata +215 -0
@@ -0,0 +1,207 @@
1
+ dir = Pathname(__FILE__).dirname.expand_path / 'associations'
2
+
3
+ require dir / 'relationship'
4
+ require dir / 'relationship_chain'
5
+ require dir / 'many_to_many'
6
+ require dir / 'many_to_one'
7
+ require dir / 'one_to_many'
8
+ require dir / 'one_to_one'
9
+
10
+ module DataMapper
11
+ module Associations
12
+ include Assertions
13
+
14
+ class ImmutableAssociationError < RuntimeError
15
+ end
16
+
17
+ class UnsavedParentError < RuntimeError
18
+ end
19
+
20
+ # Returns all relationships that are many-to-one for this model.
21
+ #
22
+ # Used to find the relationships that require properties in any Repository.
23
+ #
24
+ # Example:
25
+ # class Plur
26
+ # include DataMapper::Resource
27
+ # def self.default_repository_name
28
+ # :plur_db
29
+ # end
30
+ # repository(:plupp_db) do
31
+ # has 1, :plupp
32
+ # end
33
+ # end
34
+ #
35
+ # This resource has a many-to-one to the Plupp resource residing in the :plupp_db repository,
36
+ # but the Plur resource needs the plupp_id property no matter what repository itself lives in,
37
+ # ie we need to create that property when we migrate etc.
38
+ #
39
+ # Used in DataMapper::Model.properties_with_subclasses
40
+ #
41
+ # @api private
42
+ def many_to_one_relationships
43
+ relationships unless @relationships # needs to be initialized!
44
+ @relationships.values.collect do |rels| rels.values end.flatten.select do |relationship| relationship.child_model == self end
45
+ end
46
+
47
+ def relationships(repository_name = default_repository_name)
48
+ @relationships ||= {}
49
+ @relationships[repository_name] ||= repository_name == Repository.default_name ? {} : relationships(Repository.default_name).dup
50
+ end
51
+
52
+ def n
53
+ 1.0/0
54
+ end
55
+
56
+ ##
57
+ # A shorthand, clear syntax for defining one-to-one, one-to-many and
58
+ # many-to-many resource relationships.
59
+ #
60
+ # @example [Usage]
61
+ # * has 1, :friend # one friend
62
+ # * has n, :friends # many friends
63
+ # * has 1..3, :friends
64
+ # # many friends (at least 1, at most 3)
65
+ # * has 3, :friends
66
+ # # many friends (exactly 3)
67
+ # * has 1, :friend, :class_name => 'User'
68
+ # # one friend with the class name User
69
+ # * has 3, :friends, :through => :friendships
70
+ # # many friends through the friendships relationship
71
+ # * has n, :friendships => :friends
72
+ # # identical to above example
73
+ #
74
+ # @param cardinality [Integer, Range, Infinity]
75
+ # cardinality that defines the association type and constraints
76
+ # @param name <Symbol> the name that the association will be referenced by
77
+ # @param opts <Hash> an options hash
78
+ #
79
+ # @option :through[Symbol] A association that this join should go through to form
80
+ # a many-to-many association
81
+ # @option :class_name[String] The name of the class to associate with, if omitted
82
+ # then the association name is assumed to match the class name
83
+ # @option :remote_name[Symbol] In the case of a :through option being present, the
84
+ # name of the relationship on the other end of the :through-relationship
85
+ # to be linked to this relationship.
86
+ #
87
+ # @return [DataMapper::Association::Relationship] the relationship that was
88
+ # created to reflect either a one-to-one, one-to-many or many-to-many
89
+ # relationship
90
+ # @raise [ArgumentError] if the cardinality was not understood. Should be a
91
+ # Integer, Range or Infinity(n)
92
+ #
93
+ # @api public
94
+ def has(cardinality, name, options = {})
95
+
96
+ # NOTE: the reason for this fix is that with the ability to pass in two
97
+ # hashes into has() there might be instances where people attempt to
98
+ # pass in the options into the name part and not know why things aren't
99
+ # working for them.
100
+ if name.kind_of?(Hash)
101
+ name_through, through = name.keys.first, name.values.first
102
+ cardinality_string = cardinality.to_s == 'Infinity' ? 'n' : cardinality.inspect
103
+ warn("In #{self.name} 'has #{cardinality_string}, #{name_through.inspect} => #{through.inspect}' is deprecated. Use 'has #{cardinality_string}, #{name_through.inspect}, :through => #{through.inspect}' instead")
104
+ end
105
+
106
+ options = options.merge(extract_min_max(cardinality))
107
+ options = options.merge(extract_throughness(name))
108
+
109
+ # do not remove this. There is alot of confusion on people's
110
+ # part about what the first argument to has() is. For the record it
111
+ # is the min cardinality and max cardinality of the association.
112
+ # simply put, it constraints the number of resources that will be
113
+ # returned by the association. It is not, as has been assumed,
114
+ # the number of results on the left and right hand side of the
115
+ # reltionship.
116
+ if options[:min] == n && options[:max] == n
117
+ raise ArgumentError, 'Cardinality may not be n..n. The cardinality specifies the min/max number of results from the association', caller
118
+ end
119
+
120
+ klass = options[:max] == 1 ? OneToOne : OneToMany
121
+ klass = ManyToMany if options[:through] == DataMapper::Resource
122
+ relationship = klass.setup(options.delete(:name), self, options)
123
+
124
+ # Please leave this in - I will release contextual serialization soon
125
+ # which requires this -- guyvdb
126
+ # TODO convert this to a hook in the plugin once hooks work on class
127
+ # methods
128
+ self.init_has_relationship_for_serialization(relationship) if self.respond_to?(:init_has_relationship_for_serialization)
129
+
130
+ relationship
131
+ end
132
+
133
+ ##
134
+ # A shorthand, clear syntax for defining many-to-one resource relationships.
135
+ #
136
+ # @example [Usage]
137
+ # * belongs_to :user # many_to_one, :friend
138
+ # * belongs_to :friend, :class_name => 'User' # many_to_one :friends
139
+ #
140
+ # @param name [Symbol] The name that the association will be referenced by
141
+ # @see #has
142
+ #
143
+ # @return [DataMapper::Association::ManyToOne] The association created
144
+ # should not be accessed directly
145
+ #
146
+ # @api public
147
+ def belongs_to(name, options={})
148
+ @_valid_relations = false
149
+
150
+ if options.key?(:class_name) && !options.key?(:child_key)
151
+ warn "The inferred child_key will changing to be prefixed with the relationship name #{name}. " \
152
+ "When using :class_name in belongs_to specify the :child_key explicitly to avoid problems." \
153
+ "#{caller(0)[1]}"
154
+ end
155
+
156
+ relationship = ManyToOne.setup(name, self, options)
157
+ # Please leave this in - I will release contextual serialization soon
158
+ # which requires this -- guyvdb
159
+ # TODO convert this to a hook in the plugin once hooks work on class
160
+ # methods
161
+ self.init_belongs_relationship_for_serialization(relationship) if self.respond_to?(:init_belongs_relationship_for_serialization)
162
+
163
+ relationship
164
+ end
165
+
166
+ private
167
+
168
+ def extract_throughness(name)
169
+ assert_kind_of 'name', name, Hash, Symbol
170
+
171
+ case name
172
+ when Hash
173
+ unless name.keys.size == 1
174
+ raise ArgumentError, "name must have only one key, but had #{name.keys.size}", caller(2)
175
+ end
176
+
177
+ { :name => name.keys.first, :through => name.values.first }
178
+ when Symbol
179
+ { :name => name }
180
+ end
181
+ end
182
+
183
+ # A support method form converting Integer, Range or Infinity values into a
184
+ # { :min => x, :max => y } hash.
185
+ #
186
+ # @api private
187
+ def extract_min_max(constraints)
188
+ assert_kind_of 'constraints', constraints, Integer, Range unless constraints == n
189
+
190
+ case constraints
191
+ when Integer
192
+ { :min => constraints, :max => constraints }
193
+ when Range
194
+ if constraints.first > constraints.last
195
+ raise ArgumentError, "Constraint min (#{constraints.first}) cannot be larger than the max (#{constraints.last})"
196
+ end
197
+
198
+ { :min => constraints.first, :max => constraints.last }
199
+ when n
200
+ { :min => 0, :max => n }
201
+ end
202
+ end
203
+ end # module Associations
204
+
205
+ Model.append_extensions DataMapper::Associations
206
+
207
+ end # module DataMapper
@@ -0,0 +1,147 @@
1
+ require File.join(File.dirname(__FILE__), "one_to_many")
2
+ module DataMapper
3
+ module Associations
4
+ module ManyToMany
5
+ extend Assertions
6
+
7
+ # Setup many to many relationship between two models
8
+ # -
9
+ # @api private
10
+ def self.setup(name, model, options = {})
11
+ assert_kind_of 'name', name, Symbol
12
+ assert_kind_of 'model', model, Model
13
+ assert_kind_of 'options', options, Hash
14
+
15
+ repository_name = model.repository.name
16
+
17
+ model.class_eval <<-EOS, __FILE__, __LINE__
18
+ def #{name}(query = {})
19
+ #{name}_association.all(query)
20
+ end
21
+
22
+ def #{name}=(children)
23
+ #{name}_association.replace(children)
24
+ end
25
+
26
+ private
27
+
28
+ def #{name}_association
29
+ @#{name}_association ||= begin
30
+ unless relationship = model.relationships(#{repository_name.inspect})[#{name.inspect}]
31
+ raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
32
+ end
33
+ association = Proxy.new(relationship, self)
34
+ parent_associations << association
35
+ association
36
+ end
37
+ end
38
+ EOS
39
+
40
+ opts = options.dup
41
+ opts.delete(:through)
42
+ opts[:child_model] ||= opts.delete(:class_name) || Extlib::Inflection.classify(name)
43
+ opts[:parent_model] = model
44
+ opts[:repository_name] = repository_name
45
+ opts[:remote_relationship_name] ||= opts.delete(:remote_name) || Extlib::Inflection.tableize(opts[:child_model])
46
+ opts[:parent_key] = opts[:parent_key]
47
+ opts[:child_key] = opts[:child_key]
48
+ opts[:mutable] = true
49
+
50
+ names = [ opts[:child_model], opts[:parent_model].name ].sort
51
+ model_name = names.join.gsub("::", "")
52
+ storage_name = Extlib::Inflection.tableize(Extlib::Inflection.pluralize(names[0]) + names[1])
53
+
54
+ opts[:near_relationship_name] = Extlib::Inflection.tableize(model_name).to_sym
55
+
56
+ model.has(model.n, opts[:near_relationship_name])
57
+
58
+ relationship = model.relationships(repository_name)[name] = RelationshipChain.new(opts)
59
+
60
+ unless Object.const_defined?(model_name)
61
+ model = DataMapper::Model.new(storage_name)
62
+
63
+ model.class_eval <<-EOS, __FILE__, __LINE__
64
+ def self.name; #{model_name.inspect} end
65
+ def self.default_repository_name; #{repository_name.inspect} end
66
+ def self.many_to_many; true end
67
+ EOS
68
+
69
+ names.each do |n|
70
+ model.belongs_to(Extlib::Inflection.underscore(n).gsub('/', '_').to_sym)
71
+ end
72
+
73
+ Object.const_set(model_name, model)
74
+ end
75
+
76
+ relationship
77
+ end
78
+
79
+ class Proxy < DataMapper::Associations::OneToMany::Proxy
80
+ def delete(resource)
81
+ through = near_association.get(*(@parent.key + resource.key))
82
+ near_association.delete(through)
83
+ orphan_resource(super)
84
+ end
85
+
86
+ def clear
87
+ near_association.clear
88
+ super
89
+ end
90
+
91
+ def destroy
92
+ near_association.destroy
93
+ super
94
+ end
95
+
96
+ def save
97
+ end
98
+
99
+ private
100
+
101
+ def new_child(attributes)
102
+ remote_relationship.parent_model.new(attributes)
103
+ end
104
+
105
+ def relate_resource(resource)
106
+ assert_mutable
107
+ add_default_association_values(resource)
108
+ @orphans.delete(resource)
109
+
110
+ # TODO: fix this so it does not automatically save on append, if possible
111
+ resource.save if resource.new_record?
112
+ through_resource = @relationship.child_model.new
113
+ @relationship.child_key.zip(@relationship.parent_key) do |child_key,parent_key|
114
+ through_resource.send("#{child_key.name}=", parent_key.get(@parent))
115
+ end
116
+ remote_relationship.child_key.zip(remote_relationship.parent_key) do |child_key,parent_key|
117
+ through_resource.send("#{child_key.name}=", parent_key.get(resource))
118
+ end
119
+ near_association << through_resource
120
+
121
+ resource
122
+ end
123
+
124
+ def orphan_resource(resource)
125
+ assert_mutable
126
+ @orphans << resource
127
+ resource
128
+ end
129
+
130
+ def assert_mutable
131
+ end
132
+
133
+ def remote_relationship
134
+ @remote_relationship ||= @relationship.send(:remote_relationship)
135
+ end
136
+
137
+ def near_association
138
+ @near_association ||= @parent.send(near_relationship_name)
139
+ end
140
+
141
+ def near_relationship_name
142
+ @near_relationship_name ||= @relationship.send(:instance_variable_get, :@near_relationship_name)
143
+ end
144
+ end # class Proxy
145
+ end # module ManyToMany
146
+ end # module Associations
147
+ end # module DataMapper
@@ -0,0 +1,107 @@
1
+ module DataMapper
2
+ module Associations
3
+ module ManyToOne
4
+ extend Assertions
5
+
6
+ # Setup many to one relationship between two models
7
+ # -
8
+ # @api private
9
+ def self.setup(name, model, options = {})
10
+ assert_kind_of 'name', name, Symbol
11
+ assert_kind_of 'model', model, Model
12
+ assert_kind_of 'options', options, Hash
13
+
14
+ repository_name = model.repository.name
15
+
16
+ model.class_eval <<-EOS, __FILE__, __LINE__
17
+ def #{name}
18
+ #{name}_association.nil? ? nil : #{name}_association
19
+ end
20
+
21
+ def #{name}=(parent)
22
+ #{name}_association.replace(parent)
23
+ end
24
+
25
+ private
26
+
27
+ def #{name}_association
28
+ @#{name}_association ||= begin
29
+ unless relationship = model.relationships(#{repository_name.inspect})[:#{name}]
30
+ raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
31
+ end
32
+ association = Proxy.new(relationship, self)
33
+ child_associations << association
34
+ association
35
+ end
36
+ end
37
+ EOS
38
+
39
+ model.relationships(repository_name)[name] = Relationship.new(
40
+ name,
41
+ repository_name,
42
+ model,
43
+ options.fetch(:class_name, Extlib::Inflection.classify(name)),
44
+ options
45
+ )
46
+ end
47
+
48
+ class Proxy
49
+ include Assertions
50
+
51
+ instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ object_id kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m.to_s) }
52
+
53
+ def replace(parent)
54
+ @parent = parent
55
+ @relationship.attach_parent(@child, @parent)
56
+ self
57
+ end
58
+
59
+ def save
60
+ return false if @parent.nil?
61
+ return true unless parent.new_record?
62
+
63
+ @relationship.with_repository(parent) do
64
+ result = parent.save
65
+ @relationship.child_key.set(@child, @relationship.parent_key.get(parent)) if result
66
+ result
67
+ end
68
+ end
69
+
70
+ def reload
71
+ @parent = nil
72
+ self
73
+ end
74
+
75
+ def kind_of?(klass)
76
+ super || parent.kind_of?(klass)
77
+ end
78
+
79
+ def respond_to?(method, include_private = false)
80
+ super || parent.respond_to?(method, include_private)
81
+ end
82
+
83
+ def instance_variable_get(variable)
84
+ super || parent.instance_variable_get(variable)
85
+ end
86
+
87
+ private
88
+
89
+ def initialize(relationship, child)
90
+ assert_kind_of 'relationship', relationship, Relationship
91
+ assert_kind_of 'child', child, Resource
92
+
93
+ @relationship = relationship
94
+ @child = child
95
+ end
96
+
97
+ def parent
98
+ @parent ||= @relationship.get_parent(@child)
99
+ end
100
+
101
+ def method_missing(method, *args, &block)
102
+ parent.__send__(method, *args, &block)
103
+ end
104
+ end # class Proxy
105
+ end # module ManyToOne
106
+ end # module Associations
107
+ end # module DataMapper