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.
- data/CHANGELOG +3043 -0
- data/README +360 -0
- data/RUNNING_UNIT_TESTS +64 -0
- data/Rakefile +226 -0
- data/examples/associations.png +0 -0
- data/examples/associations.rb +87 -0
- data/examples/shared_setup.rb +15 -0
- data/examples/validation.rb +85 -0
- data/install.rb +30 -0
- data/lib/active_record.rb +85 -0
- data/lib/active_record/acts/list.rb +244 -0
- data/lib/active_record/acts/nested_set.rb +211 -0
- data/lib/active_record/acts/tree.rb +89 -0
- data/lib/active_record/aggregations.rb +191 -0
- data/lib/active_record/associations.rb +1637 -0
- data/lib/active_record/associations/association_collection.rb +190 -0
- data/lib/active_record/associations/association_proxy.rb +158 -0
- data/lib/active_record/associations/belongs_to_association.rb +56 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
- data/lib/active_record/associations/has_many_association.rb +210 -0
- data/lib/active_record/associations/has_many_through_association.rb +247 -0
- data/lib/active_record/associations/has_one_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +2164 -0
- data/lib/active_record/calculations.rb +270 -0
- data/lib/active_record/callbacks.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
- data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
- data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
- data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
- data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
- data/lib/active_record/deprecated_associations.rb +104 -0
- data/lib/active_record/deprecated_finders.rb +44 -0
- data/lib/active_record/fixtures.rb +628 -0
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +394 -0
- data/lib/active_record/observer.rb +178 -0
- data/lib/active_record/query_cache.rb +64 -0
- data/lib/active_record/reflection.rb +222 -0
- data/lib/active_record/schema.rb +58 -0
- data/lib/active_record/schema_dumper.rb +149 -0
- data/lib/active_record/timestamp.rb +51 -0
- data/lib/active_record/transactions.rb +136 -0
- data/lib/active_record/validations.rb +843 -0
- data/lib/active_record/vendor/db2.rb +362 -0
- data/lib/active_record/vendor/mysql.rb +1214 -0
- data/lib/active_record/vendor/simple.rb +693 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
- data/lib/active_record/wrappings.rb +58 -0
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +59 -0
- data/test/abstract_unit.rb +77 -0
- data/test/active_schema_test_mysql.rb +31 -0
- data/test/adapter_test.rb +87 -0
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +95 -0
- data/test/all.sh +8 -0
- data/test/ar_schema_test.rb +33 -0
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations/callbacks_test.rb +126 -0
- data/test/associations/cascaded_eager_loading_test.rb +138 -0
- data/test/associations/eager_test.rb +393 -0
- data/test/associations/extension_test.rb +42 -0
- data/test/associations/join_model_test.rb +497 -0
- data/test/associations_test.rb +1809 -0
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +1586 -0
- data/test/binary_test.rb +37 -0
- data/test/calculations_test.rb +219 -0
- data/test/callbacks_test.rb +377 -0
- data/test/class_inheritable_attributes_test.rb +32 -0
- data/test/column_alias_test.rb +17 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +25 -0
- data/test/connections/native_firebird/connection.rb +26 -0
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +24 -0
- data/test/connections/native_openbase/connection.rb +21 -0
- data/test/connections/native_oracle/connection.rb +27 -0
- data/test/connections/native_postgresql/connection.rb +23 -0
- data/test/connections/native_sqlite/connection.rb +34 -0
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
- data/test/connections/native_sqlserver/connection.rb +23 -0
- data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
- data/test/connections/native_sybase/connection.rb +23 -0
- data/test/copy_table_sqlite.rb +64 -0
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/default_test_firebird.rb +16 -0
- data/test/defaults_test.rb +60 -0
- data/test/deprecated_associations_test.rb +396 -0
- data/test/deprecated_finder_test.rb +151 -0
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +504 -0
- data/test/fixtures/accounts.yml +28 -0
- data/test/fixtures/author.rb +99 -0
- data/test/fixtures/author_favorites.yml +4 -0
- data/test/fixtures/authors.yml +7 -0
- data/test/fixtures/auto_id.rb +4 -0
- data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
- data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
- data/test/fixtures/bad_fixtures/blank_line +3 -0
- data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
- data/test/fixtures/bad_fixtures/missing_value +1 -0
- data/test/fixtures/binary.rb +2 -0
- data/test/fixtures/categories.yml +14 -0
- data/test/fixtures/categories/special_categories.yml +9 -0
- data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
- data/test/fixtures/categories_ordered.yml +7 -0
- data/test/fixtures/categories_posts.yml +23 -0
- data/test/fixtures/categorization.rb +5 -0
- data/test/fixtures/categorizations.yml +17 -0
- data/test/fixtures/category.rb +20 -0
- data/test/fixtures/column_name.rb +3 -0
- data/test/fixtures/comment.rb +23 -0
- data/test/fixtures/comments.yml +59 -0
- data/test/fixtures/companies.yml +55 -0
- data/test/fixtures/company.rb +107 -0
- data/test/fixtures/company_in_module.rb +59 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +4 -0
- data/test/fixtures/course.rb +3 -0
- data/test/fixtures/courses.yml +7 -0
- data/test/fixtures/customer.rb +55 -0
- data/test/fixtures/customers.yml +17 -0
- data/test/fixtures/db_definitions/db2.drop.sql +32 -0
- data/test/fixtures/db_definitions/db2.sql +231 -0
- data/test/fixtures/db_definitions/db22.drop.sql +2 -0
- data/test/fixtures/db_definitions/db22.sql +5 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
- data/test/fixtures/db_definitions/firebird.sql +304 -0
- data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
- data/test/fixtures/db_definitions/firebird2.sql +6 -0
- data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
- data/test/fixtures/db_definitions/frontbase.sql +268 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
- data/test/fixtures/db_definitions/mysql.sql +234 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase.sql +302 -0
- data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
- data/test/fixtures/db_definitions/openbase2.sql +7 -0
- data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
- data/test/fixtures/db_definitions/oracle.sql +325 -0
- data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle2.sql +6 -0
- data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
- data/test/fixtures/db_definitions/postgresql.sql +263 -0
- data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/postgresql2.sql +5 -0
- data/test/fixtures/db_definitions/schema.rb +60 -0
- data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
- data/test/fixtures/db_definitions/sqlite.sql +215 -0
- data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlite2.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
- data/test/fixtures/db_definitions/sqlserver.sql +243 -0
- data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
- data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
- data/test/fixtures/db_definitions/sybase.sql +218 -0
- data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
- data/test/fixtures/db_definitions/sybase2.sql +5 -0
- data/test/fixtures/default.rb +2 -0
- data/test/fixtures/developer.rb +52 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +17 -0
- data/test/fixtures/developers_projects/david_action_controller +3 -0
- data/test/fixtures/developers_projects/david_active_record +3 -0
- data/test/fixtures/developers_projects/jamis_active_record +2 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/entrant.rb +3 -0
- data/test/fixtures/entrants.yml +14 -0
- data/test/fixtures/fk_test_has_fk.yml +3 -0
- data/test/fixtures/fk_test_has_pk.yml +2 -0
- data/test/fixtures/flowers.jpg +0 -0
- data/test/fixtures/funny_jokes.yml +10 -0
- data/test/fixtures/joke.rb +6 -0
- data/test/fixtures/keyboard.rb +3 -0
- data/test/fixtures/legacy_thing.rb +3 -0
- data/test/fixtures/legacy_things.yml +3 -0
- data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
- data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixed_case_monkey.rb +3 -0
- data/test/fixtures/mixed_case_monkeys.yml +6 -0
- data/test/fixtures/mixin.rb +63 -0
- data/test/fixtures/mixins.yml +127 -0
- data/test/fixtures/movie.rb +5 -0
- data/test/fixtures/movies.yml +7 -0
- data/test/fixtures/naked/csv/accounts.csv +1 -0
- data/test/fixtures/naked/yml/accounts.yml +1 -0
- data/test/fixtures/naked/yml/companies.yml +1 -0
- data/test/fixtures/naked/yml/courses.yml +1 -0
- data/test/fixtures/order.rb +4 -0
- data/test/fixtures/people.yml +3 -0
- data/test/fixtures/person.rb +4 -0
- data/test/fixtures/post.rb +58 -0
- data/test/fixtures/posts.yml +48 -0
- data/test/fixtures/project.rb +27 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/reader.rb +4 -0
- data/test/fixtures/readers.yml +4 -0
- data/test/fixtures/reply.rb +37 -0
- data/test/fixtures/subject.rb +4 -0
- data/test/fixtures/subscriber.rb +6 -0
- data/test/fixtures/subscribers/first +2 -0
- data/test/fixtures/subscribers/second +2 -0
- data/test/fixtures/tag.rb +7 -0
- data/test/fixtures/tagging.rb +6 -0
- data/test/fixtures/taggings.yml +18 -0
- data/test/fixtures/tags.yml +7 -0
- data/test/fixtures/task.rb +3 -0
- data/test/fixtures/tasks.yml +7 -0
- data/test/fixtures/topic.rb +25 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +401 -0
- data/test/inheritance_test.rb +205 -0
- data/test/lifecycle_test.rb +137 -0
- data/test/locking_test.rb +190 -0
- data/test/method_scoping_test.rb +416 -0
- data/test/migration_test.rb +768 -0
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +196 -0
- data/test/mixin_test.rb +550 -0
- data/test/modules_test.rb +34 -0
- data/test/multiple_db_test.rb +60 -0
- data/test/pk_test.rb +104 -0
- data/test/readonly_test.rb +107 -0
- data/test/reflection_test.rb +159 -0
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +96 -0
- data/test/schema_test_postgresql.rb +64 -0
- data/test/synonym_test_oracle.rb +17 -0
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +48 -0
- data/test/transactions_test.rb +230 -0
- data/test/unconnected_test.rb +32 -0
- data/test/validations_test.rb +1097 -0
- data/test/xml_serialization_test.rb +125 -0
- 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
|