Overriding default Rails date_select - ruby-on-rails

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.

Related

Namespaced class references inside other classes generate uninitialized constant errors

in my Rails app I'm making liberal use of class namespacing like so:
class Person
class PrimaryEmailAddress
class Update < Trailblazer::Operation
def persist(options, **)
Shared::Property::HasOne::Update.(
{property_class: PrimaryEmailAddress, property_value: #params[:email_address]},
'current_user' => self['current_user'], parent_node: options[:person])
end
end
end
end
Unfortunately, the ruby interpreter keeps thinking that my namespaced, embedded functions are part of other class' namespace, and it throws uninitialized constant errors. i.e. at runtime I get an error like this
uninitialized constant Person::Shared::Property
Basically, the interpreter is looking at this function Shared::Property::HasOne::Update and treating it like it's in the Person namespace, when it's not, and then throwing an error.
I can get around the problem by explicitely stating that the function is in the Object namespace, like so Object::Shared::Property::HasOne::Update, but adding Object:: all over the place is annoying and ugly. Is there a better solution anyone knows of? Short of rewriting all of my class names / namespacing.
I imagine part of the problem is that Person::Shared is a valid namespace, and so the ruby interpreter starts treating the Shared::Property:: ... reference like I just forgot to add Person to the beginning.
I really appreciate any feedback!!
Found the answer: by adding :: in front of a class reference, I force the ruby interpreter to look at the top level namespace. i.e. instead of Object::Shared::Property::HasOne::Update I can do ::Shared::Property::HasOne::Update, which I find more readable.
While there are a fair number of questions about uninitialized constant problems, I had trouble finding this answer because all the questions I found were framed in specific cases, rather then being genericized. It seems likely that this is a duplicate question and I just haven't found the other one, but I'm going to post this QA here in case I'm wrong and this helps someone else running into this same problem.
This question ended up leading me to the correct answer.

Class name is not defined in the module unless it's used in the code

I have created some classes in a module, f.i. Request (app/models/api/request.rb)
class Api::Request
end
And now I want to check in the console does the class exist?
>> Api.const_defined?('Request')
=> false
>> Api::Request
=> Api::Request
>> Api.const_defined?('Request')
=> true
Strange, but the script cannot see the class name unless I use it in the code.
How to fix this? And if you can explain the issue, it will be great.
The project uses Rails 2.3
The problem here is with the rails autoload mechanism. Basically what it does is to react to the method const_missing which you can define on every object. When this method is called, Rails looks for a file which is likely to contain a definition for the missing constant and requires it. The problem is that
Api::Request
triggers const_missing but
Api.const_defined?('Request')
does not.
You can write your own const_defined? like this (the code would go into a rails initializer):
def Module.autoload_const_defined?(name)
self.const_get name
ensure
return self.const_defined?(name)
end
which first tries to autoload the constant.
For anyone coming upon this in future - it's much better style to do something like the following:
def Module.autoload_const_defined?(name)
const_get(name)
rescue NameError => e
const_defined?(name)
end
return inside an ensure block means that any exceptions that occur are silently thrown away, and have the potential to make debugging really tricky.

Using Ruby alias to extend a Gem

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

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.

How to find where a method is defined at runtime?

We recently had a problem where, after a series of commits had occurred, a backend process failed to run. Now, we were good little boys and girls and ran rake test after every check-in but, due to some oddities in Rails' library loading, it only occurred when we ran it directly from Mongrel in production mode.
I tracked the bug down and it was due to a new Rails gem overwriting a method in the String class in a way that broke one narrow use in the runtime Rails code.
Anyway, long story short, is there a way, at runtime, to ask Ruby where a method has been defined? Something like whereami( :foo ) that returns /path/to/some/file.rb line #45? In this case, telling me that it was defined in class String would be unhelpful, because it was overloaded by some library.
I cannot guarantee the source lives in my project, so grepping for 'def foo' won't necessarily give me what I need, not to mention if I have many def foo's, sometimes I don't know until runtime which one I may be using.
This is really late, but here's how you can find where a method is defined:
http://gist.github.com/76951
# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
def crime
end
end
class Fixnum
include Perpetrator
end
p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>
If you're on Ruby 1.9+, you can use source_location
require 'csv'
p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>
CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]
Note that this won't work on everything, like native compiled code. The Method class has some neat functions, too, like Method#owner which returns the file where the method is defined.
EDIT: Also see the __file__ and __line__ and notes for REE in the other answer, they're handy too. -- wg
You can actually go a bit further than the solution above. For Ruby 1.8 Enterprise Edition, there is the __file__ and __line__ methods on Method instances:
require 'rubygems'
require 'activesupport'
m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>
m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64
For Ruby 1.9 and beyond, there is source_location (thanks Jonathan!):
require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago> # comes from the Numeric module
m.source_location # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]
I'm coming late to this thread, and am surprised that nobody mentioned Method#owner.
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Copying my answer from a newer similar question that adds new information to this problem.
Ruby 1.9 has method called source_location:
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)
This has been backported to 1.8.7 by this gem:
ruby18_source_location
So you can request for the method:
m = Foo::Bar.method(:create)
And then ask for the source_location of that method:
m.source_location
This will return an array with filename and line number.
E.g for ActiveRecord::Base#validates this returns:
ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0#arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]
For classes and modules, Ruby does not offer built in support, but there is an excellent Gist out there that builds upon source_location to return file for a given method or first file for a class if no method was specified:
ruby where_is module
In action:
where_is(ActiveRecord::Base, :validates)
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0#arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]
On Macs with TextMate installed, this also pops up the editor at the specified location.
Maybe the #source_location can help to find where is the method come from.
ex:
ModelName.method(:has_one).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
OR
ModelName.new.method(:valid?).source_location
Return
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]
This may help but you would have to code it yourself. Pasted from the blog:
Ruby provides a method_added()
callback that is invoked every time a
method is added or redefined within a
class. It’s part of the Module class,
and every Class is a Module. There are
also two related callbacks called
method_removed() and
method_undefined().
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
If you can crash the method, you'll get a backtrace which will tell you exactly where it is.
Unfortunately, if you can't crash it then you can't find out where it has been defined. If you attempt to monkey with the method by overwriting it or overriding it, then any crash will come from your overwritten or overridden method, and it won't be any use.
Useful ways of crashing methods:
Pass nil where it forbids it - a lot of the time the method will raise an ArgumentError or the ever-present NoMethodError on a nil class.
If you have inside knowledge of the method, and you know that the method in turn calls some other method, then you can overrwrite the other method, and raise inside that.
Very late answer :) But earlier answers did not help me
set_trace_func proc{ |event, file, line, id, binding, classname|
printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
# call your method
set_trace_func nil
You might be able to do something like this:
foo_finder.rb:
class String
def String.method_added(name)
if (name==:foo)
puts "defining #{name} in:\n\t"
puts caller.join("\n\t")
end
end
end
Then ensure foo_finder is loaded first with something like
ruby -r foo_finder.rb railsapp
(I've only messed with rails, so I don't know exactly, but I imagine there's a way to start it sort of like this.)
This will show you all the re-definitions of String#foo. With a little meta-programming, you could generalize it for whatever function you want. But it does need to be loaded BEFORE the file that actually does the re-definition.
You can always get a backtrace of where you are by using caller().

Resources