has_many_polymorphs 2.2
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 +86 -0
- data/LICENSE +184 -0
- data/Manifest +173 -0
- data/README +205 -0
- data/Rakefile +28 -0
- data/TODO +2 -0
- data/examples/hmph.rb +69 -0
- data/generators/tagging/tagging_generator.rb +97 -0
- data/generators/tagging/templates/migration.rb +28 -0
- data/generators/tagging/templates/tag.rb +39 -0
- data/generators/tagging/templates/tag_test.rb +15 -0
- data/generators/tagging/templates/tagging.rb +16 -0
- data/generators/tagging/templates/tagging_extensions.rb +203 -0
- data/generators/tagging/templates/tagging_test.rb +85 -0
- data/generators/tagging/templates/taggings.yml +23 -0
- data/generators/tagging/templates/tags.yml +7 -0
- data/has_many_polymorphs.gemspec +36 -0
- data/init.rb +2 -0
- data/lib/has_many_polymorphs/association.rb +160 -0
- data/lib/has_many_polymorphs/autoload.rb +69 -0
- data/lib/has_many_polymorphs/base.rb +60 -0
- data/lib/has_many_polymorphs/class_methods.rb +600 -0
- data/lib/has_many_polymorphs/configuration.rb +19 -0
- data/lib/has_many_polymorphs/debugging_tools.rb +103 -0
- data/lib/has_many_polymorphs/rake_task_redefine_task.rb +35 -0
- data/lib/has_many_polymorphs/reflection.rb +58 -0
- data/lib/has_many_polymorphs/support_methods.rb +88 -0
- data/lib/has_many_polymorphs.rb +27 -0
- data/test/fixtures/bow_wows.yml +10 -0
- data/test/fixtures/cats.yml +18 -0
- data/test/fixtures/eaters_foodstuffs.yml +0 -0
- data/test/fixtures/fish.yml +12 -0
- data/test/fixtures/frogs.yml +5 -0
- data/test/fixtures/keep_your_enemies_close.yml +0 -0
- data/test/fixtures/little_whale_pupils.yml +0 -0
- data/test/fixtures/people.yml +7 -0
- data/test/fixtures/petfoods.yml +11 -0
- data/test/fixtures/whales.yml +5 -0
- data/test/fixtures/wild_boars.yml +10 -0
- data/test/generator/tagging_generator_test.rb +42 -0
- data/test/integration/app/README +182 -0
- data/test/integration/app/Rakefile +19 -0
- data/test/integration/app/app/controllers/application.rb +7 -0
- data/test/integration/app/app/controllers/bones_controller.rb +5 -0
- data/test/integration/app/app/helpers/addresses_helper.rb +2 -0
- data/test/integration/app/app/helpers/application_helper.rb +3 -0
- data/test/integration/app/app/helpers/bones_helper.rb +2 -0
- data/test/integration/app/app/helpers/sellers_helper.rb +28 -0
- data/test/integration/app/app/helpers/states_helper.rb +2 -0
- data/test/integration/app/app/helpers/users_helper.rb +2 -0
- data/test/integration/app/app/models/bone.rb +2 -0
- data/test/integration/app/app/models/double_sti_parent.rb +2 -0
- data/test/integration/app/app/models/double_sti_parent_relationship.rb +2 -0
- data/test/integration/app/app/models/organic_substance.rb +2 -0
- data/test/integration/app/app/models/single_sti_parent.rb +4 -0
- data/test/integration/app/app/models/single_sti_parent_relationship.rb +4 -0
- data/test/integration/app/app/models/stick.rb +2 -0
- data/test/integration/app/app/models/stone.rb +2 -0
- data/test/integration/app/app/views/addresses/edit.html.erb +12 -0
- data/test/integration/app/app/views/addresses/index.html.erb +18 -0
- data/test/integration/app/app/views/addresses/new.html.erb +11 -0
- data/test/integration/app/app/views/addresses/show.html.erb +3 -0
- data/test/integration/app/app/views/bones/index.rhtml +5 -0
- data/test/integration/app/app/views/layouts/addresses.html.erb +17 -0
- data/test/integration/app/app/views/layouts/sellers.html.erb +17 -0
- data/test/integration/app/app/views/layouts/states.html.erb +17 -0
- data/test/integration/app/app/views/layouts/users.html.erb +17 -0
- data/test/integration/app/app/views/sellers/edit.html.erb +12 -0
- data/test/integration/app/app/views/sellers/index.html.erb +20 -0
- data/test/integration/app/app/views/sellers/new.html.erb +11 -0
- data/test/integration/app/app/views/sellers/show.html.erb +3 -0
- data/test/integration/app/app/views/states/edit.html.erb +12 -0
- data/test/integration/app/app/views/states/index.html.erb +19 -0
- data/test/integration/app/app/views/states/new.html.erb +11 -0
- data/test/integration/app/app/views/states/show.html.erb +3 -0
- data/test/integration/app/app/views/users/edit.html.erb +12 -0
- data/test/integration/app/app/views/users/index.html.erb +22 -0
- data/test/integration/app/app/views/users/new.html.erb +11 -0
- data/test/integration/app/app/views/users/show.html.erb +3 -0
- data/test/integration/app/config/boot.rb +110 -0
- data/test/integration/app/config/database.yml +17 -0
- data/test/integration/app/config/environment.rb +19 -0
- data/test/integration/app/config/environment.rb.canonical +19 -0
- data/test/integration/app/config/environments/development.rb +9 -0
- data/test/integration/app/config/environments/production.rb +18 -0
- data/test/integration/app/config/environments/test.rb +19 -0
- data/test/integration/app/config/locomotive.yml +6 -0
- data/test/integration/app/config/routes.rb +33 -0
- data/test/integration/app/config/ultrasphinx/default.base +56 -0
- data/test/integration/app/config/ultrasphinx/development.conf.canonical +155 -0
- data/test/integration/app/db/migrate/001_create_sticks.rb +11 -0
- data/test/integration/app/db/migrate/002_create_stones.rb +11 -0
- data/test/integration/app/db/migrate/003_create_organic_substances.rb +11 -0
- data/test/integration/app/db/migrate/004_create_bones.rb +8 -0
- data/test/integration/app/db/migrate/005_create_single_sti_parents.rb +11 -0
- data/test/integration/app/db/migrate/006_create_double_sti_parents.rb +11 -0
- data/test/integration/app/db/migrate/007_create_single_sti_parent_relationships.rb +13 -0
- data/test/integration/app/db/migrate/008_create_double_sti_parent_relationships.rb +14 -0
- data/test/integration/app/db/migrate/009_create_library_model.rb +11 -0
- data/test/integration/app/doc/README_FOR_APP +2 -0
- data/test/integration/app/generators/commenting_generator_test.rb +83 -0
- data/test/integration/app/lib/library_model.rb +2 -0
- data/test/integration/app/public/404.html +30 -0
- data/test/integration/app/public/500.html +30 -0
- data/test/integration/app/public/dispatch.cgi +10 -0
- data/test/integration/app/public/dispatch.fcgi +24 -0
- data/test/integration/app/public/dispatch.rb +10 -0
- data/test/integration/app/public/favicon.ico +0 -0
- data/test/integration/app/public/images/rails.png +0 -0
- data/test/integration/app/public/index.html +277 -0
- data/test/integration/app/public/javascripts/application.js +2 -0
- data/test/integration/app/public/javascripts/controls.js +833 -0
- data/test/integration/app/public/javascripts/dragdrop.js +942 -0
- data/test/integration/app/public/javascripts/effects.js +1088 -0
- data/test/integration/app/public/javascripts/prototype.js +2515 -0
- data/test/integration/app/public/robots.txt +1 -0
- data/test/integration/app/public/stylesheets/scaffold.css +74 -0
- data/test/integration/app/script/about +3 -0
- data/test/integration/app/script/breakpointer +3 -0
- data/test/integration/app/script/console +3 -0
- data/test/integration/app/script/destroy +3 -0
- data/test/integration/app/script/generate +3 -0
- data/test/integration/app/script/performance/benchmarker +3 -0
- data/test/integration/app/script/performance/profiler +3 -0
- data/test/integration/app/script/plugin +3 -0
- data/test/integration/app/script/process/inspector +3 -0
- data/test/integration/app/script/process/reaper +3 -0
- data/test/integration/app/script/process/spawner +3 -0
- data/test/integration/app/script/runner +3 -0
- data/test/integration/app/script/server +3 -0
- data/test/integration/app/test/fixtures/double_sti_parent_relationships.yml +7 -0
- data/test/integration/app/test/fixtures/double_sti_parents.yml +7 -0
- data/test/integration/app/test/fixtures/organic_substances.yml +5 -0
- data/test/integration/app/test/fixtures/single_sti_parent_relationships.yml +7 -0
- data/test/integration/app/test/fixtures/single_sti_parents.yml +7 -0
- data/test/integration/app/test/fixtures/sticks.yml +7 -0
- data/test/integration/app/test/fixtures/stones.yml +7 -0
- data/test/integration/app/test/functional/addresses_controller_test.rb +57 -0
- data/test/integration/app/test/functional/bones_controller_test.rb +8 -0
- data/test/integration/app/test/functional/sellers_controller_test.rb +57 -0
- data/test/integration/app/test/functional/states_controller_test.rb +57 -0
- data/test/integration/app/test/functional/users_controller_test.rb +57 -0
- data/test/integration/app/test/test_helper.rb +8 -0
- data/test/integration/app/test/unit/bone_test.rb +8 -0
- data/test/integration/app/test/unit/double_sti_parent_relationship_test.rb +8 -0
- data/test/integration/app/test/unit/double_sti_parent_test.rb +8 -0
- data/test/integration/app/test/unit/organic_substance_test.rb +8 -0
- data/test/integration/app/test/unit/single_sti_parent_relationship_test.rb +8 -0
- data/test/integration/app/test/unit/single_sti_parent_test.rb +8 -0
- data/test/integration/app/test/unit/stick_test.rb +8 -0
- data/test/integration/app/test/unit/stone_test.rb +8 -0
- data/test/integration/server_test.rb +43 -0
- data/test/models/aquatic/fish.rb +5 -0
- data/test/models/aquatic/pupils_whale.rb +7 -0
- data/test/models/aquatic/whale.rb +15 -0
- data/test/models/beautiful_fight_relationship.rb +26 -0
- data/test/models/canine.rb +9 -0
- data/test/models/cat.rb +5 -0
- data/test/models/dog.rb +18 -0
- data/test/models/eaters_foodstuff.rb +8 -0
- data/test/models/frog.rb +4 -0
- data/test/models/kitten.rb +3 -0
- data/test/models/parentship.rb +4 -0
- data/test/models/person.rb +9 -0
- data/test/models/petfood.rb +39 -0
- data/test/models/tabby.rb +2 -0
- data/test/models/wild_boar.rb +3 -0
- data/test/modules/extension_module.rb +9 -0
- data/test/modules/other_extension_module.rb +9 -0
- data/test/patches/symlinked_plugins_1.2.6.diff +46 -0
- data/test/schema.rb +87 -0
- data/test/setup.rb +14 -0
- data/test/test_helper.rb +52 -0
- data/test/unit/has_many_polymorphs_test.rb +713 -0
- data.tar.gz.sig +1 -0
- metadata +279 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
module ActiveRecord #:nodoc:
|
|
2
|
+
module Associations #:nodoc:
|
|
3
|
+
|
|
4
|
+
class PolymorphicError < ActiveRecordError #:nodoc:
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class PolymorphicMethodNotSupportedError < ActiveRecordError #:nodoc:
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# The association class for a <tt>has_many_polymorphs</tt> association.
|
|
11
|
+
class PolymorphicAssociation < HasManyThroughAssociation
|
|
12
|
+
|
|
13
|
+
# Push a record onto the association. Triggers a database load for a uniqueness check only if <tt>:skip_duplicates</tt> is <tt>true</tt>. Return value is undefined.
|
|
14
|
+
def <<(*records)
|
|
15
|
+
return if records.empty?
|
|
16
|
+
|
|
17
|
+
if @reflection.options[:skip_duplicates]
|
|
18
|
+
_logger_debug "Loading instances for polymorphic duplicate push check; use :skip_duplicates => false and perhaps a database constraint to avoid this possible performance issue"
|
|
19
|
+
load_target
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@reflection.klass.transaction do
|
|
23
|
+
flatten_deeper(records).each do |record|
|
|
24
|
+
if @owner.new_record? or not record.respond_to?(:new_record?) or record.new_record?
|
|
25
|
+
raise PolymorphicError, "You can't associate unsaved records."
|
|
26
|
+
end
|
|
27
|
+
next if @reflection.options[:skip_duplicates] and @target.include? record
|
|
28
|
+
@owner.send(@reflection.through_reflection.name).proxy_target << @reflection.klass.create!(construct_join_attributes(record))
|
|
29
|
+
@target << record if loaded?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias :push :<<
|
|
37
|
+
alias :concat :<<
|
|
38
|
+
|
|
39
|
+
# Runs a <tt>find</tt> against the association contents, returning the matched records. All regular <tt>find</tt> options except <tt>:include</tt> are supported.
|
|
40
|
+
def find(*args)
|
|
41
|
+
opts = args._extract_options!
|
|
42
|
+
opts.delete :include
|
|
43
|
+
super(*(args + [opts]))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def construct_scope
|
|
47
|
+
_logger_warn "Warning; not all usage scenarios for polymorphic scopes are supported yet."
|
|
48
|
+
super
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Deletes a record from the association. Return value is undefined.
|
|
52
|
+
def delete(*records)
|
|
53
|
+
records = flatten_deeper(records)
|
|
54
|
+
records.reject! {|record| @target.delete(record) if record.new_record?}
|
|
55
|
+
return if records.empty?
|
|
56
|
+
|
|
57
|
+
@reflection.klass.transaction do
|
|
58
|
+
records.each do |record|
|
|
59
|
+
joins = @reflection.through_reflection.name
|
|
60
|
+
@owner.send(joins).delete(@owner.send(joins).select do |join|
|
|
61
|
+
join.send(@reflection.options[:polymorphic_key]) == record.id and
|
|
62
|
+
join.send(@reflection.options[:polymorphic_type_key]) == "#{record.class.base_class}"
|
|
63
|
+
end)
|
|
64
|
+
@target.delete(record)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Clears all records from the association. Returns an empty array.
|
|
70
|
+
def clear(klass = nil)
|
|
71
|
+
load_target
|
|
72
|
+
return if @target.empty?
|
|
73
|
+
|
|
74
|
+
if klass
|
|
75
|
+
delete(@target.select {|r| r.is_a? klass })
|
|
76
|
+
else
|
|
77
|
+
@owner.send(@reflection.through_reflection.name).clear
|
|
78
|
+
@target.clear
|
|
79
|
+
end
|
|
80
|
+
[]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
protected
|
|
84
|
+
|
|
85
|
+
# undef :sum
|
|
86
|
+
# undef :create!
|
|
87
|
+
|
|
88
|
+
def construct_quoted_owner_attributes(*args) #:nodoc:
|
|
89
|
+
# no access to returning() here? why not?
|
|
90
|
+
type_key = @reflection.options[:foreign_type_key]
|
|
91
|
+
h = {@reflection.primary_key_name => @owner.id}
|
|
92
|
+
h[type_key] = @owner.class.base_class.name if type_key
|
|
93
|
+
h
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def construct_from #:nodoc:
|
|
97
|
+
# build the FROM part of the query, in this case, the polymorphic join table
|
|
98
|
+
@reflection.klass.quoted_table_name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def construct_owner #:nodoc:
|
|
102
|
+
# the table name for the owner object's class
|
|
103
|
+
@owner.class.quoted_table_name
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def construct_owner_key #:nodoc:
|
|
107
|
+
# the primary key field for the owner object
|
|
108
|
+
@owner.class.primary_key
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def construct_select(custom_select = nil) #:nodoc:
|
|
112
|
+
# build the select query
|
|
113
|
+
selected = custom_select || @reflection.options[:select]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def construct_joins(custom_joins = nil) #:nodoc:
|
|
117
|
+
# build the string of default joins
|
|
118
|
+
"JOIN #{construct_owner} AS polymorphic_parent ON #{construct_from}.#{@reflection.options[:foreign_key]} = polymorphic_parent.#{construct_owner_key} " +
|
|
119
|
+
@reflection.options[:from].map do |plural|
|
|
120
|
+
klass = plural._as_class
|
|
121
|
+
"LEFT JOIN #{klass.quoted_table_name} ON #{construct_from}.#{@reflection.options[:polymorphic_key]} = #{klass.quoted_table_name}.#{klass.primary_key} AND #{construct_from}.#{@reflection.options[:polymorphic_type_key]} = #{@reflection.klass.quote_value(klass.base_class.name)}"
|
|
122
|
+
end.uniq.join(" ") + " #{custom_joins}"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def construct_conditions #:nodoc:
|
|
126
|
+
# build the fully realized condition string
|
|
127
|
+
conditions = construct_quoted_owner_attributes.map do |field, value|
|
|
128
|
+
"#{construct_from}.#{field} = #{@reflection.klass.quote_value(value)}" if value
|
|
129
|
+
end
|
|
130
|
+
conditions << custom_conditions if custom_conditions
|
|
131
|
+
"(" + conditions.compact.join(') AND (') + ")"
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def custom_conditions #:nodoc:
|
|
135
|
+
# custom conditions... not as messy as has_many :through because our joins are a little smarter
|
|
136
|
+
if @reflection.options[:conditions]
|
|
137
|
+
"(" + interpolate_sql(@reflection.klass.send(:sanitize_sql, @reflection.options[:conditions])) + ")"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
alias :construct_owner_attributes :construct_quoted_owner_attributes
|
|
142
|
+
alias :conditions :custom_conditions # XXX possibly not necessary
|
|
143
|
+
alias :sql_conditions :custom_conditions # XXX ditto
|
|
144
|
+
|
|
145
|
+
# construct attributes for join for a particular record
|
|
146
|
+
def construct_join_attributes(record) #:nodoc:
|
|
147
|
+
{@reflection.options[:polymorphic_key] => record.id,
|
|
148
|
+
@reflection.options[:polymorphic_type_key] => "#{record.class.base_class}",
|
|
149
|
+
@reflection.options[:foreign_key] => @owner.id}.merge(@reflection.options[:foreign_type_key] ?
|
|
150
|
+
{@reflection.options[:foreign_type_key] => "#{@owner.class.base_class}"} : {}) # for double-sided relationships
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def build(attrs = nil) #:nodoc:
|
|
154
|
+
raise PolymorphicMethodNotSupportedError, "You can't associate new records."
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'initializer' unless defined? ::Rails::Initializer
|
|
2
|
+
require 'action_controller/dispatcher' unless defined? ::ActionController::Dispatcher
|
|
3
|
+
|
|
4
|
+
module HasManyPolymorphs
|
|
5
|
+
|
|
6
|
+
=begin rdoc
|
|
7
|
+
Searches for models that use <tt>has_many_polymorphs</tt> or <tt>acts_as_double_polymorphic_join</tt> and makes sure that they get loaded during app initialization. This ensures that helper methods are injected into the target classes.
|
|
8
|
+
|
|
9
|
+
Note that you can override DEFAULT_OPTIONS via Rails::Configuration#has_many_polymorphs_options. For example, if you need an application extension to be required before has_many_polymorphs loads your models, add an <tt>after_initialize</tt> block in <tt>config/environment.rb</tt> that appends to the <tt>'requirements'</tt> key:
|
|
10
|
+
Rails::Initializer.run do |config|
|
|
11
|
+
# your other configuration here
|
|
12
|
+
|
|
13
|
+
config.after_initialize do
|
|
14
|
+
config.has_many_polymorphs_options['requirements'] << 'lib/my_extension'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
=end
|
|
19
|
+
|
|
20
|
+
DEFAULT_OPTIONS = {
|
|
21
|
+
:file_pattern => "#{RAILS_ROOT}/app/models/**/*.rb",
|
|
22
|
+
:file_exclusions => ['svn', 'CVS', 'bzr'],
|
|
23
|
+
:methods => ['has_many_polymorphs', 'acts_as_double_polymorphic_join'],
|
|
24
|
+
:requirements => []}
|
|
25
|
+
|
|
26
|
+
mattr_accessor :options
|
|
27
|
+
@@options = HashWithIndifferentAccess.new(DEFAULT_OPTIONS)
|
|
28
|
+
|
|
29
|
+
# Dispatcher callback to load polymorphic relationships from the top down.
|
|
30
|
+
def self.autoload
|
|
31
|
+
|
|
32
|
+
_logger_debug "autoload hook invoked"
|
|
33
|
+
|
|
34
|
+
options[:requirements].each do |requirement|
|
|
35
|
+
_logger_warn "forcing requirement load of #{requirement}"
|
|
36
|
+
require requirement
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Dir.glob(options[:file_pattern]).each do |filename|
|
|
40
|
+
next if filename =~ /#{options[:file_exclusions].join("|")}/
|
|
41
|
+
open filename do |file|
|
|
42
|
+
if file.grep(/#{options[:methods].join("|")}/).any?
|
|
43
|
+
begin
|
|
44
|
+
model = File.basename(filename)[0..-4].camelize
|
|
45
|
+
_logger_warn "preloading parent model #{model}"
|
|
46
|
+
model.constantize
|
|
47
|
+
rescue Object => e
|
|
48
|
+
_logger_warn "#{model} could not be preloaded: #{e.inspect}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Rails::Initializer #:nodoc:
|
|
58
|
+
# Make sure it gets loaded in the console, tests, and migrations
|
|
59
|
+
def after_initialize_with_autoload
|
|
60
|
+
after_initialize_without_autoload
|
|
61
|
+
HasManyPolymorphs.autoload
|
|
62
|
+
end
|
|
63
|
+
alias_method_chain :after_initialize, :autoload
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
ActionController::Dispatcher.to_prepare(:has_many_polymorphs_autoload) do
|
|
67
|
+
# Make sure it gets loaded in the app
|
|
68
|
+
HasManyPolymorphs.autoload
|
|
69
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
class Base
|
|
4
|
+
|
|
5
|
+
class << self
|
|
6
|
+
|
|
7
|
+
# Interprets a polymorphic row from a unified SELECT, returning the appropriate ActiveRecord instance. Overrides ActiveRecord::Base.instantiate_without_callbacks.
|
|
8
|
+
def instantiate_with_polymorphic_checks(record)
|
|
9
|
+
if record['polymorphic_parent_class']
|
|
10
|
+
reflection = record['polymorphic_parent_class'].constantize.reflect_on_association(record['polymorphic_association_id'].to_sym)
|
|
11
|
+
# _logger_debug "Instantiating a polymorphic row for #{record['polymorphic_parent_class']}.reflect_on_association(:#{record['polymorphic_association_id']})"
|
|
12
|
+
|
|
13
|
+
# rewrite the record with the right column names
|
|
14
|
+
table_aliases = reflection.options[:table_aliases].dup
|
|
15
|
+
record = Hash[*table_aliases.keys.map {|key| [key, record[table_aliases[key]]] }.flatten]
|
|
16
|
+
|
|
17
|
+
# find the real child class
|
|
18
|
+
klass = record["#{self.table_name}.#{reflection.options[:polymorphic_type_key]}"].constantize
|
|
19
|
+
if sti_klass = record["#{klass.table_name}.#{klass.inheritance_column}"]
|
|
20
|
+
klass = klass.class_eval do compute_type(sti_klass) end # in case of namespaced STI models
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# check that the join actually joined to something
|
|
24
|
+
unless (child_id = record["#{self.table_name}.#{reflection.options[:polymorphic_key]}"]) == record["#{klass.table_name}.#{klass.primary_key}"]
|
|
25
|
+
raise ActiveRecord::Associations::PolymorphicError,
|
|
26
|
+
"Referential integrity violation; child <#{klass.name}:#{child_id}> was not found for #{reflection.name.inspect}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# eject the join keys
|
|
30
|
+
# XXX not very readable
|
|
31
|
+
record = Hash[*record._select do |column, value|
|
|
32
|
+
column[/^#{klass.table_name}/]
|
|
33
|
+
end.map do |column, value|
|
|
34
|
+
[column[/\.(.*)/, 1], value]
|
|
35
|
+
end.flatten]
|
|
36
|
+
|
|
37
|
+
# allocate and assign values
|
|
38
|
+
returning(klass.allocate) do |obj|
|
|
39
|
+
obj.instance_variable_set("@attributes", record)
|
|
40
|
+
obj.instance_variable_set("@attributes_cache", Hash.new)
|
|
41
|
+
|
|
42
|
+
if obj.respond_to_without_attributes?(:after_find)
|
|
43
|
+
obj.send(:callback, :after_find)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if obj.respond_to_without_attributes?(:after_initialize)
|
|
47
|
+
obj.send(:callback, :after_initialize)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
else
|
|
52
|
+
instantiate_without_polymorphic_checks(record)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
alias_method_chain :instantiate, :polymorphic_checks
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|