activerecord_authorails 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. data/CHANGELOG +3043 -0
  2. data/README +360 -0
  3. data/RUNNING_UNIT_TESTS +64 -0
  4. data/Rakefile +226 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +85 -0
  9. data/install.rb +30 -0
  10. data/lib/active_record.rb +85 -0
  11. data/lib/active_record/acts/list.rb +244 -0
  12. data/lib/active_record/acts/nested_set.rb +211 -0
  13. data/lib/active_record/acts/tree.rb +89 -0
  14. data/lib/active_record/aggregations.rb +191 -0
  15. data/lib/active_record/associations.rb +1637 -0
  16. data/lib/active_record/associations/association_collection.rb +190 -0
  17. data/lib/active_record/associations/association_proxy.rb +158 -0
  18. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  19. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  20. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
  21. data/lib/active_record/associations/has_many_association.rb +210 -0
  22. data/lib/active_record/associations/has_many_through_association.rb +247 -0
  23. data/lib/active_record/associations/has_one_association.rb +80 -0
  24. data/lib/active_record/attribute_methods.rb +75 -0
  25. data/lib/active_record/base.rb +2164 -0
  26. data/lib/active_record/calculations.rb +270 -0
  27. data/lib/active_record/callbacks.rb +367 -0
  28. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
  34. data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
  35. data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
  36. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
  38. data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
  39. data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
  43. data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
  44. data/lib/active_record/deprecated_associations.rb +104 -0
  45. data/lib/active_record/deprecated_finders.rb +44 -0
  46. data/lib/active_record/fixtures.rb +628 -0
  47. data/lib/active_record/locking/optimistic.rb +106 -0
  48. data/lib/active_record/locking/pessimistic.rb +77 -0
  49. data/lib/active_record/migration.rb +394 -0
  50. data/lib/active_record/observer.rb +178 -0
  51. data/lib/active_record/query_cache.rb +64 -0
  52. data/lib/active_record/reflection.rb +222 -0
  53. data/lib/active_record/schema.rb +58 -0
  54. data/lib/active_record/schema_dumper.rb +149 -0
  55. data/lib/active_record/timestamp.rb +51 -0
  56. data/lib/active_record/transactions.rb +136 -0
  57. data/lib/active_record/validations.rb +843 -0
  58. data/lib/active_record/vendor/db2.rb +362 -0
  59. data/lib/active_record/vendor/mysql.rb +1214 -0
  60. data/lib/active_record/vendor/simple.rb +693 -0
  61. data/lib/active_record/version.rb +9 -0
  62. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  63. data/lib/active_record/wrappings.rb +58 -0
  64. data/lib/active_record/xml_serialization.rb +308 -0
  65. data/test/aaa_create_tables_test.rb +59 -0
  66. data/test/abstract_unit.rb +77 -0
  67. data/test/active_schema_test_mysql.rb +31 -0
  68. data/test/adapter_test.rb +87 -0
  69. data/test/adapter_test_sqlserver.rb +81 -0
  70. data/test/aggregations_test.rb +95 -0
  71. data/test/all.sh +8 -0
  72. data/test/ar_schema_test.rb +33 -0
  73. data/test/association_inheritance_reload.rb +14 -0
  74. data/test/associations/callbacks_test.rb +126 -0
  75. data/test/associations/cascaded_eager_loading_test.rb +138 -0
  76. data/test/associations/eager_test.rb +393 -0
  77. data/test/associations/extension_test.rb +42 -0
  78. data/test/associations/join_model_test.rb +497 -0
  79. data/test/associations_test.rb +1809 -0
  80. data/test/attribute_methods_test.rb +49 -0
  81. data/test/base_test.rb +1586 -0
  82. data/test/binary_test.rb +37 -0
  83. data/test/calculations_test.rb +219 -0
  84. data/test/callbacks_test.rb +377 -0
  85. data/test/class_inheritable_attributes_test.rb +32 -0
  86. data/test/column_alias_test.rb +17 -0
  87. data/test/connection_test_firebird.rb +8 -0
  88. data/test/connections/native_db2/connection.rb +25 -0
  89. data/test/connections/native_firebird/connection.rb +26 -0
  90. data/test/connections/native_frontbase/connection.rb +27 -0
  91. data/test/connections/native_mysql/connection.rb +24 -0
  92. data/test/connections/native_openbase/connection.rb +21 -0
  93. data/test/connections/native_oracle/connection.rb +27 -0
  94. data/test/connections/native_postgresql/connection.rb +23 -0
  95. data/test/connections/native_sqlite/connection.rb +34 -0
  96. data/test/connections/native_sqlite3/connection.rb +34 -0
  97. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  98. data/test/connections/native_sqlserver/connection.rb +23 -0
  99. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  100. data/test/connections/native_sybase/connection.rb +23 -0
  101. data/test/copy_table_sqlite.rb +64 -0
  102. data/test/datatype_test_postgresql.rb +52 -0
  103. data/test/default_test_firebird.rb +16 -0
  104. data/test/defaults_test.rb +60 -0
  105. data/test/deprecated_associations_test.rb +396 -0
  106. data/test/deprecated_finder_test.rb +151 -0
  107. data/test/empty_date_time_test.rb +25 -0
  108. data/test/finder_test.rb +504 -0
  109. data/test/fixtures/accounts.yml +28 -0
  110. data/test/fixtures/author.rb +99 -0
  111. data/test/fixtures/author_favorites.yml +4 -0
  112. data/test/fixtures/authors.yml +7 -0
  113. data/test/fixtures/auto_id.rb +4 -0
  114. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  115. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  116. data/test/fixtures/bad_fixtures/blank_line +3 -0
  117. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  118. data/test/fixtures/bad_fixtures/missing_value +1 -0
  119. data/test/fixtures/binary.rb +2 -0
  120. data/test/fixtures/categories.yml +14 -0
  121. data/test/fixtures/categories/special_categories.yml +9 -0
  122. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  123. data/test/fixtures/categories_ordered.yml +7 -0
  124. data/test/fixtures/categories_posts.yml +23 -0
  125. data/test/fixtures/categorization.rb +5 -0
  126. data/test/fixtures/categorizations.yml +17 -0
  127. data/test/fixtures/category.rb +20 -0
  128. data/test/fixtures/column_name.rb +3 -0
  129. data/test/fixtures/comment.rb +23 -0
  130. data/test/fixtures/comments.yml +59 -0
  131. data/test/fixtures/companies.yml +55 -0
  132. data/test/fixtures/company.rb +107 -0
  133. data/test/fixtures/company_in_module.rb +59 -0
  134. data/test/fixtures/computer.rb +3 -0
  135. data/test/fixtures/computers.yml +4 -0
  136. data/test/fixtures/course.rb +3 -0
  137. data/test/fixtures/courses.yml +7 -0
  138. data/test/fixtures/customer.rb +55 -0
  139. data/test/fixtures/customers.yml +17 -0
  140. data/test/fixtures/db_definitions/db2.drop.sql +32 -0
  141. data/test/fixtures/db_definitions/db2.sql +231 -0
  142. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  143. data/test/fixtures/db_definitions/db22.sql +5 -0
  144. data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
  145. data/test/fixtures/db_definitions/firebird.sql +304 -0
  146. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  148. data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
  149. data/test/fixtures/db_definitions/frontbase.sql +268 -0
  150. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  151. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  152. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  153. data/test/fixtures/db_definitions/mysql.sql +234 -0
  154. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  156. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  157. data/test/fixtures/db_definitions/openbase.sql +302 -0
  158. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  159. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  160. data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
  161. data/test/fixtures/db_definitions/oracle.sql +325 -0
  162. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  163. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  164. data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
  165. data/test/fixtures/db_definitions/postgresql.sql +263 -0
  166. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  167. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  168. data/test/fixtures/db_definitions/schema.rb +60 -0
  169. data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
  170. data/test/fixtures/db_definitions/sqlite.sql +215 -0
  171. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  172. data/test/fixtures/db_definitions/sqlite2.sql +5 -0
  173. data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
  174. data/test/fixtures/db_definitions/sqlserver.sql +243 -0
  175. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  176. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  177. data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
  178. data/test/fixtures/db_definitions/sybase.sql +218 -0
  179. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  180. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  181. data/test/fixtures/default.rb +2 -0
  182. data/test/fixtures/developer.rb +52 -0
  183. data/test/fixtures/developers.yml +21 -0
  184. data/test/fixtures/developers_projects.yml +17 -0
  185. data/test/fixtures/developers_projects/david_action_controller +3 -0
  186. data/test/fixtures/developers_projects/david_active_record +3 -0
  187. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  188. data/test/fixtures/edge.rb +5 -0
  189. data/test/fixtures/edges.yml +6 -0
  190. data/test/fixtures/entrant.rb +3 -0
  191. data/test/fixtures/entrants.yml +14 -0
  192. data/test/fixtures/fk_test_has_fk.yml +3 -0
  193. data/test/fixtures/fk_test_has_pk.yml +2 -0
  194. data/test/fixtures/flowers.jpg +0 -0
  195. data/test/fixtures/funny_jokes.yml +10 -0
  196. data/test/fixtures/joke.rb +6 -0
  197. data/test/fixtures/keyboard.rb +3 -0
  198. data/test/fixtures/legacy_thing.rb +3 -0
  199. data/test/fixtures/legacy_things.yml +3 -0
  200. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  201. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  202. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  203. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  204. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  205. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  206. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  207. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  208. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  209. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  210. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  211. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  212. data/test/fixtures/mixed_case_monkey.rb +3 -0
  213. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  214. data/test/fixtures/mixin.rb +63 -0
  215. data/test/fixtures/mixins.yml +127 -0
  216. data/test/fixtures/movie.rb +5 -0
  217. data/test/fixtures/movies.yml +7 -0
  218. data/test/fixtures/naked/csv/accounts.csv +1 -0
  219. data/test/fixtures/naked/yml/accounts.yml +1 -0
  220. data/test/fixtures/naked/yml/companies.yml +1 -0
  221. data/test/fixtures/naked/yml/courses.yml +1 -0
  222. data/test/fixtures/order.rb +4 -0
  223. data/test/fixtures/people.yml +3 -0
  224. data/test/fixtures/person.rb +4 -0
  225. data/test/fixtures/post.rb +58 -0
  226. data/test/fixtures/posts.yml +48 -0
  227. data/test/fixtures/project.rb +27 -0
  228. data/test/fixtures/projects.yml +7 -0
  229. data/test/fixtures/reader.rb +4 -0
  230. data/test/fixtures/readers.yml +4 -0
  231. data/test/fixtures/reply.rb +37 -0
  232. data/test/fixtures/subject.rb +4 -0
  233. data/test/fixtures/subscriber.rb +6 -0
  234. data/test/fixtures/subscribers/first +2 -0
  235. data/test/fixtures/subscribers/second +2 -0
  236. data/test/fixtures/tag.rb +7 -0
  237. data/test/fixtures/tagging.rb +6 -0
  238. data/test/fixtures/taggings.yml +18 -0
  239. data/test/fixtures/tags.yml +7 -0
  240. data/test/fixtures/task.rb +3 -0
  241. data/test/fixtures/tasks.yml +7 -0
  242. data/test/fixtures/topic.rb +25 -0
  243. data/test/fixtures/topics.yml +22 -0
  244. data/test/fixtures/vertex.rb +9 -0
  245. data/test/fixtures/vertices.yml +4 -0
  246. data/test/fixtures_test.rb +401 -0
  247. data/test/inheritance_test.rb +205 -0
  248. data/test/lifecycle_test.rb +137 -0
  249. data/test/locking_test.rb +190 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +768 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_nested_set_test.rb +196 -0
  254. data/test/mixin_test.rb +550 -0
  255. data/test/modules_test.rb +34 -0
  256. data/test/multiple_db_test.rb +60 -0
  257. data/test/pk_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +159 -0
  260. data/test/schema_authorization_test_postgresql.rb +75 -0
  261. data/test/schema_dumper_test.rb +96 -0
  262. data/test/schema_test_postgresql.rb +64 -0
  263. data/test/synonym_test_oracle.rb +17 -0
  264. data/test/table_name_test_sqlserver.rb +23 -0
  265. data/test/threaded_connections_test.rb +48 -0
  266. data/test/transactions_test.rb +230 -0
  267. data/test/unconnected_test.rb +32 -0
  268. data/test/validations_test.rb +1097 -0
  269. data/test/xml_serialization_test.rb +125 -0
  270. metadata +365 -0
@@ -0,0 +1,190 @@
1
+ require 'set'
2
+
3
+ module ActiveRecord
4
+ module Associations
5
+ class AssociationCollection < AssociationProxy #:nodoc:
6
+ def to_ary
7
+ load_target
8
+ @target.to_ary
9
+ end
10
+
11
+ def reset
12
+ reset_target!
13
+ @loaded = false
14
+ end
15
+
16
+ # Add +records+ to this association. Returns +self+ so method calls may be chained.
17
+ # Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
18
+ def <<(*records)
19
+ result = true
20
+ load_target
21
+
22
+ @owner.transaction do
23
+ flatten_deeper(records).each do |record|
24
+ raise_on_type_mismatch(record)
25
+ callback(:before_add, record)
26
+ result &&= insert_record(record) unless @owner.new_record?
27
+ @target << record
28
+ callback(:after_add, record)
29
+ end
30
+ end
31
+
32
+ result && self
33
+ end
34
+
35
+ alias_method :push, :<<
36
+ alias_method :concat, :<<
37
+
38
+ # Remove all records from this association
39
+ def delete_all
40
+ load_target
41
+ delete(@target)
42
+ reset_target!
43
+ end
44
+
45
+ # Calculate sum using SQL, not Enumerable
46
+ def sum(*args, &block)
47
+ calculate(:sum, *args, &block)
48
+ end
49
+
50
+ # Remove +records+ from this association. Does not destroy +records+.
51
+ def delete(*records)
52
+ records = flatten_deeper(records)
53
+ records.each { |record| raise_on_type_mismatch(record) }
54
+ records.reject! { |record| @target.delete(record) if record.new_record? }
55
+ return if records.empty?
56
+
57
+ @owner.transaction do
58
+ records.each { |record| callback(:before_remove, record) }
59
+ delete_records(records)
60
+ records.each do |record|
61
+ @target.delete(record)
62
+ callback(:after_remove, record)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Removes all records from this association. Returns +self+ so method calls may be chained.
68
+ def clear
69
+ return self if length.zero? # forces load_target if hasn't happened already
70
+
71
+ if @reflection.options[:dependent] && @reflection.options[:dependent] == :delete_all
72
+ destroy_all
73
+ else
74
+ delete_all
75
+ end
76
+
77
+ self
78
+ end
79
+
80
+ def destroy_all
81
+ @owner.transaction do
82
+ each { |record| record.destroy }
83
+ end
84
+
85
+ reset_target!
86
+ end
87
+
88
+ def create(attributes = {})
89
+ # Can't use Base.create since the foreign key may be a protected attribute.
90
+ if attributes.is_a?(Array)
91
+ attributes.collect { |attr| create(attr) }
92
+ else
93
+ record = build(attributes)
94
+ record.save unless @owner.new_record?
95
+ record
96
+ end
97
+ end
98
+
99
+ # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and
100
+ # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero
101
+ # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length.
102
+ def size
103
+ if loaded? && !@reflection.options[:uniq]
104
+ @target.size
105
+ elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
106
+ unsaved_records = Array(@target.detect { |r| r.new_record? })
107
+ unsaved_records.size + count_records
108
+ else
109
+ count_records
110
+ end
111
+ end
112
+
113
+ # Returns the size of the collection by loading it and calling size on the array. If you want to use this method to check
114
+ # whether the collection is empty, use collection.length.zero? instead of collection.empty?
115
+ def length
116
+ load_target.size
117
+ end
118
+
119
+ def empty?
120
+ size.zero?
121
+ end
122
+
123
+ def uniq(collection = self)
124
+ seen = Set.new
125
+ collection.inject([]) do |kept, record|
126
+ unless seen.include?(record.id)
127
+ kept << record
128
+ seen << record.id
129
+ end
130
+ kept
131
+ end
132
+ end
133
+
134
+ # Replace this collection with +other_array+
135
+ # This will perform a diff and delete/add only records that have changed.
136
+ def replace(other_array)
137
+ other_array.each { |val| raise_on_type_mismatch(val) }
138
+
139
+ load_target
140
+ other = other_array.size < 100 ? other_array : other_array.to_set
141
+ current = @target.size < 100 ? @target : @target.to_set
142
+
143
+ @owner.transaction do
144
+ delete(@target.select { |v| !other.include?(v) })
145
+ concat(other_array.select { |v| !current.include?(v) })
146
+ end
147
+ end
148
+
149
+ protected
150
+ def reset_target!
151
+ @target = Array.new
152
+ end
153
+
154
+ def find_target
155
+ records =
156
+ if @reflection.options[:finder_sql]
157
+ @reflection.klass.find_by_sql(@finder_sql)
158
+ else
159
+ find(:all)
160
+ end
161
+
162
+ @reflection.options[:uniq] ? uniq(records) : records
163
+ end
164
+
165
+ private
166
+ def callback(method, record)
167
+ callbacks_for(method).each do |callback|
168
+ case callback
169
+ when Symbol
170
+ @owner.send(callback, record)
171
+ when Proc, Method
172
+ callback.call(@owner, record)
173
+ else
174
+ if callback.respond_to?(method)
175
+ callback.send(method, @owner, record)
176
+ else
177
+ raise ActiveRecordError, "Callbacks must be a symbol denoting the method to call, a string to be evaluated, a block to be invoked, or an object responding to the callback method."
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ def callbacks_for(callback_name)
184
+ full_callback_name = "#{callback_name}_for_#{@reflection.name}"
185
+ @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
186
+ end
187
+
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,158 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class AssociationProxy #:nodoc:
4
+ attr_reader :reflection
5
+ alias_method :proxy_respond_to?, :respond_to?
6
+ alias_method :proxy_extend, :extend
7
+ delegate :to_param, :to => :proxy_target
8
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_)/ }
9
+
10
+ def initialize(owner, reflection)
11
+ @owner, @reflection = owner, reflection
12
+ Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) }
13
+ reset
14
+ end
15
+
16
+ def proxy_owner
17
+ @owner
18
+ end
19
+
20
+ def proxy_reflection
21
+ @reflection
22
+ end
23
+
24
+ def proxy_target
25
+ @target
26
+ end
27
+
28
+ def respond_to?(symbol, include_priv = false)
29
+ proxy_respond_to?(symbol, include_priv) || (load_target && @target.respond_to?(symbol, include_priv))
30
+ end
31
+
32
+ # Explicitly proxy === because the instance method removal above
33
+ # doesn't catch it.
34
+ def ===(other)
35
+ load_target
36
+ other === @target
37
+ end
38
+
39
+ def aliased_table_name
40
+ @reflection.klass.table_name
41
+ end
42
+
43
+ def conditions
44
+ @conditions ||= interpolate_sql(sanitize_sql(@reflection.options[:conditions])) if @reflection.options[:conditions]
45
+ end
46
+ alias :sql_conditions :conditions
47
+
48
+ def reset
49
+ @target = nil
50
+ @loaded = false
51
+ end
52
+
53
+ def reload
54
+ reset
55
+ load_target
56
+ end
57
+
58
+ def loaded?
59
+ @loaded
60
+ end
61
+
62
+ def loaded
63
+ @loaded = true
64
+ end
65
+
66
+ def target
67
+ @target
68
+ end
69
+
70
+ def target=(target)
71
+ @target = target
72
+ loaded
73
+ end
74
+
75
+ protected
76
+ def dependent?
77
+ @reflection.options[:dependent] || false
78
+ end
79
+
80
+ def quoted_record_ids(records)
81
+ records.map { |record| record.quoted_id }.join(',')
82
+ end
83
+
84
+ def interpolate_sql_options!(options, *keys)
85
+ keys.each { |key| options[key] &&= interpolate_sql(options[key]) }
86
+ end
87
+
88
+ def interpolate_sql(sql, record = nil)
89
+ @owner.send(:interpolate_sql, sql, record)
90
+ end
91
+
92
+ def sanitize_sql(sql)
93
+ @reflection.klass.send(:sanitize_sql, sql)
94
+ end
95
+
96
+ def extract_options_from_args!(args)
97
+ @owner.send(:extract_options_from_args!, args)
98
+ end
99
+
100
+ def set_belongs_to_association_for(record)
101
+ if @reflection.options[:as]
102
+ record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
103
+ record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
104
+ else
105
+ record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
106
+ end
107
+ end
108
+
109
+ def merge_options_from_reflection!(options)
110
+ options.reverse_merge!(
111
+ :group => @reflection.options[:group],
112
+ :limit => @reflection.options[:limit],
113
+ :offset => @reflection.options[:offset],
114
+ :joins => @reflection.options[:joins],
115
+ :include => @reflection.options[:include],
116
+ :select => @reflection.options[:select]
117
+ )
118
+ end
119
+
120
+ private
121
+ def method_missing(method, *args, &block)
122
+ if load_target
123
+ @target.send(method, *args, &block)
124
+ end
125
+ end
126
+
127
+ def load_target
128
+ return nil unless defined?(@loaded)
129
+
130
+ if !loaded? and (!@owner.new_record? || foreign_key_present)
131
+ @target = find_target
132
+ end
133
+
134
+ @loaded = true
135
+ @target
136
+ rescue ActiveRecord::RecordNotFound
137
+ reset
138
+ end
139
+
140
+ # Can be overwritten by associations that might have the foreign key available for an association without
141
+ # having the object itself (and still being a new record). Currently, only belongs_to present this scenario.
142
+ def foreign_key_present
143
+ false
144
+ end
145
+
146
+ def raise_on_type_mismatch(record)
147
+ unless record.is_a?(@reflection.klass)
148
+ raise ActiveRecord::AssociationTypeMismatch, "#{@reflection.class_name} expected, got #{record.class}"
149
+ end
150
+ end
151
+
152
+ # Array#flatten has problems with recursive arrays. Going one level deeper solves the majority of the problems.
153
+ def flatten_deeper(array)
154
+ array.collect { |element| element.respond_to?(:flatten) ? element.flatten : element }.flatten
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,56 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class BelongsToAssociation < AssociationProxy #:nodoc:
4
+ def create(attributes = {})
5
+ replace(@reflection.klass.create(attributes))
6
+ end
7
+
8
+ def build(attributes = {})
9
+ replace(@reflection.klass.new(attributes))
10
+ end
11
+
12
+ def replace(record)
13
+ counter_cache_name = @reflection.counter_cache_column
14
+
15
+ if record.nil?
16
+ if counter_cache_name && @owner[counter_cache_name] && !@owner.new_record?
17
+ @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
18
+ end
19
+
20
+ @target = @owner[@reflection.primary_key_name] = nil
21
+ else
22
+ raise_on_type_mismatch(record)
23
+
24
+ if counter_cache_name && !@owner.new_record?
25
+ @reflection.klass.increment_counter(counter_cache_name, record.id)
26
+ @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
27
+ end
28
+
29
+ @target = (AssociationProxy === record ? record.target : record)
30
+ @owner[@reflection.primary_key_name] = record.id unless record.new_record?
31
+ @updated = true
32
+ end
33
+
34
+ loaded
35
+ record
36
+ end
37
+
38
+ def updated?
39
+ @updated
40
+ end
41
+
42
+ private
43
+ def find_target
44
+ @reflection.klass.find(
45
+ @owner[@reflection.primary_key_name],
46
+ :conditions => conditions,
47
+ :include => @reflection.options[:include]
48
+ )
49
+ end
50
+
51
+ def foreign_key_present
52
+ !@owner[@reflection.primary_key_name].nil?
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Associations
3
+ class BelongsToPolymorphicAssociation < AssociationProxy #:nodoc:
4
+ def replace(record)
5
+ if record.nil?
6
+ @target = @owner[@reflection.primary_key_name] = @owner[@reflection.options[:foreign_type]] = nil
7
+ else
8
+ @target = (AssociationProxy === record ? record.target : record)
9
+
10
+ unless record.new_record?
11
+ @owner[@reflection.primary_key_name] = record.id
12
+ @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
13
+ end
14
+
15
+ @updated = true
16
+ end
17
+
18
+ loaded
19
+ record
20
+ end
21
+
22
+ def updated?
23
+ @updated
24
+ end
25
+
26
+ private
27
+ def find_target
28
+ return nil if association_class.nil?
29
+
30
+ if @reflection.options[:conditions]
31
+ association_class.find(
32
+ @owner[@reflection.primary_key_name],
33
+ :conditions => conditions,
34
+ :include => @reflection.options[:include]
35
+ )
36
+ else
37
+ association_class.find(@owner[@reflection.primary_key_name], :include => @reflection.options[:include])
38
+ end
39
+ end
40
+
41
+ def foreign_key_present
42
+ !@owner[@reflection.primary_key_name].nil?
43
+ end
44
+
45
+ def association_class
46
+ @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
47
+ end
48
+ end
49
+ end
50
+ end