views:

1236

answers:

4

Rails provides a number of extensions to core Ruby classes. One of these is the to_sentence method, added to the Array class, allowing 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 } # give something like: "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).

+10  A: 

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.

pts
like was mentioned, add all your custom extensions to a config/initializers/*.rb file
cpjolicoeur
Thanks. Will check it out.
dcw
+4  A: 

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
August Lilleaas
Thanks. btw, I think it's good practice to remember the previous defn, i.e. add an "alias old_to_sentence to_sentence" before the defn of the new method and its aliasing.
dcw
+4  A: 

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)

nasmorn
A: 

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.