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.
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