sskirby-activerecord 3.2.1

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 (150) hide show
  1. data/CHANGELOG.md +6749 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +222 -0
  4. data/examples/associations.png +0 -0
  5. data/examples/performance.rb +177 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record.rb +147 -0
  8. data/lib/active_record/aggregations.rb +255 -0
  9. data/lib/active_record/associations.rb +1604 -0
  10. data/lib/active_record/associations/alias_tracker.rb +79 -0
  11. data/lib/active_record/associations/association.rb +239 -0
  12. data/lib/active_record/associations/association_scope.rb +119 -0
  13. data/lib/active_record/associations/belongs_to_association.rb +79 -0
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +34 -0
  15. data/lib/active_record/associations/builder/association.rb +55 -0
  16. data/lib/active_record/associations/builder/belongs_to.rb +85 -0
  17. data/lib/active_record/associations/builder/collection_association.rb +75 -0
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -0
  19. data/lib/active_record/associations/builder/has_many.rb +71 -0
  20. data/lib/active_record/associations/builder/has_one.rb +62 -0
  21. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  22. data/lib/active_record/associations/collection_association.rb +574 -0
  23. data/lib/active_record/associations/collection_proxy.rb +132 -0
  24. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +62 -0
  25. data/lib/active_record/associations/has_many_association.rb +108 -0
  26. data/lib/active_record/associations/has_many_through_association.rb +180 -0
  27. data/lib/active_record/associations/has_one_association.rb +73 -0
  28. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  29. data/lib/active_record/associations/join_dependency.rb +214 -0
  30. data/lib/active_record/associations/join_dependency/join_association.rb +154 -0
  31. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  32. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  33. data/lib/active_record/associations/join_helper.rb +55 -0
  34. data/lib/active_record/associations/preloader.rb +177 -0
  35. data/lib/active_record/associations/preloader/association.rb +127 -0
  36. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  37. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  38. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  39. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  40. data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
  41. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  42. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  43. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  44. data/lib/active_record/associations/preloader/through_association.rb +67 -0
  45. data/lib/active_record/associations/singular_association.rb +64 -0
  46. data/lib/active_record/associations/through_association.rb +83 -0
  47. data/lib/active_record/attribute_assignment.rb +221 -0
  48. data/lib/active_record/attribute_methods.rb +272 -0
  49. data/lib/active_record/attribute_methods/before_type_cast.rb +31 -0
  50. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  51. data/lib/active_record/attribute_methods/dirty.rb +101 -0
  52. data/lib/active_record/attribute_methods/primary_key.rb +114 -0
  53. data/lib/active_record/attribute_methods/query.rb +39 -0
  54. data/lib/active_record/attribute_methods/read.rb +135 -0
  55. data/lib/active_record/attribute_methods/serialization.rb +93 -0
  56. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -0
  57. data/lib/active_record/attribute_methods/write.rb +69 -0
  58. data/lib/active_record/autosave_association.rb +422 -0
  59. data/lib/active_record/base.rb +716 -0
  60. data/lib/active_record/callbacks.rb +275 -0
  61. data/lib/active_record/coders/yaml_column.rb +41 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +188 -0
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +58 -0
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +388 -0
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -0
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +115 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +492 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +598 -0
  70. data/lib/active_record/connection_adapters/abstract_adapter.rb +296 -0
  71. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  72. data/lib/active_record/connection_adapters/column.rb +270 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +288 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +426 -0
  75. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1261 -0
  76. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  77. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -0
  78. data/lib/active_record/connection_adapters/sqlite_adapter.rb +577 -0
  79. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  80. data/lib/active_record/counter_cache.rb +119 -0
  81. data/lib/active_record/dynamic_finder_match.rb +56 -0
  82. data/lib/active_record/dynamic_matchers.rb +79 -0
  83. data/lib/active_record/dynamic_scope_match.rb +23 -0
  84. data/lib/active_record/errors.rb +195 -0
  85. data/lib/active_record/explain.rb +85 -0
  86. data/lib/active_record/explain_subscriber.rb +21 -0
  87. data/lib/active_record/fixtures.rb +906 -0
  88. data/lib/active_record/fixtures/file.rb +65 -0
  89. data/lib/active_record/identity_map.rb +156 -0
  90. data/lib/active_record/inheritance.rb +167 -0
  91. data/lib/active_record/integration.rb +49 -0
  92. data/lib/active_record/locale/en.yml +40 -0
  93. data/lib/active_record/locking/optimistic.rb +183 -0
  94. data/lib/active_record/locking/pessimistic.rb +77 -0
  95. data/lib/active_record/log_subscriber.rb +68 -0
  96. data/lib/active_record/migration.rb +765 -0
  97. data/lib/active_record/migration/command_recorder.rb +105 -0
  98. data/lib/active_record/model_schema.rb +366 -0
  99. data/lib/active_record/nested_attributes.rb +469 -0
  100. data/lib/active_record/observer.rb +121 -0
  101. data/lib/active_record/persistence.rb +372 -0
  102. data/lib/active_record/query_cache.rb +74 -0
  103. data/lib/active_record/querying.rb +58 -0
  104. data/lib/active_record/railtie.rb +119 -0
  105. data/lib/active_record/railties/console_sandbox.rb +6 -0
  106. data/lib/active_record/railties/controller_runtime.rb +49 -0
  107. data/lib/active_record/railties/databases.rake +620 -0
  108. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  109. data/lib/active_record/readonly_attributes.rb +26 -0
  110. data/lib/active_record/reflection.rb +534 -0
  111. data/lib/active_record/relation.rb +534 -0
  112. data/lib/active_record/relation/batches.rb +90 -0
  113. data/lib/active_record/relation/calculations.rb +354 -0
  114. data/lib/active_record/relation/delegation.rb +49 -0
  115. data/lib/active_record/relation/finder_methods.rb +398 -0
  116. data/lib/active_record/relation/predicate_builder.rb +58 -0
  117. data/lib/active_record/relation/query_methods.rb +417 -0
  118. data/lib/active_record/relation/spawn_methods.rb +148 -0
  119. data/lib/active_record/result.rb +34 -0
  120. data/lib/active_record/sanitization.rb +194 -0
  121. data/lib/active_record/schema.rb +58 -0
  122. data/lib/active_record/schema_dumper.rb +204 -0
  123. data/lib/active_record/scoping.rb +152 -0
  124. data/lib/active_record/scoping/default.rb +142 -0
  125. data/lib/active_record/scoping/named.rb +202 -0
  126. data/lib/active_record/serialization.rb +18 -0
  127. data/lib/active_record/serializers/xml_serializer.rb +202 -0
  128. data/lib/active_record/session_store.rb +358 -0
  129. data/lib/active_record/store.rb +50 -0
  130. data/lib/active_record/test_case.rb +73 -0
  131. data/lib/active_record/timestamp.rb +113 -0
  132. data/lib/active_record/transactions.rb +360 -0
  133. data/lib/active_record/translation.rb +22 -0
  134. data/lib/active_record/validations.rb +83 -0
  135. data/lib/active_record/validations/associated.rb +43 -0
  136. data/lib/active_record/validations/uniqueness.rb +180 -0
  137. data/lib/active_record/version.rb +10 -0
  138. data/lib/rails/generators/active_record.rb +25 -0
  139. data/lib/rails/generators/active_record/migration.rb +15 -0
  140. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  141. data/lib/rails/generators/active_record/migration/templates/migration.rb +31 -0
  142. data/lib/rails/generators/active_record/model/model_generator.rb +43 -0
  143. data/lib/rails/generators/active_record/model/templates/migration.rb +15 -0
  144. data/lib/rails/generators/active_record/model/templates/model.rb +7 -0
  145. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  146. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  147. data/lib/rails/generators/active_record/observer/templates/observer.rb +4 -0
  148. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +25 -0
  149. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +12 -0
  150. metadata +242 -0
@@ -0,0 +1,82 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters # :nodoc:
3
+ module QueryCache
4
+ class << self
5
+ def included(base)
6
+ dirties_query_cache base, :insert, :update, :delete
7
+ end
8
+
9
+ def dirties_query_cache(base, *method_names)
10
+ method_names.each do |method_name|
11
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
12
+ def #{method_name}(*) # def update_with_query_dirty(*args)
13
+ clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
14
+ super # update_without_query_dirty(*args)
15
+ end # end
16
+ end_code
17
+ end
18
+ end
19
+ end
20
+
21
+ attr_reader :query_cache, :query_cache_enabled
22
+
23
+ # Enable the query cache within the block.
24
+ def cache
25
+ old, @query_cache_enabled = @query_cache_enabled, true
26
+ yield
27
+ ensure
28
+ clear_query_cache
29
+ @query_cache_enabled = old
30
+ end
31
+
32
+ def enable_query_cache!
33
+ @query_cache_enabled = true
34
+ end
35
+
36
+ def disable_query_cache!
37
+ @query_cache_enabled = false
38
+ end
39
+
40
+ # Disable the query cache within the block.
41
+ def uncached
42
+ old, @query_cache_enabled = @query_cache_enabled, false
43
+ yield
44
+ ensure
45
+ @query_cache_enabled = old
46
+ end
47
+
48
+ # Clears the query cache.
49
+ #
50
+ # One reason you may wish to call this method explicitly is between queries
51
+ # that ask the database to randomize results. Otherwise the cache would see
52
+ # the same SQL query and repeatedly return the same result each time, silently
53
+ # undermining the randomness you were expecting.
54
+ def clear_query_cache
55
+ @query_cache.clear
56
+ end
57
+
58
+ def select_all(arel, name = nil, binds = [])
59
+ if @query_cache_enabled
60
+ sql = to_sql(arel)
61
+ cache_sql(sql, binds) { super(sql, name, binds) }
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ private
68
+ def cache_sql(sql, binds)
69
+ result =
70
+ if @query_cache[sql].key?(binds)
71
+ ActiveSupport::Notifications.instrument("sql.active_record",
72
+ :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
73
+ @query_cache[sql][binds]
74
+ else
75
+ @query_cache[sql][binds] = yield
76
+ end
77
+
78
+ result.collect { |row| row.dup }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,115 @@
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
+ return "'#{quote_string(value)}'" unless column
16
+
17
+ case column.type
18
+ when :binary then "'#{quote_string(column.string_to_binary(value))}'"
19
+ when :integer then value.to_i.to_s
20
+ when :float then value.to_f.to_s
21
+ else
22
+ "'#{quote_string(value)}'"
23
+ end
24
+
25
+ when true, false
26
+ if column && column.type == :integer
27
+ value ? '1' : '0'
28
+ else
29
+ value ? quoted_true : quoted_false
30
+ end
31
+ # BigDecimals need to be put in a non-normalized form and quoted.
32
+ when nil then "NULL"
33
+ when BigDecimal then value.to_s('F')
34
+ when Numeric then value.to_s
35
+ when Date, Time then "'#{quoted_date(value)}'"
36
+ when Symbol then "'#{quote_string(value.to_s)}'"
37
+ else
38
+ "'#{quote_string(YAML.dump(value))}'"
39
+ end
40
+ end
41
+
42
+ # Cast a +value+ to a type that the database understands. For example,
43
+ # SQLite does not understand dates, so this method will convert a Date
44
+ # to a String.
45
+ def type_cast(value, column)
46
+ return value.id if value.respond_to?(:quoted_id)
47
+
48
+ case value
49
+ when String, ActiveSupport::Multibyte::Chars
50
+ value = value.to_s
51
+ return value unless column
52
+
53
+ case column.type
54
+ when :binary then value
55
+ when :integer then value.to_i
56
+ when :float then value.to_f
57
+ else
58
+ value
59
+ end
60
+
61
+ when true, false
62
+ if column && column.type == :integer
63
+ value ? 1 : 0
64
+ else
65
+ value ? 't' : 'f'
66
+ end
67
+ # BigDecimals need to be put in a non-normalized form and quoted.
68
+ when nil then nil
69
+ when BigDecimal then value.to_s('F')
70
+ when Numeric then value
71
+ when Date, Time then quoted_date(value)
72
+ when Symbol then value.to_s
73
+ else
74
+ YAML.dump(value)
75
+ end
76
+ end
77
+
78
+ # Quotes a string, escaping any ' (single quote) and \ (backslash)
79
+ # characters.
80
+ def quote_string(s)
81
+ s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
82
+ end
83
+
84
+ # Quotes the column name. Defaults to no quoting.
85
+ def quote_column_name(column_name)
86
+ column_name
87
+ end
88
+
89
+ # Quotes the table name. Defaults to column name quoting.
90
+ def quote_table_name(table_name)
91
+ quote_column_name(table_name)
92
+ end
93
+
94
+ def quoted_true
95
+ "'t'"
96
+ end
97
+
98
+ def quoted_false
99
+ "'f'"
100
+ end
101
+
102
+ def quoted_date(value)
103
+ if value.acts_like?(:time)
104
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
105
+
106
+ if value.respond_to?(zone_conversion_method)
107
+ value = value.send(zone_conversion_method)
108
+ end
109
+ end
110
+
111
+ value.to_s(:db)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,492 @@
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
+ class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders) #:nodoc:
10
+ end
11
+
12
+ # Abstract representation of a column definition. Instances of this type
13
+ # are typically created by methods in TableDefinition, and added to the
14
+ # +columns+ attribute of said TableDefinition object, in order to be used
15
+ # for generating a number of table creation or table changing SQL statements.
16
+ class ColumnDefinition < Struct.new(:base, :name, :type, :limit, :precision, :scale, :default, :null) #:nodoc:
17
+
18
+ def string_to_binary(value)
19
+ value
20
+ end
21
+
22
+ def sql_type
23
+ base.type_to_sql(type.to_sym, limit, precision, scale) rescue type
24
+ end
25
+
26
+ def to_sql
27
+ column_sql = "#{base.quote_column_name(name)} #{sql_type}"
28
+ column_options = {}
29
+ column_options[:null] = null unless null.nil?
30
+ column_options[:default] = default unless default.nil?
31
+ add_column_options!(column_sql, column_options) unless type.to_sym == :primary_key
32
+ column_sql
33
+ end
34
+
35
+ private
36
+
37
+ def add_column_options!(sql, options)
38
+ base.add_column_options!(sql, options.merge(:column => self))
39
+ end
40
+ end
41
+
42
+ # Represents the schema of an SQL table in an abstract way. This class
43
+ # provides methods for manipulating the schema representation.
44
+ #
45
+ # Inside migration files, the +t+ object in +create_table+ and
46
+ # +change_table+ is actually of this type:
47
+ #
48
+ # class SomeMigration < ActiveRecord::Migration
49
+ # def up
50
+ # create_table :foo do |t|
51
+ # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
52
+ # end
53
+ # end
54
+ #
55
+ # def down
56
+ # ...
57
+ # end
58
+ # end
59
+ #
60
+ # The table definitions
61
+ # The Columns are stored as a ColumnDefinition in the +columns+ attribute.
62
+ class TableDefinition
63
+ # An array of ColumnDefinition objects, representing the column changes
64
+ # that have been defined.
65
+ attr_accessor :columns
66
+
67
+ def initialize(base)
68
+ @columns = []
69
+ @columns_hash = {}
70
+ @base = base
71
+ end
72
+
73
+ def xml(*args)
74
+ raise NotImplementedError unless %w{
75
+ sqlite mysql mysql2
76
+ }.include? @base.adapter_name.downcase
77
+
78
+ options = args.extract_options!
79
+ column(args[0], :text, options)
80
+ end
81
+
82
+ # Appends a primary key definition to the table definition.
83
+ # Can be called multiple times, but this is probably not a good idea.
84
+ def primary_key(name)
85
+ column(name, :primary_key)
86
+ end
87
+
88
+ # Returns a ColumnDefinition for the column with name +name+.
89
+ def [](name)
90
+ @columns_hash[name.to_s]
91
+ end
92
+
93
+ # Instantiates a new column for the table.
94
+ # The +type+ parameter is normally one of the migrations native types,
95
+ # which is one of the following:
96
+ # <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
97
+ # <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
98
+ # <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
99
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
100
+ #
101
+ # You may use a type not in this list as long as it is supported by your
102
+ # database (for example, "polygon" in MySQL), but this will not be database
103
+ # agnostic and should usually be avoided.
104
+ #
105
+ # Available options are (none of these exists by default):
106
+ # * <tt>:limit</tt> -
107
+ # Requests a maximum column length. This is number of characters for <tt>:string</tt> and
108
+ # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
109
+ # * <tt>:default</tt> -
110
+ # The column's default value. Use nil for NULL.
111
+ # * <tt>:null</tt> -
112
+ # Allows or disallows +NULL+ values in the column. This option could
113
+ # have been named <tt>:null_allowed</tt>.
114
+ # * <tt>:precision</tt> -
115
+ # Specifies the precision for a <tt>:decimal</tt> column.
116
+ # * <tt>:scale</tt> -
117
+ # Specifies the scale for a <tt>:decimal</tt> column.
118
+ #
119
+ # For clarity's sake: the precision is the number of significant digits,
120
+ # while the scale is the number of digits that can be stored following
121
+ # the decimal point. For example, the number 123.45 has a precision of 5
122
+ # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
123
+ # range from -999.99 to 999.99.
124
+ #
125
+ # Please be aware of different RDBMS implementations behavior with
126
+ # <tt>:decimal</tt> columns:
127
+ # * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
128
+ # <tt>:precision</tt>, and makes no comments about the requirements of
129
+ # <tt>:precision</tt>.
130
+ # * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
131
+ # Default is (10,0).
132
+ # * PostgreSQL: <tt>:precision</tt> [1..infinity],
133
+ # <tt>:scale</tt> [0..infinity]. No default.
134
+ # * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
135
+ # Internal storage as strings. No default.
136
+ # * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
137
+ # but the maximum supported <tt>:precision</tt> is 16. No default.
138
+ # * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
139
+ # Default is (38,0).
140
+ # * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
141
+ # Default unknown.
142
+ # * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
143
+ # Default (9,0). Internal types NUMERIC and DECIMAL have different
144
+ # storage rules, decimal being better.
145
+ # * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
146
+ # Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
147
+ # NUMERIC is 19, and DECIMAL is 38.
148
+ # * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
149
+ # Default (38,0).
150
+ # * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
151
+ # Default (38,0).
152
+ # * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
153
+ #
154
+ # This method returns <tt>self</tt>.
155
+ #
156
+ # == Examples
157
+ # # Assuming +td+ is an instance of TableDefinition
158
+ # td.column(:granted, :boolean)
159
+ # # granted BOOLEAN
160
+ #
161
+ # td.column(:picture, :binary, :limit => 2.megabytes)
162
+ # # => picture BLOB(2097152)
163
+ #
164
+ # td.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
165
+ # # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
166
+ #
167
+ # td.column(:bill_gates_money, :decimal, :precision => 15, :scale => 2)
168
+ # # => bill_gates_money DECIMAL(15,2)
169
+ #
170
+ # td.column(:sensor_reading, :decimal, :precision => 30, :scale => 20)
171
+ # # => sensor_reading DECIMAL(30,20)
172
+ #
173
+ # # While <tt>:scale</tt> defaults to zero on most databases, it
174
+ # # probably wouldn't hurt to include it.
175
+ # td.column(:huge_integer, :decimal, :precision => 30)
176
+ # # => huge_integer DECIMAL(30)
177
+ #
178
+ # # Defines a column with a database-specific type.
179
+ # td.column(:foo, 'polygon')
180
+ # # => foo polygon
181
+ #
182
+ # == Short-hand examples
183
+ #
184
+ # Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
185
+ # They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
186
+ # in a single statement.
187
+ #
188
+ # What can be written like this with the regular calls to column:
189
+ #
190
+ # create_table "products", :force => true do |t|
191
+ # t.column "shop_id", :integer
192
+ # t.column "creator_id", :integer
193
+ # t.column "name", :string, :default => "Untitled"
194
+ # t.column "value", :string, :default => "Untitled"
195
+ # t.column "created_at", :datetime
196
+ # t.column "updated_at", :datetime
197
+ # end
198
+ #
199
+ # Can also be written as follows using the short-hand:
200
+ #
201
+ # create_table :products do |t|
202
+ # t.integer :shop_id, :creator_id
203
+ # t.string :name, :value, :default => "Untitled"
204
+ # t.timestamps
205
+ # end
206
+ #
207
+ # There's a short-hand method for each of the type values declared at the top. And then there's
208
+ # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
209
+ #
210
+ # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
211
+ # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
212
+ # options, these will be used when creating the <tt>_type</tt> column. So what can be written like this:
213
+ #
214
+ # create_table :taggings do |t|
215
+ # t.integer :tag_id, :tagger_id, :taggable_id
216
+ # t.string :tagger_type
217
+ # t.string :taggable_type, :default => 'Photo'
218
+ # end
219
+ #
220
+ # Can also be written as follows using references:
221
+ #
222
+ # create_table :taggings do |t|
223
+ # t.references :tag
224
+ # t.references :tagger, :polymorphic => true
225
+ # t.references :taggable, :polymorphic => { :default => 'Photo' }
226
+ # end
227
+ def column(name, type, options = {})
228
+ name = name.to_s
229
+ type = type.to_sym
230
+
231
+ column = self[name] || new_column_definition(@base, name, type)
232
+
233
+ limit = options.fetch(:limit) do
234
+ native[type][:limit] if native[type].is_a?(Hash)
235
+ end
236
+
237
+ column.limit = limit
238
+ column.precision = options[:precision]
239
+ column.scale = options[:scale]
240
+ column.default = options[:default]
241
+ column.null = options[:null]
242
+ self
243
+ end
244
+
245
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
246
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
247
+ def #{column_type}(*args) # def string(*args)
248
+ options = args.extract_options! # options = args.extract_options!
249
+ column_names = args # column_names = args
250
+ type = :'#{column_type}' # type = :string
251
+ column_names.each { |name| column(name, type, options) } # column_names.each { |name| column(name, type, options) }
252
+ end # end
253
+ EOV
254
+ end
255
+
256
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
257
+ # <tt>:updated_at</tt> to the table.
258
+ def timestamps(*args)
259
+ options = { :null => false }.merge(args.extract_options!)
260
+ column(:created_at, :datetime, options)
261
+ column(:updated_at, :datetime, options)
262
+ end
263
+
264
+ def references(*args)
265
+ options = args.extract_options!
266
+ polymorphic = options.delete(:polymorphic)
267
+ args.each do |col|
268
+ column("#{col}_id", :integer, options)
269
+ column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
270
+ end
271
+ end
272
+ alias :belongs_to :references
273
+
274
+ # Returns a String whose contents are the column definitions
275
+ # concatenated together. This string can then be prepended and appended to
276
+ # to generate the final SQL to create the table.
277
+ def to_sql
278
+ @columns.map { |c| c.to_sql } * ', '
279
+ end
280
+
281
+ private
282
+ def new_column_definition(base, name, type)
283
+ definition = ColumnDefinition.new base, name, type
284
+ @columns << definition
285
+ @columns_hash[name] = definition
286
+ definition
287
+ end
288
+
289
+ def native
290
+ @base.native_database_types
291
+ end
292
+ end
293
+
294
+ # Represents an SQL table in an abstract way for updating a table.
295
+ # Also see TableDefinition and SchemaStatements#create_table
296
+ #
297
+ # Available transformations are:
298
+ #
299
+ # change_table :table do |t|
300
+ # t.column
301
+ # t.index
302
+ # t.timestamps
303
+ # t.change
304
+ # t.change_default
305
+ # t.rename
306
+ # t.references
307
+ # t.belongs_to
308
+ # t.string
309
+ # t.text
310
+ # t.integer
311
+ # t.float
312
+ # t.decimal
313
+ # t.datetime
314
+ # t.timestamp
315
+ # t.time
316
+ # t.date
317
+ # t.binary
318
+ # t.boolean
319
+ # t.remove
320
+ # t.remove_references
321
+ # t.remove_belongs_to
322
+ # t.remove_index
323
+ # t.remove_timestamps
324
+ # end
325
+ #
326
+ class Table
327
+ def initialize(table_name, base)
328
+ @table_name = table_name
329
+ @base = base
330
+ end
331
+
332
+ # Adds a new column to the named table.
333
+ # See TableDefinition#column for details of the options you can use.
334
+ # ===== Example
335
+ # ====== Creating a simple column
336
+ # t.column(:name, :string)
337
+ def column(column_name, type, options = {})
338
+ @base.add_column(@table_name, column_name, type, options)
339
+ end
340
+
341
+ # Checks to see if a column exists. See SchemaStatements#column_exists?
342
+ def column_exists?(column_name, type = nil, options = {})
343
+ @base.column_exists?(@table_name, column_name, type, options)
344
+ end
345
+
346
+ # Adds a new index to the table. +column_name+ can be a single Symbol, or
347
+ # an Array of Symbols. See SchemaStatements#add_index
348
+ #
349
+ # ===== Examples
350
+ # ====== Creating a simple index
351
+ # t.index(:name)
352
+ # ====== Creating a unique index
353
+ # t.index([:branch_id, :party_id], :unique => true)
354
+ # ====== Creating a named index
355
+ # t.index([:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
356
+ def index(column_name, options = {})
357
+ @base.add_index(@table_name, column_name, options)
358
+ end
359
+
360
+ # Checks to see if an index exists. See SchemaStatements#index_exists?
361
+ def index_exists?(column_name, options = {})
362
+ @base.index_exists?(@table_name, column_name, options)
363
+ end
364
+
365
+ # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
366
+ # ===== Example
367
+ # t.timestamps
368
+ def timestamps
369
+ @base.add_timestamps(@table_name)
370
+ end
371
+
372
+ # Changes the column's definition according to the new options.
373
+ # See TableDefinition#column for details of the options you can use.
374
+ # ===== Examples
375
+ # t.change(:name, :string, :limit => 80)
376
+ # t.change(:description, :text)
377
+ def change(column_name, type, options = {})
378
+ @base.change_column(@table_name, column_name, type, options)
379
+ end
380
+
381
+ # Sets a new default value for a column. See SchemaStatements#change_column_default
382
+ # ===== Examples
383
+ # t.change_default(:qualification, 'new')
384
+ # t.change_default(:authorized, 1)
385
+ def change_default(column_name, default)
386
+ @base.change_column_default(@table_name, column_name, default)
387
+ end
388
+
389
+ # Removes the column(s) from the table definition.
390
+ # ===== Examples
391
+ # t.remove(:qualification)
392
+ # t.remove(:qualification, :experience)
393
+ def remove(*column_names)
394
+ @base.remove_column(@table_name, column_names)
395
+ end
396
+
397
+ # Removes the given index from the table.
398
+ #
399
+ # ===== Examples
400
+ # ====== Remove the index_table_name_on_column in the table_name table
401
+ # t.remove_index :column
402
+ # ====== Remove the index named index_table_name_on_branch_id in the table_name table
403
+ # t.remove_index :column => :branch_id
404
+ # ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
405
+ # t.remove_index :column => [:branch_id, :party_id]
406
+ # ====== Remove the index named by_branch_party in the table_name table
407
+ # t.remove_index :name => :by_branch_party
408
+ def remove_index(options = {})
409
+ @base.remove_index(@table_name, options)
410
+ end
411
+
412
+ # Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
413
+ # ===== Example
414
+ # t.remove_timestamps
415
+ def remove_timestamps
416
+ @base.remove_timestamps(@table_name)
417
+ end
418
+
419
+ # Renames a column.
420
+ # ===== Example
421
+ # t.rename(:description, :name)
422
+ def rename(column_name, new_column_name)
423
+ @base.rename_column(@table_name, column_name, new_column_name)
424
+ end
425
+
426
+ # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
427
+ # <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
428
+ # ===== Examples
429
+ # t.references(:goat)
430
+ # t.references(:goat, :polymorphic => true)
431
+ # t.belongs_to(:goat)
432
+ def references(*args)
433
+ options = args.extract_options!
434
+ polymorphic = options.delete(:polymorphic)
435
+ args.each do |col|
436
+ @base.add_column(@table_name, "#{col}_id", :integer, options)
437
+ @base.add_column(@table_name, "#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) unless polymorphic.nil?
438
+ end
439
+ end
440
+ alias :belongs_to :references
441
+
442
+ # Removes a reference. Optionally removes a +type+ column.
443
+ # <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
444
+ # ===== Examples
445
+ # t.remove_references(:goat)
446
+ # t.remove_references(:goat, :polymorphic => true)
447
+ # t.remove_belongs_to(:goat)
448
+ def remove_references(*args)
449
+ options = args.extract_options!
450
+ polymorphic = options.delete(:polymorphic)
451
+ args.each do |col|
452
+ @base.remove_column(@table_name, "#{col}_id")
453
+ @base.remove_column(@table_name, "#{col}_type") unless polymorphic.nil?
454
+ end
455
+ end
456
+ alias :remove_belongs_to :remove_references
457
+
458
+ # Adds a column or columns of a specified type
459
+ # ===== Examples
460
+ # t.string(:goat)
461
+ # t.string(:goat, :sheep)
462
+ %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type|
463
+ class_eval <<-EOV, __FILE__, __LINE__ + 1
464
+ def #{column_type}(*args) # def string(*args)
465
+ options = args.extract_options! # options = args.extract_options!
466
+ column_names = args # column_names = args
467
+ type = :'#{column_type}' # type = :string
468
+ column_names.each do |name| # column_names.each do |name|
469
+ column = ColumnDefinition.new(@base, name.to_s, type) # column = ColumnDefinition.new(@base, name, type)
470
+ if options[:limit] # if options[:limit]
471
+ column.limit = options[:limit] # column.limit = options[:limit]
472
+ elsif native[type].is_a?(Hash) # elsif native[type].is_a?(Hash)
473
+ column.limit = native[type][:limit] # column.limit = native[type][:limit]
474
+ end # end
475
+ column.precision = options[:precision] # column.precision = options[:precision]
476
+ column.scale = options[:scale] # column.scale = options[:scale]
477
+ column.default = options[:default] # column.default = options[:default]
478
+ column.null = options[:null] # column.null = options[:null]
479
+ @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options)
480
+ end # end
481
+ end # end
482
+ EOV
483
+ end
484
+
485
+ private
486
+ def native
487
+ @base.native_database_types
488
+ end
489
+ end
490
+
491
+ end
492
+ end