Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redefining Rails' methods

The Rails method Array#to_sentence allows for the following:

['a', 'b', 'c'].to_sentence # gives: "a, b, and c"

I would like to extend this method to allow it to take a block, so that you can do something like the following (where people is an array of Person objects, which have the name attribute):

people.to_sentence { |person| person.name }
# => "Bill, John, and Mark"

I don't have a problem with writing the extension method. But I can't work out where to put it. The Rails core extensions get loaded somewhere down in the depths of ActiveSupport. My need is for a place where user-defined code is always loaded, and is pre-loaded (before any application code).

like image 200
dcw Avatar asked May 09 '09 00:05

dcw


People also ask

What is method redefinition?

The redefined method accesses the private components of the redefined class and not any private components of the same name in the superclass. In the redefined method, the implementation of the direct superclass can be called using super->meth.

What is method redefinition in Java?

Static method of parent class can be re-declared by child class, which hides the parent class static method. This is known as Redefinition in Java.

What is Define_method in Ruby?

define_method is a method defined in Module class which you can use to create methods dynamically. To use define_method , you call it with the name of the new method and a block where the parameters of the block become the parameters of the new method.


4 Answers

Create config/initializers/super_to_sentence.rb. All files in this directory are loaded after Rails has been loaded, so you'll have a chance to override Rails' definition of Array#to_sentence.

For code you want to load before Rails gets loaded, add it to config/environment.rb.

like image 188
pts Avatar answered Oct 04 '22 18:10

pts


I like to do this:

# config/initializers/app.rb
Dir[File.join(Rails.root, "lib", "core_ext", "*.rb")].each {|l| require l }

# lib/core_ext/array.rb
class Array
  def to_sentence_with_block(*args, &block)
    if block_given?
      # do something...
      # to_sentence_without_block(*args) perhaps?
    else
      to_sentence_without_block(*args)
    end
  end
  alias_method_chain :to_sentence, :block
end
like image 45
August Lilleaas Avatar answered Oct 04 '22 19:10

August Lilleaas


I think this is an ugly idea. Why dont you just write

people.collect { |person| person.name }.to_sentence

This looks almost the same and will not confuse other people reading your code (like yourself in 2 years)

like image 14
nasmorn Avatar answered Oct 04 '22 17:10

nasmorn


just searching around the web, seems like good practice is to add it in lib/

so if you wanna extend some ruby class (in my case, DateTime), just put the code in .rb and then in config/environment.rb:

  config.after_initialize do
    require "lib/super_datetime.rb"
  end

my super_datetime.rb looks like this (code from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/140184):

class DateTime
  def days_in_month
    self::class.civil(year, month, -1).day
  end
  alias dim days_in_month
  def weekdays
    (1..dim).to_a
  end
end

restart your server and you'll have access to the new method for all DateTime objects.

like image 1
GibsoundSG Avatar answered Oct 04 '22 18:10

GibsoundSG