datamapper 0.3.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
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