activerecord 2.0.5 → 2.1.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 (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # # Same as above, just using explicit class references
20
20
  # ActiveRecord::Base.observers = Cacher, GarbageCollector
21
21
  #
22
- # Note: Setting this does not instantiate the observers yet. #instantiate_observers is
22
+ # Note: Setting this does not instantiate the observers yet. +instantiate_observers+ is
23
23
  # called during startup, and before each development request.
24
24
  def observers=(*observers)
25
25
  @observers = observers.flatten
@@ -30,7 +30,7 @@ module ActiveRecord
30
30
  @observers ||= []
31
31
  end
32
32
 
33
- # Instantiate the global ActiveRecord observers
33
+ # Instantiate the global Active Record observers.
34
34
  def instantiate_observers
35
35
  return if @observers.blank?
36
36
  @observers.each do |observer|
@@ -125,6 +125,20 @@ module ActiveRecord
125
125
  #
126
126
  # Observers will not be invoked unless you define these in your application configuration.
127
127
  #
128
+ # == Loading
129
+ #
130
+ # Observers register themselves in the model class they observe, since it is the class that
131
+ # notifies them of events when they occur. As a side-effect, when an observer is loaded its
132
+ # corresponding model class is loaded.
133
+ #
134
+ # Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
135
+ # application initializers. Now observers are loaded after application initializers,
136
+ # so observed models can make use of extensions.
137
+ #
138
+ # If by any chance you are using observed models in the initialization you can still
139
+ # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
140
+ # singletons and that call instantiates and registers them.
141
+ #
128
142
  class Observer
129
143
  include Singleton
130
144
 
@@ -137,10 +151,10 @@ module ActiveRecord
137
151
  end
138
152
 
139
153
  # The class observed by default is inferred from the observer's class name:
140
- # assert_equal [Person], PersonObserver.observed_class
154
+ # assert_equal Person, PersonObserver.observed_class
141
155
  def observed_class
142
- if observed_class_name = name.scan(/(.*)Observer/)[0]
143
- observed_class_name[0].constantize
156
+ if observed_class_name = name[/(.*)Observer/, 1]
157
+ observed_class_name.constantize
144
158
  else
145
159
  nil
146
160
  end
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  end
46
46
 
47
47
  # Returns an array of AssociationReflection objects for all the associations in the class. If you only want to reflect on a
48
- # certain association type, pass in the symbol (:has_many, :has_one, :belongs_to) for that as the first parameter.
48
+ # certain association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>, <tt>:belongs_to</tt>) for that as the first parameter.
49
49
  # Example:
50
50
  #
51
51
  # Account.reflect_on_all_associations # returns an array of all associations
@@ -76,34 +76,38 @@ module ActiveRecord
76
76
  @macro, @name, @options, @active_record = macro, name, options, active_record
77
77
  end
78
78
 
79
- # Returns the name of the macro, so it would return :balance for "composed_of :balance, :class_name => 'Money'" or
80
- # :clients for "has_many :clients".
79
+ # Returns the name of the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return
80
+ # <tt>:balance</tt> or for <tt>has_many :clients</tt> it will return <tt>:clients</tt>.
81
81
  def name
82
82
  @name
83
83
  end
84
84
 
85
- # Returns the type of the macro, so it would return :composed_of for
86
- # "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
85
+ # Returns the macro type. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> will return <tt>:composed_of</tt>
86
+ # or for <tt>has_many :clients</tt> will return <tt>:has_many</tt>.
87
87
  def macro
88
88
  @macro
89
89
  end
90
90
 
91
- # Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for
92
- # "composed_of :balance, :class_name => 'Money'" or {} for "has_many :clients".
91
+ # Returns the hash of options used for the macro. For example, it would return <tt>{ :class_name => "Money" }</tt> for
92
+ # <tt>composed_of :balance, :class_name => 'Money'</tt> or +{}+ for <tt>has_many :clients</tt>.
93
93
  def options
94
94
  @options
95
95
  end
96
96
 
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.
97
+ # Returns the class for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money
98
+ # class and <tt>has_many :clients</tt> returns the Client class.
99
99
  def klass
100
100
  @klass ||= class_name.constantize
101
101
  end
102
102
 
103
+ # Returns the class name for the macro. For example, <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>'Money'</tt>
104
+ # and <tt>has_many :clients</tt> returns <tt>'Client'</tt>.
103
105
  def class_name
104
106
  @class_name ||= options[:class_name] || derive_class_name
105
107
  end
106
108
 
109
+ # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
110
+ # and +other_aggregation+ has an options hash assigned to it.
107
111
  def ==(other_aggregation)
108
112
  name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
109
113
  end
@@ -129,6 +133,10 @@ module ActiveRecord
129
133
  @table_name ||= klass.table_name
130
134
  end
131
135
 
136
+ def quoted_table_name
137
+ @quoted_table_name ||= klass.quoted_table_name
138
+ end
139
+
132
140
  def primary_key_name
133
141
  @primary_key_name ||= options[:foreign_key] || derive_primary_key_name
134
142
  end
@@ -145,22 +153,34 @@ module ActiveRecord
145
153
  end
146
154
  end
147
155
 
156
+ # Returns the AssociationReflection object specified in the <tt>:through</tt> option
157
+ # of a HasManyThrough or HasOneThrough association. Example:
158
+ #
159
+ # class Post < ActiveRecord::Base
160
+ # has_many :taggings
161
+ # has_many :tags, :through => :taggings
162
+ # end
163
+ #
164
+ # tags_reflection = Post.reflect_on_association(:tags)
165
+ # taggings_reflection = tags_reflection.through_reflection
166
+ #
148
167
  def through_reflection
149
168
  @through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
150
169
  end
151
170
 
152
- # Gets an array of possible :through source reflection names
171
+ # Gets an array of possible <tt>:through</tt> source reflection names:
153
172
  #
154
- # [singularized, pluralized]
173
+ # [:singularized, :pluralized]
155
174
  #
156
175
  def source_reflection_names
157
176
  @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
158
177
  end
159
178
 
160
- # Gets the source of the through reflection. It checks both a singularized and pluralized form for :belongs_to or :has_many.
161
- # (The :tags association on Tagging below)
179
+ # Gets the source of the through reflection. It checks both a singularized and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
180
+ # (The <tt>:tags</tt> association on Tagging below.)
162
181
  #
163
- # class Post
182
+ # class Post < ActiveRecord::Base
183
+ # has_many :taggings
164
184
  # has_many :tags, :through => :taggings
165
185
  # end
166
186
  #
@@ -30,28 +30,21 @@ module ActiveRecord
30
30
 
31
31
  # Eval the given block. All methods available to the current connection
32
32
  # adapter are available within the block, so you can easily use the
33
- # database definition DSL to build up your schema (#create_table,
34
- # #add_index, etc.).
33
+ # database definition DSL to build up your schema (+create_table+,
34
+ # +add_index+, etc.).
35
35
  #
36
36
  # The +info+ hash is optional, and if given is used to define metadata
37
- # about the current schema (like the schema's version):
37
+ # about the current schema (currently, only the schema's version):
38
38
  #
39
- # ActiveRecord::Schema.define(:version => 15) do
39
+ # ActiveRecord::Schema.define(:version => 20380119000001) do
40
40
  # ...
41
41
  # end
42
42
  def self.define(info={}, &block)
43
43
  instance_eval(&block)
44
44
 
45
- unless info.empty?
46
- initialize_schema_information
47
- cols = columns('schema_info')
48
-
49
- info = info.map do |k,v|
50
- v = Base.connection.quote(v, cols.detect { |c| c.name == k.to_s })
51
- "#{k} = #{v}"
52
- end
53
-
54
- Base.connection.update "UPDATE #{Migrator.schema_info_table_name} SET #{info.join(", ")}"
45
+ unless info[:version].blank?
46
+ initialize_schema_migrations_table
47
+ assume_migrated_upto_version info[:version]
55
48
  end
56
49
  end
57
50
  end
@@ -30,15 +30,15 @@ module ActiveRecord
30
30
  def initialize(connection)
31
31
  @connection = connection
32
32
  @types = @connection.native_database_types
33
- @info = @connection.select_one("SELECT * FROM schema_info") rescue nil
33
+ @version = Migrator::current_version rescue nil
34
34
  end
35
35
 
36
36
  def header(stream)
37
- define_params = @info ? ":version => #{@info['version']}" : ""
37
+ define_params = @version ? ":version => #{@version}" : ""
38
38
 
39
39
  stream.puts <<HEADER
40
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
41
+ # please use the migrations feature of Active Record to incrementally modify your database, and
42
42
  # then regenerate this schema definition.
43
43
  #
44
44
  # Note that this schema.rb definition is the authoritative source for your database schema. If you need
@@ -59,7 +59,7 @@ HEADER
59
59
 
60
60
  def tables(stream)
61
61
  @connection.tables.sort.each do |tbl|
62
- next if ["schema_info", ignore_tables].flatten.any? do |ignored|
62
+ next if ['schema_migrations', ignore_tables].flatten.any? do |ignored|
63
63
  case ignored
64
64
  when String; tbl == ignored
65
65
  when Regexp; tbl =~ ignored
@@ -8,11 +8,11 @@ module ActiveRecord #:nodoc:
8
8
  end
9
9
 
10
10
  # To replicate the behavior in ActiveRecord#attributes,
11
- # :except takes precedence over :only. If :only is not set
11
+ # <tt>:except</tt> takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
12
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.
13
+ # then because <tt>:except</tt> is set to a default value, the second
14
+ # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
15
+ # <tt>:only</tt> is set, always delete <tt>:except</tt>.
16
16
  def serializable_attribute_names
17
17
  attribute_names = @record.attribute_names
18
18
 
@@ -38,7 +38,7 @@ module ActiveRecord #:nodoc:
38
38
  serializable_attribute_names + serializable_method_names
39
39
  end
40
40
 
41
- # Add associations specified via the :includes option.
41
+ # Add associations specified via the <tt>:includes</tt> option.
42
42
  # Expects a block that takes as arguments:
43
43
  # +association+ - name of the association
44
44
  # +records+ - the association record(s) to be serialized
@@ -1,5 +1,10 @@
1
1
  module ActiveRecord #:nodoc:
2
2
  module Serialization
3
+ def self.included(base)
4
+ base.cattr_accessor :include_root_in_json, :instance_writer => false
5
+ base.extend ClassMethods
6
+ end
7
+
3
8
  # Returns a JSON string representing the model. Some configuration is
4
9
  # available through +options+.
5
10
  #
@@ -8,37 +13,32 @@ module ActiveRecord #:nodoc:
8
13
  #
9
14
  # konata = User.find(1)
10
15
  # konata.to_json
16
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
17
+ # "created_at": "2006/08/01", "awesome": true}
11
18
  #
12
- # {"id": 1, "name": "Konata Izumi", "age": 16,
13
- # "created_at": "2006/08/01", "awesome": true}
14
- #
15
- # The :only and :except options can be used to limit the attributes
16
- # included, and work similar to the #attributes method. For example:
19
+ # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the attributes
20
+ # included, and work similar to the +attributes+ method. For example:
17
21
  #
18
22
  # konata.to_json(:only => [ :id, :name ])
19
- #
20
- # {"id": 1, "name": "Konata Izumi"}
23
+ # # => {"id": 1, "name": "Konata Izumi"}
21
24
  #
22
25
  # konata.to_json(:except => [ :id, :created_at, :age ])
26
+ # # => {"name": "Konata Izumi", "awesome": true}
23
27
  #
24
- # {"name": "Konata Izumi", "awesome": true}
25
- #
26
- # To include any methods on the model, use :methods.
28
+ # To include any methods on the model, use <tt>:methods</tt>.
27
29
  #
28
30
  # konata.to_json(:methods => :permalink)
31
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
32
+ # "created_at": "2006/08/01", "awesome": true,
33
+ # "permalink": "1-konata-izumi"}
29
34
  #
30
- # {"id": 1, "name": "Konata Izumi", "age": 16,
31
- # "created_at": "2006/08/01", "awesome": true,
32
- # "permalink": "1-konata-izumi"}
33
- #
34
- # To include associations, use :include.
35
+ # To include associations, use <tt>:include</tt>.
35
36
  #
36
37
  # konata.to_json(:include => :posts)
37
- #
38
- # {"id": 1, "name": "Konata Izumi", "age": 16,
39
- # "created_at": "2006/08/01", "awesome": true,
40
- # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
41
- # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
38
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
39
+ # "created_at": "2006/08/01", "awesome": true,
40
+ # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"},
41
+ # {"id": 2, author_id: 1, "title": "So I was thinking"}]}
42
42
  #
43
43
  # 2nd level and higher order associations work as well:
44
44
  #
@@ -46,15 +46,18 @@ module ActiveRecord #:nodoc:
46
46
  # :include => { :comments => {
47
47
  # :only => :body } },
48
48
  # :only => :title } })
49
- #
50
- # {"id": 1, "name": "Konata Izumi", "age": 16,
51
- # "created_at": "2006/08/01", "awesome": true,
52
- # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
53
- # "title": "Welcome to the weblog"},
54
- # {"comments": [{"body": "Don't think too hard"}],
55
- # "title": "So I was thinking"}]}
49
+ # # => {"id": 1, "name": "Konata Izumi", "age": 16,
50
+ # "created_at": "2006/08/01", "awesome": true,
51
+ # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}],
52
+ # "title": "Welcome to the weblog"},
53
+ # {"comments": [{"body": "Don't think too hard"}],
54
+ # "title": "So I was thinking"}]}
56
55
  def to_json(options = {})
57
- JsonSerializer.new(self, options).to_s
56
+ if include_root_in_json
57
+ "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
58
+ else
59
+ JsonSerializer.new(self, options).to_s
60
+ end
58
61
  end
59
62
 
60
63
  def from_json(json)
@@ -67,5 +70,11 @@ module ActiveRecord #:nodoc:
67
70
  serializable_record.to_json
68
71
  end
69
72
  end
73
+
74
+ module ClassMethods
75
+ def json_class_name
76
+ @json_class_name ||= name.demodulize.underscore.inspect
77
+ end
78
+ end
70
79
  end
71
80
  end
@@ -1,11 +1,11 @@
1
1
  module ActiveRecord #:nodoc:
2
2
  module Serialization
3
- # Builds an XML document to represent the model. Some configuration is
4
- # available through +options+, however more complicated cases should
5
- # override ActiveRecord's to_xml.
3
+ # Builds an XML document to represent the model. Some configuration is
4
+ # available through +options+. However more complicated cases should
5
+ # override ActiveRecord::Base#to_xml.
6
6
  #
7
7
  # By default the generated XML document will include the processing
8
- # instruction and all object's attributes. For example:
8
+ # instruction and all the object's attributes. For example:
9
9
  #
10
10
  # <?xml version="1.0" encoding="UTF-8"?>
11
11
  # <topic>
@@ -22,12 +22,12 @@ module ActiveRecord #:nodoc:
22
22
  # <last-read type="date">2004-04-15</last-read>
23
23
  # </topic>
24
24
  #
25
- # This behavior can be controlled with :only, :except,
26
- # :skip_instruct, :skip_types and :dasherize. The :only and
27
- # :except options are the same as for the #attributes method.
28
- # The default is to dasherize all column names, to disable this,
29
- # set :dasherize to false. To not have the column type included
30
- # in the XML output, set :skip_types to true.
25
+ # This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
26
+ # <tt>:skip_instruct</tt>, <tt>:skip_types</tt> and <tt>:dasherize</tt>.
27
+ # The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
28
+ # +attributes+ method. The default is to dasherize all column names, but you
29
+ # can disable this setting <tt>:dasherize</tt> to +false+. To not have the
30
+ # column type included in the XML output set <tt>:skip_types</tt> to +true+.
31
31
  #
32
32
  # For instance:
33
33
  #
@@ -43,7 +43,7 @@ module ActiveRecord #:nodoc:
43
43
  # <last-read type="date">2004-04-15</last-read>
44
44
  # </topic>
45
45
  #
46
- # To include first level associations use :include
46
+ # To include first level associations use <tt>:include</tt>:
47
47
  #
48
48
  # firm.to_xml :include => [ :account, :clients ]
49
49
  #
@@ -68,7 +68,37 @@ module ActiveRecord #:nodoc:
68
68
  # </account>
69
69
  # </firm>
70
70
  #
71
- # To include any methods on the object(s) being called use :methods
71
+ # To include deeper levels of associations pass a hash like this:
72
+ #
73
+ # firm.to_xml :include => {:account => {}, :clients => {:include => :address}}
74
+ # <?xml version="1.0" encoding="UTF-8"?>
75
+ # <firm>
76
+ # <id type="integer">1</id>
77
+ # <rating type="integer">1</rating>
78
+ # <name>37signals</name>
79
+ # <clients type="array">
80
+ # <client>
81
+ # <rating type="integer">1</rating>
82
+ # <name>Summit</name>
83
+ # <address>
84
+ # ...
85
+ # </address>
86
+ # </client>
87
+ # <client>
88
+ # <rating type="integer">1</rating>
89
+ # <name>Microsoft</name>
90
+ # <address>
91
+ # ...
92
+ # </address>
93
+ # </client>
94
+ # </clients>
95
+ # <account>
96
+ # <id type="integer">1</id>
97
+ # <credit-limit type="integer">50</credit-limit>
98
+ # </account>
99
+ # </firm>
100
+ #
101
+ # To include any methods on the model being called use <tt>:methods</tt>:
72
102
  #
73
103
  # firm.to_xml :methods => [ :calculated_earnings, :real_earnings ]
74
104
  #
@@ -78,9 +108,8 @@ module ActiveRecord #:nodoc:
78
108
  # <real-earnings>5</real-earnings>
79
109
  # </firm>
80
110
  #
81
- # To call any Proc's on the object(s) use :procs. The Proc's
82
- # are passed a modified version of the options hash that was
83
- # given to #to_xml.
111
+ # To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
112
+ # modified version of the options hash that was given to +to_xml+:
84
113
  #
85
114
  # proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
86
115
  # firm.to_xml :procs => [ proc ]
@@ -90,7 +119,7 @@ module ActiveRecord #:nodoc:
90
119
  # <abc>def</abc>
91
120
  # </firm>
92
121
  #
93
- # Alternatively, you can also just yield the builder object as part of the to_xml call:
122
+ # Alternatively, you can yield the builder object as part of the +to_xml+ call:
94
123
  #
95
124
  # firm.to_xml do |xml|
96
125
  # xml.creator do
@@ -107,8 +136,9 @@ module ActiveRecord #:nodoc:
107
136
  # </creator>
108
137
  # </firm>
109
138
  #
110
- # You can override the to_xml method in your ActiveRecord::Base
111
- # subclasses if you need to. The general form of doing this is
139
+ # As noted above, you may override +to_xml+ in your ActiveRecord::Base
140
+ # subclasses to have complete control about what's generated. The general
141
+ # form of doing this is:
112
142
  #
113
143
  # class IHaveMyOwnXML < ActiveRecord::Base
114
144
  # def to_xml(options = {})
@@ -155,13 +185,6 @@ module ActiveRecord #:nodoc:
155
185
  !options.has_key?(:dasherize) || options[:dasherize]
156
186
  end
157
187
 
158
-
159
- # To replicate the behavior in ActiveRecord#attributes,
160
- # :except takes precedence over :only. If :only is not set
161
- # for a N level model but is set for the N+1 level models,
162
- # then because :except is set to a default value, the second
163
- # level model can have both :except and :only set. So if
164
- # :only is set, always delete :except.
165
188
  def serializable_attributes
166
189
  serializable_attribute_names.collect { |name| Attribute.new(name, @record) }
167
190
  end
@@ -250,14 +273,14 @@ module ActiveRecord #:nodoc:
250
273
  end
251
274
 
252
275
  # There is a significant speed improvement if the value
253
- # does not need to be escaped, as #tag! escapes all values
254
- # to ensure that valid XML is generated. For known binary
276
+ # does not need to be escaped, as <tt>tag!</tt> escapes all values
277
+ # to ensure that valid XML is generated. For known binary
255
278
  # values, it is at least an order of magnitude faster to
256
279
  # Base64 encode binary values and directly put them in the
257
280
  # output XML than to pass the original value or the Base64
258
- # encoded value to the #tag! method. It definitely makes
281
+ # encoded value to the <tt>tag!</tt> method. It definitely makes
259
282
  # no sense to Base64 encode the value and then give it to
260
- # #tag!, since that just adds additional overhead.
283
+ # <tt>tag!</tt>, since that just adds additional overhead.
261
284
  def needs_encoding?
262
285
  ![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
263
286
  end