Fix creating polymorphic associations #35
Conversation
| require "polymorphic_integer_type/extensions" | ||
| require "polymorphic_integer_type/mapping" | ||
| require "polymorphic_integer_type/module_generator" | ||
| require "polymorphic_integer_type/belongs_to_polymorphic_association_extension" |
There was a problem hiding this comment.
this require was the heart of the issue, as it was previously tied to an active record version < 5.2.0, but in reality we want it all the time
| private def replace_keys(record) | ||
| super | ||
| owner[reflection.foreign_type] = record.class.base_class | ||
| owner[reflection.foreign_type] = record.class |
There was a problem hiding this comment.
send the class object instead of the base_class so we can try several methods like #polymorphic_name, #sti_name, #to_s etc
| end | ||
|
|
||
| include(foreign_type_extension) | ||
| ModuleGenerator.generate_and_include(self, foreign_type, name) |
There was a problem hiding this comment.
previously, the define_method blocks that were abstracted would capture the mapping variable and could cause really odd behaviour when stubbing it in specs, this ensures that all the stuff it needs is encapsulated in a class and isn't affected by the outside world.
| @@mapping[as] || {} | ||
| end | ||
|
|
||
| singleton_class.send(:alias_method, :[]=, :add) |
There was a problem hiding this comment.
thought it was odd we have an #add method that behaves canonically like []= so made an alias 🤷♂️
| spec.add_development_dependency "rspec" | ||
| spec.add_development_dependency "sqlite3" | ||
| spec.add_development_dependency "byebug" | ||
| spec.add_development_dependency "pry-byebug" |
There was a problem hiding this comment.
cause we want choice! (I hate debugger since it has no colors)
| belongs_to :source, polymorphic: true, integer_type: true | ||
| belongs_to :target, polymorphic: true, integer_type: true | ||
|
|
||
| def source=(val) |
There was a problem hiding this comment.
these appeared unused and didn't affect specs so bye bye
| @@ -0,0 +1,34 @@ | |||
| module PolymorphicIntegerType | |||
| class ModuleGenerator | |||
| def self.generate_and_include(klass,foreign_type, name) | |||
There was a problem hiding this comment.
Do you have a unit test for this method?
There was a problem hiding this comment.
It's pretty difficult to unit test this in isolation since it involves setting up polymorphic models and then overriding some of the behaviour(which is basically what we do in the specs already). This functionality was already tested thoroughly in the specs and this just abstracts it away into class
|
|
||
| # Required way to dynamically define a class method on the model | ||
| singleton_class.__send__(:define_method, "#{foreign_type}_mapping") do | ||
| define_singleton_method("#{foreign_type}_mapping") do |
There was a problem hiding this comment.
just switching to more idiomatic way of defining singleton methods
There was a problem hiding this comment.
IIRC, define_singleton_method didn't exist in earlier versions of Ruby when we first wrote this module, and __send__ was the only way to do it. This is much nicer to read.
|
|
||
| link = Namespaced::Plant.create(name: "Oak").source_links.new | ||
| expect(link.source_type).to eq("Namespaced::Plant") | ||
| allow(Link).to receive(:source_type_mapping).and_return({1 => "Person", 2 => "Animal", 3 => "Plant"}) |
There was a problem hiding this comment.
woops, don't think this is needed, gonna remove it
daniel-almeida
left a comment
There was a problem hiding this comment.
Good job with the comments! They made this much easier to review. 💯
| @@ -0,0 +1,11 @@ | |||
| module Namespaced | |||
| class Activity< ActiveRecord::Base | |||
There was a problem hiding this comment.
| class Activity< ActiveRecord::Base | |
| class Activity < ActiveRecord::Base |
fimmtiu
left a comment
There was a problem hiding this comment.
Approved with one trivial suggestion.
bb77ff5 to
9b62446
Compare
We previously were only including the
BelongsToPolymorphicAssociationmodule in specific activerecord versions. This module overides thereplace_keysmethod to ensure we aren't sending the class's#polymorphic_namewhen creating associations. When this isn't included we send the polymorphic name which includes the class's namespace and can cause us to not find it in the mapping (assuming the mappings don't include namespaces).By including it, we send the class object itself and let our fallback logic kick in to try several methods until we get a hit from the mapping.
It also cleans up a few other gotchas like creating a new ModuleGenerator. The generator defines an anonmyous module we can use for calling super since the previous implementation was all inline causing us to capture local variables in the blocks and behave oddly.