activerecord 1.15.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -84,10 +84,11 @@ module ActiveRecord
84
84
  #
85
85
  # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
86
86
  # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
87
- # differently than the class you're interested in observing, you can use the Observer.observe class method:
87
+ # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
88
+ # either the concrete class (Product) or a symbol for that class (:product):
88
89
  #
89
90
  # class AuditObserver < ActiveRecord::Observer
90
- # observe Account
91
+ # observe :account
91
92
  #
92
93
  # def after_update(account)
93
94
  # AuditTrail.new(account, "UPDATED")
@@ -97,7 +98,7 @@ module ActiveRecord
97
98
  # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
98
99
  #
99
100
  # class AuditObserver < ActiveRecord::Observer
100
- # observe Account, Balance
101
+ # observe :account, :balance
101
102
  #
102
103
  # def after_update(record)
103
104
  # AuditTrail.new(record, "UPDATED")
@@ -127,20 +128,22 @@ module ActiveRecord
127
128
  class Observer
128
129
  include Singleton
129
130
 
130
- # Observer subclasses should be reloaded by the dispatcher in Rails
131
- # when Dependencies.mechanism = :load.
132
- include Reloadable::Deprecated
133
-
134
131
  class << self
135
132
  # Attaches the observer to the supplied model classes.
136
133
  def observe(*models)
134
+ models.flatten!
135
+ models.collect! { |model| model.is_a?(Symbol) ? model.to_s.camelize.constantize : model }
137
136
  define_method(:observed_classes) { Set.new(models) }
138
137
  end
139
138
 
140
139
  # The class observed by default is inferred from the observer's class name:
141
140
  # assert_equal [Person], PersonObserver.observed_class
142
141
  def observed_class
143
- name.scan(/(.*)Observer/)[0][0].constantize
142
+ if observed_class_name = name.scan(/(.*)Observer/)[0]
143
+ observed_class_name[0].constantize
144
+ else
145
+ nil
146
+ end
144
147
  end
145
148
  end
146
149
 
@@ -163,11 +166,11 @@ module ActiveRecord
163
166
 
164
167
  protected
165
168
  def observed_classes
166
- Set.new([self.class.observed_class].flatten)
169
+ Set.new([self.class.observed_class].compact.flatten)
167
170
  end
168
171
 
169
172
  def observed_subclasses
170
- observed_classes.sum(&:subclasses)
173
+ observed_classes.collect(&:subclasses).flatten
171
174
  end
172
175
 
173
176
  def add_observer!(klass)
@@ -1,64 +1,23 @@
1
1
  module ActiveRecord
2
- class QueryCache #:nodoc:
3
- def initialize(connection)
4
- @connection = connection
5
- @query_cache = {}
6
- end
7
-
8
- def clear_query_cache
9
- @query_cache = {}
10
- end
11
-
12
- def select_all(sql, name = nil)
13
- (@query_cache[sql] ||= @connection.select_all(sql, name)).dup
14
- end
15
-
16
- def select_one(sql, name = nil)
17
- @query_cache[sql] ||= @connection.select_one(sql, name)
18
- end
19
-
20
- def columns(table_name, name = nil)
21
- @query_cache["SHOW FIELDS FROM #{table_name}"] ||= @connection.columns(table_name, name)
22
- end
23
-
24
- def insert(sql, name = nil, pk = nil, id_value = nil)
25
- clear_query_cache
26
- @connection.insert(sql, name, pk, id_value)
27
- end
28
-
29
- def update(sql, name = nil)
30
- clear_query_cache
31
- @connection.update(sql, name)
32
- end
33
-
34
- def delete(sql, name = nil)
35
- clear_query_cache
36
- @connection.delete(sql, name)
37
- end
38
-
39
- private
40
- def method_missing(method, *arguments, &proc)
41
- @connection.send(method, *arguments, &proc)
2
+ module QueryCache
3
+ # Enable the query cache within the block if Active Record is configured.
4
+ def cache(&block)
5
+ if ActiveRecord::Base.configurations.blank?
6
+ yield
7
+ else
8
+ connection.cache(&block)
42
9
  end
43
- end
44
-
45
- class Base
46
- # Set the connection for the class with caching on
47
- class << self
48
- alias_method :connection_without_query_cache=, :connection=
10
+ rescue
11
+ yield # if the database is not present, don't let the cache spoil the party
12
+ end
49
13
 
50
- def connection=(spec)
51
- if spec.is_a?(ConnectionSpecification) and spec.config[:query_cache]
52
- spec = QueryCache.new(self.send(spec.adapter_method, spec.config))
53
- end
54
- self.connection_without_query_cache = spec
14
+ # Disable the query cache within the block if Active Record is configured.
15
+ def uncached(&block)
16
+ if ActiveRecord::Base.configurations.blank?
17
+ yield
18
+ else
19
+ connection.uncached(&block)
55
20
  end
56
21
  end
57
22
  end
58
-
59
- class AbstractAdapter #:nodoc:
60
- # Stub method to be able to treat the connection the same whether the query cache has been turned on or not
61
- def clear_query_cache
62
- end
63
- end
64
23
  end
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
  reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
45
45
  end
46
46
 
47
- # Returns an array of AssociationReflection objects for all the aggregations in the class. If you only want to reflect on a
47
+ # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a
48
48
  # certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
49
49
  # Example:
50
50
  #
@@ -56,7 +56,7 @@ module ActiveRecord
56
56
  macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
57
57
  end
58
58
 
59
- # Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
59
+ # Returns the AssociationReflection object for the named +association+ (use the symbol). Example:
60
60
  #
61
61
  # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
62
62
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
@@ -71,6 +71,7 @@ module ActiveRecord
71
71
  # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
72
72
  class MacroReflection
73
73
  attr_reader :active_record
74
+
74
75
  def initialize(macro, name, options, active_record)
75
76
  @macro, @name, @options, @active_record = macro, name, options, active_record
76
77
  end
@@ -81,7 +82,7 @@ module ActiveRecord
81
82
  @name
82
83
  end
83
84
 
84
- # Returns the name of the macro, so it would return :composed_of for
85
+ # Returns the type of the macro, so it would return :composed_of for
85
86
  # "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
86
87
  def macro
87
88
  @macro
@@ -93,30 +94,29 @@ module ActiveRecord
93
94
  @options
94
95
  end
95
96
 
96
- # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" would return the Money class and
97
- # "has_many :clients" would return the Client class.
98
- def klass() end
99
-
97
+ # Returns the class for the macro, so "composed_of :balance, :class_name => 'Money'" returns the Money class and
98
+ # "has_many :clients" returns the Client class.
99
+ def klass
100
+ @klass ||= class_name.constantize
101
+ end
102
+
100
103
  def class_name
101
- @class_name ||= name_to_class_name(name.id2name)
104
+ @class_name ||= options[:class_name] || derive_class_name
102
105
  end
103
106
 
104
107
  def ==(other_aggregation)
105
108
  name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
106
109
  end
110
+
111
+ private
112
+ def derive_class_name
113
+ name.to_s.camelize
114
+ end
107
115
  end
108
116
 
109
117
 
110
118
  # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
111
119
  class AggregateReflection < MacroReflection #:nodoc:
112
- def klass
113
- @klass ||= Object.const_get(options[:class_name] || class_name)
114
- end
115
-
116
- private
117
- def name_to_class_name(name)
118
- name.capitalize.gsub(/_(.)/) { |s| $1.capitalize }
119
- end
120
120
  end
121
121
 
122
122
  # Holds all the meta-data about an association as it was specified in the Active Record class.
@@ -130,17 +130,9 @@ module ActiveRecord
130
130
  end
131
131
 
132
132
  def primary_key_name
133
- return @primary_key_name if @primary_key_name
134
- case
135
- when macro == :belongs_to
136
- @primary_key_name = options[:foreign_key] || class_name.foreign_key
137
- when options[:as]
138
- @primary_key_name = options[:foreign_key] || "#{options[:as]}_id"
139
- else
140
- @primary_key_name = options[:foreign_key] || active_record.name.foreign_key
141
- end
133
+ @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
142
134
  end
143
-
135
+
144
136
  def association_foreign_key
145
137
  @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
146
138
  end
@@ -202,19 +194,24 @@ module ActiveRecord
202
194
  end
203
195
 
204
196
  private
205
- def name_to_class_name(name)
206
- if name =~ /::/
207
- name
197
+ def derive_class_name
198
+ # get the class_name of the belongs_to association of the through reflection
199
+ if through_reflection
200
+ options[:source_type] || source_reflection.class_name
201
+ else
202
+ class_name = name.to_s.camelize
203
+ class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
204
+ class_name
205
+ end
206
+ end
207
+
208
+ def derive_primary_key_name
209
+ if macro == :belongs_to
210
+ "#{name}_id"
211
+ elsif options[:as]
212
+ "#{options[:as]}_id"
208
213
  else
209
- if options[:class_name]
210
- options[:class_name]
211
- elsif through_reflection # get the class_name of the belongs_to association of the through reflection
212
- options[:source_type] || source_reflection.class_name
213
- else
214
- class_name = name.to_s.camelize
215
- class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro)
216
- class_name
217
- end
214
+ active_record.name.foreign_key
218
215
  end
219
216
  end
220
217
  end
@@ -8,16 +8,16 @@ module ActiveRecord
8
8
  #
9
9
  # ActiveRecord::Schema.define do
10
10
  # create_table :authors do |t|
11
- # t.column :name, :string, :null => false
11
+ # t.string :name, :null => false
12
12
  # end
13
13
  #
14
14
  # add_index :authors, :name, :unique
15
15
  #
16
16
  # create_table :posts do |t|
17
- # t.column :author_id, :integer, :null => false
18
- # t.column :subject, :string
19
- # t.column :body, :text
20
- # t.column :private, :boolean, :default => false
17
+ # t.integer :author_id, :null => false
18
+ # t.string :subject
19
+ # t.text :body
20
+ # t.boolean :private, :default => false
21
21
  # end
22
22
  #
23
23
  # add_index :posts, :author_id
@@ -37,9 +37,16 @@ module ActiveRecord
37
37
  define_params = @info ? ":version => #{@info['version']}" : ""
38
38
 
39
39
  stream.puts <<HEADER
40
- # This file is autogenerated. Instead of editing this file, please use the
41
- # migrations feature of ActiveRecord to incrementally modify your database, and
40
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
41
+ # please use the migrations feature of ActiveRecord to incrementally modify your database, and
42
42
  # then regenerate this schema definition.
43
+ #
44
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
45
+ # to create the application database on another system, you should be using db:schema:load, not running
46
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
47
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
48
+ #
49
+ # It's strongly recommended to check this file into your version control system.
43
50
 
44
51
  ActiveRecord::Schema.define(#{define_params}) do
45
52
 
@@ -54,8 +61,8 @@ HEADER
54
61
  @connection.tables.sort.each do |tbl|
55
62
  next if ["schema_info", ignore_tables].flatten.any? do |ignored|
56
63
  case ignored
57
- when String: tbl == ignored
58
- when Regexp: tbl =~ ignored
64
+ when String; tbl == ignored
65
+ when Regexp; tbl =~ ignored
59
66
  else
60
67
  raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
61
68
  end
@@ -89,22 +96,37 @@ HEADER
89
96
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
90
97
  next if column.name == pk
91
98
  spec = {}
92
- spec[:name] = column.name.inspect
93
- spec[:type] = column.type.inspect
94
- spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
99
+ spec[:name] = column.name.inspect
100
+ spec[:type] = column.type.to_s
101
+ spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && column.type != :decimal
95
102
  spec[:precision] = column.precision.inspect if !column.precision.nil?
96
- spec[:scale] = column.scale.inspect if !column.scale.nil?
97
- spec[:null] = 'false' if !column.null
98
- spec[:default] = default_string(column.default) if !column.default.nil?
103
+ spec[:scale] = column.scale.inspect if !column.scale.nil?
104
+ spec[:null] = 'false' if !column.null
105
+ spec[:default] = default_string(column.default) if !column.default.nil?
99
106
  (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
100
107
  spec
101
108
  end.compact
102
- keys = [:name, :type, :limit, :precision, :scale, :default, :null] & column_specs.map{ |spec| spec.keys }.inject([]){ |a,b| a | b }
109
+
110
+ # find all migration keys used in this table
111
+ keys = [:name, :limit, :precision, :scale, :default, :null] & column_specs.map(&:keys).flatten
112
+
113
+ # figure out the lengths for each column based on above keys
103
114
  lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
104
- format_string = lengths.map{ |len| "%-#{len}s" }.join("")
115
+
116
+ # the string we're going to sprintf our values against, with standardized column widths
117
+ format_string = lengths.map{ |len| "%-#{len}s" }
118
+
119
+ # find the max length for the 'type' column, which is special
120
+ type_length = column_specs.map{ |column| column[:type].length }.max
121
+
122
+ # add column type definition to our format string
123
+ format_string.unshift " t.%-#{type_length}s "
124
+
125
+ format_string *= ''
126
+
105
127
  column_specs.each do |colspec|
106
128
  values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
107
- tbl.print " t.column "
129
+ values.unshift colspec[:type]
108
130
  tbl.print((format_string % values).gsub(/,\s*$/, ''))
109
131
  tbl.puts
110
132
  end
@@ -0,0 +1,98 @@
1
+ module ActiveRecord #:nodoc:
2
+ module Serialization
3
+ class Serializer #:nodoc:
4
+ attr_reader :options
5
+
6
+ def initialize(record, options = {})
7
+ @record, @options = record, options.dup
8
+ end
9
+
10
+ # To replicate the behavior in ActiveRecord#attributes,
11
+ # :except takes precedence over :only. If :only is not set
12
+ # for a N level model but is set for the N+1 level models,
13
+ # then because :except is set to a default value, the second
14
+ # level model can have both :except and :only set. So if
15
+ # :only is set, always delete :except.
16
+ def serializable_attribute_names
17
+ attribute_names = @record.attribute_names
18
+
19
+ if options[:only]
20
+ options.delete(:except)
21
+ attribute_names = attribute_names & Array(options[:only]).collect { |n| n.to_s }
22
+ else
23
+ options[:except] = Array(options[:except]) | Array(@record.class.inheritance_column)
24
+ attribute_names = attribute_names - options[:except].collect { |n| n.to_s }
25
+ end
26
+
27
+ attribute_names
28
+ end
29
+
30
+ def serializable_method_names
31
+ Array(options[:methods]).inject([]) do |method_attributes, name|
32
+ method_attributes << name if @record.respond_to?(name.to_s)
33
+ method_attributes
34
+ end
35
+ end
36
+
37
+ def serializable_names
38
+ serializable_attribute_names + serializable_method_names
39
+ end
40
+
41
+ # Add associations specified via the :includes option.
42
+ # Expects a block that takes as arguments:
43
+ # +association+ - name of the association
44
+ # +records+ - the association record(s) to be serialized
45
+ # +opts+ - options for the association records
46
+ def add_includes(&block)
47
+ if include_associations = options.delete(:include)
48
+ base_only_or_except = { :except => options[:except],
49
+ :only => options[:only] }
50
+
51
+ include_has_options = include_associations.is_a?(Hash)
52
+ associations = include_has_options ? include_associations.keys : Array(include_associations)
53
+
54
+ for association in associations
55
+ records = case @record.class.reflect_on_association(association).macro
56
+ when :has_many, :has_and_belongs_to_many
57
+ @record.send(association).to_a
58
+ when :has_one, :belongs_to
59
+ @record.send(association)
60
+ end
61
+
62
+ unless records.nil?
63
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
64
+ opts = options.merge(association_options)
65
+ yield(association, records, opts)
66
+ end
67
+ end
68
+
69
+ options[:include] = include_associations
70
+ end
71
+ end
72
+
73
+ def serializable_record
74
+ returning(serializable_record = {}) do
75
+ serializable_names.each { |name| serializable_record[name] = @record.send(name) }
76
+ add_includes do |association, records, opts|
77
+ if records.is_a?(Enumerable)
78
+ serializable_record[association] = records.collect { |r| self.class.new(r, opts).serializable_record }
79
+ else
80
+ serializable_record[association] = self.class.new(records, opts).serializable_record
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def serialize
87
+ # overwrite to implement
88
+ end
89
+
90
+ def to_s(&block)
91
+ serialize(&block)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ require 'active_record/serializers/xml_serializer'
98
+ require 'active_record/serializers/json_serializer'