datamapper 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/CHANGELOG +5 -1
  2. data/FAQ +96 -0
  3. data/QUICKLINKS +12 -0
  4. data/README +57 -155
  5. data/environment.rb +61 -43
  6. data/example.rb +30 -12
  7. data/lib/data_mapper.rb +6 -1
  8. data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
  9. data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
  10. data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
  11. data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
  12. data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
  14. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
  15. data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
  16. data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
  17. data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
  18. data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
  19. data/lib/data_mapper/associations.rb +75 -3
  20. data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
  21. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
  22. data/lib/data_mapper/associations/has_many_association.rb +168 -61
  23. data/lib/data_mapper/associations/has_n_association.rb +23 -3
  24. data/lib/data_mapper/attributes.rb +73 -0
  25. data/lib/data_mapper/auto_migrations.rb +2 -6
  26. data/lib/data_mapper/base.rb +5 -9
  27. data/lib/data_mapper/database.rb +4 -3
  28. data/lib/data_mapper/embedded_value.rb +66 -30
  29. data/lib/data_mapper/identity_map.rb +1 -3
  30. data/lib/data_mapper/is/tree.rb +121 -0
  31. data/lib/data_mapper/migration.rb +155 -0
  32. data/lib/data_mapper/persistence.rb +532 -218
  33. data/lib/data_mapper/property.rb +306 -0
  34. data/lib/data_mapper/query.rb +164 -0
  35. data/lib/data_mapper/support/blank.rb +2 -2
  36. data/lib/data_mapper/support/connection_pool.rb +5 -6
  37. data/lib/data_mapper/support/enumerable.rb +3 -3
  38. data/lib/data_mapper/support/errors.rb +10 -1
  39. data/lib/data_mapper/support/inflector.rb +174 -238
  40. data/lib/data_mapper/support/object.rb +54 -0
  41. data/lib/data_mapper/support/serialization.rb +19 -1
  42. data/lib/data_mapper/support/string.rb +7 -16
  43. data/lib/data_mapper/support/symbol.rb +3 -15
  44. data/lib/data_mapper/support/typed_set.rb +68 -0
  45. data/lib/data_mapper/types/base.rb +44 -0
  46. data/lib/data_mapper/types/string.rb +34 -0
  47. data/lib/data_mapper/validations/number_validator.rb +40 -0
  48. data/lib/data_mapper/validations/string_validator.rb +20 -0
  49. data/lib/data_mapper/validations/validator.rb +13 -0
  50. data/performance.rb +26 -1
  51. data/profile_data_mapper.rb +1 -1
  52. data/rakefile.rb +42 -2
  53. data/spec/acts_as_tree_spec.rb +11 -3
  54. data/spec/adapters/data_object_adapter_spec.rb +31 -0
  55. data/spec/associations/belongs_to_association_spec.rb +98 -0
  56. data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
  57. data/spec/associations/has_many_association_spec.rb +337 -0
  58. data/spec/attributes_spec.rb +23 -1
  59. data/spec/auto_migrations_spec.rb +86 -29
  60. data/spec/callbacks_spec.rb +107 -0
  61. data/spec/column_spec.rb +5 -2
  62. data/spec/count_command_spec.rb +33 -1
  63. data/spec/database_spec.rb +18 -0
  64. data/spec/dependency_spec.rb +4 -2
  65. data/spec/embedded_value_spec.rb +8 -8
  66. data/spec/fixtures/people.yaml +1 -1
  67. data/spec/fixtures/projects.yaml +10 -1
  68. data/spec/fixtures/tasks.yaml +6 -0
  69. data/spec/fixtures/tasks_tasks.yaml +2 -0
  70. data/spec/fixtures/tomatoes.yaml +1 -0
  71. data/spec/is_a_tree_spec.rb +149 -0
  72. data/spec/load_command_spec.rb +71 -9
  73. data/spec/magic_columns_spec.rb +17 -2
  74. data/spec/migration_spec.rb +267 -0
  75. data/spec/models/animal.rb +1 -1
  76. data/spec/models/candidate.rb +8 -0
  77. data/spec/models/career.rb +1 -1
  78. data/spec/models/chain.rb +8 -0
  79. data/spec/models/comment.rb +1 -1
  80. data/spec/models/exhibit.rb +1 -1
  81. data/spec/models/fence.rb +7 -0
  82. data/spec/models/fruit.rb +2 -2
  83. data/spec/models/job.rb +8 -0
  84. data/spec/models/person.rb +2 -3
  85. data/spec/models/post.rb +1 -1
  86. data/spec/models/project.rb +21 -1
  87. data/spec/models/section.rb +1 -1
  88. data/spec/models/serializer.rb +1 -1
  89. data/spec/models/task.rb +9 -0
  90. data/spec/models/tomato.rb +27 -0
  91. data/spec/models/user.rb +8 -2
  92. data/spec/models/zoo.rb +2 -7
  93. data/spec/paranoia_spec.rb +1 -1
  94. data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
  95. data/spec/postgres_spec.rb +48 -6
  96. data/spec/property_spec.rb +90 -9
  97. data/spec/query_spec.rb +71 -5
  98. data/spec/save_command_spec.rb +11 -0
  99. data/spec/spec_helper.rb +14 -11
  100. data/spec/support/blank_spec.rb +8 -0
  101. data/spec/support/inflector_spec.rb +41 -0
  102. data/spec/support/object_spec.rb +9 -0
  103. data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
  104. data/spec/support/silence_spec.rb +15 -0
  105. data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
  106. data/spec/support/struct_spec.rb +12 -0
  107. data/spec/support/typed_set_spec.rb +66 -0
  108. data/spec/table_spec.rb +3 -3
  109. data/spec/types/string.rb +81 -0
  110. data/spec/validates_uniqueness_of_spec.rb +17 -0
  111. data/spec/validations/number_validator.rb +59 -0
  112. data/spec/validations/string_validator.rb +14 -0
  113. metadata +59 -17
  114. data/do_performance.rb +0 -153
  115. data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
  116. data/lib/data_mapper/support/weak_hash.rb +0 -46
  117. data/spec/active_record_impersonation_spec.rb +0 -129
  118. data/spec/associations_spec.rb +0 -232
  119. data/spec/conditions_spec.rb +0 -49
  120. data/spec/has_many_association_spec.rb +0 -173
  121. data/spec/models/animals_exhibit.rb +0 -8
@@ -17,6 +17,9 @@ module DataMapper
17
17
  TRUE_ALIASES << "T".freeze << "\004\bT".freeze
18
18
  FALSE_ALIASES << "F".freeze << "\004\bF".freeze
19
19
 
20
+ def empty_insert_sql
21
+ "() VALUES ()"
22
+ end
20
23
 
21
24
  def create_connection
22
25
 
@@ -28,6 +31,7 @@ module DataMapper
28
31
  builder['password', :password]
29
32
  builder['dbname', :database]
30
33
  builder['socket', :socket]
34
+ builder['port', :port]
31
35
 
32
36
  logger.debug { connection_string.strip }
33
37
 
@@ -37,6 +37,7 @@ module DataMapper
37
37
  builder['password', :password]
38
38
  builder['dbname', :database]
39
39
  builder['socket', :socket]
40
+ builder['port', :port]
40
41
  conn = DataObject::Postgres::Connection.new(connection_string.strip)
41
42
  conn.logger = self.logger
42
43
  conn.open
@@ -239,8 +240,13 @@ module DataMapper
239
240
  end
240
241
  end
241
242
 
243
+ # size is still required, as length in postgres behaves slightly differently
242
244
  def size
243
- nil
245
+ case self.type
246
+ #strings in postgres can be unlimited length
247
+ when :string then return (@options.has_key?(:length) || @options.has_key?(:size) ? @size : nil)
248
+ else nil
249
+ end
244
250
  end
245
251
  end # class Column
246
252
  end # module Mappings
@@ -23,11 +23,12 @@ module DataMapper
23
23
  end
24
24
 
25
25
  def type_cast_boolean(raw_value)
26
+ return nil if raw_value.nil? || (raw_value.respond_to?(:empty?) && raw_value.empty?)
26
27
  case raw_value
27
28
  when TrueClass, FalseClass then raw_value
28
29
  when *self::class::TRUE_ALIASES then true
29
30
  when *self::class::FALSE_ALIASES then false
30
- else "Can't type-cast #{raw_value.inspect} to a boolean"
31
+ else raise CoersionError.new("Can't type-cast #{raw_value.inspect} to a boolean")
31
32
  end
32
33
  end
33
34
 
@@ -45,7 +46,7 @@ module DataMapper
45
46
 
46
47
  def type_cast_class(raw_value)
47
48
  return nil if raw_value.blank?
48
- Kernel::const_get(raw_value)
49
+ Object::recursive_const_get(raw_value)
49
50
  end
50
51
 
51
52
  def type_cast_integer(raw_value)
@@ -56,10 +56,14 @@ module DataMapper
56
56
  # setting both the original_value, and the
57
57
  # instance-variable through method chaining to avoid
58
58
  # lots of extra short-lived local variables.
59
- type_casted_values[column.name] = instance.instance_variable_set(
60
- column.instance_variable_name,
61
- column.type_cast_value(values[index])
62
- )
59
+ begin
60
+ type_casted_values[column.name] = instance.instance_variable_set(
61
+ column.instance_variable_name,
62
+ column.type_cast_value(values[index])
63
+ )
64
+ rescue => e
65
+ raise MaterializationError.new("Failed to materialize column #{column.name.inspect} with value #{values[index].inspect}\n#{e.display}")
66
+ end
63
67
  end
64
68
 
65
69
  instance.original_values = type_casted_values
@@ -71,7 +75,11 @@ module DataMapper
71
75
  return instance
72
76
 
73
77
  rescue => e
74
- raise MaterializationError.new("Failed to materialize row: #{values.inspect}\n#{e.to_yaml}")
78
+ if e.is_a?(MaterializationError)
79
+ raise e
80
+ else
81
+ raise MaterializationError.new("Failed to materialize row: #{values.inspect}\n#{e.display}")
82
+ end
75
83
  end
76
84
 
77
85
  def loaded_set
@@ -84,7 +92,7 @@ module DataMapper
84
92
  instance = @database_context.identity_map.get(@klass, instance_id)
85
93
 
86
94
  if instance.nil? || @reload
87
- instance = instance_type.new() if instance.nil?
95
+ instance = instance_type.allocate() if instance.nil?
88
96
  instance.instance_variable_set(:@__key, instance_id)
89
97
  instance.instance_variable_set(:@new_record, false)
90
98
  @database_context.identity_map.set(instance)
@@ -358,7 +366,11 @@ module DataMapper
358
366
  else raise "CAN HAS CRASH? #{clause.inspect}"
359
367
  end
360
368
  rescue => e
361
- raise ConditionsError.new(clause, value, e)
369
+ if e.is_a?(ConditionsError)
370
+ raise e
371
+ else
372
+ raise ConditionsError.new(clause, value, e)
373
+ end
362
374
  end
363
375
 
364
376
  private
@@ -368,10 +380,12 @@ module DataMapper
368
380
  qualify_columns = qualify_columns?
369
381
  @columns_for_select = []
370
382
 
371
- columns.each_with_index do |column,i|
383
+ i = 0
384
+ columns.each do |column|
372
385
  class_for_loader = column.table.klass
373
386
  @loaders[class_for_loader].add_column(column, i) if class_for_loader
374
387
  @columns_for_select << column.to_sql(qualify_columns)
388
+ i += 1
375
389
  end
376
390
 
377
391
  @columns_for_select
@@ -482,8 +496,13 @@ module DataMapper
482
496
 
483
497
  case x = conditions_hash.delete(:conditions)
484
498
  when Array then
485
- clause = x.shift
486
- expression_to_sql(clause, x, collection)
499
+ # DO NOT mutate incoming Array values!!!
500
+ # Otherwise the mutated version may impact all the
501
+ # way up to the options passed to the finders,
502
+ # and have unintended side-effects.
503
+ array_copy = x.dup
504
+ clause = array_copy.shift
505
+ expression_to_sql(clause, array_copy, collection)
487
506
  when Hash then
488
507
  x.each_pair do |key,value|
489
508
  expression_to_sql(key, value, collection)
@@ -22,6 +22,10 @@ module DataMapper
22
22
  def each
23
23
  @set.each { |name, association| yield(association) }
24
24
  end
25
+
26
+ def empty?
27
+ @set.empty?
28
+ end
25
29
  end
26
30
 
27
31
  end # module Mappings
@@ -7,7 +7,7 @@ module DataMapper
7
7
  # Ordinal, Length/Size, Nullability are just a few.
8
8
  class Column
9
9
  attr_reader :type, :name, :ordinal, :size, :default, :check
10
- attr_writer :lazy, :index
10
+ attr_writer :lazy, :index, :unique
11
11
  attr_accessor :table, :options
12
12
 
13
13
  def initialize(adapter, table, name, type, ordinal, options = {})
@@ -15,13 +15,17 @@ module DataMapper
15
15
  @table = table
16
16
  @name, self.type, @options = name.to_sym, type, options
17
17
  @ordinal = ordinal
18
-
18
+ parse_options!
19
+ end
20
+
21
+ def parse_options!
19
22
  @key = @options[:key] == true || @options[:serial] == true
20
23
  @nullable = @options.has_key?(:nullable) ? @options[:nullable] : !@key
21
24
  @lazy = @options.has_key?(:lazy) ? @options[:lazy] : (@type == :text && !@key)
22
25
  @serial = @options[:serial] == true
23
26
  @default = @options[:default]
24
- @unique = @options.has_value?(:unique)
27
+
28
+ @unique = if @options[:index] == :unique then @options.delete(:index); true else false end
25
29
  @index = @options[:index]
26
30
  @check = @options[:check] # only for postgresql
27
31
 
@@ -36,8 +40,11 @@ module DataMapper
36
40
  else nil
37
41
  end
38
42
  end
43
+ @size = @size.last if @size.is_a?(Range)
39
44
  end
40
45
 
46
+ def defaulted?() instance_variables.include?("@default") end
47
+
41
48
  def type=(value)
42
49
  self.flush_sql_caches!
43
50
  @type = value
@@ -167,7 +174,7 @@ module DataMapper
167
174
  def rename!(new_name)
168
175
  old_name = name # Store the old_name
169
176
 
170
- new_column = @table.add_column(new_name, self.type, self.options)
177
+ new_column = @table.add_column(new_name, self.type, self.options.merge(:ordinal => self.ordinal))
171
178
 
172
179
  # Create the new column
173
180
  new_column.create!
@@ -181,11 +188,8 @@ module DataMapper
181
188
  command.execute_non_query
182
189
  end
183
190
 
184
- # Swap column names
185
- self.name, new_column.name = new_column.name, self.name
186
- # Drop the old column
187
- new_column.drop!
188
- true
191
+ self.drop!
192
+ new_column
189
193
  end
190
194
 
191
195
  def to_long_form
@@ -0,0 +1,172 @@
1
+ module DataMapper
2
+ module Adapters
3
+ module Sql
4
+ module Mappings
5
+
6
+ class Conditions
7
+ def initialize(table, adapter, qualify_columns=false, options={})
8
+ @table = table
9
+ @qualify_columns = qualify_columns
10
+
11
+ # BEGIN: Partion out the options hash into general options,
12
+ # and conditions.
13
+ standard_find_options = adapter.class::FIND_OPTIONS
14
+ conditions_hash = {}
15
+
16
+ options.each do |key,value|
17
+ unless standard_find_options.include?(key) && key != :conditions
18
+ conditions_hash[key] = value
19
+ end
20
+ end
21
+ # END
22
+
23
+ @conditions = parse_conditions(conditions_hash)
24
+ end
25
+
26
+ # Generate a statement after 'WHERE' based on the initialization
27
+ # arguments.
28
+ def to_params_sql
29
+ parameters = []
30
+ sql = ""
31
+
32
+ unless @conditions.empty?
33
+ sql << ' WHERE ('
34
+
35
+ last_index = @conditions.size
36
+ current_index = 0
37
+
38
+ @conditions.each do |condition|
39
+ case condition
40
+ when String then sql << condition
41
+ when Array then
42
+ sql << condition.shift
43
+ parameters += condition
44
+ else
45
+ raise "Unable to parse condition: #{condition.inspect}" if condition
46
+ end
47
+
48
+ if (current_index += 1) == last_index
49
+ sql << ')'
50
+ else
51
+ sql << ') AND ('
52
+ end
53
+ end
54
+ end
55
+
56
+ parameters.unshift(sql)
57
+ end
58
+
59
+ def parse_conditions(conditions_hash)
60
+ collection = []
61
+
62
+ case x = conditions_hash.delete(:conditions)
63
+ when Array then
64
+ # DO NOT mutate incoming Array values!!!
65
+ # Otherwise the mutated version may impact all the
66
+ # way up to the options passed to the finders,
67
+ # and have unintended side-effects.
68
+ array_copy = x.dup
69
+ clause = array_copy.shift
70
+ expression_to_sql(clause, array_copy, collection)
71
+ when Hash then
72
+ x.each_pair do |key,value|
73
+ expression_to_sql(key, value, collection)
74
+ end
75
+ else
76
+ raise "Unable to parse conditions: #{x.inspect}" if x
77
+ end
78
+
79
+ if @table.paranoid?
80
+ conditions_hash[@table.paranoid_column.name] = nil
81
+ end
82
+
83
+ conditions_hash.each_pair do |key,value|
84
+ expression_to_sql(key, value, collection)
85
+ end
86
+
87
+ collection
88
+ end
89
+
90
+ # expression_to_sql takes a set of arguments, and turns them into a an
91
+ # Array of generated SQL, followed by optional Values to interpolate as SQL-Parameters.
92
+ #
93
+ # Parameters:
94
+ # +clause+ The name of the column as a Symbol, a raw-SQL String, a Mappings::Column
95
+ # instance, or a Symbol::Operator.
96
+ # +value+ The Value for the condition.
97
+ # +collector+ An Array representing all conditions that is appended to by expression_to_sql
98
+ #
99
+ # Returns: Undefined Output. The work performed is added to the +collector+ argument.
100
+ # Example:
101
+ # conditions = []
102
+ # expression_to_sql(:name, 'Bob', conditions)
103
+ # => +undefined return value+
104
+ # conditions.inspect
105
+ # => ["name = ?", 'Bob']
106
+ def expression_to_sql(clause, value, collector)
107
+ case clause
108
+ when Symbol::Operator then
109
+ operator = case clause.type
110
+ when :gt then '>'
111
+ when :gte then '>='
112
+ when :lt then '<'
113
+ when :lte then '<='
114
+ when :not then inequality_operator(value)
115
+ when :eql then equality_operator(value)
116
+ when :like then equality_operator(value, 'LIKE')
117
+ when :in then equality_operator(value)
118
+ else raise ArgumentError.new('Operator type not supported')
119
+ end
120
+ #Table[column name] is column.to_sql(true/false based on associations or not)
121
+ collector << ["#{@table[clause].to_sql(@qualify_columns)} #{operator} ?", value]
122
+ when Symbol then
123
+ collector << ["#{@table[clause].to_sql(@qualify_columns)} #{equality_operator(value)} ?", value]
124
+ when String then
125
+ collector << [clause, *value]
126
+ when Mappings::Column then
127
+ collector << ["#{clause.to_sql(@qualify_columns)} #{equality_operator(value)} ?", value]
128
+ else raise "CAN HAS CRASH? #{clause.inspect}"
129
+ end
130
+ rescue => e
131
+ raise ConditionsError.new(clause, value, e)
132
+ end
133
+
134
+ def equality_operator(value, default = '=')
135
+ case value
136
+ when NilClass then 'IS'
137
+ when Array then 'IN'
138
+ else default
139
+ end
140
+ end
141
+
142
+ def inequality_operator(value, default = '<>')
143
+ case value
144
+ when NilClass then 'IS NOT'
145
+ when Array then 'NOT IN'
146
+ else default
147
+ end
148
+ end
149
+
150
+ class ConditionsError < StandardError
151
+
152
+ attr_reader :inner_error
153
+
154
+ def initialize(clause, value, inner_error)
155
+ @clause, @value, @inner_error = clause, value, inner_error
156
+ end
157
+
158
+ def message
159
+ "Conditions (:clause => #{@clause.inspect}, :value => #{@value.inspect}) failed: #{@inner_error}"
160
+ end
161
+
162
+ def backtrace
163
+ @inner_error.backtrace
164
+ end
165
+
166
+ end
167
+
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/column'
2
+ require File.dirname(__FILE__) + '/conditions'
2
3
  require File.dirname(__FILE__) + '/associations_set'
3
4
 
4
5
  module DataMapper
@@ -45,6 +46,10 @@ module DataMapper
45
46
  @schema || @schema = @adapter.schema
46
47
  end
47
48
 
49
+ def conditions(args)
50
+ Conditions.new(self, @adapter, @associations.empty?, args)
51
+ end
52
+
48
53
  def paranoid?
49
54
  @paranoid
50
55
  end
@@ -73,6 +78,12 @@ module DataMapper
73
78
  @associations
74
79
  end
75
80
 
81
+ def activate_associations!(force = false)
82
+ @associations.each do |association|
83
+ association.activate! force
84
+ end
85
+ end
86
+
76
87
  def reflect_columns
77
88
  @adapter.reflect_columns(self)
78
89
  end
@@ -85,6 +96,11 @@ module DataMapper
85
96
 
86
97
  self.columns
87
98
  end
99
+
100
+ def mapped_column_exists?(column_name)
101
+ @columns.each {|column| return true if column.name == column_name}
102
+ false
103
+ end
88
104
 
89
105
  def exists?
90
106
  @adapter.connection do |db|
@@ -136,19 +152,22 @@ module DataMapper
136
152
 
137
153
  def truncate!
138
154
  @adapter.connection do |db|
139
- result = db.create_command("TRUNCATE TABLE #{to_sql}").execute_non_query
155
+ result = db.create_command(to_truncate_sql).execute_non_query
140
156
  database.identity_map.clear!(name)
141
157
  result.to_i > 0
142
158
  end
143
159
  end
144
160
 
145
- def count(*args)
146
- @adapter.connection do |db|
147
- sql = "SELECT COUNT(*) AS row_count FROM #{to_sql}"
148
- sql << "WHERE #{args}" unless args.empty?
149
-
161
+ def count(args={})
162
+ sql = "SELECT COUNT(*) AS row_count FROM #{to_sql}"
163
+ parameters = []
164
+
165
+ paramsql, *parameters = conditions(args).to_params_sql
166
+ sql << paramsql #gotta shift it in
167
+
168
+ @adapter.connection do |db|
150
169
  command = db.create_command(sql)
151
- command.execute_reader do |reader|
170
+ command.execute_reader(*parameters) do |reader|
152
171
  if reader.has_rows?
153
172
  reader.current_row.first.to_i
154
173
  else
@@ -175,13 +194,17 @@ module DataMapper
175
194
  end
176
195
  end
177
196
 
178
- def key
197
+ def key
179
198
  @key || begin
180
199
  @key = @columns.find { |column| column.key? }
181
200
 
182
201
  if @key.nil?
183
- @key = add_column(:id, :integer, :serial => true, :ordinal => -1)
184
- @klass.send(:attr_reader, :id) unless @klass.nil? || @klass.methods.include?(:id)
202
+ unless @klass.nil?
203
+ @klass.send(:property, :id, :integer, :serial => true, :ordinal => -1)
204
+ @key = self[:id]
205
+ else
206
+ @key = add_column(:id, :integer, :serial => true, :ordinal => -1)
207
+ end
185
208
  end
186
209
 
187
210
  @key
@@ -269,13 +292,13 @@ module DataMapper
269
292
  @custom_name = value
270
293
  self.name
271
294
  end
272
-
295
+
273
296
  def default_foreign_key
274
- @default_foreign_key || (@default_foreign_key = "#{Inflector.underscore(Inflector.singularize(name))}_#{key.name}".freeze)
297
+ @default_foreign_key ||= Inflector.foreign_key(@klass_or_name, key.name).freeze
275
298
  end
276
299
 
277
300
  def to_sql
278
- @to_sql || @to_sql = quote_table.freeze
301
+ @to_sql ||= quote_table.freeze
279
302
  end
280
303
 
281
304
  def to_s
@@ -343,6 +366,10 @@ module DataMapper
343
366
  queries
344
367
  end
345
368
 
369
+ def to_truncate_sql
370
+ "TRUNCATE TABLE #{to_sql}"
371
+ end
372
+
346
373
  def to_drop_sql
347
374
  @to_drop_sql || @to_drop_sql = "DROP TABLE #{to_sql}"
348
375
  end
@@ -399,6 +426,7 @@ module DataMapper
399
426
  @to_drop_sql = nil
400
427
  @to_sql = nil
401
428
  @name = nil
429
+ @columns_hash.clear
402
430
 
403
431
  if flush_columns
404
432
  @columns.each do |column|
@@ -411,9 +439,7 @@ module DataMapper
411
439
 
412
440
  def activate!
413
441
  @activated = true
414
- @associations.each do |association|
415
- association.activate!
416
- end
442
+ activate_associations!
417
443
  end
418
444
 
419
445
  def activated?
@@ -430,4 +456,4 @@ module DataMapper
430
456
  end
431
457
  end
432
458
  end
433
- end
459
+ end