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,178 @@
1
+ require 'singleton'
2
+ require 'set'
3
+
4
+ module ActiveRecord
5
+ module Observing # :nodoc:
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # Activates the observers assigned. Examples:
12
+ #
13
+ # # Calls PersonObserver.instance
14
+ # ActiveRecord::Base.observers = :person_observer
15
+ #
16
+ # # Calls Cacher.instance and GarbageCollector.instance
17
+ # ActiveRecord::Base.observers = :cacher, :garbage_collector
18
+ #
19
+ # # Same as above, just using explicit class references
20
+ # ActiveRecord::Base.observers = Cacher, GarbageCollector
21
+ #
22
+ # Note: Setting this does not instantiate the observers yet. #instantiate_observers is
23
+ # called during startup, and before each development request.
24
+ def observers=(*observers)
25
+ @observers = observers.flatten
26
+ end
27
+
28
+ # Gets the current observers.
29
+ def observers
30
+ @observers ||= []
31
+ end
32
+
33
+ # Instantiate the global ActiveRecord observers
34
+ def instantiate_observers
35
+ return if @observers.blank?
36
+ @observers.each do |observer|
37
+ if observer.respond_to?(:to_sym) # Symbol or String
38
+ observer.to_s.camelize.constantize.instance
39
+ elsif observer.respond_to?(:instance)
40
+ observer.instance
41
+ else
42
+ raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance"
43
+ end
44
+ end
45
+ end
46
+
47
+ protected
48
+ # Notify observers when the observed class is subclassed.
49
+ def inherited(subclass)
50
+ super
51
+ changed
52
+ notify_observers :observed_class_inherited, subclass
53
+ end
54
+ end
55
+ end
56
+
57
+ # Observer classes respond to lifecycle callbacks to implement trigger-like
58
+ # behavior outside the original class. This is a great way to reduce the
59
+ # clutter that normally comes when the model class is burdened with
60
+ # functionality that doesn't pertain to the core responsibility of the
61
+ # class. Example:
62
+ #
63
+ # class CommentObserver < ActiveRecord::Observer
64
+ # def after_save(comment)
65
+ # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
66
+ # end
67
+ # end
68
+ #
69
+ # This Observer sends an email when a Comment#save is finished.
70
+ #
71
+ # class ContactObserver < ActiveRecord::Observer
72
+ # def after_create(contact)
73
+ # contact.logger.info('New contact added!')
74
+ # end
75
+ #
76
+ # def after_destroy(contact)
77
+ # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
78
+ # end
79
+ # end
80
+ #
81
+ # This Observer uses logger to log when specific callbacks are triggered.
82
+ #
83
+ # == Observing a class that can't be inferred
84
+ #
85
+ # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
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:
88
+ #
89
+ # class AuditObserver < ActiveRecord::Observer
90
+ # observe Account
91
+ #
92
+ # def after_update(account)
93
+ # AuditTrail.new(account, "UPDATED")
94
+ # end
95
+ # end
96
+ #
97
+ # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
98
+ #
99
+ # class AuditObserver < ActiveRecord::Observer
100
+ # observe Account, Balance
101
+ #
102
+ # def after_update(record)
103
+ # AuditTrail.new(record, "UPDATED")
104
+ # end
105
+ # end
106
+ #
107
+ # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
108
+ #
109
+ # == Available callback methods
110
+ #
111
+ # The observer can implement callback methods for each of the methods described in the Callbacks module.
112
+ #
113
+ # == Storing Observers in Rails
114
+ #
115
+ # If you're using Active Record within Rails, observer classes are usually stored in app/models with the
116
+ # naming convention of app/models/audit_observer.rb.
117
+ #
118
+ # == Configuration
119
+ #
120
+ # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration setting in your
121
+ # <tt>config/environment.rb</tt> file.
122
+ #
123
+ # config.active_record.observers = :comment_observer, :signup_observer
124
+ #
125
+ # Observers will not be invoked unless you define these in your application configuration.
126
+ #
127
+ class Observer
128
+ include Singleton
129
+
130
+ # Observer subclasses should be reloaded by the dispatcher in Rails
131
+ # when Dependencies.mechanism = :load.
132
+ include Reloadable::Deprecated
133
+
134
+ class << self
135
+ # Attaches the observer to the supplied model classes.
136
+ def observe(*models)
137
+ define_method(:observed_classes) { Set.new(models) }
138
+ end
139
+
140
+ # The class observed by default is inferred from the observer's class name:
141
+ # assert_equal [Person], PersonObserver.observed_class
142
+ def observed_class
143
+ name.scan(/(.*)Observer/)[0][0].constantize
144
+ end
145
+ end
146
+
147
+ # Start observing the declared classes and their subclasses.
148
+ def initialize
149
+ Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass }
150
+ end
151
+
152
+ # Send observed_method(object) if the method exists.
153
+ def update(observed_method, object) #:nodoc:
154
+ send(observed_method, object) if respond_to?(observed_method)
155
+ end
156
+
157
+ # Special method sent by the observed class when it is inherited.
158
+ # Passes the new subclass.
159
+ def observed_class_inherited(subclass) #:nodoc:
160
+ self.class.observe(observed_classes + [subclass])
161
+ add_observer!(subclass)
162
+ end
163
+
164
+ protected
165
+ def observed_classes
166
+ Set.new([self.class.observed_class].flatten)
167
+ end
168
+
169
+ def observed_subclasses
170
+ observed_classes.sum(&:subclasses)
171
+ end
172
+
173
+ def add_observer!(klass)
174
+ klass.add_observer(self)
175
+ klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find)
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,64 @@
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)
42
+ 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=
49
+
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
55
+ end
56
+ end
57
+ 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
+ end
@@ -0,0 +1,222 @@
1
+ module ActiveRecord
2
+ module Reflection # :nodoc:
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ # Reflection allows you to interrogate Active Record classes and objects about their associations and aggregations.
8
+ # This information can, for example, be used in a form builder that took an Active Record object and created input
9
+ # fields for all of the attributes depending on their type and displayed the associations to other objects.
10
+ #
11
+ # You can find the interface for the AggregateReflection and AssociationReflection classes in the abstract MacroReflection class.
12
+ module ClassMethods
13
+ def create_reflection(macro, name, options, active_record)
14
+ case macro
15
+ when :has_many, :belongs_to, :has_one, :has_and_belongs_to_many
16
+ reflection = AssociationReflection.new(macro, name, options, active_record)
17
+ when :composed_of
18
+ reflection = AggregateReflection.new(macro, name, options, active_record)
19
+ end
20
+ write_inheritable_hash :reflections, name => reflection
21
+ reflection
22
+ end
23
+
24
+ # Returns a hash containing all AssociationReflection objects for the current class
25
+ # Example:
26
+ #
27
+ # Invoice.reflections
28
+ # Account.reflections
29
+ #
30
+ def reflections
31
+ read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
32
+ end
33
+
34
+ # Returns an array of AggregateReflection objects for all the aggregations in the class.
35
+ def reflect_on_all_aggregations
36
+ reflections.values.select { |reflection| reflection.is_a?(AggregateReflection) }
37
+ end
38
+
39
+ # Returns the AggregateReflection object for the named +aggregation+ (use the symbol). Example:
40
+ #
41
+ # Account.reflect_on_aggregation(:balance) # returns the balance AggregateReflection
42
+ #
43
+ def reflect_on_aggregation(aggregation)
44
+ reflections[aggregation].is_a?(AggregateReflection) ? reflections[aggregation] : nil
45
+ end
46
+
47
+ # Returns an array of AssociationReflection objects for all the aggregations 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.
49
+ # Example:
50
+ #
51
+ # Account.reflect_on_all_associations # returns an array of all associations
52
+ # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
53
+ #
54
+ def reflect_on_all_associations(macro = nil)
55
+ association_reflections = reflections.values.select { |reflection| reflection.is_a?(AssociationReflection) }
56
+ macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
57
+ end
58
+
59
+ # Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
60
+ #
61
+ # Account.reflect_on_association(:owner) # returns the owner AssociationReflection
62
+ # Invoice.reflect_on_association(:line_items).macro # returns :has_many
63
+ #
64
+ def reflect_on_association(association)
65
+ reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
66
+ end
67
+ end
68
+
69
+
70
+ # Abstract base class for AggregateReflection and AssociationReflection that describes the interface available for both of
71
+ # those classes. Objects of AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
72
+ class MacroReflection
73
+ attr_reader :active_record
74
+ def initialize(macro, name, options, active_record)
75
+ @macro, @name, @options, @active_record = macro, name, options, active_record
76
+ end
77
+
78
+ # Returns the name of the macro, so it would return :balance for "composed_of :balance, :class_name => 'Money'" or
79
+ # :clients for "has_many :clients".
80
+ def name
81
+ @name
82
+ end
83
+
84
+ # Returns the name of the macro, so it would return :composed_of for
85
+ # "composed_of :balance, :class_name => 'Money'" or :has_many for "has_many :clients".
86
+ def macro
87
+ @macro
88
+ end
89
+
90
+ # Returns the hash of options used for the macro, so it would return { :class_name => "Money" } for
91
+ # "composed_of :balance, :class_name => 'Money'" or {} for "has_many :clients".
92
+ def options
93
+ @options
94
+ end
95
+
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
+
100
+ def class_name
101
+ @class_name ||= name_to_class_name(name.id2name)
102
+ end
103
+
104
+ def ==(other_aggregation)
105
+ name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
106
+ end
107
+ end
108
+
109
+
110
+ # Holds all the meta-data about an aggregation as it was specified in the Active Record class.
111
+ 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
+ end
121
+
122
+ # Holds all the meta-data about an association as it was specified in the Active Record class.
123
+ class AssociationReflection < MacroReflection #:nodoc:
124
+ def klass
125
+ @klass ||= active_record.send(:compute_type, class_name)
126
+ end
127
+
128
+ def table_name
129
+ @table_name ||= klass.table_name
130
+ end
131
+
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
142
+ end
143
+
144
+ def association_foreign_key
145
+ @association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
146
+ end
147
+
148
+ def counter_cache_column
149
+ if options[:counter_cache] == true
150
+ "#{active_record.name.underscore.pluralize}_count"
151
+ elsif options[:counter_cache]
152
+ options[:counter_cache]
153
+ end
154
+ end
155
+
156
+ def through_reflection
157
+ @through_reflection ||= options[:through] ? active_record.reflect_on_association(options[:through]) : false
158
+ end
159
+
160
+ # Gets an array of possible :through source reflection names
161
+ #
162
+ # [singularized, pluralized]
163
+ #
164
+ def source_reflection_names
165
+ @source_reflection_names ||= (options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }
166
+ end
167
+
168
+ # Gets the source of the through reflection. It checks both a singularized and pluralized form for :belongs_to or :has_many.
169
+ # (The :tags association on Tagging below)
170
+ #
171
+ # class Post
172
+ # has_many :tags, :through => :taggings
173
+ # end
174
+ #
175
+ def source_reflection
176
+ return nil unless through_reflection
177
+ @source_reflection ||= source_reflection_names.collect { |name| through_reflection.klass.reflect_on_association(name) }.compact.first
178
+ end
179
+
180
+ def check_validity!
181
+ if options[:through]
182
+ if through_reflection.nil?
183
+ raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
184
+ end
185
+
186
+ if source_reflection.nil?
187
+ raise HasManyThroughSourceAssociationNotFoundError.new(self)
188
+ end
189
+
190
+ if options[:source_type] && source_reflection.options[:polymorphic].nil?
191
+ raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
192
+ end
193
+
194
+ if source_reflection.options[:polymorphic] && options[:source_type].nil?
195
+ raise HasManyThroughAssociationPolymorphicError.new(active_record.name, self, source_reflection)
196
+ end
197
+
198
+ unless [:belongs_to, :has_many].include?(source_reflection.macro) && source_reflection.options[:through].nil?
199
+ raise HasManyThroughSourceAssociationMacroError.new(self)
200
+ end
201
+ end
202
+ end
203
+
204
+ private
205
+ def name_to_class_name(name)
206
+ if name =~ /::/
207
+ name
208
+ 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
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end