activerecord_authorails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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