datamapper 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +5 -1
- data/FAQ +96 -0
- data/QUICKLINKS +12 -0
- data/README +57 -155
- data/environment.rb +61 -43
- data/example.rb +30 -12
- data/lib/data_mapper.rb +6 -1
- data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
- data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
- data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
- data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
- data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
- data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
- data/lib/data_mapper/associations.rb +75 -3
- data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
- data/lib/data_mapper/associations/has_many_association.rb +168 -61
- data/lib/data_mapper/associations/has_n_association.rb +23 -3
- data/lib/data_mapper/attributes.rb +73 -0
- data/lib/data_mapper/auto_migrations.rb +2 -6
- data/lib/data_mapper/base.rb +5 -9
- data/lib/data_mapper/database.rb +4 -3
- data/lib/data_mapper/embedded_value.rb +66 -30
- data/lib/data_mapper/identity_map.rb +1 -3
- data/lib/data_mapper/is/tree.rb +121 -0
- data/lib/data_mapper/migration.rb +155 -0
- data/lib/data_mapper/persistence.rb +532 -218
- data/lib/data_mapper/property.rb +306 -0
- data/lib/data_mapper/query.rb +164 -0
- data/lib/data_mapper/support/blank.rb +2 -2
- data/lib/data_mapper/support/connection_pool.rb +5 -6
- data/lib/data_mapper/support/enumerable.rb +3 -3
- data/lib/data_mapper/support/errors.rb +10 -1
- data/lib/data_mapper/support/inflector.rb +174 -238
- data/lib/data_mapper/support/object.rb +54 -0
- data/lib/data_mapper/support/serialization.rb +19 -1
- data/lib/data_mapper/support/string.rb +7 -16
- data/lib/data_mapper/support/symbol.rb +3 -15
- data/lib/data_mapper/support/typed_set.rb +68 -0
- data/lib/data_mapper/types/base.rb +44 -0
- data/lib/data_mapper/types/string.rb +34 -0
- data/lib/data_mapper/validations/number_validator.rb +40 -0
- data/lib/data_mapper/validations/string_validator.rb +20 -0
- data/lib/data_mapper/validations/validator.rb +13 -0
- data/performance.rb +26 -1
- data/profile_data_mapper.rb +1 -1
- data/rakefile.rb +42 -2
- data/spec/acts_as_tree_spec.rb +11 -3
- data/spec/adapters/data_object_adapter_spec.rb +31 -0
- data/spec/associations/belongs_to_association_spec.rb +98 -0
- data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
- data/spec/associations/has_many_association_spec.rb +337 -0
- data/spec/attributes_spec.rb +23 -1
- data/spec/auto_migrations_spec.rb +86 -29
- data/spec/callbacks_spec.rb +107 -0
- data/spec/column_spec.rb +5 -2
- data/spec/count_command_spec.rb +33 -1
- data/spec/database_spec.rb +18 -0
- data/spec/dependency_spec.rb +4 -2
- data/spec/embedded_value_spec.rb +8 -8
- data/spec/fixtures/people.yaml +1 -1
- data/spec/fixtures/projects.yaml +10 -1
- data/spec/fixtures/tasks.yaml +6 -0
- data/spec/fixtures/tasks_tasks.yaml +2 -0
- data/spec/fixtures/tomatoes.yaml +1 -0
- data/spec/is_a_tree_spec.rb +149 -0
- data/spec/load_command_spec.rb +71 -9
- data/spec/magic_columns_spec.rb +17 -2
- data/spec/migration_spec.rb +267 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/candidate.rb +8 -0
- data/spec/models/career.rb +1 -1
- data/spec/models/chain.rb +8 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/exhibit.rb +1 -1
- data/spec/models/fence.rb +7 -0
- data/spec/models/fruit.rb +2 -2
- data/spec/models/job.rb +8 -0
- data/spec/models/person.rb +2 -3
- data/spec/models/post.rb +1 -1
- data/spec/models/project.rb +21 -1
- data/spec/models/section.rb +1 -1
- data/spec/models/serializer.rb +1 -1
- data/spec/models/task.rb +9 -0
- data/spec/models/tomato.rb +27 -0
- data/spec/models/user.rb +8 -2
- data/spec/models/zoo.rb +2 -7
- data/spec/paranoia_spec.rb +1 -1
- data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
- data/spec/postgres_spec.rb +48 -6
- data/spec/property_spec.rb +90 -9
- data/spec/query_spec.rb +71 -5
- data/spec/save_command_spec.rb +11 -0
- data/spec/spec_helper.rb +14 -11
- data/spec/support/blank_spec.rb +8 -0
- data/spec/support/inflector_spec.rb +41 -0
- data/spec/support/object_spec.rb +9 -0
- data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
- data/spec/support/silence_spec.rb +15 -0
- data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
- data/spec/support/struct_spec.rb +12 -0
- data/spec/support/typed_set_spec.rb +66 -0
- data/spec/table_spec.rb +3 -3
- data/spec/types/string.rb +81 -0
- data/spec/validates_uniqueness_of_spec.rb +17 -0
- data/spec/validations/number_validator.rb +59 -0
- data/spec/validations/string_validator.rb +14 -0
- metadata +59 -17
- data/do_performance.rb +0 -153
- data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
- data/lib/data_mapper/support/weak_hash.rb +0 -46
- data/spec/active_record_impersonation_spec.rb +0 -129
- data/spec/associations_spec.rb +0 -232
- data/spec/conditions_spec.rb +0 -49
- data/spec/has_many_association_spec.rb +0 -173
- data/spec/models/animals_exhibit.rb +0 -8
@@ -3,9 +3,7 @@ module DataMapper
|
|
3
3
|
def auto_migrate!
|
4
4
|
if self::subclasses.empty?
|
5
5
|
table = database.table(self)
|
6
|
-
table.
|
7
|
-
association.activate!
|
8
|
-
end
|
6
|
+
table.activate_associations!
|
9
7
|
|
10
8
|
table.create!(true)
|
11
9
|
else
|
@@ -20,9 +18,7 @@ module DataMapper
|
|
20
18
|
table.add_column(column.name, column.type, column.options)
|
21
19
|
end
|
22
20
|
|
23
|
-
table.
|
24
|
-
association.activate!
|
25
|
-
end
|
21
|
+
table.activate_associations!
|
26
22
|
|
27
23
|
return table.create!(true)
|
28
24
|
end
|
data/lib/data_mapper/base.rb
CHANGED
@@ -1,21 +1,17 @@
|
|
1
1
|
require 'data_mapper/persistence'
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'ferret'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
2
|
+
require 'data_mapper/is/tree'
|
7
3
|
|
8
4
|
module DataMapper
|
9
5
|
|
10
6
|
class Base
|
11
|
-
|
7
|
+
include DataMapper::Is::Tree
|
12
8
|
|
13
9
|
def self.inherited(klass)
|
14
|
-
DataMapper::Persistence
|
10
|
+
klass.send(:include, DataMapper::Persistence)
|
15
11
|
end
|
16
12
|
|
17
|
-
def self.
|
18
|
-
|
13
|
+
def self.auto_migrate!
|
14
|
+
DataMapper::Persistence.auto_migrate!
|
19
15
|
end
|
20
16
|
end
|
21
17
|
end
|
data/lib/data_mapper/database.rb
CHANGED
@@ -102,7 +102,7 @@ module DataMapper
|
|
102
102
|
#
|
103
103
|
# This is what gives us thread safety, boys and girls
|
104
104
|
def self.context
|
105
|
-
Thread::current[:
|
105
|
+
Thread::current[:database_contexts] || Thread::current[:database_contexts] = []
|
106
106
|
end
|
107
107
|
|
108
108
|
# Setup creates a database and sets all of your properties for that database.
|
@@ -158,13 +158,14 @@ module DataMapper
|
|
158
158
|
# Creates a new database object with the name you specify, and a default set of options.
|
159
159
|
#
|
160
160
|
# The default options are as follows:
|
161
|
-
# { :host => 'localhost', :database => nil, :username => 'root', :password => '', :adapter = nil }
|
161
|
+
# { :host => 'localhost', :database => nil, :port => nil, :username => 'root', :password => '', :adapter = nil }
|
162
162
|
def initialize(name)
|
163
163
|
@name = name
|
164
164
|
|
165
165
|
@adapter = nil
|
166
166
|
@host = 'localhost'
|
167
167
|
@database = nil
|
168
|
+
@port = nil
|
168
169
|
@schema_search_path = nil
|
169
170
|
@username = 'root'
|
170
171
|
@password = ''
|
@@ -176,7 +177,7 @@ module DataMapper
|
|
176
177
|
|
177
178
|
attr_reader :name, :adapter, :log_stream
|
178
179
|
|
179
|
-
attr_accessor :host, :database, :schema_search_path, :username, :password, :log_level, :index_path, :socket
|
180
|
+
attr_accessor :host, :database, :port, :schema_search_path, :username, :password, :log_level, :index_path, :socket
|
180
181
|
|
181
182
|
def log_stream=(val)
|
182
183
|
@log_stream = (val.is_a?(String) && val =~ /STDOUT/ ? STDOUT : val)
|
@@ -1,73 +1,109 @@
|
|
1
1
|
module DataMapper
|
2
2
|
|
3
|
+
# == EmbeddedValue
|
4
|
+
# As an alternative to an extraneous has_one association, EmbeddedValue offers a means
|
5
|
+
# to serialize component objects to a table without having to define an entirely new model.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# class Person < DataMapper::Base
|
10
|
+
#
|
11
|
+
# property :name, :string
|
12
|
+
# property :occupation, :string
|
13
|
+
#
|
14
|
+
# embed :address, :prefix => true do
|
15
|
+
# property :street, :string
|
16
|
+
# property :city, :string
|
17
|
+
# property :state, :string, :size => 2
|
18
|
+
# property :zip_code, :string, :size => 10
|
19
|
+
#
|
20
|
+
# def city_state_zip_code
|
21
|
+
# "#{city}, #{state} #{zip_code}"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# Columns for the Address model will appear in the Person table. Passing
|
28
|
+
# <tt>:prefix => true</tt> will prefix the column name with the parent table's name.
|
29
|
+
# The default behavior is to use the columns as they are defined. Using the above
|
30
|
+
# example, the database table structure will become:
|
31
|
+
#
|
32
|
+
# Column Datatype, Options
|
33
|
+
# ===============================================================
|
34
|
+
# name :string
|
35
|
+
# occupation :string
|
36
|
+
# address_street :string
|
37
|
+
# address_city :string
|
38
|
+
# address_state :string, :size => 2
|
39
|
+
# address_zip_code :string, :size => 10
|
40
|
+
#
|
41
|
+
# EmbeddedValue's become instance methods off of an instance of the parent
|
42
|
+
# class and return a sub-class of the parent class.
|
43
|
+
#
|
44
|
+
# bob = Person.first(:name => 'Bob')
|
45
|
+
# bob.address # => #<Person::Address:0x1a492b8>
|
46
|
+
# bob.address.city # => "Pittsburgh"
|
47
|
+
# bob.address.city_state_zip_code # => "Pitsburgh, PA 90210"
|
48
|
+
|
3
49
|
class EmbeddedValue
|
4
50
|
EMBEDDED_PROPERTIES = []
|
5
51
|
|
6
52
|
def initialize(instance)
|
7
53
|
@instance = instance
|
8
54
|
@container_prefix = ''
|
9
|
-
|
10
|
-
# force lazy load on access to any lazy-loaded embed property
|
11
|
-
@instance.lazy_load!(*self.class::EMBEDDED_PROPERTIES) # if @container_lazy
|
12
55
|
end
|
13
56
|
|
14
57
|
def self.inherited(base)
|
15
58
|
base.const_set('EMBEDDED_PROPERTIES', [])
|
16
59
|
end
|
17
60
|
|
18
|
-
# add an embedded property
|
61
|
+
# add an embedded property. For more information about how to define properties, visit Property.
|
19
62
|
def self.property(name, type, options = {})
|
20
63
|
# set lazy option on the mapping if defined in the embed block
|
21
64
|
options[:lazy] ||= @container_lazy
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
mapping = database.schema[containing_class].add_column("#{@container_prefix}#{name}", type, options)
|
32
|
-
|
33
|
-
self::EMBEDDED_PROPERTIES << "#{@container_prefix}#{name}"
|
34
|
-
define_property_getter(name, mapping, reader_visibility)
|
35
|
-
define_property_setter(name, mapping, writer_visibility)
|
65
|
+
|
66
|
+
options[:reader] ||= options[:accessor] || @container_reader_visibility
|
67
|
+
options[:writer] ||= options[:accessor] || @container_writer_visibility
|
68
|
+
|
69
|
+
property_name = @container_prefix ? @container_prefix + name.to_s : name
|
70
|
+
|
71
|
+
property = containing_class.property(property_name, type, options)
|
72
|
+
define_property_getter(name, property)
|
73
|
+
define_property_setter(name, property)
|
36
74
|
end
|
37
75
|
|
38
76
|
# define embedded property getters
|
39
|
-
def self.define_property_getter(name,
|
40
|
-
# add convenience method on non-embedded base for #update_attributes
|
41
|
-
self.containing_class.property_getter(mapping, visibility)
|
77
|
+
def self.define_property_getter(name, property) # :nodoc:
|
42
78
|
|
43
79
|
# add the method on the embedded class
|
44
80
|
class_eval <<-EOS
|
45
|
-
#{
|
81
|
+
#{property.reader_visibility.to_s}
|
46
82
|
def #{name}
|
47
|
-
@instance.
|
83
|
+
#{"@instance.lazy_load!("+ property.name.inspect + ")" if property.lazy?}
|
84
|
+
@instance.instance_variable_get(#{property.instance_variable_name.inspect})
|
48
85
|
end
|
49
86
|
EOS
|
50
87
|
|
51
88
|
# add a shortcut boolean? method if applicable (ex: activated?)
|
52
|
-
if
|
53
|
-
class_eval("alias #{name}? #{name}")
|
89
|
+
if property.type == :boolean
|
90
|
+
class_eval("alias #{property.name}? #{property.name}")
|
54
91
|
end
|
55
92
|
end
|
56
93
|
|
57
94
|
# define embedded property setters
|
58
|
-
def self.define_property_setter(name,
|
59
|
-
# add convenience method on non-embedded base for #update_attributes
|
60
|
-
self.containing_class.property_setter(mapping, visibility)
|
95
|
+
def self.define_property_setter(name, property) # :nodoc:
|
61
96
|
|
62
97
|
# add the method on the embedded class
|
63
98
|
class_eval <<-EOS
|
64
|
-
#{
|
99
|
+
#{property.writer_visibility.to_s}
|
65
100
|
def #{name.to_s.sub(/\?$/, '')}=(value)
|
66
|
-
@instance.instance_variable_set(#{
|
101
|
+
@instance.instance_variable_set(#{property.instance_variable_name.inspect}, value)
|
67
102
|
end
|
68
103
|
EOS
|
69
104
|
end
|
70
105
|
|
106
|
+
# returns the class in which the EmbeddedValue is declared
|
71
107
|
def self.containing_class
|
72
108
|
@containing_class || @containing_class = begin
|
73
109
|
tree = name.split('::')
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'data_mapper/support/weak_hash'
|
2
|
-
|
3
1
|
module DataMapper
|
4
2
|
|
5
3
|
# Tracks objects to help ensure that each object gets loaded only once.
|
@@ -39,7 +37,7 @@ module DataMapper
|
|
39
37
|
|
40
38
|
private
|
41
39
|
def mapped_class(klass)
|
42
|
-
|
40
|
+
unless klass.superclass.respond_to?(:persistent?)
|
43
41
|
klass
|
44
42
|
else
|
45
43
|
mapped_class(klass.superclass)
|
@@ -0,0 +1,121 @@
|
|
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
|
@@ -0,0 +1,155 @@
|
|
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
|