Using Ruby alias to extend a Gem - ruby-on-rails

This is more a theoretical question, but I am curious anyway. I am a ruby / ruby on rails newbie (but with a lot of ancient experience in other languages / frameworks) so this is mainly a curious / learning question. Thanks in advance for any help!
I thought I could do a quick extension to a ruby gem using alias as follows:
module InstallMyExtension
def self.included(base)
base.class_eval {
alias :some_method_in_gem_without_my_extension :some_method_in_gem
alias :some_method_in_gem :some_method_in_gem_with_my_extension
}
end
def some_method_in_gem_with_my_extension
debugger
# ... do fun stuff here
some_method_in_gem_without_my_extension
end
end
Then in some initialization file I do:
Foo::SomeControllerInFoo.send :include, InstallMyExtension
I learned this technique in the Radiant CMS where its used all over the place to extend base behavior. I understand this technique is now disapproved of, but it seemed like a quick way to just try some ideas out, before forking a branch on the gem, etc, etc
First off is there a better way in Rails 3 to do a quick hack extension like this (which might be useful just to test a theory, before forking the gems etc???)
Second off, its not working, and there are multiple things I don't understand
Then let me explain the weirdness I am seeing:
Even if I do do the the "include" as shown above, when I go into the console I see some really weird behavior, that I don't understand:
1) I type Foo::SomeControllerInFoo i get back Foo::SomeControllerInFoo as I would expect. HOWEVER if run the same exact expression a second time, Foo::SomeControllerInFoo comes back undefined!
2) Just to play around I did foo = Foo::SomeControllerInFoo, and then I can do foo.send, foo.methods, whatever I like, but only if I save the copy of the class in foo! What's with that?
3) If I now do foo.send :include, MyExtension the behavior within the debug console is as expected (i.e. the original class contained in the gem now has my behavior added to it.) HOWEVER running the same code during initialization has no effect. Nothing breaks, but the controller is not extended.

Weird that it doesn't work, I just tried again to be sure and that does the trick (put this code in a file within config/initializers).
I always use a shortcut:
alias_method_chain :some_method_in_gem, :my_extension
instead of the two aliases lines, but it's exactly the same.
You could overwrite some methods much more easily using class_eval directly. Again in an initializer:
Foo::SomeControllerInFoo.class_eval do
def some_method_in_gem
#your redefinition
end
end
Sorry but no added value for your other questions: seems just really weird and buggy.
Just to be sure, when you want to run the method defined in your controller, do:
c = Foo::SomeControllerInFoo.new
c.method_name

Related

Understanding creation of acts_as_anything

I successfully created a gem having some classes and modules to be'ing able to to something like that in ANY kind of class in a Rails project:
class AnyRubyOrActiveModelClass
acts_as_anything [:foo, :bar]
def foo
.. do some foo
end
def self.bar
.. do some bar
end
end
To do so I created a Module in my gem that looked something like that:
module InstanceMagic
class << self.class.superclass
define_method(:acts_as_anything) do |methods|
self.class_eval do
include ClassMagic
.. do some alias_method with given methods
end
end
end
This module successfully aliased my method :foo from the example above, the second module ClassMagic did the same for my :bar class method (following the advice from here).
In a testproject that approach worked very well. In a real life project it led to interference with another gem taking a - maybe similar - approach. This gem complained about missing methods in a class even when I only integrated my gem into the project - not even integrated acts_as_anything into the class.
So I broke down my code to only that:
module InstanceMagic
class << self.class.superclass
define_method(:acts_as_anything) do |methods|
#really empty here
end
end
As a result the other gem still breaks.
I used class << self.class.superclass to explicitly extend Object, so that even non ActiveSomething classes but ALL classes are affected and my acts_as_anything is available. So I remain with three questions.
Why do the last 5 lines of code break another gem and making it complain about missing methods it's trying to dynamically create? I would like to understand.
Is there a better approach to achieve my goal?
When I use method_added and singleton_method_added (what I actually do inside my modules), should I look for these methods whether they already exist, alias the "original" ones, insert my ones and call the original ones after I have done my job?
Knowing this is a lot I still hope someone can point me into the right direction.
Thank you.
Felix
The only thing I can think of is some sort of order dependency (long shot). Try to put your code in an initializer in the rails app and see if it still causes the same problems.

Overriding default Rails date_select

So what I'd like to do is to override the default date_select method (I'd like to make an 'optional / unspecified' date input). What I've tried so far is this:
lib/overrides.rb
ActionView::Helpers::DateHelper::DateTimeSelector.class_eval do
def build_selects_from_types(order)
select = ''
order.reverse.each do |type|
separator = separator(type) unless type == order.first # don't add on last field
select.insert(0, separator.to_s + send("select_#{type}").to_s)
end
select.insert(0, '<p>HI!!</p>') # or whatever...
select.html_safe
end
end
I then required 'overrides' at the bottom of environment.rb but when starting WEBrick I get this error:
~/.rvm/gems/ruby-1.9.2-p0/gems/activesupport-3.0.0/lib/active_support/dependencies.rb:479:in
`load_missing_constant':
ActionView::Helpers is not missing
constant DateTimeSelector!
(ArgumentError)
So I obviously don't really know what I'm doing but this seems like a reasonable thing to attempt at least.
The error above seems to imply that it can't find the DateTimeSelector class but I've peered at the code in ~/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.0.0/lib/action_view/helpers/date_helper.rb and I think I've got the module hierarchy right. Is it because it's a private Rails class?
Any thoughts are most welcome :)
In Ruby doesn't exist the concept of private class. Classes are never private.
The reason for the error is because the path is invalid. It should be
ActionView::Helpers::DateTimeSelector
not
ActionView::Helpers::DateHelper::DateTimeSelector
BTW, what you are trying to do is absolutely a bad idea. The fact that Ruby gives you the power of reopening classes and "patch" methods, doesn't mean you should do this for such this kind of customizations.
You should never make these chances to the Rails codebase unless you really know what you are doing. The risk is to break things that depends on this method.
The right way to go is do define a new helper and build your own logic.

Ruby on Rails: alias_method_chain, what exactly does it do?

I've tried reading through various blog posts that attempt to explain alias_method_chain and the reasons to use it and not use it. In particular, I took heed to:
http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain
and
http://yehudakatz.com/2009/03/06/alias_method_chain-in-models/
I still do not see any practical use for alias_method_chain. Would anyone be able to explain a few things.
1 - is it still used at all?
2 - when would you use alias_method_chain and why?
1 - is it still used at all?
Apparently yes, alias_method_chain() is still used in Rails (as of version 3.0.0).
2 - when would you use
alias_method_chain and why?
(Note: the following is largely based on the discussion of alias_method_chain() in Metaprogramming Ruby by Paolo Perrotta, which is an excellent book that you should get your hands on.)
Let's start with a basic example:
class Klass
def salute
puts "Aloha!"
end
end
Klass.new.salute # => Aloha!
Now suppose that we want to surround Klass#salute() with logging behavior. We can do that what Perrotta calls an around alias:
class Klass
def salute_with_log
puts "Calling method..."
salute_without_log
puts "...Method called"
end
alias_method :salute_without_log, :salute
alias_method :salute, :salute_with_log
end
Klass.new.salute
# Prints the following:
# Calling method...
# Aloha!
# ...Method called
We defined a new method called salute_with_log() and aliased it to salute(). The code that used to call salute() still works, but it gets the new logging behavior as well. We also defined an alias to the original salute(), so we can still salute without logging:
Klass.new.salute_without_log # => Aloha!
So, salute() is now called salute_without_log(). If we want logging, we can call either salute_with_log() or salute(), which are aliases of the same method. Confused? Good!
According to Perrotta, this kind of around alias is very common in Rails:
Look at another example of Rails
solving a problem its own way. A few
versions ago, the Rails code contained
many instances of the same idiom: an
Around Alias (155) was used to add a
feature to a method, and the old
version of the method was renamed to
something like
method_without_feature(). Apart from
the method names, which changed every
time, the code that did this was
always the same, duplicated all over
the place. In most languages, you
cannot avoid that kind of duplication.
In Ruby, you can sprinkle some
metaprogramming magic over your
pattern and extract it into its own
method... and thus was born
alias_method_chain().
In other words, you provide the original method, foo(), and the enhanced method, foo_with_feature(), and you end up with three methods: foo(), foo_with_feature(), and foo_without_feature(). The first two include the feature, while the third doesn't. Instead of duplicating these aliases all around, alias_method_chain() provided by ActiveSupport does all the aliasing for you.
alias_method_chain has been deprecated in Rails 5 in favour of Module#prepend.
Pull request: https://github.com/rails/rails/pull/19434
Changelog: https://github.com/rails/rails/blob/b292b76c2dd0f04fb090d49b90716a0e6037b41a/guides/source/5_0_release_notes.md#deprecations-4
I'm not sure if it's gone out of style with Rails 3 or not, but it is still actively used in versions before that.
You use it to inject some functionality before (or after) a method is called, without modifying any place that calls that method. See this example:
module SwitchableSmtp
module InstanceMethods
def deliver_with_switchable_smtp!(mail = #mail)
unless logger.nil?
logger.info "Switching SMTP server to: #{custom_smtp.inspect}"
end
ActionMailer::Base.smtp_settings = custom_smtp unless custom_smtp.nil?
deliver_without_switchable_smtp!(mail = #mail)
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
receiver.class_eval do
alias_method_chain :deliver!, :switchable_smtp
end
end
end
That's an addition to ActionMailer to allow swapping out of the SMTP settings on each call to deliver!. By calling alias_method_chain you are able to define a method deliver_with_switchable_smtp! in which you do your custom stuff, and call deliver_without_switchable_smtp! from there when you're done.
alias_method_chain aliases the old deliver! to your new custom method, so the rest of your app doesn't even know deliver! now does your custom stuff too.
is it used at all?
Seems so. It's a common practice among Rails developers
when would you use alias_method_chain and why?
Despite the warnings, alias_method_chain is still the main strategy used when injecting functionality to an existing method, at least was in Rails 2.x and is followed by many people extending it. Yehuda ought to remove alias_method_chain from rails 3.0 to say from his posts and comments in Rails tickets. It is still used by many extensions that add custom behavior at certain points of the execution, such as loggers, error reporters, benchmarking, data injection, etc.
IMO, the best alternative is to include a module, thus you have decoration over delegation. (For example, follow example 4 in this post). That way you can alter the objects even individually if you'd like, without polluting the class' methods. The downside to this is that the method lookup chain increases for each module you inject, but this is what modules are for anyway.
Very interesting question, will keep a look on what other people think about it.

Ruby on Rails: Learning ActionController class - Question on $:.unshift activesupport_path and autoload method

Inside ActionController class (rails/actionpack/lib/action_controller.lib) I found several weird code. I don't really have a mentor to learn Ruby on Rails from, so this forum is my only hope:
Question #1: Could anyone help me explain these lines of codes?
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
Especially the line with $:.unshift activesupport_path
In my thought, it tries to require active_support class, and if that doesn't work, it looks if activesupport_path is a directory, if it is, then . . . I totally lost it.
Question #2: What autoload method is for?
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
.
.
.
Question #3: If I later find a method I don't understand what for, how is the best way to find out? As for that autoload method case, I tried to find it across my project (I have my Rails code frozen there) but couldn't find any clue. I searched for "def autoload". Am I doing things wrong? Is my IDE, TextMate just doesn't cut it?
Thank you!
In order for a file to be required you have to ensure that the path to it is in the Ruby $LOAD_PATH variable. This is has a short-hand version $: for legacy reasons, inheriting this from Perl.
When you call require, the interpreter looks for a .rb file in each of the paths given there until it finds a match. If it finds one, it is loaded. If not you get an exception.
Often you will see lines like this in files:
# script/something
# This appends "script/../lib" to the $LOAD_PATH, but this expands to
# something like "/home/user/project/lib" depending on the details of
# your installation.
$: << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))
You can use standard Array modifiers on $LOAD_PATH like unshift, push, and <<.
The first block of code is attempting to load active_support and only if that fails does it go about modifying the $LOAD_PATH to include the likely location of this file based on the path to the file making the require call. They do this because typically all gems from the Rails bundle are installed in the same base directory.
The reason for using unshift is to put that path at the highest priority, inserted at the front of the list. The << or push method adds to the end, lowest priority.
When you require a file it is loaded in, parsed, and evaluated, an operation which can take a small but measurable amount of time and will consume more memory to hold any class or method definitions inside the file, as well as any data such as string constants that may be declared. Loading in every single element of a library like ActiveRecord using require will require a considerable amount of memory, and this will import every database driver available, not just the ones that are actually used.
Ruby allows you to declare a class and a path to the file where it is defined, but with the advantage of not actually loading it in at that moment. This means that references to that class don't cause script errors in other parts of your application that make use of them.
You will often see declarations like this:
class Foo
# Declare the class Foo::Bar to be defined in foo/bar.rb
autoload(:Bar, 'foo/bar')
end
When using autoload you need to keep in mind that the class name is always defined within the scope of the module or class declaring it. In this example Bar is within Foo, or Foo::Bar using Ruby naming conventions.
When you make use of the Bar class, the foo/bar.rb file will be required. Think of it as creating a stub Bar class that transforms into the real class once it's actually exercised.
This is a great way of keeping a lot of options open, with many different modules ready to use, but without having to load everything into memory up front.
As for the third question, searchable documentation like APIDock will help you try and find more information on methods. The distinction between Ruby and Rails is often blurred, so you may have to check through both to be sure. Rails adds a lot of methods to core Ruby classes, so don't take the listing of methods available to be complete on either side. They work in conjunction.
Sometimes it pays to search for def methodname when trying to find out about where methodname originates, although this covers only conventional declarations. That method may be an alias from a mechanism like method_alias or may have been dynamically created using define_method, you can never really be sure until you dig around. At least 90% of the methods in Rails are declared the conventional way, though, so most of the time a simple search will yield what you want.

Ruby on Rails: Modules vs. Classes

I'm trying to add a function that will be accessible throughout all parts of my program. I want something like:
def GlobalFunctions.my_function(x,y)
puts x + y
end
to be accessible for all models. Specifically I am trying to use a function like this in my seeds.rb file but I am most likely going to be reusing the code and don't want any redundancy. Now I know I can make a simple class, but I could also make a module. What are some reasons to go in either direction? And once I've decided on which type to use, how do I make it accessible throughout the whole program?
I have tried a module, but I keep getting " Expected app/[module file] to define [ModuleName]"
You'd define a class for something you'll want to make instances of. In this case, a module would probably be better, as modules basically just group code together:
module GlobalFunctions
def self.my_function(x,y)
puts x+y
end
end
GlobalFunctions.my_function(4,5) # => 9
Alternatively, you could define it on Kernel, and then just call it anywhere. Kernel is where methods like puts are defined.
def Kernel.my_function(x,y)
puts x + y
end
my_function(4,5) # => 9
Adding methods to Kernel (answer from PreciousBodilyFluids) is usually considered a bad smell and can lead to some really hard to find bugs in large projects.
It's much more accepted to relevantly namespace the code and put in to /lib/.
class Formatting
def self.bold(str)
return "<strong>#{str}</strong>"
end
end
You can then:
require 'formatting'
puts Formatting.bold("text")
or
require 'formatting'
include Formatting
puts bold("text")
It'll be clear to anyone who comes to the code later what you're using too. If you're using Rails you won't need the require.
PreciousBodilyFluids is correct, and if this GlobalFunctions is part of a RoR project, you may want to name the file global_functions.rb and place it in the lib/ directory to help you avoid the error message you posted at the end of your question.

Resources