I won't say much about how we handle our system extensions at Groupon, except, that it is very structured with a centralized place to discover all of the extensions to Rails internals in a single place (expect a more formal description of our process later from our Team!). Also, the extension points that we chose to use have been the parts of Rails which are the least subject to change (and have demonstrated the greatest resiliency and consistency over the last 3-4 years of active development).
Anyway, this morning, I ran into one of the (very few) cases where testing truly required assertions at various points in an alias_method_chain stack. As you can imagine, if you are alias_method_chaining ActiveRecord::Base#find, for example, you'll most likely have architected your system in such a way, that every link in the chain is standard, unalterable behavior (we absolutely do not muck with #find, btw. It is already an atrocious method). For these cases, I employ this little hack...
ActiveSupport::CoreExtensions::Module.class_eval do
def alias_method_chain_unlink(target, feature)
# Strip out punctuation on predicates or bang methods since
# e.g. target?_without_feature is not a valid method name.
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
yield(aliased_target, punctuation) if block_given?
with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
alias_method target, without_method
case
when public_method_defined?(without_method)
public target
when protected_method_defined?(without_method)
protected target
when private_method_defined?(without_method)
private target
end
end
end
Notice its a natural inversion of 'alias_method_chain' implemention. This method would probably never fly with the core team, so it is merely a re-implementation/repurposing rather than an attempt to DRY the aliasing code.
As an example of what I'm talking about, imagine that you have this chain..
class User
def cry
puts "cry"
end
end
User.new.cry
User.class_eval do
def cry_with_emotion
puts "WAH WAH WAH"
cry_without_emotion
end
alias_method_chain :cry, :emotion
end
User.new.cry
Let us say that you want to alter the emotive nature of that extension in the same ruby session
User.class_eval do
def cry_with_emotion # slightly less emotive
puts "Wah Wah Wah"
cry_without_emotion
end
end
User.new.cry
User.class_eval do
define_method :cry_with_emotion do
puts "Wah Wah Wah"
end
end
User.new.cry # no luck again eh??
As you can see, redefining an interstitial method does not automatically make it part of the chain! Try this on for size (after redefining your method).
User.class_eval do
alias_method_chain_unlink :cry, :emotion
alias_method_chain :cry, :emotion
end
User.new.cry
That works! If you can imagine it, this hack can serve to either allow you to remove something out of a long alias_method_chain, or, allow redefinition of chain link (for lack of a better term)! I might have more to say about metaprogramming in Ruby later, but for now, may this approach save someone as much time as it saved me!
Genial fill someone in on and this enter helped me alot in my college assignement. Gratefulness you seeking your information.
ReplyDeleteNice dispatch and this mail helped me alot in my college assignement. Thank you on your information.
ReplyDeletewhy not...
ReplyDeleteAmiable dispatch and this enter helped me alot in my college assignement. Say thank you you for your information.
ReplyDeleteAmiable fill someone in on and this post helped me alot in my college assignement. Gratefulness you on your information.
ReplyDeletenice topic , hunt this from blogsearch and good luck for you.just tally up the rss feed to my reader,keep bring up to date!
ReplyDeleteGenial dispatch and this fill someone in on helped me alot in my college assignement. Thanks you for your information.
ReplyDeleteSorry for my bad english. Thank you so much for your good post. Your post helped me in my college assignment, If you can provide me more details please email me.
ReplyDelete