square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,81 @@
1
+ require 'active_support/core_ext/object/duplicable'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module QueryCache
6
+ class << self
7
+ def included(base)
8
+ dirties_query_cache base, :insert, :update, :delete
9
+ end
10
+
11
+ def dirties_query_cache(base, *method_names)
12
+ method_names.each do |method_name|
13
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
14
+ def #{method_name}(*) # def update_with_query_dirty(*args)
15
+ clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
16
+ super # update_without_query_dirty(*args)
17
+ end # end
18
+ end_code
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_reader :query_cache, :query_cache_enabled
24
+
25
+ # Enable the query cache within the block.
26
+ def cache
27
+ old, @query_cache_enabled = @query_cache_enabled, true
28
+ yield
29
+ ensure
30
+ clear_query_cache
31
+ @query_cache_enabled = old
32
+ end
33
+
34
+ # Disable the query cache within the block.
35
+ def uncached
36
+ old, @query_cache_enabled = @query_cache_enabled, false
37
+ yield
38
+ ensure
39
+ @query_cache_enabled = old
40
+ end
41
+
42
+ # Clears the query cache.
43
+ #
44
+ # One reason you may wish to call this method explicitly is between queries
45
+ # that ask the database to randomize results. Otherwise the cache would see
46
+ # the same SQL query and repeatedly return the same result each time, silently
47
+ # undermining the randomness you were expecting.
48
+ def clear_query_cache
49
+ @query_cache.clear
50
+ end
51
+
52
+ def select_all(*args)
53
+ if @query_cache_enabled
54
+ cache_sql(args.first) { super }
55
+ else
56
+ super
57
+ end
58
+ end
59
+
60
+ private
61
+ def cache_sql(sql)
62
+ result =
63
+ if @query_cache.has_key?(sql)
64
+ ActiveSupport::Notifications.instrument("sql.active_record",
65
+ :sql => sql, :name => "CACHE", :connection_id => self.object_id)
66
+ @query_cache[sql]
67
+ else
68
+ @query_cache[sql] = yield
69
+ end
70
+
71
+ if Array === result
72
+ result.collect { |row| row.dup }
73
+ else
74
+ result.duplicable? ? result.dup : result
75
+ end
76
+ rescue TypeError
77
+ result
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,73 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module Quoting
6
+ # Quotes the column value to help prevent
7
+ # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
8
+ def quote(value, column = nil)
9
+ # records are quoted as their primary key
10
+ return value.quoted_id if value.respond_to?(:quoted_id)
11
+
12
+ case value
13
+ when String, ActiveSupport::Multibyte::Chars
14
+ value = value.to_s
15
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
16
+ "'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
17
+ elsif column && [:integer, :float].include?(column.type)
18
+ value = column.type == :integer ? value.to_i : value.to_f
19
+ value.to_s
20
+ else
21
+ "'#{quote_string(value)}'" # ' (for ruby-mode)
22
+ end
23
+ when NilClass then "NULL"
24
+ when TrueClass then (column && column.type == :integer ? '1' : quoted_true)
25
+ when FalseClass then (column && column.type == :integer ? '0' : quoted_false)
26
+ when Float, Fixnum, Bignum then value.to_s
27
+ # BigDecimals need to be output in a non-normalized form and quoted.
28
+ when BigDecimal then value.to_s('F')
29
+ when Symbol then "'#{quote_string(value.to_s)}'"
30
+ else
31
+ if value.acts_like?(:date) || value.acts_like?(:time)
32
+ "'#{quoted_date(value)}'"
33
+ else
34
+ "'#{quote_string(value.to_yaml)}'"
35
+ end
36
+ end
37
+ end
38
+
39
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
40
+ # characters.
41
+ def quote_string(s)
42
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
43
+ end
44
+
45
+ # Quotes the column name. Defaults to no quoting.
46
+ def quote_column_name(column_name)
47
+ column_name
48
+ end
49
+
50
+ # Quotes the table name. Defaults to column name quoting.
51
+ def quote_table_name(table_name)
52
+ quote_column_name(table_name)
53
+ end
54
+
55
+ def quoted_true
56
+ "'t'"
57
+ end
58
+
59
+ def quoted_false
60
+ "'f'"
61
+ end
62
+
63
+ def quoted_date(value)
64
+ if value.acts_like?(:time)
65
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
66
+ value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
67
+ else
68
+ value
69
+ end.to_s(:db)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,739 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'date'
3
+ require 'set'
4
+ require 'bigdecimal'
5
+ require 'bigdecimal/util'
6
+
7
+ module ActiveRecord
8
+ module ConnectionAdapters #:nodoc:
9
+ # An abstract definition of a column in a table.
10
+ class Column
11
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
12
+ FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE'].to_set
13
+
14
+ module Format
15
+ ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
16
+ ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
17
+ end
18
+
19
+ attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale
20
+ attr_accessor :primary
21
+
22
+ # Instantiates a new column in the table.
23
+ #
24
+ # +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
25
+ # +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
26
+ # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
27
+ # <tt>company_name varchar(60)</tt>.
28
+ # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
29
+ # +null+ determines if this column allows +NULL+ values.
30
+ def initialize(name, default, sql_type = nil, null = true)
31
+ @name, @sql_type, @null = name, sql_type, null
32
+ @limit, @precision, @scale = extract_limit(sql_type), extract_precision(sql_type), extract_scale(sql_type)
33
+ @type = simplified_type(sql_type)
34
+ @default = extract_default(default)
35
+
36
+ @primary = nil
37
+ end
38
+
39
+ # Returns +true+ if the column is either of type string or text.
40
+ def text?
41
+ type == :string || type == :text
42
+ end
43
+
44
+ # Returns +true+ if the column is either of type integer, float or decimal.
45
+ def number?
46
+ type == :integer || type == :float || type == :decimal
47
+ end
48
+
49
+ def has_default?
50
+ !default.nil?
51
+ end
52
+
53
+ # Returns the Ruby class that corresponds to the abstract data type.
54
+ def klass
55
+ case type
56
+ when :integer then Fixnum
57
+ when :float then Float
58
+ when :decimal then BigDecimal
59
+ when :datetime then Time
60
+ when :date then Date
61
+ when :timestamp then Time
62
+ when :time then Time
63
+ when :text, :string then String
64
+ when :binary then String
65
+ when :boolean then Object
66
+ end
67
+ end
68
+
69
+ # Casts value (which is a String) to an appropriate instance.
70
+ def type_cast(value)
71
+ return nil if value.nil?
72
+ case type
73
+ when :string then value
74
+ when :text then value
75
+ when :integer then value.to_i rescue value ? 1 : 0
76
+ when :float then value.to_f
77
+ when :decimal then self.class.value_to_decimal(value)
78
+ when :datetime then self.class.string_to_time(value)
79
+ when :timestamp then self.class.string_to_time(value)
80
+ when :time then self.class.string_to_dummy_time(value)
81
+ when :date then self.class.string_to_date(value)
82
+ when :binary then self.class.binary_to_string(value)
83
+ when :boolean then self.class.value_to_boolean(value)
84
+ else value
85
+ end
86
+ end
87
+
88
+ def type_cast_code(var_name)
89
+ case type
90
+ when :string then nil
91
+ when :text then nil
92
+ when :integer then "(#{var_name}.to_i rescue #{var_name} ? 1 : 0)"
93
+ when :float then "#{var_name}.to_f"
94
+ when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
95
+ when :datetime then "#{self.class.name}.string_to_time(#{var_name})"
96
+ when :timestamp then "#{self.class.name}.string_to_time(#{var_name})"
97
+ when :time then "#{self.class.name}.string_to_dummy_time(#{var_name})"
98
+ when :date then "#{self.class.name}.string_to_date(#{var_name})"
99
+ when :binary then "#{self.class.name}.binary_to_string(#{var_name})"
100
+ when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
101
+ else nil
102
+ end
103
+ end
104
+
105
+ # Returns the human name of the column name.
106
+ #
107
+ # ===== Examples
108
+ # Column.new('sales_stage', ...).human_name # => 'Sales stage'
109
+ def human_name
110
+ Base.human_attribute_name(@name)
111
+ end
112
+
113
+ def extract_default(default)
114
+ type_cast(default)
115
+ end
116
+
117
+ class << self
118
+ # Used to convert from Strings to BLOBs
119
+ def string_to_binary(value)
120
+ value
121
+ end
122
+
123
+ # Used to convert from BLOBs to Strings
124
+ def binary_to_string(value)
125
+ value
126
+ end
127
+
128
+ def string_to_date(string)
129
+ return string unless string.is_a?(String)
130
+ return nil if string.empty?
131
+
132
+ fast_string_to_date(string) || fallback_string_to_date(string)
133
+ end
134
+
135
+ def string_to_time(string)
136
+ return string unless string.is_a?(String)
137
+ return nil if string.empty?
138
+
139
+ fast_string_to_time(string) || fallback_string_to_time(string)
140
+ end
141
+
142
+ def string_to_dummy_time(string)
143
+ return string unless string.is_a?(String)
144
+ return nil if string.empty?
145
+
146
+ string_to_time "2000-01-01 #{string}"
147
+ end
148
+
149
+ # convert something to a boolean
150
+ def value_to_boolean(value)
151
+ if value.is_a?(String) && value.blank?
152
+ nil
153
+ else
154
+ TRUE_VALUES.include?(value)
155
+ end
156
+ end
157
+
158
+ # convert something to a BigDecimal
159
+ def value_to_decimal(value)
160
+ # Using .class is faster than .is_a? and
161
+ # subclasses of BigDecimal will be handled
162
+ # in the else clause
163
+ if value.class == BigDecimal
164
+ value
165
+ elsif value.respond_to?(:to_d)
166
+ value.to_d
167
+ else
168
+ value.to_s.to_d
169
+ end
170
+ end
171
+
172
+ protected
173
+ # '0.123456' -> 123456
174
+ # '1.123456' -> 123456
175
+ def microseconds(time)
176
+ ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i
177
+ end
178
+
179
+ def new_date(year, mon, mday)
180
+ if year && year != 0
181
+ Date.new(year, mon, mday) rescue nil
182
+ end
183
+ end
184
+
185
+ def new_time(year, mon, mday, hour, min, sec, microsec)
186
+ # Treat 0000-00-00 00:00:00 as nil.
187
+ return nil if year.nil? || year == 0
188
+
189
+ Time.time_with_datetime_fallback(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
190
+ end
191
+
192
+ def fast_string_to_date(string)
193
+ if string =~ Format::ISO_DATE
194
+ new_date $1.to_i, $2.to_i, $3.to_i
195
+ end
196
+ end
197
+
198
+ # Doesn't handle time zones.
199
+ def fast_string_to_time(string)
200
+ if string =~ Format::ISO_DATETIME
201
+ microsec = ($7.to_f * 1_000_000).to_i
202
+ new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
203
+ end
204
+ end
205
+
206
+ def fallback_string_to_date(string)
207
+ new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
208
+ end
209
+
210
+ def fallback_string_to_time(string)
211
+ time_hash = Date._parse(string)
212
+ time_hash[:sec_fraction] = microseconds(time_hash)
213
+
214
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
215
+ end
216
+ end
217
+
218
+ private
219
+ def extract_limit(sql_type)
220
+ $1.to_i if sql_type =~ /\((.*)\)/
221
+ end
222
+
223
+ def extract_precision(sql_type)
224
+ $2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
225
+ end
226
+
227
+ def extract_scale(sql_type)
228
+ case sql_type
229
+ when /^(numeric|decimal|number)\((\d+)\)/i then 0
230
+ when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
231
+ end
232
+ end
233
+
234
+ def simplified_type(field_type)
235
+ case field_type
236
+ when /int/i
237
+ :integer
238
+ when /float|double/i
239
+ :float
240
+ when /decimal|numeric|number/i
241
+ extract_scale(field_type) == 0 ? :integer : :decimal
242
+ when /datetime/i
243
+ :datetime
244
+ when /timestamp/i
245
+ :timestamp
246
+ when /time/i
247
+ :time
248
+ when /date/i
249
+ :date
250
+ when /clob/i, /text/i
251
+ :text
252
+ when /blob/i, /binary/i
253
+ :binary
254
+ when /char/i, /string/i
255
+ :string
256
+ when /boolean/i
257
+ :boolean
258
+ end
259
+ end
260
+ end
261
+
262
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths) #:nodoc:
263
+ end
264
+
265
+ # Abstract representation of a column definition. Instances of this type
266
+ # are typically created by methods in TableDefinition, and added to the
267
+ # +columns+ attribute of said TableDefinition object, in order to be used
268
+ # for generating a number of table creation or table changing SQL statements.
269
+ class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
270
+
271
+ def sql_type
272
+ base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
273
+ end
274
+
275
+ def to_sql
276
+ column_sql = "#{base.quote_column_name(name)} #{sql_type}"
277
+ column_options = {}
278
+ column_options[:null] = null unless null.nil?
279
+ column_options[:default] = default unless default.nil?
280
+ add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
281
+ column_sql
282
+ end
283
+
284
+ private
285
+
286
+ def add_column_options!(sql, options)
287
+ base.add_column_options!(sql, options.merge(:column => self))
288
+ end
289
+ end
290
+
291
+ # Represents the schema of an SQL table in an abstract way. This class
292
+ # provides methods for manipulating the schema representation.
293
+ #
294
+ # Inside migration files, the +t+ object in +create_table+ and
295
+ # +change_table+ is actually of this type:
296
+ #
297
+ # class SomeMigration < ActiveRecord::Migration
298
+ # def self.up
299
+ # create_table :foo do |t|
300
+ # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
301
+ # end
302
+ # end
303
+ #
304
+ # def self.down
305
+ # ...
306
+ # end
307
+ # end
308
+ #
309
+ # The table definitions
310
+ # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
311
+ class TableDefinition
312
+ # An array of ColumnDefinition objects, representing the column changes
313
+ # that have been defined.
314
+ attr_accessor :columns
315
+
316
+ def initialize(base)
317
+ @columns = []
318
+ @base = base
319
+ end
320
+
321
+ #Handles non supported datatypes - e.g. XML
322
+ def method_missing(symbol, *args)
323
+ if symbol.to_s == 'xml'
324
+ xml_column_fallback(args)
325
+ else
326
+ super
327
+ end
328
+ end
329
+
330
+ def xml_column_fallback(*args)
331
+ case @base.adapter_name.downcase
332
+ when 'sqlite', 'mysql'
333
+ options = args.extract_options!
334
+ column(args[0], :text, options)
335
+ end
336
+ end
337
+
338
+ # Appends a primary key definition to the table definition.
339
+ # Can be called multiple times, but this is probably not a good idea.
340
+ def primary_key(name)
341
+ column(name, :primary_key)
342
+ end
343
+
344
+ # Returns a ColumnDefinition for the column with name +name+.
345
+ def [](name)
346
+ @columns.find {|column| column.name.to_s == name.to_s}
347
+ end
348
+
349
+ # Instantiates a new column for the table.
350
+ # The +type+ parameter is normally one of the migrations native types,
351
+ # which is one of the following:
352
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
353
+ # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
354
+ # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
355
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
356
+ #
357
+ # You may use a type not in this list as long as it is supported by your
358
+ # database (for example, "polygon" in MySQL), but this will not be database
359
+ # agnostic and should usually be avoided.
360
+ #
361
+ # Available options are (none of these exists by default):
362
+ # * <tt>:limit</tt> -
363
+ # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
364
+ # <tt>:text</tt> columns and number of bytes for :binary and :integer columns.
365
+ # * <tt>:default</tt> -
366
+ # The column's default value. Use nil for NULL.
367
+ # * <tt>:null</tt> -
368
+ # Allows or disallows +NULL+ values in the column. This option could
369
+ # have been named <tt>:null_allowed</tt>.
370
+ # * <tt>:precision</tt> -
371
+ # Specifies the precision for a <tt>:decimal</tt> column.
372
+ # * <tt>:scale</tt> -
373
+ # Specifies the scale for a <tt>:decimal</tt> column.
374
+ #
375
+ # For clarity's sake: the precision is the number of significant digits,
376
+ # while the scale is the number of digits that can be stored following
377
+ # the decimal point. For example, the number 123.45 has a precision of 5
378
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
379
+ # range from -999.99 to 999.99.
380
+ #
381
+ # Please be aware of different RDBMS implementations behavior with
382
+ # <tt>:decimal</tt> columns:
383
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
384
+ # <tt>:precision</tt>, and makes no comments about the requirements of
385
+ # <tt>:precision</tt>.
386
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
387
+ # Default is (10,0).
388
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
389
+ # <tt>:scale</tt> [0..infinity]. No default.
390
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
391
+ # Internal storage as strings. No default.
392
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
393
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
394
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
395
+ # Default is (38,0).
396
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
397
+ # Default unknown.
398
+ # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
399
+ # Default (9,0). Internal types NUMERIC and DECIMAL have different
400
+ # storage rules, decimal being better.
401
+ # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
402
+ # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
403
+ # NUMERIC is 19, and DECIMAL is 38.
404
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
405
+ # Default (38,0).
406
+ # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
407
+ # Default (38,0).
408
+ # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
409
+ #
410
+ # This method returns <tt>self</tt>.
411
+ #
412
+ # == Examples
413
+ # # Assuming td is an instance of TableDefinition
414
+ # td.column(:granted, :boolean)
415
+ # # granted BOOLEAN
416
+ #
417
+ # td.column(:picture, :binary, :limit => 2.megabytes)
418
+ # # => picture BLOB(2097152)
419
+ #
420
+ # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
421
+ # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
422
+ #
423
+ # td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
424
+ # # => bill_gates_money DECIMAL(15,2)
425
+ #
426
+ # td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
427
+ # # => sensor_reading DECIMAL(30,20)
428
+ #
429
+ # # While <tt>:scale</tt> defaults to zero on most databases, it
430
+ # # probably wouldn't hurt to include it.
431
+ # td.column(:huge_integer, :decimal, :precision => 30)
432
+ # # => huge_integer DECIMAL(30)
433
+ #
434
+ # # Defines a column with a database-specific type.
435
+ # td.column(:foo, 'polygon')
436
+ # # => foo polygon
437
+ #
438
+ # == Short-hand examples
439
+ #
440
+ # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
441
+ # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
442
+ # in a single statement.
443
+ #
444
+ # What can be written like this with the regular calls to column:
445
+ #
446
+ # create_table "products", :force => true do |t|
447
+ # t.column "shop_id", :integer
448
+ # t.column "creator_id", :integer
449
+ # t.column "name", :string, :default => "Untitled"
450
+ # t.column "value", :string, :default => "Untitled"
451
+ # t.column "created_at", :datetime
452
+ # t.column "updated_at", :datetime
453
+ # end
454
+ #
455
+ # Can also be written as follows using the short-hand:
456
+ #
457
+ # create_table :products do |t|
458
+ # t.integer :shop_id, :creator_id
459
+ # t.string :name, :value, :default => "Untitled"
460
+ # t.timestamps
461
+ # end
462
+ #
463
+ # There's a short-hand method for each of the type values declared at the top. And then there's
464
+ # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes.
465
+ #
466
+ # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
467
+ # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
468
+ # options, these will be used when creating the <tt>_type</tt> column. So what can be written like this:
469
+ #
470
+ # create_table :taggings do |t|
471
+ # t.integer :tag_id, :tagger_id, :taggable_id
472
+ # t.string :tagger_type
473
+ # t.string :taggable_type, :default => 'Photo'
474
+ # end
475
+ #
476
+ # Can also be written as follows using references:
477
+ #
478
+ # create_table :taggings do |t|
479
+ # t.references :tag
480
+ # t.references :tagger, :polymorphic => true
481
+ # t.references :taggable, :polymorphic => { :default => 'Photo' }
482
+ # end
483
+ def column(name, type, options = {})
484
+ column = self[name] || ColumnDefinition.new(@base, name, type)
485
+ if options[:limit]
486
+ column.limit = options[:limit]
487
+ elsif native[type.to_sym].is_a?(Hash)
488
+ column.limit = native[type.to_sym][:limit]
489
+ end
490
+ column.precision = options[:precision]
491
+ column.scale = options[:scale]
492
+ column.default = options[:default]
493
+ column.null = options[:null]
494
+ @columns << column unless @columns.include? column
495
+ self
496
+ end
497
+
498
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
499
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
500
+ def #{column_type}(*args) # def string(*args)
501
+ options = args.extract_options! # options = args.extract_options!
502
+ column_names = args # column_names = args
503
+ #
504
+ column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) }
505
+ end # end
506
+ EOV
507
+ end
508
+
509
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
510
+ # <tt>:updated_at</tt> to the table.
511
+ def timestamps(*args)
512
+ options = args.extract_options!
513
+ column(:created_at, :datetime, options)
514
+ column(:updated_at, :datetime, options)
515
+ end
516
+
517
+ def references(*args)
518
+ options = args.extract_options!
519
+ polymorphic = options.delete(:polymorphic)
520
+ args.each do |col|
521
+ column("#{col}_id", :integer, options)
522
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
523
+ end
524
+ end
525
+ alias :belongs_to :references
526
+
527
+ # Returns a String whose contents are the column definitions
528
+ # concatenated together. This string can then be prepended and appended to
529
+ # to generate the final SQL to create the table.
530
+ def to_sql
531
+ @columns.map { |c| c.to_sql } * ', '
532
+ end
533
+
534
+ private
535
+ def native
536
+ @base.native_database_types
537
+ end
538
+ end
539
+
540
+ # Represents an SQL table in an abstract way for updating a table.
541
+ # Also see TableDefinition and SchemaStatements#create_table
542
+ #
543
+ # Available transformations are:
544
+ #
545
+ # change_table :table do |t|
546
+ # t.column
547
+ # t.index
548
+ # t.timestamps
549
+ # t.change
550
+ # t.change_default
551
+ # t.rename
552
+ # t.references
553
+ # t.belongs_to
554
+ # t.string
555
+ # t.text
556
+ # t.integer
557
+ # t.float
558
+ # t.decimal
559
+ # t.datetime
560
+ # t.timestamp
561
+ # t.time
562
+ # t.date
563
+ # t.binary
564
+ # t.boolean
565
+ # t.remove
566
+ # t.remove_references
567
+ # t.remove_belongs_to
568
+ # t.remove_index
569
+ # t.remove_timestamps
570
+ # end
571
+ #
572
+ class Table
573
+ def initialize(table_name, base)
574
+ @table_name = table_name
575
+ @base = base
576
+ end
577
+
578
+ # Adds a new column to the named table.
579
+ # See TableDefinition#column for details of the options you can use.
580
+ # ===== Example
581
+ # ====== Creating a simple column
582
+ # t.column(:name, :string)
583
+ def column(column_name, type, options = {})
584
+ @base.add_column(@table_name, column_name, type, options)
585
+ end
586
+
587
+ # Checks to see if a column exists. See SchemaStatements#column_exists?
588
+ def column_exists?(column_name, type = nil, options = nil)
589
+ @base.column_exists?(@table_name, column_name, type, options)
590
+ end
591
+
592
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
593
+ # an Array of Symbols. See SchemaStatements#add_index
594
+ #
595
+ # ===== Examples
596
+ # ====== Creating a simple index
597
+ # t.index(:name)
598
+ # ====== Creating a unique index
599
+ # t.index([:branch_id, :party_id], :unique => true)
600
+ # ====== Creating a named index
601
+ # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
602
+ def index(column_name, options = {})
603
+ @base.add_index(@table_name, column_name, options)
604
+ end
605
+
606
+ # Checks to see if an index exists. See SchemaStatements#index_exists?
607
+ def index_exists?(column_name, options = {})
608
+ @base.index_exists?(@table_name, column_name, options)
609
+ end
610
+
611
+ # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps
612
+ # ===== Example
613
+ # t.timestamps
614
+ def timestamps
615
+ @base.add_timestamps(@table_name)
616
+ end
617
+
618
+ # Changes the column's definition according to the new options.
619
+ # See TableDefinition#column for details of the options you can use.
620
+ # ===== Examples
621
+ # t.change(:name, :string, :limit => 80)
622
+ # t.change(:description, :text)
623
+ def change(column_name, type, options = {})
624
+ @base.change_column(@table_name, column_name, type, options)
625
+ end
626
+
627
+ # Sets a new default value for a column. See SchemaStatements#change_column_default
628
+ # ===== Examples
629
+ # t.change_default(:qualification, 'new')
630
+ # t.change_default(:authorized, 1)
631
+ def change_default(column_name, default)
632
+ @base.change_column_default(@table_name, column_name, default)
633
+ end
634
+
635
+ # Removes the column(s) from the table definition.
636
+ # ===== Examples
637
+ # t.remove(:qualification)
638
+ # t.remove(:qualification, :experience)
639
+ def remove(*column_names)
640
+ @base.remove_column(@table_name, column_names)
641
+ end
642
+
643
+ # Removes the given index from the table.
644
+ #
645
+ # ===== Examples
646
+ # ====== Remove the suppliers_name_index in the suppliers table
647
+ # t.remove_index :name
648
+ # ====== Remove the index named accounts_branch_id_index in the accounts table
649
+ # t.remove_index :column => :branch_id
650
+ # ====== Remove the index named accounts_branch_id_party_id_index in the accounts table
651
+ # t.remove_index :column => [:branch_id, :party_id]
652
+ # ====== Remove the index named by_branch_party in the accounts table
653
+ # t.remove_index :name => :by_branch_party
654
+ def remove_index(options = {})
655
+ @base.remove_index(@table_name, options)
656
+ end
657
+
658
+ # Removes the timestamp columns (created_at and updated_at) from the table.
659
+ # ===== Example
660
+ # t.remove_timestamps
661
+ def remove_timestamps
662
+ @base.remove_timestamps(@table_name)
663
+ end
664
+
665
+ # Renames a column.
666
+ # ===== Example
667
+ # t.rename(:description, :name)
668
+ def rename(column_name, new_column_name)
669
+ @base.rename_column(@table_name, column_name, new_column_name)
670
+ end
671
+
672
+ # Adds a reference. Optionally adds a +type+ column.
673
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
674
+ # ===== Examples
675
+ # t.references(:goat)
676
+ # t.references(:goat, :polymorphic => true)
677
+ # t.belongs_to(:goat)
678
+ def references(*args)
679
+ options = args.extract_options!
680
+ polymorphic = options.delete(:polymorphic)
681
+ args.each do |col|
682
+ @base.add_column(@table_name, "#{col}_id", :integer, options)
683
+ @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
684
+ end
685
+ end
686
+ alias :belongs_to :references
687
+
688
+ # Removes a reference. Optionally removes a +type+ column.
689
+ # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
690
+ # ===== Examples
691
+ # t.remove_references(:goat)
692
+ # t.remove_references(:goat, :polymorphic => true)
693
+ # t.remove_belongs_to(:goat)
694
+ def remove_references(*args)
695
+ options = args.extract_options!
696
+ polymorphic = options.delete(:polymorphic)
697
+ args.each do |col|
698
+ @base.remove_column(@table_name, "#{col}_id")
699
+ @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
700
+ end
701
+ end
702
+ alias :remove_belongs_to :remove_references
703
+
704
+ # Adds a column or columns of a specified type
705
+ # ===== Examples
706
+ # t.string(:goat)
707
+ # t.string(:goat, :sheep)
708
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
709
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
710
+ def #{column_type}(*args) # def string(*args)
711
+ options = args.extract_options! # options = args.extract_options!
712
+ column_names = args # column_names = args
713
+ #
714
+ column_names.each do |name| # column_names.each do |name|
715
+ column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string')
716
+ if options[:limit] # if options[:limit]
717
+ column.limit = options[:limit] # column.limit = options[:limit]
718
+ elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash)
719
+ column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit]
720
+ end # end
721
+ column.precision = options[:precision] # column.precision = options[:precision]
722
+ column.scale = options[:scale] # column.scale = options[:scale]
723
+ column.default = options[:default] # column.default = options[:default]
724
+ column.null = options[:null] # column.null = options[:null]
725
+ @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
726
+ end # end
727
+ end # end
728
+ EOV
729
+ end
730
+
731
+ private
732
+ def native
733
+ @base.native_database_types
734
+ end
735
+ end
736
+
737
+ end
738
+ end
739
+