datamapper 0.3.2 → 0.9.3

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 (180) hide show
  1. data/History.txt +0 -0
  2. data/Manifest.txt +5 -0
  3. data/README.txt +11 -0
  4. data/Rakefile +70 -0
  5. data/lib/datamapper.rb +8 -0
  6. metadata +152 -319
  7. data/CHANGELOG +0 -145
  8. data/FAQ +0 -96
  9. data/MIT-LICENSE +0 -22
  10. data/QUICKLINKS +0 -12
  11. data/README +0 -105
  12. data/environment.rb +0 -62
  13. data/example.rb +0 -156
  14. data/lib/data_mapper.rb +0 -88
  15. data/lib/data_mapper/adapters/abstract_adapter.rb +0 -43
  16. data/lib/data_mapper/adapters/data_object_adapter.rb +0 -480
  17. data/lib/data_mapper/adapters/mysql_adapter.rb +0 -72
  18. data/lib/data_mapper/adapters/postgresql_adapter.rb +0 -258
  19. data/lib/data_mapper/adapters/sql/coersion.rb +0 -134
  20. data/lib/data_mapper/adapters/sql/commands/load_command.rb +0 -545
  21. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +0 -34
  22. data/lib/data_mapper/adapters/sql/mappings/column.rb +0 -279
  23. data/lib/data_mapper/adapters/sql/mappings/conditions.rb +0 -172
  24. data/lib/data_mapper/adapters/sql/mappings/schema.rb +0 -60
  25. data/lib/data_mapper/adapters/sql/mappings/table.rb +0 -459
  26. data/lib/data_mapper/adapters/sql/quoting.rb +0 -24
  27. data/lib/data_mapper/adapters/sqlite3_adapter.rb +0 -159
  28. data/lib/data_mapper/associations.rb +0 -106
  29. data/lib/data_mapper/associations/belongs_to_association.rb +0 -160
  30. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +0 -437
  31. data/lib/data_mapper/associations/has_many_association.rb +0 -283
  32. data/lib/data_mapper/associations/has_n_association.rb +0 -143
  33. data/lib/data_mapper/associations/reference.rb +0 -47
  34. data/lib/data_mapper/attributes.rb +0 -73
  35. data/lib/data_mapper/auto_migrations.rb +0 -36
  36. data/lib/data_mapper/base.rb +0 -17
  37. data/lib/data_mapper/callbacks.rb +0 -107
  38. data/lib/data_mapper/context.rb +0 -112
  39. data/lib/data_mapper/database.rb +0 -234
  40. data/lib/data_mapper/dependency_queue.rb +0 -28
  41. data/lib/data_mapper/embedded_value.rb +0 -145
  42. data/lib/data_mapper/identity_map.rb +0 -47
  43. data/lib/data_mapper/is/tree.rb +0 -121
  44. data/lib/data_mapper/migration.rb +0 -155
  45. data/lib/data_mapper/persistence.rb +0 -852
  46. data/lib/data_mapper/property.rb +0 -310
  47. data/lib/data_mapper/query.rb +0 -164
  48. data/lib/data_mapper/support/blank.rb +0 -35
  49. data/lib/data_mapper/support/connection_pool.rb +0 -117
  50. data/lib/data_mapper/support/enumerable.rb +0 -35
  51. data/lib/data_mapper/support/errors.rb +0 -16
  52. data/lib/data_mapper/support/inflector.rb +0 -265
  53. data/lib/data_mapper/support/object.rb +0 -54
  54. data/lib/data_mapper/support/serialization.rb +0 -96
  55. data/lib/data_mapper/support/silence.rb +0 -10
  56. data/lib/data_mapper/support/string.rb +0 -72
  57. data/lib/data_mapper/support/struct.rb +0 -7
  58. data/lib/data_mapper/support/symbol.rb +0 -82
  59. data/lib/data_mapper/support/typed_set.rb +0 -65
  60. data/lib/data_mapper/types/base.rb +0 -44
  61. data/lib/data_mapper/types/string.rb +0 -34
  62. data/lib/data_mapper/validatable_extensions/errors.rb +0 -12
  63. data/lib/data_mapper/validatable_extensions/macros.rb +0 -7
  64. data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +0 -62
  65. data/lib/data_mapper/validatable_extensions/validation_base.rb +0 -18
  66. data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +0 -43
  67. data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +0 -7
  68. data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +0 -7
  69. data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +0 -7
  70. data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +0 -28
  71. data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +0 -15
  72. data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +0 -7
  73. data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +0 -7
  74. data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +0 -7
  75. data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +0 -40
  76. data/lib/data_mapper/validations.rb +0 -20
  77. data/lib/data_mapper/validations/number_validator.rb +0 -40
  78. data/lib/data_mapper/validations/string_validator.rb +0 -20
  79. data/lib/data_mapper/validations/validator.rb +0 -13
  80. data/performance.rb +0 -307
  81. data/plugins/can_has_sphinx/LICENSE +0 -23
  82. data/plugins/can_has_sphinx/README +0 -4
  83. data/plugins/can_has_sphinx/REVISION +0 -1
  84. data/plugins/can_has_sphinx/Rakefile +0 -22
  85. data/plugins/can_has_sphinx/init.rb +0 -1
  86. data/plugins/can_has_sphinx/install.rb +0 -1
  87. data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +0 -123
  88. data/plugins/can_has_sphinx/lib/sphinx.rb +0 -460
  89. data/plugins/can_has_sphinx/scripts/sphinx.sh +0 -47
  90. data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +0 -41
  91. data/profile_data_mapper.rb +0 -40
  92. data/rakefile.rb +0 -159
  93. data/spec/acts_as_tree_spec.rb +0 -67
  94. data/spec/adapters/data_object_adapter_spec.rb +0 -31
  95. data/spec/associations/belongs_to_association_spec.rb +0 -98
  96. data/spec/associations/has_and_belongs_to_many_association_spec.rb +0 -377
  97. data/spec/associations/has_many_association_spec.rb +0 -337
  98. data/spec/attributes_spec.rb +0 -52
  99. data/spec/auto_migrations_spec.rb +0 -101
  100. data/spec/callbacks_spec.rb +0 -186
  101. data/spec/can_has_sphinx.rb +0 -5
  102. data/spec/coersion_spec.rb +0 -41
  103. data/spec/column_spec.rb +0 -114
  104. data/spec/count_command_spec.rb +0 -45
  105. data/spec/database_spec.rb +0 -18
  106. data/spec/dataobjects_spec.rb +0 -27
  107. data/spec/delete_command_spec.rb +0 -11
  108. data/spec/dependency_spec.rb +0 -29
  109. data/spec/embedded_value_spec.rb +0 -161
  110. data/spec/fixtures/animals.yaml +0 -33
  111. data/spec/fixtures/animals_exhibits.yaml +0 -2
  112. data/spec/fixtures/careers.yaml +0 -5
  113. data/spec/fixtures/comments.yaml +0 -1
  114. data/spec/fixtures/exhibits.yaml +0 -90
  115. data/spec/fixtures/fruit.yaml +0 -6
  116. data/spec/fixtures/people.yaml +0 -37
  117. data/spec/fixtures/posts.yaml +0 -3
  118. data/spec/fixtures/projects.yaml +0 -13
  119. data/spec/fixtures/sections.yaml +0 -5
  120. data/spec/fixtures/serializers.yaml +0 -6
  121. data/spec/fixtures/tasks.yaml +0 -6
  122. data/spec/fixtures/tasks_tasks.yaml +0 -2
  123. data/spec/fixtures/tomatoes.yaml +0 -1
  124. data/spec/fixtures/users.yaml +0 -1
  125. data/spec/fixtures/zoos.yaml +0 -24
  126. data/spec/is_a_tree_spec.rb +0 -149
  127. data/spec/legacy_spec.rb +0 -16
  128. data/spec/load_command_spec.rb +0 -322
  129. data/spec/magic_columns_spec.rb +0 -26
  130. data/spec/migration_spec.rb +0 -267
  131. data/spec/mock_adapter.rb +0 -20
  132. data/spec/models/animal.rb +0 -12
  133. data/spec/models/candidate.rb +0 -8
  134. data/spec/models/career.rb +0 -7
  135. data/spec/models/chain.rb +0 -8
  136. data/spec/models/comment.rb +0 -6
  137. data/spec/models/exhibit.rb +0 -14
  138. data/spec/models/fence.rb +0 -7
  139. data/spec/models/fruit.rb +0 -8
  140. data/spec/models/job.rb +0 -8
  141. data/spec/models/person.rb +0 -30
  142. data/spec/models/post.rb +0 -14
  143. data/spec/models/project.rb +0 -41
  144. data/spec/models/sales_person.rb +0 -5
  145. data/spec/models/section.rb +0 -8
  146. data/spec/models/serializer.rb +0 -5
  147. data/spec/models/task.rb +0 -9
  148. data/spec/models/tomato.rb +0 -27
  149. data/spec/models/user.rb +0 -12
  150. data/spec/models/zoo.rb +0 -13
  151. data/spec/natural_key_spec.rb +0 -36
  152. data/spec/paranoia_spec.rb +0 -38
  153. data/spec/persistence_spec.rb +0 -479
  154. data/spec/postgres_spec.rb +0 -96
  155. data/spec/property_spec.rb +0 -151
  156. data/spec/query_spec.rb +0 -77
  157. data/spec/save_command_spec.rb +0 -94
  158. data/spec/schema_spec.rb +0 -8
  159. data/spec/serialize_spec.rb +0 -19
  160. data/spec/single_table_inheritance_spec.rb +0 -43
  161. data/spec/spec_helper.rb +0 -45
  162. data/spec/support/blank_spec.rb +0 -8
  163. data/spec/support/inflector_spec.rb +0 -41
  164. data/spec/support/object_spec.rb +0 -9
  165. data/spec/support/serialization_spec.rb +0 -61
  166. data/spec/support/silence_spec.rb +0 -15
  167. data/spec/support/string_spec.rb +0 -7
  168. data/spec/support/struct_spec.rb +0 -12
  169. data/spec/support/typed_set_spec.rb +0 -66
  170. data/spec/symbolic_operators_spec.rb +0 -27
  171. data/spec/table_spec.rb +0 -79
  172. data/spec/types/string.rb +0 -81
  173. data/spec/validates_confirmation_of_spec.rb +0 -55
  174. data/spec/validates_format_of_spec.rb +0 -78
  175. data/spec/validates_length_of_spec.rb +0 -117
  176. data/spec/validates_uniqueness_of_spec.rb +0 -92
  177. data/spec/validations/number_validator.rb +0 -59
  178. data/spec/validations/string_validator.rb +0 -14
  179. data/spec/validations_spec.rb +0 -141
  180. data/tasks/fixtures.rb +0 -53
@@ -1,121 +0,0 @@
1
- module DataMapper
2
- module Is
3
- module Tree
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- # An extension to DataMapper to easily allow the creation of tree structures from your DataMapper Models.
9
- # This requires a foreign key property for your model, which by default would be called :parent_id.
10
- #
11
- # Example:
12
- #
13
- # class Category < DataMapper::Base
14
- # property :parent_id, :integer
15
- # property :name, :string
16
- #
17
- # is_a_tree :order => "name"
18
- # end
19
- #
20
- # root
21
- # +- child
22
- # +- grandchild1
23
- # +- grandchild2
24
- #
25
- # root = Category.create("name" => "root")
26
- # child = root.children.create("name" => "child")
27
- # grandchild1 = child1.children.create("name" => "grandchild1")
28
- # grandchild2 = child2.children.create("name" => "grandchild2")
29
- #
30
- # root.parent # => nil
31
- # child.parent # => root
32
- # root.children # => [child]
33
- # root.children.first.children.first # => grandchild1
34
- # Category.first_root # => root
35
- # Category.roots # => [root]
36
- #
37
- # The following instance methods are added:
38
- # * <tt>children</tt> - Returns all nodes with the current node as their parent, in the order specified by
39
- # <tt>:order</tt> (<tt>[grandchild1, grandchild2]</tt> when called on <tt>child</tt>)
40
- # * <tt>parent</tt> - Returns the node referenced by the foreign key (<tt>:parent_id</tt> by
41
- # default) (<tt>root</tt> when called on <tt>child</tt>)
42
- # * <tt>siblings</tt> - Returns all the children of the parent, excluding the current node
43
- # (<tt>[grandchild2]</tt> when called on <tt>grandchild1</tt>)
44
- # * <tt>generation</tt> - Returns all the children of the parent, including the current node (<tt>
45
- # [grandchild1, grandchild2]</tt> when called on <tt>grandchild1</tt>)
46
- # * <tt>ancestors</tt> - Returns all the ancestors of the current node (<tt>[root, child1]</tt>
47
- # when called on <tt>grandchild2</tt>)
48
- # * <tt>root</tt> - Returns the root of the current node (<tt>root</tt> when called on <tt>grandchild2</tt>)
49
- #
50
- # Author:: Timothy Bennett (http://lanaer.com)
51
- module ClassMethods
52
- # Configuration options are:
53
- #
54
- # * <tt>foreign_key</tt> - specifies the column name to use for tracking of the tree (default: +parent_id+)
55
- # * <tt>order</tt> - makes it possible to sort the children according to this SQL snippet.
56
- # * <tt>counter_cache</tt> - keeps a count in a +children_count+ column if set to +true+ (default: +false+).
57
- def is_a_tree(options = {})
58
- configuration = { :foreign_key => "parent_id" }
59
- configuration.update(options) if options.is_a?(Hash)
60
-
61
- belongs_to :parent, :class_name => name, :foreign_key => configuration[:foreign_key], :counter_cache => configuration[:counter_cache]
62
- has_many :children, :class_name => name, :foreign_key => configuration[:foreign_key], :order => configuration[:order]
63
-
64
- include DataMapper::Is::Tree::InstanceMethods
65
-
66
- class_eval <<-CLASS
67
- def self.roots
68
- self.all :#{configuration[:foreign_key]} => nil, :order => #{configuration[:order].inspect}
69
- end
70
-
71
- def self.first_root
72
- self.first :#{configuration[:foreign_key]} => nil, :order => #{configuration[:order].inspect}
73
- end
74
- CLASS
75
-
76
- class << self
77
- alias_method :root, :first_root # for people used to the ActiveRecord acts_as_tree
78
- end
79
- end
80
-
81
- alias_method :can_has_tree, :is_a_tree # just for fun ;)
82
- end
83
-
84
- module InstanceMethods
85
- # Returns list of ancestors, starting with the root.
86
- #
87
- # grandchild1.ancestors # => [root, child]
88
- def ancestors
89
- node, nodes = self, []
90
- nodes << node = node.parent while node.parent
91
- nodes.reverse
92
- end
93
-
94
- # Returns the root node of the current node’s tree.
95
- #
96
- # grandchild1.root # => root
97
- def root
98
- node = self
99
- node = node.parent while node.parent
100
- node
101
- end
102
-
103
- # Returns all siblings of the current node.
104
- #
105
- # grandchild1.siblings # => [grandchild2]
106
- def siblings
107
- generation - [self]
108
- end
109
-
110
- # Returns all children of the current node’s parent.
111
- #
112
- # grandchild1.generation # => [grandchild1, grandchild2]
113
- def generation
114
- parent ? parent.children : self.class.roots
115
- end
116
-
117
- alias_method :self_and_siblings, :generation # for those used to the ActiveRecord acts_as_tree
118
- end
119
- end
120
- end
121
- end
@@ -1,155 +0,0 @@
1
- module DataMapper
2
- class Migration
3
- class Table
4
-
5
- MAPPINGS = DataMapper::Adapters::Sql::Mappings unless defined?(MAPPINGS)
6
-
7
- attr_accessor :name
8
-
9
- def initialize(table = nil, options = {})
10
- @name, @options = table, options
11
- @columns = []
12
- end
13
-
14
- def self.create(table)
15
- table.create!
16
- end
17
-
18
- def self.drop(table_name)
19
- database.table(klass(table_name)).drop!
20
- end
21
-
22
- def self.add_column(table, column, type, options = {})
23
- column = table.add_column column, type, options
24
- column.create!
25
- end
26
-
27
- def self.remove_column(table, column)
28
- column = table[column]
29
- column.drop!
30
- end
31
-
32
- def add(column, type, options = {})
33
- column_data = [column, type, options]
34
- exists? ? self.class.add_column(table, *column_data) : table.add_column(*column_data)
35
- end
36
-
37
- def remove(column)
38
- self.class.remove_column table, column
39
- end
40
-
41
- def rename(old_column, new_column)
42
- column = table[old_column]
43
- column.rename!(new_column)
44
- end
45
-
46
- def alter(column, type, options = {})
47
- column = table[column]
48
- column.type = type
49
- column.options = options
50
- column.parse_options!
51
- column.alter!
52
- end
53
-
54
- def exists?
55
- database.table_exists?(klass)
56
- end
57
-
58
- def after_create!
59
- unless exists?
60
- table.add_column(:id, :integer, { :key => true }) unless @options[:id] == false
61
- self.class.create(table)
62
- end
63
- end
64
-
65
- # Rails Style
66
-
67
- def column(name, type, options = {})
68
- add(name, type, options)
69
- end
70
-
71
- # klass!
72
-
73
- def table
74
- @table ||= database.table(klass)
75
- end
76
-
77
- def klass
78
- @klass ||= self.class.klass(self.name)
79
- end
80
-
81
- def self.klass(table)
82
- table_name = table.to_s
83
- class_name = Inflector::classify(table_name)
84
- klass = Inflector::constantize(class_name)
85
- rescue NameError
86
- module_eval <<-classdef
87
- class ::#{class_name} < DataMapper::Base
88
- end
89
- classdef
90
- klass = eval("#{class_name}")
91
- ensure
92
- klass
93
- end
94
-
95
- end
96
-
97
- class << self
98
-
99
- def up; end
100
-
101
- def down; end
102
-
103
- def migrate(direction = :up)
104
- send(direction)
105
- end
106
-
107
- def table(table = nil, options = {}, &block)
108
- if table && block
109
- table = DataMapper::Migration::Table.new(table, options)
110
- table.instance_eval &block
111
- table.after_create!
112
- else
113
- return DataMapper::Migration::Table
114
- end
115
- end
116
-
117
- # Rails Style
118
-
119
- def create_table(table_name, options = {}, &block)
120
- new_table = table.new(table_name, options)
121
- yield new_table
122
- new_table.after_create!
123
- end
124
-
125
- def drop_table(table_name)
126
- table.drop(table_name)
127
- end
128
-
129
- def add_column(table_name, column, type, options = {})
130
- table table_name do
131
- add column, type, options
132
- end
133
- end
134
-
135
- def rename_column(table_name, old_column_name, new_column_name)
136
- table table_name do
137
- rename old_column_name, new_column_name
138
- end
139
- end
140
-
141
- def change_column(table_name, column_name, type, options = {})
142
- table table_name do
143
- alter column_name, type, options
144
- end
145
- end
146
-
147
- def remove_column(table_name, column)
148
- table table_name do
149
- remove column
150
- end
151
- end
152
- end
153
- end
154
-
155
- end
@@ -1,852 +0,0 @@
1
- require 'data_mapper/property'
2
- require 'data_mapper/attributes'
3
- require 'data_mapper/support/serialization'
4
- require 'data_mapper/validations'
5
- require 'data_mapper/associations'
6
- require 'data_mapper/callbacks'
7
- require 'data_mapper/embedded_value'
8
- require 'data_mapper/auto_migrations'
9
- require 'data_mapper/dependency_queue'
10
- require 'data_mapper/support/struct'
11
-
12
- module DataMapper
13
- # See DataMapper::Persistence::ClassMethods for DataMapper's DSL documentation.
14
- module Persistence
15
-
16
- class IncompleteModelDefinitionError < StandardError
17
- end
18
-
19
- # This probably needs to be protected
20
- attr_accessor :loaded_set
21
-
22
- include Comparable
23
-
24
- def self.included(klass)
25
-
26
- klass.extend(ClassMethods)
27
- klass.extend(ConvenienceMethods::ClassMethods)
28
-
29
- klass.send(:include, ConvenienceMethods::InstanceMethods)
30
- klass.send(:include, Attributes)
31
- klass.send(:include, Associations)
32
- klass.send(:include, Validations)
33
- klass.send(:include, CallbacksHelper)
34
- klass.send(:include, Support::Serialization)
35
-
36
- klass.instance_variable_set('@properties', [])
37
-
38
- klass.send :extend, AutoMigrations
39
- klass.subclasses
40
- DataMapper::Persistence::subclasses << klass unless klass == DataMapper::Base
41
- klass.send(:undef_method, :id) if method_defined?(:id)
42
-
43
- # When this class is sub-classed, copy the declared columns.
44
- klass.class_eval do
45
- def self.subclasses
46
- @subclasses || (@subclasses = Support::TypedSet.new(Class))
47
- end
48
-
49
- def self.inherited(subclass)
50
- super_table = database.table(self)
51
-
52
- if super_table.type_column.nil?
53
- super_table.add_column(:type, :class, {})
54
- end
55
-
56
- subclass.instance_variable_set('@properties', self.instance_variable_get("@properties").dup)
57
- subclass.instance_variable_set("@callbacks", self.callbacks.dup)
58
-
59
- self::subclasses << subclass
60
- end
61
-
62
- def self.persistent?
63
- true
64
- end
65
- end
66
- end
67
-
68
- # Migrates the database schema based on the properties defined within
69
- # models. This includes removing fields no longer listed in models and
70
- # adding new ones.
71
- #
72
- # This is destructive. Any data stored in the database will be destroyed
73
- # when this method is called.
74
- #
75
- # ==== Returns
76
- # True:: successfully automigrated database
77
- # False:: an error occured when automigrating the database
78
- #
79
- # @public
80
- def self.auto_migrate!
81
- subclasses.each do |subclass|
82
- subclass.auto_migrate!
83
- end
84
- end
85
-
86
-
87
- # Drops all tables known by the schema
88
- #
89
- # ==== Returns
90
- # True:: successfully automigrated database
91
- # False:: an error occured when automigrating the database
92
- #
93
- # @public
94
- def self.drop_all_tables!
95
- database.adapter.schema.each do |table|
96
- table.drop!
97
- end
98
- end
99
-
100
- # Track classes that include this module.
101
- # ==== Returns
102
- # Support::TypedSet::
103
- # contains classes that include or inherit from this module
104
- #
105
- # @semipublic
106
- def self.subclasses
107
- @subclasses || (@subclasses = Support::TypedSet.new(Class))
108
- end
109
-
110
- # Track classes that include this module.
111
- # ==== Returns
112
- # Support::TypedSet::
113
- # contains classes that include or inherit from this module
114
- #
115
- # @semipublic
116
- def self.dependencies
117
- @dependency_queue || (@dependency_queue = DependencyQueue.new)
118
- end
119
-
120
- def initialize(details = nil)
121
- check_for_properties!
122
- if details
123
- initialize_with_attributes(details)
124
- end
125
- end
126
-
127
- def initialize_with_attributes(details)
128
- case details
129
- when Hash then self.attributes = details
130
- when details.respond_to?(:persistent?) then self.private_attributes = details.attributes
131
- when Struct then self.private_attributes = details.attributes
132
- end
133
- end
134
-
135
- def check_for_properties!
136
- raise IncompleteModelDefinitionError.new("Models must have at least one property to be initialized.") if self.class.properties.empty?
137
- end
138
-
139
- module ConvenienceMethods
140
- module InstanceMethods
141
-
142
- # Save updated properties to the database.
143
- #
144
- # ==== Returns
145
- # True:: successfully saved the object to the database
146
- # False:: an error occured when saving the object to the database.
147
- # check valid? to see if validation error occured
148
- #
149
- # @public
150
- def save
151
- database_context.save(self)
152
- end
153
-
154
- # This behaves in the same way as save, but raises a ValidationError
155
- # if the model is invalid. Successful saves return true.
156
- #
157
- # ==== Returns
158
- # True:: successfully saved the object to the database
159
- #
160
- # ==== Raises
161
- # ValidationError::
162
- # The object could not be saved to the database due to validation
163
- # errors
164
- # @public
165
- def save!
166
- raise ValidationError.new(errors) unless save
167
- return true
168
- end
169
-
170
- # Reloads a model's properties from the database. This also includes
171
- # data for any associated models that have been loaded from the
172
- # database.
173
- #
174
- # You can limit the properties being reloaded by passing in an array
175
- # of symbols.
176
- def reload!(cols = nil)
177
- database_context.first(self.class, key, :select => ([self.class.table.key.to_sym] + (cols || original_values.keys)).uniq, :reload => true)
178
- self.loaded_associations.each { |association| association.reload! }
179
- self
180
- end
181
- alias reload reload!
182
-
183
- # Deletes the model from the database and de-activates associations
184
- def destroy!
185
- database_context.destroy(self)
186
- end
187
- end
188
-
189
- module ClassMethods
190
-
191
- # Attempts to find an object using options passed as
192
- # search_attributes, and falls back to creating the object if it
193
- # can't find it.
194
- #
195
- # ==== Parameters
196
- # search_attributes <hash>::
197
- # attributes used to perform the search, and which can be later
198
- # merged with create_attributes when creating a record
199
- # create_attributes <hash>::
200
- # attributes which are merged into the search_attributes when a
201
- # record is unfound and needs to be created
202
- #
203
- # ==== Returns
204
- # Object:: the found or created object from the database
205
- #
206
- # ==== Raises
207
- # ValidationError::
208
- # An object was not found, and could not be created due to errors
209
- # in validation.
210
- # DataObject::QueryError::
211
- # The database threw an error
212
- # -
213
- # @public
214
- def find_or_create(search_attributes, create_attributes = {})
215
- first(search_attributes) || create(search_attributes.merge(create_attributes))
216
- end
217
-
218
- # returns an array of objects matching <tt>options</tt>.
219
- #
220
- # ==== Parameters
221
- # options <hash>::
222
- # hash of parameters to search by
223
- #
224
- # ==== Returns
225
- # Array:: contains all matched objects from the database, or an
226
- # empty set
227
- #
228
- # ==== Options
229
- # Basics:
230
- # Widget.all # => no conditions
231
- # Widget.all :order => 'created_at desc' # => ORDER BY created_at desc
232
- # Widget.all :limit => 10 # => LIMIT 10
233
- # Widget.all :offset => 100 # => OFFSET 100
234
- # Widget.all :include => [:gadgets] # => performs the JOIN according to
235
- # its association with Gadgets
236
- #
237
- # Any non-standard options are assumed to be column names and are ANDed together:
238
- # Widget.all :age => 10 # => WHERE age = 10
239
- # Widget.all :age => 10, :title => 'Toy' # => WHERE age = 10 AND title = 'Toy'
240
- #
241
- # Using Symbol Operators[link:classes/DataMapper/Support/Symbol/Operator.html]:
242
- # Widget.all :age.gt => 20 # => WHERE age > 10
243
- # Widget.all :age.gte => 20, :name.like => '%Toy%' # => WHERE age >= 10 and name like '%Toy%'
244
- #
245
- # Variations of syntax include the :conditions => {} as well as interpolated arrays
246
- # Widget.all :conditions => {:age => 10} # => WHERE age = 10
247
- # Widget.all :conditions => ["age = ?", 10] # => WHERE age = 10
248
- #
249
- # Syntaxes can be mixed-and-matched as well
250
- # Widget.all :conditions => ["age = ?", 10], :title => 'Toy'
251
- # # => WHERE age = 10 AND title = 'Toy'
252
- #
253
- # ==== Raises
254
- # DataMapper::Adapters::Sql::Commands::LoadCommand::ConditionsError::
255
- # A query could not be constructed from the hash passed in as
256
- # <tt>options</tt>
257
- # DataObject::QueryError::
258
- # The database threw an error
259
- # -
260
- # @public
261
- def all(options = {})
262
- database.all(self, options)
263
- end
264
-
265
- # Allows you to iterate over a collection of matching records. The
266
- # first argument is the find options. The second is a block that will
267
- # be called for every matching record.
268
- #
269
- # The valid options are the same as those documented in #all,
270
- # except the <tt>:offset</tt> option, which is not allowed.
271
- def each(options = {}, &b)
272
- raise ArgumentError.new(":offset is not supported with the #each method") if options.has_key?(:offset)
273
-
274
- offset = 0
275
- limit = options[:limit] || (self::const_defined?('DEFAULT_LIMIT') ? self::DEFAULT_LIMIT : 500)
276
-
277
- until (results = all(options.merge(:limit => limit, :offset => offset))).empty?
278
- results.each(&b)
279
- offset += limit
280
- end
281
- end
282
-
283
- # Returns the first object which matches the query generated from the arguments
284
- #
285
- # ==== Parameters
286
- # see all()
287
- #
288
- # ==== Returns
289
- # Object:: first object from the database which matches the query
290
- # nil:: no object could be found which matches the query
291
- #
292
- # ==== Raises
293
- # DataMapper::Adapters::Sql::Commands::LoadCommand::ConditionsError::
294
- # A query could not be generated from the arguments passed in
295
- # DataObject::QueryError::
296
- # The database threw an error
297
- # -
298
- # @public
299
- def first(*args)
300
- database.first(self, *args)
301
- end
302
-
303
- # returns the count of rows that match the given options hash. See
304
- # all() for a list of possible arguments.
305
- # NOTE: discards <tt>:offset</tt>, <tt>:limit</tt>, <tt>:order</tt>
306
- #
307
- # ==== Parameters
308
- # see all().
309
- #
310
- # ==== Returns
311
- # Integer:: number of rows matching query
312
- #
313
- # ==== Raises
314
- # DataMapper::Adapters::Sql::Commands::LoadCommand::ConditionsError::
315
- # A query could not be generated from the arguments passed in
316
- # DataObject::QueryError::
317
- # The database threw an error
318
- # -
319
- # @public
320
- def count(*args)
321
- database.count(self, *args)
322
- end
323
-
324
- # Does what it says. Deletes all records in a model's table.
325
- # before_destroy and after_destroy callbacks are called and
326
- # paranoia is respected.
327
- #
328
- # ==== Returns
329
- # nil:: successfully deleted all rows
330
- #
331
- # ==== Raises
332
- # DataObject::QueryError::
333
- # The database threw an error
334
- # -
335
- # @public
336
- def delete_all
337
- database.delete_all(self)
338
- end
339
-
340
- def truncate!
341
- database.truncate(self)
342
- end
343
-
344
- # This method allows for ActiveRecord style queries. The first
345
- # argument is a symbol indicating a search for a single record or a
346
- # collection — <tt>:first</tt> and <tt>:all</tt> respectively. The
347
- # second argument is the hash of options for your query. For a list
348
- # of valid options, please refer to the #all method.
349
- #
350
- # Widget.find(:all, :active => true) # => An array of active widgets
351
- # Widget.find(:first, :active => true) # => The first active widget found
352
- def find(type_or_id, options = {})
353
- case type_or_id
354
- when :first then first(options)
355
- when :all then all(options)
356
- else first(type_or_id, options)
357
- end
358
- end
359
-
360
- # supply this method with the full SQL you wish to search on, and it
361
- # will return an array of Structs with your results set in them.
362
- #
363
- # NOTE: this does NOT return objects of a specific type, but rather
364
- # Struct objects with as many attributes as what you requested in
365
- # your full SQL query. These structs are read-only.
366
- #
367
- # If you only indicate you want 1 specific column, Datamapper and
368
- # DataObjects will do their best to type-cast the result as best they
369
- # can, rather than supplying you with an array of length 1 containing
370
- # Structs with 1 attribute.
371
- def find_by_sql(*args)
372
- DataMapper::database.query(*args)
373
- end
374
-
375
- # finds a single row from the database by it's primary key.
376
- # If you declared a property with <tt>:key => true</tt>, it's safe to
377
- # use here.
378
- # Example:
379
- # Widget.get(100) # => widget with the primary key of 100
380
- # Widget.get('Toy') # => widget with the primary natural key of 'Toy'
381
- def get(*keys)
382
- database.get(self, keys)
383
- end
384
-
385
-
386
- # synonym for get()
387
- # ==== Parameters
388
- # keys <any>:: keys which which to look up objects in the table.
389
- #
390
- # ==== Returns
391
- # object :: object matching the request
392
- #
393
- # ==== Raises
394
- # DataMapper::ObjectNotFoundError
395
- # could not find the object requested
396
- # -
397
- # @public
398
- def [](*keys)
399
- # Eventually this ArgumentError should be removed. It's only here
400
- # to help
401
- # migrate users away from the [options_hash] syntax, which is no
402
- # longer supported.
403
- raise ArgumentError.new('Hash is not a valid key') if keys.size == 1 && keys.first.is_a?(Hash)
404
- instance = database.get(self, keys)
405
- raise ObjectNotFoundError.new() unless instance
406
- return instance
407
- end
408
-
409
- # creates (and saves) a new instance of the object.
410
- def create(attributes)
411
- instance = self.new_with_attributes(attributes)
412
- instance.save
413
- instance
414
- end
415
-
416
- # the same as create(), though will raise an ObjectNotFoundError if
417
- # the instance could not be saved
418
- def create!(attributes)
419
- instance = create(attributes)
420
- raise ObjectNotFoundError.new(instance) if instance.new_record?
421
- instance
422
- end
423
- end
424
- end
425
-
426
- module ClassMethods
427
-
428
- def new_with_attributes(details)
429
- instance = allocate
430
- instance.initialize_with_attributes(details)
431
- instance
432
- end
433
-
434
- # Track classes that include this module.
435
- def subclasses
436
- @subclasses || (@subclasses = [])
437
- end
438
-
439
- def logger
440
- database.logger
441
- end
442
-
443
- def transaction
444
- yield
445
- end
446
-
447
- # The foreign key for a model. It is based on the lowercased and
448
- # underscored name of the class, suffixed with <tt>_id</tt>.
449
- #
450
- # Widget.foreign_key # => "widget_id"
451
- # NewsItem.foreign_key # => "news_item_id"
452
- def foreign_key
453
- Inflector.underscore(self.name) + "_id"
454
- end
455
-
456
- def extended(klass)
457
- end
458
-
459
- def table
460
- database.table(self)
461
- end
462
-
463
- # Adds property accessors for a field that you'd like to be able to
464
- # modify. The DataMapper doesn't
465
- # use the table schema to infer accessors, you must explicity call
466
- # #property to add field accessors
467
- # to your model.
468
- #
469
- # Can accept an unlimited amount of property names. Optionally, you may
470
- # pass the property names as an
471
- # array.
472
- #
473
- # For more documentation, see Property.
474
- #
475
- # EXAMPLE:
476
- # class CellProvider
477
- # property :name, :string
478
- # property :rating_number, :rating_percent, :integer # will create two properties with same type and text
479
- # property [:bill_to, :ship_to, :mail_to], :text, :lazy => false # will create three properties all with same type and text
480
- # end
481
- #
482
- # att = CellProvider.new(:name => 'AT&T')
483
- # att.rating = 3
484
- # puts att.name, att.rating
485
- #
486
- # => AT&T
487
- # => 3
488
- #
489
- # OPTIONS:
490
- # * <tt>lazy</tt>: Lazy load the specified property (:lazy => true). False by default.
491
- # * <tt>accessor</tt>: Set method visibility for the property accessors. Affects both
492
- # reader and writer. Allowable values are :public, :protected, :private. Defaults to
493
- # :public
494
- # * <tt>reader</tt>: Like the accessor option but affects only the property reader.
495
- # * <tt>writer</tt>: Like the accessor option but affects only the property writer.
496
- # * <tt>protected</tt>: Alias for :reader => :public, :writer => :protected
497
- # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
498
-
499
- def property(*columns_and_options)
500
- columns, options = columns_and_options.partition {|item| not item.is_a?(Hash)}
501
- options = (options.empty? ? {} : options[0])
502
- type = columns.pop
503
-
504
- @properties ||= []
505
- new_properties = []
506
-
507
- columns.flatten.each do |name|
508
- property = DataMapper::Property.new(self, name, type, options)
509
- new_properties << property
510
- @properties << property
511
- end
512
-
513
- return (new_properties.length == 1 ? new_properties[0] : new_properties)
514
- end
515
-
516
- # TODO: Figure out how to make EmbeddedValue work with new property
517
- # code. EV relies on these next two methods.
518
- def property_getter(mapping, visibility = :public)
519
- if mapping.lazy?
520
- class_eval <<-EOS
521
- #{visibility.to_s}
522
- def #{mapping.name}
523
- lazy_load!(#{mapping.name.inspect})
524
- class << self;
525
- attr_accessor #{mapping.name.inspect}
526
- end
527
- @#{mapping.name}
528
- end
529
- EOS
530
- else
531
- class_eval("#{visibility.to_s}; def #{mapping.name}; #{mapping.instance_variable_name} end") unless [ :public, :private, :protected ].include?(mapping.name)
532
- end
533
-
534
- if mapping.type == :boolean
535
- class_eval("#{visibility.to_s}; def #{mapping.name.to_s.ensure_ends_with('?')}; #{mapping.instance_variable_name} end")
536
- end
537
-
538
- rescue SyntaxError
539
- raise SyntaxError.new(mapping)
540
- end
541
-
542
- def property_setter(mapping, visibility = :public)
543
- if mapping.lazy?
544
- class_eval <<-EOS
545
- #{visibility.to_s}
546
- def #{mapping.name}=(value)
547
- class << self;
548
- attr_accessor #{mapping.name.inspect}
549
- end
550
- @#{mapping.name} = value
551
- end
552
- EOS
553
- else
554
- class_eval("#{visibility.to_s}; def #{mapping.name}=(value); #{mapping.instance_variable_name} = value end")
555
- end
556
- rescue SyntaxError
557
- raise SyntaxError.new(mapping)
558
- end
559
-
560
- # Allows you to override the table name for a model.
561
- # EXAMPLE:
562
- # class WorkItem
563
- # set_table_name 't_work_item_list'
564
- # end
565
- def set_table_name(value)
566
- database.table(self).name = value
567
- end
568
-
569
- # An embedded value maps the values of an object to fields in the
570
- # record of the object's owner.
571
- # #embed takes a symbol to define the embedded class, options, and
572
- # an optional block. See
573
- # examples for use cases.
574
- #
575
- # EXAMPLE:
576
- # class CellPhone < DataMapper::Base
577
- # property :number, :string
578
- #
579
- # embed :owner, :prefix => true do
580
- # property :name, :string
581
- # property :address, :string
582
- # end
583
- # end
584
- #
585
- # my_phone = CellPhone.new
586
- # my_phone.owner.name = "Nick"
587
- # puts my_phone.owner.name
588
- #
589
- # => Nick
590
- #
591
- # OPTIONS:
592
- # * <tt>prefix</tt>: define a column prefix, so instead of mapping
593
- # :address to an 'address' column, it would map to
594
- # 'owner_address' in the example above. If
595
- # :prefix => true is specified, the prefix will
596
- # be the name of the symbol given as the first
597
- # parameter. If the prefix is a string the
598
- # specified
599
- # string will be used for the prefix.
600
- # * <tt>lazy</tt>: lazy-load all embedded values at the same time.
601
- # :lazy => true to enable. Disabled (false) by
602
- # default.
603
- # * <tt>accessor</tt>: Set method visibility for all embedded
604
- # properties. Affects both reader and writer.
605
- # Allowable values are :public, :protected,
606
- # :private. Defaults to :public
607
- # * <tt>reader</tt>: Like the accessor option but affects only
608
- # embedded property readers.
609
- # * <tt>writer</tt>: Like the accessor option but affects only
610
- # embedded property writers.
611
- # * <tt>protected</tt>: Alias for :reader => :public,
612
- # :writer => :protected
613
- # * <tt>private</tt>: Alias for :reader => :public, :writer => :private
614
- #
615
- def embed(name, options = {}, &block)
616
- EmbeddedValue::define(self, name, options, &block)
617
- end
618
-
619
- # Returns the hash of properties for this model.
620
- def properties
621
- @properties
622
- end
623
-
624
- # Creates a composite index for an arbitrary number of database columns.
625
- # Note that it also is possible to specify single indexes directly for
626
- # each property.
627
- #
628
- # === EXAMPLE WITH COMPOSITE INDEX:
629
- # class Person < DataMapper::Base
630
- # property :server_id, :integer
631
- # property :name, :string
632
- #
633
- # index [:server_id, :name]
634
- # end
635
- #
636
- # === EXAMPLE WITH COMPOSITE UNIQUE INDEX:
637
- # class Person < DataMapper::Base
638
- # property :server_id, :integer
639
- # property :name, :string
640
- #
641
- # index [:server_id, :name], :unique => true
642
- # end
643
- #
644
- # === SINGLE INDEX EXAMPLES:
645
- # * property :name, :index => true
646
- # * property :name, :index => :unique
647
- def index(indexes, unique = false)
648
- if indexes.kind_of?(Array) # if given an index of multiple columns
649
- database.schema[self].add_composite_index(indexes, unique)
650
- else
651
- raise ArgumentError.new("You must supply an array for the composite index")
652
- end
653
- end
654
-
655
- end
656
-
657
- # Lazy-loads the attributes for a loaded_set, then overwrites the
658
- # accessors
659
- # for the named methods so that the lazy_loading is skipped the second
660
- # time.
661
- def lazy_load!(*names)
662
-
663
- names = names.map { |name| name.to_sym }.reject { |name| lazy_loaded_attributes.include?(name) }
664
-
665
- reset_attribute = lambda do |instance|
666
- singleton_class = (class << instance; self end)
667
- names.each do |name|
668
- instance.lazy_loaded_attributes << name
669
- singleton_class.send(:attr_accessor, name)
670
- end
671
- end
672
-
673
- unless names.empty? || new_record? || loaded_set.nil?
674
-
675
- key = database_context.table(self.class).key.to_sym
676
- keys_to_select = loaded_set.map do |instance|
677
- instance.send(key)
678
- end
679
-
680
- database_context.all(
681
- self.class,
682
- :select => ([key] + names),
683
- :reload => true,
684
- key => keys_to_select
685
- ).each(&reset_attribute)
686
- else
687
- reset_attribute[self]
688
- end
689
- end
690
-
691
- def database_context
692
- @database_context || ( @database_context = database )
693
- end
694
-
695
- def database_context=(value)
696
- @database_context = value
697
- end
698
-
699
- def logger
700
- self.class.logger
701
- end
702
-
703
- # Returns <tt>true</tt> if this model hasn't been saved to the
704
- # database, <tt>false</tt> otherwise.
705
- def new_record?
706
- @new_record.nil? || @new_record
707
- end
708
-
709
- # Returns a Set containing the properties that have had their
710
- # <tt>:lazy</tt> option set to true, or are lazily loaded by
711
- # default — i.e. text fields.
712
- def lazy_loaded_attributes
713
- @lazy_loaded_attributes || @lazy_loaded_attributes = Set.new
714
- end
715
-
716
- # Accepts a hash of properties and values to be updated and then calls #save
717
- def update_attributes(update_hash)
718
- self.attributes = update_hash
719
- self.save
720
- end
721
-
722
- # Returns <tt>true</tt> if the unsaved model has had properties changed
723
- # since it was loaded from the database. Returns <tt>false</tt> otherwise.
724
- def dirty?(cleared = Set.new)
725
- return false if cleared.include?(self)
726
- cleared << self
727
-
728
- result = database_context.table(self).columns.any? do |column|
729
- if column.type == :object
730
- Marshal.dump(self.instance_variable_get(column.instance_variable_name)) != original_values[column.name]
731
- else
732
- self.instance_variable_get(column.instance_variable_name) != original_values[column.name]
733
- end
734
- end
735
-
736
- return true if result
737
-
738
- loaded_associations.any? do |loaded_association|
739
- loaded_association.dirty?(cleared)
740
- end
741
- end
742
-
743
- # For unsaved models, returns a hash of properties that have had their
744
- # values changed since it was loaded from the database.
745
- def dirty_attributes
746
- pairs = {}
747
-
748
- database_context.table(self).columns.each do |column|
749
- value = instance_variable_get(column.instance_variable_name)
750
- if value != original_values[column.name] && (!new_record? || !column.serial?)
751
- pairs[column.name] = column.type != :object ? value : YAML.dump(value)
752
- end
753
- end
754
-
755
- pairs
756
- end
757
-
758
- def original_values=(values)
759
- values.each_pair do |k,v|
760
- original_values[k] = case v
761
- when String, Date, Time then v.dup
762
- # when column.type == :object then Marshal.dump(v)
763
- else v
764
- end
765
- end
766
- end
767
-
768
- def original_values
769
- class << self
770
- attr_reader :original_values
771
- end
772
-
773
- @original_values = {}
774
- end
775
-
776
- def loaded_set=(value)
777
- value << self
778
- @loaded_set = value
779
- end
780
-
781
- def inspect
782
- inspected_attributes = attributes.map { |k,v| "@#{k}=#{v.inspect}" }
783
-
784
- instance_variables.each do |name|
785
- if instance_variable_get(name).kind_of?(Associations::HasManyAssociation)
786
- inspected_attributes << "#{name}=#{instance_variable_get(name).inspect}"
787
- end
788
- end
789
-
790
- "#<%s:0x%x @new_record=%s, %s>" % [self.class.name, (object_id * 2), new_record?, inspected_attributes.join(', ')]
791
- end
792
-
793
- def loaded_associations
794
- @loaded_associations || @loaded_associations = []
795
- end
796
-
797
- def key=(value)
798
- key_column = database_context.table(self.class).key
799
- @__key = key_column.type_cast_value(value)
800
- instance_variable_set(key_column.instance_variable_name, @__key)
801
- end
802
-
803
- def key
804
- @__key || @__key = begin
805
- key_column = database_context.table(self.class).key
806
- key_column.type_cast_value(instance_variable_get(key_column.instance_variable_name))
807
- end
808
- end
809
-
810
- def keys
811
- self.class.table.keys.map do |column|
812
- column.type_cast_value(instance_variable_get(column.instance_variable_name))
813
- end.compact
814
- end
815
-
816
- def <=>(other)
817
- keys <=> other.keys
818
- end
819
-
820
- # Look to ::included for __hash alias
821
- def hash
822
- @__hash || @__hash = keys.empty? ? super : keys.hash
823
- end
824
-
825
- def eql?(other)
826
- return false unless other.is_a?(self.class) || self.is_a?(other.class)
827
- comparator = keys.empty? ? :private_attributes : :keys
828
- send(comparator) == other.send(comparator)
829
- end
830
-
831
- def ==(other)
832
- eql?(other)
833
- end
834
-
835
- # Returns the difference between two objects, in terms of their
836
- # attributes.
837
- def ^(other)
838
- results = {}
839
-
840
- self_attributes, other_attributes = attributes, other.attributes
841
-
842
- self_attributes.each_pair do |k,v|
843
- other_value = other_attributes[k]
844
- unless v == other_value
845
- results[k] = [v, other_value]
846
- end
847
- end
848
-
849
- results
850
- end
851
- end
852
- end