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,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__ class kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m) }
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
@@ -0,0 +1,318 @@
1
+ module DataMapper
2
+ module Associations
3
+ module OneToMany
4
+ extend Assertions
5
+
6
+ # Setup one to many 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}(query = {})
18
+ #{name}_association.all(query)
19
+ end
20
+
21
+ def #{name}=(children)
22
+ #{name}_association.replace(children)
23
+ end
24
+
25
+ private
26
+
27
+ def #{name}_association
28
+ @#{name}_association ||= begin
29
+ unless relationship = model.relationships(#{repository_name.inspect})[#{name.inspect}]
30
+ raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
31
+ end
32
+ association = Proxy.new(relationship, self)
33
+ parent_associations << association
34
+ association
35
+ end
36
+ end
37
+ EOS
38
+
39
+ model.relationships(repository_name)[name] = if options.has_key?(:through)
40
+ opts = options.dup
41
+
42
+ if opts.key?(:class_name) && !opts.key?(:child_key)
43
+ warn(<<-EOS.margin)
44
+ You have specified #{model.base_model.name}.has(#{name.inspect}) with :class_name => #{opts[:class_name].inspect}. You probably also want to specify the :child_key option.
45
+ EOS
46
+ end
47
+
48
+ opts[:child_model] ||= opts.delete(:class_name) || Extlib::Inflection.classify(name)
49
+ opts[:parent_model] = model
50
+ opts[:repository_name] = repository_name
51
+ opts[:near_relationship_name] = opts.delete(:through)
52
+ opts[:remote_relationship_name] ||= opts.delete(:remote_name) || name
53
+ opts[:parent_key] = opts[:parent_key]
54
+ opts[:child_key] = opts[:child_key]
55
+
56
+ RelationshipChain.new( opts )
57
+ else
58
+ Relationship.new(
59
+ name,
60
+ repository_name,
61
+ options.fetch(:class_name, Extlib::Inflection.classify(name)),
62
+ model,
63
+ options
64
+ )
65
+ end
66
+ end
67
+
68
+ # TODO: look at making this inherit from Collection. The API is
69
+ # almost identical, and it would make more sense for the
70
+ # relationship.get_children method to return a Proxy than a
71
+ # Collection that is wrapped in a Proxy.
72
+ class Proxy
73
+ include Assertions
74
+
75
+ instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m) }
76
+
77
+ # FIXME: remove when RelationshipChain#get_children can return a Collection
78
+ def all(query = {})
79
+ query.empty? ? self : @relationship.get_children(@parent, query)
80
+ end
81
+
82
+ # FIXME: remove when RelationshipChain#get_children can return a Collection
83
+ def first(*args)
84
+ if args.last.respond_to?(:merge)
85
+ query = args.pop
86
+ @relationship.get_children(@parent, query, :first, *args)
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ def <<(resource)
93
+ assert_mutable
94
+ return self if !resource.new_record? && self.include?(resource)
95
+ super
96
+ relate_resource(resource)
97
+ self
98
+ end
99
+
100
+ def push(*resources)
101
+ assert_mutable
102
+ resources.reject { |resource| !resource.new_record? && self.include?(resource) }
103
+ super
104
+ resources.each { |resource| relate_resource(resource) }
105
+ self
106
+ end
107
+
108
+ def unshift(*resources)
109
+ assert_mutable
110
+ resources.reject { |resource| !resource.new_record? && self.include?(resource) }
111
+ super
112
+ resources.each { |resource| relate_resource(resource) }
113
+ self
114
+ end
115
+
116
+ def replace(other)
117
+ assert_mutable
118
+ each { |resource| orphan_resource(resource) }
119
+ other = other.map { |resource| resource.kind_of?(Hash) ? new_child(resource) : resource }
120
+ super
121
+ other.each { |resource| relate_resource(resource) }
122
+ self
123
+ end
124
+
125
+ def pop
126
+ assert_mutable
127
+ orphan_resource(super)
128
+ end
129
+
130
+ def shift
131
+ assert_mutable
132
+ orphan_resource(super)
133
+ end
134
+
135
+ def delete(resource)
136
+ assert_mutable
137
+ orphan_resource(super)
138
+ end
139
+
140
+ def delete_at(index)
141
+ assert_mutable
142
+ orphan_resource(super)
143
+ end
144
+
145
+ def clear
146
+ assert_mutable
147
+ each { |resource| orphan_resource(resource) }
148
+ super
149
+ self
150
+ end
151
+
152
+ def build(attributes = {})
153
+ assert_mutable
154
+ attributes = default_attributes.merge(attributes)
155
+ resource = children.respond_to?(:build) ? super(attributes) : new_child(attributes)
156
+ resource
157
+ end
158
+
159
+ def new(attributes = {})
160
+ assert_mutable
161
+ raise UnsavedParentError, 'You cannot intialize until the parent is saved' if @parent.new_record?
162
+ attributes = default_attributes.merge(attributes)
163
+ resource = children.respond_to?(:new) ? super(attributes) : @relationship.child_model.new(attributes)
164
+ self << resource
165
+ resource
166
+ end
167
+
168
+ def create(attributes = {})
169
+ assert_mutable
170
+ raise UnsavedParentError, 'You cannot create until the parent is saved' if @parent.new_record?
171
+ attributes = default_attributes.merge(attributes)
172
+ resource = children.respond_to?(:create) ? super(attributes) : @relationship.child_model.create(attributes)
173
+ self << resource
174
+ resource
175
+ end
176
+
177
+ def update(attributes = {})
178
+ assert_mutable
179
+ raise UnsavedParentError, 'You cannot mass-update until the parent is saved' if @parent.new_record?
180
+ super
181
+ end
182
+
183
+ def update!(attributes = {})
184
+ assert_mutable
185
+ raise UnsavedParentError, 'You cannot mass-update without validations until the parent is saved' if @parent.new_record?
186
+ super
187
+ end
188
+
189
+ def destroy
190
+ assert_mutable
191
+ raise UnsavedParentError, 'You cannot mass-delete until the parent is saved' if @parent.new_record?
192
+ super
193
+ end
194
+
195
+ def destroy!
196
+ assert_mutable
197
+ raise UnsavedParentError, 'You cannot mass-delete without validations until the parent is saved' if @parent.new_record?
198
+ super
199
+ end
200
+
201
+ def reload
202
+ @children = nil
203
+ self
204
+ end
205
+
206
+ def save
207
+ return true if children.frozen?
208
+
209
+ # save every resource in the collection
210
+ each { |resource| save_resource(resource) }
211
+
212
+ # save orphan resources
213
+ @orphans.each do |resource|
214
+ begin
215
+ save_resource(resource, nil)
216
+ rescue
217
+ children << resource unless children.frozen? || children.include?(resource)
218
+ raise
219
+ end
220
+ end
221
+
222
+ # FIXME: remove when RelationshipChain#get_children can return a Collection
223
+ # place the children into a Collection if not already
224
+ if children.kind_of?(Array) && !children.frozen?
225
+ @children = @relationship.get_children(@parent).replace(children)
226
+ end
227
+
228
+ true
229
+ end
230
+
231
+ def kind_of?(klass)
232
+ super || children.kind_of?(klass)
233
+ end
234
+
235
+ def respond_to?(method, include_private = false)
236
+ super || children.respond_to?(method, include_private)
237
+ end
238
+
239
+ private
240
+
241
+ def initialize(relationship, parent)
242
+ assert_kind_of 'relationship', relationship, Relationship
243
+ assert_kind_of 'parent', parent, Resource
244
+
245
+ @relationship = relationship
246
+ @parent = parent
247
+ @orphans = []
248
+ end
249
+
250
+ def children
251
+ @children ||= @relationship.get_children(@parent)
252
+ end
253
+
254
+ def assert_mutable
255
+ raise ImmutableAssociationError, 'You can not modify this association' if children.frozen?
256
+ end
257
+
258
+ def default_attributes
259
+ default_attributes = {}
260
+
261
+ @relationship.query.each do |attribute, value|
262
+ next if Query::OPTIONS.include?(attribute) || attribute.kind_of?(Query::Operator)
263
+ default_attributes[attribute] = value
264
+ end
265
+
266
+ @relationship.child_key.zip(@relationship.parent_key.get(@parent)) do |property,value|
267
+ default_attributes[property.name] = value
268
+ end
269
+
270
+ default_attributes
271
+ end
272
+
273
+ def add_default_association_values(resource)
274
+ default_attributes.each do |attribute, value|
275
+ next if !resource.respond_to?("#{attribute}=") || resource.attribute_loaded?(attribute)
276
+ resource.send("#{attribute}=", value)
277
+ end
278
+ end
279
+
280
+ def new_child(attributes)
281
+ @relationship.child_model.new(default_attributes.merge(attributes))
282
+ end
283
+
284
+ def relate_resource(resource)
285
+ assert_mutable
286
+ add_default_association_values(resource)
287
+ @orphans.delete(resource)
288
+ resource
289
+ end
290
+
291
+ def orphan_resource(resource)
292
+ assert_mutable
293
+ @orphans << resource
294
+ resource
295
+ end
296
+
297
+ def save_resource(resource, parent = @parent)
298
+ @relationship.with_repository(resource) do |r|
299
+ if parent.nil? && resource.model.respond_to?(:many_to_many)
300
+ resource.destroy
301
+ else
302
+ @relationship.attach_parent(resource, parent)
303
+ resource.save
304
+ end
305
+ end
306
+ end
307
+
308
+ def method_missing(method, *args, &block)
309
+ results = children.__send__(method, *args, &block) if children.respond_to?(method)
310
+
311
+ return self if LazyArray::RETURN_SELF.include?(method) && results.kind_of?(Array)
312
+
313
+ results
314
+ end
315
+ end # class Proxy
316
+ end # module OneToMany
317
+ end # module Associations
318
+ end # module DataMapper
@@ -0,0 +1,61 @@
1
+ module DataMapper
2
+ module Associations
3
+ module OneToOne
4
+ extend Assertions
5
+
6
+ # Setup one 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.first
19
+ end
20
+
21
+ def #{name}=(child_resource)
22
+ #{name}_association.replace(child_resource.nil? ? [] : [ child_resource ])
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 = Associations::OneToMany::Proxy.new(relationship, self)
33
+ parent_associations << association
34
+ association
35
+ end
36
+ end
37
+ EOS
38
+
39
+ model.relationships(repository_name)[name] = if options.has_key?(:through)
40
+ RelationshipChain.new(
41
+ :child_model => options.fetch(:class_name, Extlib::Inflection.classify(name)),
42
+ :parent_model => model,
43
+ :repository_name => repository_name,
44
+ :near_relationship_name => options[:through],
45
+ :remote_relationship_name => options.fetch(:remote_name, name),
46
+ :parent_key => options[:parent_key],
47
+ :child_key => options[:child_key]
48
+ )
49
+ else
50
+ Relationship.new(
51
+ name,
52
+ repository_name,
53
+ options.fetch(:class_name, Extlib::Inflection.classify(name)),
54
+ model,
55
+ options
56
+ )
57
+ end
58
+ end
59
+ end # module HasOne
60
+ end # module Associations
61
+ end # module DataMapper
@@ -0,0 +1,223 @@
1
+ module DataMapper
2
+ module Associations
3
+ class Relationship
4
+ include Assertions
5
+
6
+ OPTIONS = [ :class_name, :child_key, :parent_key, :min, :max, :through ]
7
+
8
+ # @api private
9
+ attr_reader :name, :options, :query
10
+
11
+ # @api private
12
+ def child_key
13
+ @child_key ||= begin
14
+ child_key = nil
15
+ child_model.repository.scope do |r|
16
+ model_properties = child_model.properties(r.name)
17
+
18
+ child_key = parent_key.zip(@child_properties || []).map do |parent_property,property_name|
19
+ # TODO: use something similar to DM::NamingConventions to determine the property name
20
+ parent_name = Extlib::Inflection.underscore(Extlib::Inflection.demodulize(parent_model.base_model.name))
21
+ property_name ||= "#{parent_name}_#{parent_property.name}".to_sym
22
+
23
+ if model_properties.has_property?(property_name)
24
+ model_properties[property_name]
25
+ else
26
+ options = {}
27
+
28
+ [ :length, :precision, :scale ].each do |option|
29
+ options[option] = parent_property.send(option)
30
+ end
31
+
32
+ # NOTE: hack to make each many to many child_key a true key,
33
+ # until I can figure out a better place for this check
34
+ if child_model.respond_to?(:many_to_many)
35
+ options[:key] = true
36
+ end
37
+
38
+ child_model.property(property_name, parent_property.primitive, options)
39
+ end
40
+ end
41
+ end
42
+ PropertySet.new(child_key)
43
+ end
44
+ end
45
+
46
+ # @api private
47
+ def parent_key
48
+ @parent_key ||= begin
49
+ parent_key = nil
50
+ parent_model.repository.scope do |r|
51
+ parent_key = if @parent_properties
52
+ parent_model.properties(r.name).slice(*@parent_properties)
53
+ else
54
+ parent_model.key
55
+ end
56
+ end
57
+ PropertySet.new(parent_key)
58
+ end
59
+ end
60
+
61
+ # @api private
62
+ def parent_model
63
+ Class === @parent_model ? @parent_model : (Class === @child_model ? @child_model.find_const(@parent_model) : Object.find_const(@parent_model))
64
+ rescue NameError
65
+ raise NameError, "Cannot find the parent_model #{@parent_model} for #{@child_model}"
66
+ end
67
+
68
+ # @api private
69
+ def child_model
70
+ Class === @child_model ? @child_model : (Class === @parent_model ? @parent_model.find_const(@child_model) : Object.find_const(@child_model))
71
+ rescue NameError
72
+ raise NameError, "Cannot find the child_model #{@child_model} for #{@parent_model}"
73
+ end
74
+
75
+ # @api private
76
+ def get_children(parent, options = {}, finder = :all, *args)
77
+ parent_value = parent_key.get(parent)
78
+ bind_values = [ parent_value ]
79
+
80
+ with_repository(child_model) do |r|
81
+ parent_identity_map = parent.repository.identity_map(parent_model)
82
+ child_identity_map = r.identity_map(child_model)
83
+
84
+ query_values = parent_identity_map.keys
85
+ query_values.reject! { |k| child_identity_map[k] }
86
+
87
+ bind_values = query_values unless query_values.empty?
88
+ query = child_key.zip(bind_values.transpose).to_hash
89
+
90
+ collection = child_model.send(finder, *(args.dup << @query.merge(options).merge(query)))
91
+
92
+ return collection unless collection.kind_of?(Collection) && collection.any?
93
+
94
+ grouped_collection = {}
95
+ collection.each do |resource|
96
+ child_value = child_key.get(resource)
97
+ parent_obj = parent_identity_map[child_value]
98
+ grouped_collection[parent_obj] ||= []
99
+ grouped_collection[parent_obj] << resource
100
+ end
101
+
102
+ association_accessor = "#{self.name}_association"
103
+
104
+ ret = nil
105
+ grouped_collection.each do |parent, children|
106
+ association = parent.send(association_accessor)
107
+
108
+ query = collection.query.dup
109
+ query.conditions.map! do |operator, property, bind_value|
110
+ if operator != :raw && child_key.has_property?(property.name)
111
+ bind_value = *children.map { |child| property.get(child) }.uniq
112
+ end
113
+ [ operator, property, bind_value ]
114
+ end
115
+
116
+ parents_children = Collection.new(query)
117
+ children.each { |child| parents_children.send(:add, child) }
118
+
119
+ if parent_key.get(parent) == parent_value
120
+ ret = parents_children
121
+ else
122
+ association.instance_variable_set(:@children, parents_children)
123
+ end
124
+ end
125
+
126
+ ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip([ parent_value ]).to_hash)))
127
+ end
128
+ end
129
+
130
+ # @api private
131
+ def get_parent(child, parent = nil)
132
+ child_value = child_key.get(child)
133
+ return nil unless child_value.nitems == child_value.size
134
+
135
+ with_repository(parent || parent_model) do
136
+ parent_identity_map = (parent || parent_model).repository.identity_map(parent_model.base_model)
137
+ child_identity_map = child.repository.identity_map(child_model.base_model)
138
+
139
+ if parent = parent_identity_map[child_value]
140
+ return parent
141
+ end
142
+
143
+ children = child_identity_map.values
144
+ children << child unless child_identity_map[child.key]
145
+
146
+ bind_values = children.map { |c| child_key.get(c) }.uniq
147
+ query_values = bind_values.reject { |k| parent_identity_map[k] }
148
+
149
+ bind_values = query_values unless query_values.empty?
150
+ query = parent_key.zip(bind_values.transpose).to_hash
151
+ association_accessor = "#{self.name}_association"
152
+
153
+ collection = parent_model.send(:all, query)
154
+ unless collection.empty?
155
+ collection.send(:lazy_load)
156
+ children.each do |c|
157
+ c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
158
+ end
159
+ child.send(association_accessor).instance_variable_get(:@parent)
160
+ end
161
+ end
162
+ end
163
+
164
+ # @api private
165
+ def with_repository(object = nil)
166
+ other_model = object.model == child_model ? parent_model : child_model if object.respond_to?(:model)
167
+ other_model = object == child_model ? parent_model : child_model if object.kind_of?(DataMapper::Resource)
168
+
169
+ if other_model && other_model.repository == object.repository && object.repository.name != @repository_name
170
+ object.repository.scope { |block_args| yield(*block_args) }
171
+ else
172
+ repository(@repository_name) { |block_args| yield(*block_args) }
173
+ end
174
+ end
175
+
176
+ # @api private
177
+ def attach_parent(child, parent)
178
+ child_key.set(child, parent && parent_key.get(parent))
179
+ end
180
+
181
+ private
182
+
183
+ # +child_model_name and child_properties refers to the FK, parent_model_name
184
+ # and parent_properties refer to the PK. For more information:
185
+ # http://edocs.bea.com/kodo/docs41/full/html/jdo_overview_mapping_join.html
186
+ # I wash my hands of it!
187
+ def initialize(name, repository_name, child_model, parent_model, options = {})
188
+ assert_kind_of 'name', name, Symbol
189
+ assert_kind_of 'repository_name', repository_name, Symbol
190
+ assert_kind_of 'child_model', child_model, String, Class
191
+ assert_kind_of 'parent_model', parent_model, String, Class
192
+
193
+ if child_properties = options[:child_key]
194
+ assert_kind_of 'options[:child_key]', child_properties, Array
195
+ end
196
+
197
+ if parent_properties = options[:parent_key]
198
+ assert_kind_of 'options[:parent_key]', parent_properties, Array
199
+ end
200
+
201
+ @name = name
202
+ @repository_name = repository_name
203
+ @child_model = child_model
204
+ @child_properties = child_properties # may be nil
205
+ @query = options.reject { |k,v| OPTIONS.include?(k) }
206
+ @parent_model = parent_model
207
+ @parent_properties = parent_properties # may be nil
208
+ @options = options
209
+
210
+ # attempt to load the child_key if the parent and child model constants are defined
211
+ if model_defined?(@child_model) && model_defined?(@parent_model)
212
+ child_key
213
+ end
214
+ end
215
+
216
+ # @api private
217
+ def model_defined?(model)
218
+ # TODO: figure out other ways to see if the model is loaded
219
+ model.kind_of?(Class)
220
+ end
221
+ end # class Relationship
222
+ end # module Associations
223
+ end # module DataMapper