Rails

From WhyNotWiki
Jump to: navigation, search

See also:

Contents

Getting started / tutorials / books

ONLamp.com tutorial

Ruby syntax highlighting in vim

Reference

Advice: "Work on the models and domain logic, trying stuff out in the console. Leave the controller/view stuff ’til later…" (http://woss.name/2006/05/07/notes-from-a-rails-course/)

"config/database.yml goes through the erb processor before YAML gets it." (http://woss.name/2006/05/07/notes-from-a-rails-course/) So you can put Ruby code in there.

Downloadable cheat sheets

I wouldn't actually recommend these, as I'd rather make my own cheat sheet (in this wiki) as I go along... (A cheat sheet's usefulness is kind of a personal matter.)

http://www.blainekendall.com/index.php/rubyonrailscheatsheet/

http://slash7.com/cheats/rails_files_cheatsheet.pdf

http://slash7.com/cheats/form_helpers.pdf

http://slash7.com/cheats/activerecord_cheatsheet.pdf


Configuration

http://glu.ttono.us/articles/2006/05/22/configuring-rails-environments-the-cheat-sheet

Checklist for creating a new Rails application

  • rails app_name
  • add to Subversion (see "balloon" plugin)
  • change session ID (see the section under Sessions)
  • set up ExceptionNotifier

Commands/command-line tools

rake

Rake in the context of Rails

How does Rails find/load its tasks and my tasks?

your_app/Rakefile:

require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

require 'tasks/rails'

/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/tasks/rails.rb:

  1 $VERBOSE = nil
  2
  3 # Load Rails rakefile extensions
  4 Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext }
  5
  6 # Load any custom rakefile extensions
  7 Dir["./lib/tasks/**/*.rake"].sort.each { |ext| load ext }
  8 Dir["./vendor/plugins/*/tasks/**/*.rake"].sort.each { |ext| load ext }

test-related tasks

rake test                           # Test all units and functionals / Test app + shared Glass.net models
rake test:functionals               # Run tests for functionalsdb:test:prepare
rake test:integration               # Run tests for integrationdb:test:prepare
rake test:plugins                   # Run tests for pluginsenvironment
rake test:recent                    # Run tests for recentdb:test:prepare
rake test:uncommitted               # Run tests for uncommitteddb:test:prepare
rake test:units                     # Run tests for unitsdb:test:prepare

database tasks

...are in /usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/tasks/databases.rake

db:migrate, for example, is defined as:

  desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
  task :migrate => :environment do
    ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
    Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
  end

...which then uses the Migrator class defined here:

/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/migration.rb

  class Migrator#:nodoc:
    class << self
      def migrate(migrations_path, target_version = nil)
        Base.connection.initialize_schema_information

        case
          when target_version.nil?, current_version < target_version
            up(migrations_path, target_version)
          when current_version > target_version
            down(migrations_path, target_version)
          when current_version == target_version
            return # You're on the right version
        end
      end

      def up(migrations_path, target_version = nil)
        self.new(:up, migrations_path, target_version).migrate
      end

[Rails dissections (category)]

script/console

You can specify an environment: script/console test

Understanding the Script files (./script/*)

http://wiki.rubyonrails.org/rails/pages/UnderstandingTheScriptFiles

./script/about

About your application's environment
Ruby version                 1.8.4 (i486-linux)
RubyGems version             0.8.11
Rails version                1.1.6
Active Record version        1.14.4
Action Pack version          1.12.5
Action Web Service version   1.1.6
Action Mailer version        1.2.5
Active Support version       1.3.1
Application root             /wherever/
Environment                  development
Database adapter             mysql
Database schema version      79

script/plugin

  -x, --externals                  Use svn:externals to grab the plugin.
                                   Enables plugin updates and plugin versioning.
  -o, --checkout                   Use svn checkout to grab the plugin.
                                   Enables updating but does not add a svn:externals entry.










Model-level: ActiveRecord

ActiveRecord edit

ActiveRecord / Database schemas and migrations

ActiveRecord / Database schemas and migrations edit


This is for information regarding the creation of, limitations of, etc. database schemas, ConnectionAdapters, tables, column types, etc. Most of this is done through the use of migrations, although some of these topics affect ActiveRecord in general, since your database schema can affect all aspects of ActiveRecord.

Unsorted

http://www.perham.net/mike/log/?p=462 Mutterings » Blog Archive » Rails Migrations

http://wiki.rubyonrails.org/rails/pages/Foreign+Key+Schema+Dumper+Plugin Foreign Key Schema Dumper Plugin in Ruby on Rails

Data / one-time / non-sequential migrations

...

Data migrations

The tangled web: data migrations (http://blog.brightredglow.com/2006/9/6/the-tangled-web-data-migrations). Retrieved on 2007-05-11 11:18.


Recently we had an application that needed a bunch of preloaded data. Unfortunately, it wasn’t a one-shot loading of data once the structure was mostly solidified. I needed to try out stuff and wanted the super useful migrations to be my tool. Previously, this had been done with some ad hoc rake tasks, which turned out to be, frankly, a mess. The single biggest challenge working with the data was the fact that the state of the data would be changing independent of the migrations. There was no way to ensure the state between one migration and the next. I decided that the one thing I could assert before and after a migration was that certain records with certain id’s did not exist (pre-) and then did exist (post-migration) (reverse that for migrating down). It turned out this would be good enough for what I needed. ... This capture method adds callbacks for after_create and after_destroy to keep track of the id’s. This is written out to a file in a data subdirectory of the db/migrate directory. These files can be ignored by Subversion so each developer can run the migrations on their own databases without clashing. ... This has been useful for adding and removing data while trying out different table structures. Obviously, it’s a rather targeted solution. I’ll be packaging it as a plugin and posting it to PLANET ARGON’s community site soon.


Problem: Old migrations can stop working when you make changes to the code (models)

(Includes subproblem: A migration may use a feature of a model that you later want to delete)

Should old migrations be updated to work with the new code? [1] says yes.

Migrating in two dimensions (http://scottstuff.net/blog/articles/2005/10/31/migrating-in-two-dimensions). Retrieved on 2007-05-11 11:18.


The big problem is that we’ve been using migrations wrong the whole time, and we just realized it. There are probably a dozen bugs in Typo’s bug tracker that boil down to “I fell behind the trunk and now rake migrate throws exceptions and I can’t upgrade anymore.” The problem is that migrations are designed to run against an earlier version of your database, but they use the current version of your code. The first time that this caused problems was with the migration from Typo 2.0 to 2.5–we’d added two new fields to articles. Migration number 7 added the permalink field and a before_save hook to make sure that all saved articles have permalinks. Then migration number 9 added GUIDs and a second before_save hook to fill the guid field. Both migrations did Articles.find(:all).each { |a| a.save } to update each Article and populate the new fields. This worked great for developers who frequently upgraded. A few days after the GUID migration went in, though, we started getting weird bug reports–users who tried to do both upgrades at the same time found that migration number 7 was dying. What was happening was that migration number 7 added the new permalink field to articles, but when it went to run the save loop both before_save hooks ran, and Typo tried to add a GUID to each article. However, the guid field didn’t exist yet, so the migration threw a bunch of exceptions and died. This caused a bunch of grumbling on the Typo IRC channel. We threw around a bunch of possible fixes. Our favorite was separating migrations into two parts–a schema change part and a data change part. First we’d run all of the schema changes, and then update all of the data. As a work-around, we added a hack that checked the current schema version and disabled specific before_save filters for older versions. We managed to keep this little bandaid working until a couple weeks ago, when a huge set of new migrations went it; they renamed the articles table and merged several other tables into the new contents table using STI. And, again, we found that older migrations broke when users tried to upgrade from Typo 2.5.6 to the current dev tree. Unlink the permalink/guid case, this time there was no simple workaround. We couldn’t just add a couple if statements in a filter and make it all go away. The fundamental problem is that we were using the wrong mental model for migrations. I saw migrations as a one-dimensional thing–a list of steps for migrating old data into the new format. In this view, the migration for going from schema version 6 to schema version 7 is constant–once it’s been written, the only reason to change it is if a bug turns up in the logic for that migration. Otherwise, the migration code should remain unchanged over time. And that’s the problem–migrations aren’t one-dimensional. They are (and need to be) two dimensional–the schema version is one dimension and the code version is the other. Individual migrations exist to migrate from a specific old schema version to the current version, using the current code. Each migration should change over time to adapt to the changes in the code. So, the right fix for the permalink migration that caused so many problems wasn’t to add a bunch of logic to before_save. Instead, we should have deleted the entire save loop from the migration, and trusted the GUID migration to update both fields. If that wasn’t good enough, then we should have added a new migration at the end to do permalink cleanup after the GUIDs were added. Once I came to grips with this, the migration changes needed to allow 2.5.x users to upgrade to the current trunk were pretty simple, and took about 5 minutes to write and test. Or was I the only person in the Rails universe who thought about migrations this way?


Database connection adapters / RDBMSs

MySQL

How ActiveRecord column types map to MySQL database types

ruby mysql
 :string, :limit => 1 varchar(1)
 :string varchar(255)
 :text text
 :text, :limit => 1 tinytext
 :integer int(11)
 :integer, :limit => 1 int(1)
 :integer, :limit => 10 int(10)
 :float float
 :decimal decimal(10,0)
 :decimal, :precision => 30 decimal(30,0)
 :decimal, :precision => 30, :scale => 10 decimal(30,10)
 :decimal, :scale => 10 decimal(10,0)
 :decimal, :precision => 4, :scale => 10 mysql::error: #42000for float(m,d), double(m,d) or decimal(m,d), m must be >= d (column 'column name')
 :datetime datetime
 :timestamp datetime
 :time time
 :date date
 :binary blob
 :binary, :limit => 8 tinyblob
 :boolean tinyint(1)


SQLite

SQLite is great because it spares you the work of having to create a new database (and configure users and permissions) every time you make a new app. It also lets you distribute your app and people can pretty much use it right "out of the box" (assuming they have SQLite installed on their system).

http://wiki.rubyonrails.org/rails/pages/HowtoUseSQLite HowtoUseSQLite in Ruby on Rails

This is the config/database.yml setup that I like to use when I'm just getting started on a project and I don't want to bother setting up a MySQL database:

development:
  adapter: sqlite3
  database: db/development.sqlite

test:
  adapter: sqlite3
  database: db/test.sqlite

production:
  adapter: sqlite3
  database: db/production.sqlite

On the other hand, it could easily be argued that if you're going to end up migrating to MySQL eventually, then you may as well just get it over with at the beginning.

In any case, though, I will usually end up migrating to MySQL/PostgreSQL eventually!

Here are some reasons to switch from SQLite to MySQL:

  • If you need to share the same database between multiple applications.
  • Compatibility/sameness with production: If you plan on deploying anytime soon, then it's best to develop using the same database that you'll be deploying to, in case there are any behavioral differences or feature differences between the databases. (ActiveRecord tries to make everything behave the same on all RDBMS's, but it can't make everything the same...)

So... How do you migrate your data over to a MySQL database, assuming you've accumulated some useful data in your SQLite database that you don't want to lose?

Template loop detected: Template:Include with edit link

What if I already have a database schema?? Can I still use migrations??

That's okay. Yep, you can. Just not to create your initial tables/columns...

http://greenprogrammer.blogspot.com/2006/04/further-adventures-along-our-trail.html

Use rake db:schema:dump one time when you want to start using migrations from a pre-existing SQL DDL generated schema. At this point you will be at version 0. Generate your next model or generate a new migration which will be version 1. If you back out via rake migrate VERSION=0 you will be at the last point before you started using migrations.

Here's how I do it:

  • rake db:schema:dump
  • script/generate all your models (using --skip-migration). (If you forget to use --skip-migrations, just delete any migrations that it generates (if you don't it will throw an error when you migrate, saying the table already exists).)
  • rake migrate. It may look like nothing happened, but this will actually create a new table in your database called schema_info:
# select * from schema_info;
 version
---------
       0
(1 row)
  • You can use script/console and interact with your models now.

You can also take the schema that db:schema:dump generates and throw it into your migration_000.rb.

Can I use a primary key other than 'id'?

Yep. Let's say you want the primary key of your users table to be user_id...

  • In your model, set_primary_key :user_id
  • In your fixture, make sure you specify user_id: 1 rather than id: 1 (which is what Rails will generate for you)

How do I add support for DB-specific data types in ActiveRecord?

http://ghouston.blogspot.com/2006/07/let-activerecord-support-enterprise.html Greg Houston: Let ActiveRecord support Enterprise Databases.


valid column types

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html :

 :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean

:decimal

PragDave (2006-07-14). Decimal Support in Rails (http://pragdave.pragprog.com/pragdave/2006/07/decimal_support.html). Retrieved on 2007-05-11 11:18.


A couple of blog posts ago, I commented on the dangers of converting database decimal columns into Ruby floats. And, five months early, Santa delivers. In the Rails trunk, numeric and decimal database columns with a scale factor are now converted into Ruby BigDecimal objects. If the scale factor is zero, they instead become integers. Migrations now support decimal columns too, with the addition of two new attributes, precision and scale.

   add_column :orders, :price,
              :decimal, :precision => 8, :scale => 2

valid methods

http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

  • create_table(name, options)
  • drop_table(name)
  • rename_table(old_name, new_name)
  • add_column(table_name, column_name, type, options) A default value can be specified by passing an options hash like { :default => 11 }.
  • rename_column(table_name, column_name, new_column_name)
  • change_column(table_name, column_name, type, options)
  • remove_column(table_name, column_name)
  • add_index(table_name, column_names, index_type, index_name)
  • remove_index(table_name, index_name)

how to wrap it in a transaction/commit

  def self.up
    execute "begin;"
      add_column 'table', 'column', :string
      ...
    execute "commit"
  end

Better, if it works...

  def self.up
    ActiveRecord::Migration.transaction {
      create_table :crawls do |t|
        t.column :created_at, :datetime, :null => false
      end
      add_column :snapshots, :crawl_id, :integer
      foreign_key :snapshots, :crawl_id, :crawls
    }
  end

Migrations/Postgresql: how to see the result of a query

Before I switched to using Rails migrations, I did migrations using plain Postgresql SQL commands. This had the advantage that it would tell you how many rows you updated when you did an update/delete.

How to get this information with Rails migrations?

The execute command returns a PGresult object (http://ruby.scripting.ca/postgres/rdoc/classes/PGresult.html), which you can then query to get Postgresql's output.

  def self.up
    puts execute("update orders set ...").cmdstatus
  end

UPDATE 244

Don't forget to add indexes!

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#M000612

add_index(:suppliers, :name)
add_index(:accounts, [:branch_id, :party_id], :unique => true)


ActiveRecord  edit   (Category  edit)


How do I do migrations outside of Rails?

http://blogs.pragprog.com/cgi-bin/pragdave.cgi/Tech/Ruby/MigrationsOutsideRails.rdoc

    ActiveRecord::Schema.define do
      create_table children, :force => true do |t|
        t.column :parent_id, :integer
        t.column :name,      :string
        t.column :position,  :integer
      end
    end

add_column with :null => false didn't work

add_column :my_table, :first_opened_time, :datetime, { :default => 'now', :null => false }

== AddFirstOpenedTime: migrating ========================================
-- execute("begin")
   -> 0.0003s
-- add_column(:my_table, :first_opened_time, :datetime, {:default=>"now", :null=>false})
rake aborted!
PGError: ERROR:  column "first_opened_time" contains null values
: ALTER TABLE my_table ALTER first_opened_time SET NOT NULL

Complaint: You have to use :allow_nil => true when you do validates_length_of :maximum

Otherwise you'll get "value is too long" validation errors when you try to set it to nil.

I can see why it might be intuitive for :minimum validations, but not for :maximum validations!

Can I use a composite (multi-column) primary key?

Sure. Just use http://rubyforge.org/projects/compositekeys .

http://www.pjhyett.com/posts/208-composite-primary-keys-are-good Composite Primary Keys are Good

class AddViewsTable < ActiveRecord::Migration
  def self.up
    create_table :views, :primary_key => [:topic_id, :user_id] do |t|
      t.column :topic_id, :integer
      t.column :user_id,  :integer
      t.column :post_id,  :integer
    end
  end
end

Caveat: ActiveRecord doesn't respect PostgreSQL's default values

# I had a table that had a not-null constraint on its date_time field. So when I did a plain a = MyModel.new; a.save it said 
ActiveRecord::StatementInvalid: PGError: ERROR:  null value in column "date_time" violates not-null constraint

# To solve this, I created a before_save method in my model that would initialize the column to Time.now() if it was nil. (The column was set up in PostgreSQL to have a default value of Now(), but ActiveRecord doesn't look at / respect that default. It tries to set it to nil, which translates to Null in SQL, which overrides PostgreSQL's default value.)

This works:

  def before_save
    self.date_time = Time.now() if date_time.nil?
  end

So does this:

  def before_save
    write_attribute(:date_time, Time.now()) if date_time.nil?
  end


Question: How do I ask an ActiveRecord ConnectionAdapter what its connection information (host, database name, etc.) is?

Why would you want to do that?

Well, suppose you want to put a sanity check in some important script, just to make absolutely sure that you are running it on the correct host or the correct database or something...

How to do it:

/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/postgresql_adapter.rb

      3 module ActiveRecord
      4   class Base
      5     # Establishes a connection to the database that's used by all Active Record objects
      6     def self.postgresql_connection(config) # :nodoc:
...
      9       config = config.symbolize_keys
     10       host     = config[:host]
     11       port     = config[:port] || 5432
     12       username = config[:username].to_s
     13       password = config[:password].to_s
...
     16 
     17       if config.has_key?(:database)
     18         database = config[:database]
     19       else
     20         raise ArgumentError, "No database specified. Missing argument: database."
     21       end
     22 
     23       pga = ConnectionAdapters::PostgreSQLAdapter.new(
     24         PGconn.connect(host, port, "", "", database, username, password), logger, config
     25       )
...
     32     end
     33   end

     50     class PostgreSQLAdapter < AbstractAdapter
     51       def adapter_name
     52         'PostgreSQL'
     53       end
     54 
     55       def initialize(connection, logger, config = {})
     56         super(connection, logger)
     57         @config = config
     58         @async = config[:allow_concurrency]
     59         configure_connection
     60       end

So it's stored in @config. I don't see any accessors for getting that out, so I guess we'll just have to cheat and use instance_variable_get(:@config):

p SomeModel.connection.instance_variable_get(:@config)
# {:username=>"...", :allow_concurrency=>false, :database=>"...", :password=>"...", :adapter=>"postgresql", :host=>"..."}
 


ActiveRecord

This is for information regarding using/creating ActiveRecord models -- what methods are available, how to do searching, etc.

Security

Decide which attributes should be "secure"; Deny mass-assignment to secure attributes

Securing your Rails application (http://manuals.rubyonrails.com/read/chapter/47). Retrieved on 2007-05-11 11:18.


The easiest way to create a user object from the form data in the controller is:

User.create(@params['user'])

But what happens if someone decides to save the registration form to his disk and play around with adding a few fields?

<form method="post" action="http://website.domain/user/register">
  <input type="text" name="user[name]" />
  <input type="text" name="user[password]" />
  <input type="text" name="user[role]" value="Admin" />
  <input type="text" name="user[approved]" value="1" />
</form>

He can create an account, make himself admin and approve his own account with one click.

...

Using attr_protected, we can secure the User models like this:

class User < ActiveRecord::Base
  attr_protected :approved, :role
end

This will ensure that on doing User.create(@params['user']) both @params['user']['approved'] and @params['user']['role'] will be ignored. You’ll have to manually set them like this:

user = User.new(@params['user'])
user.approved = sanitize_properly(@params['user']['approved'])
user. role    = sanitize_properly(@params['user']['role'])

...

You can also specify the protection in reverse. You simply allow access instead of deny it, so only the attributes named in attr_accessible are available for mass-assignment.

Using attr_accessible, we can secure the User models like this:

class User < ActiveRecord::Base
  attr_accessible :name, :password
end

[...] When you add new attrbutes to the User model, they’ll be protected by default.



Model.table_exists?

Is sometimes solution to [Symptoms (category)]: ActiveRecord::StatementInvalid error

At least when using SQLite3, I once received this unhelpful error message:

500 Internal Server Error

ActiveRecord::StatementInvalid
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/sqlite_adapter.rb:360:in `table_structure'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/core_ext/object/misc.rb:23:in `returning'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/sqlite_adapter.rb:359:in `table_structure'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/connection_adapters/sqlite_adapter.rb:210:in `columns'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:763:in `columns'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:2060:in `attributes_from_column_definition_without_lock'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/locking/optimistic.rb:45:in `attributes_from_column_definition'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/base.rb:1502:in `initialize_without_callbacks'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.3/lib/active_record/callbacks.rb:225:in `initialize'
./script/../config/../vendor/plugins/app_profiler/lib/app_profiler.rb:12:in `new'
./script/../config/../vendor/plugins/app_profiler/lib/app_profiler.rb:12:in `new_request'
/usr/lib/ruby/gems/1.8/gems/qualitysmith_extensions-0.0.18/lib/qualitysmith_extensions/module/guard_method.rb:44:in `disable_benchmarking'
./script/../config/../vendor/plugins/app_profiler/lib/app_profiler.rb:11:in `new_request'
./script/../config/../vendor/plugins/app_profiler/lib/app_profiler.rb:91:in `process'

Traced to:

    class SQLite3Adapter < SQLiteAdapter # :nodoc:
      def table_structure(table_name)
        returning structure = @connection.table_info(table_name) do
          raise ActiveRecord::StatementInvalid if structure.empty?
        end
      end
    end

Would have been more helpful if they had given me a more specific error, like:

"Table app_profiler_requests does not exist".

Workaround: Do the check yourself in your code/plugin:

raise "AppProfilerRequest table doesn't exist. Please run rake db:migrate:plugins PLUGIN=app_profiler" unless AppProfilerRequest.table_exists?

[Caveats (category)] (mostly ActiveRecord) [actually Ruby-general]: Always include 'self.' when calling my_attribute= from within a model!

Otherwise, it will think you are setting a local variable named my_attribute.

Example:

# Don't do this:
  def before_save
    date_time = Time.now() if date_time.nil?
  end

# Do this:
  def before_save
    self.date_time = Time.now() if date_time.nil?
  end

ActiveRecord: What's the difference between write_attribute and update_attribute?

write_attribute update_attribute
protected public
doesn't save after writing the attribute saves after writing the attribute
skips the setter method and directly modifies the internal attributes hash?? invokes the setter method (my_attribute=)

http://api.rubyonrails.org/classes/ActiveRecord/Base.html:

You can alternatively use self[:attribute]=(value) and self[:attribute] instead of write_attribute(:attribute, vaule) and read_attribute(:attribute) as a shorter form.

ActiveRecord: When to use self.my_attr, my_attr on its own, @my_attr, or read_attribute(:my_attr)??

I think the answer is, for all DB-backed attributes that are created automatically for you by the ActiveRecord framework:

  • use the plain old my_attr whenever possible (this is an accessor method that is automatically created)
  • use self.my_attr if just saying my_attr would conflict with a local variable defined in the current method. This ensures that the custom accessor is invoked if one is defined.
  • use read_attribute(:my_attr) if my_attr would conflict with a method name (that is, a custom setter).

Be aware that read_attribute(:my_attr) only uses the default accessor, which is why you should only use it if this is the behavior you want.

How do I safely include user input in my queries?

sanitize_sql

sanitize_sql(["name='%s', "foo'bar"]  =>  "name='foo''bar'"

How do I do straight-SQL select queries?

res = Order.connection.select_all("select id, quantity*unit_price as total from line_items")
p res

This produces something like

[{"total"=>"29.95", "id"=>"91"},
{"total"=>"59.90", "id"=>"92"},
{"total"=>"44.95", "id"=>"93"}]

You don't even have to use a class that makes sense; you're perfectly free to call Books.connection.select_all("select * from cars") or something.

When would you want to use raw-SQL queries like this? Well, you should only rarely need to, if ever...but at least you can! Perhaps you need to get the result of some database-specific function or select from a (database-specific) view...

with_scope

with_scope doesn’t work with find_by_sql. (http://woss.name/2006/05/07/notes-from-a-rails-course/)

with_scope can be combined with a bit of metaprogramming to automatically limit the scope of actions very easily. See "Nested with_scope" [2]. (http://woss.name/2006/05/07/notes-from-a-rails-course/)

Caveat: When I tried it, it seemed that with_scope didn't limit the scope of associations. So this would example would not do what it's author was trying to do:

def User
  def do_something_with_cool_posts_only
    Post.with_scope({:find => {:conditions => ['cool = true']} }) do
      my_cool_posts = self.posts   # nope, this actually gets *all* of my posts, not just the cool ones
      do_something_with my_cool_posts
    end
  end
end

Update (7/11/2007): This won't work. You must use Post.find(:all, :conditions=>["user_id =?", self.id]) instead of self.posts.

This is because you are limiting the scope of the Post model, and instead querying the User model (for self.posts) This won't work.

-Chinmay

Eager loading

"Appointment.find(:first, :include => [:contacts => [:addresses => :cities]]) is going to eagerly load the first appointment along with its contacts, their addresses and the addresses’ cities!" (http://woss.name/2006/05/07/notes-from-a-rails-course/)


http://weblog.rubyonrails.org/2006/03/28/rails-1-1-rjs-active-record-respond_to-integration-tests-and-500-other-things/ : has stuff on eager loading

ActiveRecord booleans

From Agile Web Development with Rails (2nd ed).20060613.pdf page 291:

# DON'T DO THIS
user = Users.find_by_name("Dave")
if user.superuser
  grant_privileges
end

# INSTEAD, DO THIS
user = Users.find_by_name("Dave")
if user.superuser?
  grant_privileges
end

foo.blank? tests to see if it’s empty [empty as in ""? or what?] or nil in Rails. 0 is not blank, though! (http://woss.name/2006/05/07/notes-from-a-rails-course/)


ActiveRecord: relationships: belongs_to or has_* ?

belongs_to :other_table if the table has a foreign key to other_table

ActiveRecord: might be useful: ActiveRecord::Base.blank?

Returns true if its receiver is nil or an empty string.

ActiveRecord: callbacks

http://rubyonrails.org/api/classes/ActiveRecord/Callbacks.html

/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/callbacks.rb

    # Is called when the object was instantiated by one of the finders, like Base.find.
    #def after_find() end

    # Is called after the object has been instantiated by a call to Base.new.
    #def after_initialize() end

after_initialize() seems to be called even when I call reload(). Is that supposed to happen? Possibly. I guess the "the object [is] instantiated by a call to Base.new" part happens even when the programmer calls model.find or model.reload rather than ModelClass.new ! Just something to be aware of.

The main reason I wanted it was so that I could have code that was only called when a record is first created, not when an existing record is re-loaded into the model. One can detect that condition like so:

  def after_initialize
    if self.new_record?
      # Do stuff that should only happen when an object is first instantiated, *before* it is saved...
    else
      # The object was instantiated from an existing record.
    end
    # The object was instantiated either from a new record or an existing record.
  end

To put it another way:

  • The after_find callback is triggered only when a find occurs
  • The after_initialize callback is triggered both when a find occurs and when a new/initialize is called

Different ways to update attributes

  • Model.new(initial_attribute_values): Updates/initializes but doesn't save. Calls validations??
  • Model.create(initial_attribute_values): Updates/initializes and saves. Calls validations.
  • model.attributes = hash_attributes: Updates in memory but doesn't saves. [Skips validations.??]
  • model.update_attribute(name, value): Updates and saves. Skips validations.
  • model.update_attributes(attributes_to_change): Updates and saves. Calls validations. If the object is invalid, the saving will fail and false will be returned.
  • model.write_attribute(name, value): private. Call it from your custom setter methods to prevent infinite loops.
  • model[:name] = value. Updates in memory, setting the attribute directly; doesn't save until you call model.save. (self[:attribute]=(value) and self[:attribute] are just a shorter form of write_attribute(:attribute, vaule) and read_attribute(:attribute).)
  • model.name = value. Updates in memory, using the setter method; doesn't save until you call model.save.

Caveat! update_attribute (singular) apparently saves the record even if it was previously unsaved, skipping validations

I spent a couple hours trying to debug some strange behavior where one of my columns was getting nil values even though I had this line:

validates_presence_of :survey_response

I couldn't figure out how it was possible for those to get in there since the validation was supposed to make that impossible. I even added a validate function:

  def validate
    if survey_response.nil?
      errors.add :survey_response, 'is missing'
    end
    #$out += "in validate: {{{ #{survey_response.inspect} }}}<br>"
  end

For some reason, though, it didn't seem to get called in the cases where it was nil. (It got called just fine when it wasn't nil.)

The reason for all this is the behavior of update_attribute... which, according to the docs:

Updates a single attribute and saves the record. This is especially useful for boolean flags on existing records. Note: This method is overwritten by the Validation module that’ll make sure that updates made with this method doesn’t get subjected to validation checks. Hence, attributes can be updated even if the full object isn’t valid.

I even read that before adding the update_attribute call in my code. "Fine," I thought, "it will skip validations when updating an existing record." But it didn't even occur to me that it actually cause the record to be saved if it was still an unsaved record, without paying any regard to my validations.

My code looked something like this, not that it matters much:

      for question_id, value in params[:survey_question_responses]
        question = SurveyQuestion.find(question_id)
        question_response = @survey_response.question_responses.select {|r|
          r.question == question
        }
        if question_response.size > 0
          question_response = question_response.first
        else
          question_response = SurveyQuestionResponse.new(:question => question)
        end
        question_response.update_attributes({:selected_option_ids, value[:selected_option_ids]})
      end
      @survey_response.question_responses << question_response

Solution: Use update_attributes (plural) instead, which doesn't have this strange behavior. Any call to be update_attribute can easily be converted into a call to update_attributes.

If you clone a model and save it, will it create a new one or keep the same id and save over the existing one?

Cloning a model is mostly (as far as I can see) a technique useful for testing...

Would something like this work, for example?


    # Set up the model once
    shop1 = Shop.new
    shop1.is_active = true
    shop1.save
    
    shop2 = shop1.clone
    shop2.save
    
    shop3 = shop1.clone
    shop3.save

    assert_equal 3, Shop.find_active_shops

Why or why not?

Miscellaneous

To find associations from the console, do Model.reflections.keys. (http://woss.name/2006/05/07/notes-from-a-rails-course/)

Caveat: "For has_many :books, :conditions => 'published = 1', we’ve discovered that if you do model_instance.books.create ..., it will not automatically set published = 1 on the new instance. So while it seems like you’re adding a books to the has_many collection of published books, you’re not. It should be possible to emulate that in some way with with_scope." (http://woss.name/2006/05/07/notes-from-a-rails-course/)





ActiveRecord / Single table inheritance

ActiveRecord / Single table inheritance edit



Introduction

It is sometimes desirable to be able to subclass your ActiveRecord objects. For example, if you want to have an Employee and a Customer, both of which share some common fields. You can do this without any problem:

class Person < ActiveRecord::Base
class Employee < Person
class Customer < Person

But once you save your object to the database, it forgets what type it is.

Solution: Single Table Inheritance. To use it, simply add a TEXT type column to your table (people in our case). Rails handles the rest.

If there are any columns that are only used by one subclass and not another, then you can just leave the values of those columns blank for the records/models that don't use them... (Somewhat wasteful and non-normalized, perhaps, but it works.)

Employee.create(:name => "Bob the underpaid", :some_column => "12345")
Customer.create(:name => "Fred the impulse spender", :another_column => "4567")

Person.find(:all).each { |person|
  puts person.class
}

Outputs:

Employee
Customer

Can you have records of the base class type?

Yes!

irb -> m = MenuDuJour::MenuItem.create

irb -> m.reload
    => #<MenuDuJour::MenuItem:0xb7513120 @attributes={"name"=>nil, "type"=>nil, "id"=>"5", "display_name"=>nil,} >

Can I change the type of a record after it's been created?

Yes!

irb -> m.type
(irb):36: warning: Object#type is deprecated; use Object#class
    => MenuDuJour::MenuItem

irb -> m.class
    => MenuDuJour::MenuItem

irb -> m.type = 'Label'
    => "Label"

irb -> m.save!
    => true

irb -> m.reload
    => #<MenuDuJour::MenuItem:0xb74fc164 @attributes={"name"=>nil, "type"=>"Label", "id"=>"5", "display_name"=>nil,} >

irb -> m.attributes['type']
    => "Label"


irb -> m.type = 'MenuItem' # Base class
    => "MenuItem"
irb -> m.save!; m.reload
    => #<MenuDuJour::MenuItem:0xb74fc164 @attributes={"name"=>nil, "type"=>"MenuItem", "id"=>"5", "display_name"=>nil,} >
irb -> m.attributes['type']
    => "MenuItem"

irb -> m.type = nil # Base class
    => nil
irb -> m.save!; m.reload
    => #<MenuDuJour::MenuItem:0xb74fc164 @attributes={"name"=>nil, "type"=>nil, "id"=>"5", "display_name"=>nil,} >
irb -> m.attributes['type']
    => nil


Reusing a shared column in two different subclasses but using a different column name

Gregory Seidman. http://lists.rubyonrails.org/pipermail/rails/2006-March/027914.html. Retrieved on 2007-05-11 11:18.


class Super < ActiveRecord::Base
  
  def self.alias_field(old_field, new_field)
    define_method(new_field) { self.send(old_field) }
    define_method("#{new_field}=") { |arg| self.send("#{old_field}=", arg) }
    define_method("#{new_field}?") { self.send("#{old_field}?") }
  end

end

class Sub < Super
  alias_field :text1, :address1
  alias_field :text2, :address2
  alias_field :text3, :zipcode
end

Now I can do all of the following:

fail "Where's the address?" unless foo.address1?
foo.address1 = '1600 Pennsylvania Ave.'
addr = foo.address1

...and it will use text1?, text1=, and text1, respectively.

The "type" column

ryanb on http://railsforum.com/viewtopic.php?pid=22131#p22131


"type" is protected from mass assignment so you have to set it directly.

ryanb on http://railsforum.com/viewtopic.php?pid=22135#p22135

[Modified quotation (category)] I think you have to set it like this:

user[:type] = params[:type] 

rather than like this:

user.type = params[:type] 

because "type" is also a reserved method name in Ruby so you can't really set it directly.

 


Associations / joins

Big caveat!!: has_and_belongs_to_many relationship caveat: make sure your join table has no id column!

Otherwise you will get DB errors saying you're duplicating a unique key (namely, 'id').

To cause it to omit the id column when you create the join table:

create_table :users_groups, :id => false do |t|
...
end

(What do I mean by "join" table? This is the table used in a has_and_belongs_to_many association. :users has_and_belongs_to_many :groups would expect (by default) a join table to exist named "groups_users".)

Is there any way to have two applications share the same database?

The immediate problem I see is that each application wants to have its own schema_info table.

I guess maybe if you only did your migrations in one application... (maybe call it "shared_migrations" and have it only contain the migrations...)

[Caveat (category)][Setting has_many association (category)]: Can't use the association [proxy] to alter the objects themselves

That's hard to explain in one sentence. Let me try again.

Group has_many :users

new_users = [user1, user2]
group.users = new_users
This works as expected if you're adding *new* things to the association. So the records for user1 and user2 will both be saved (when you save group?).

But it doesn't work if those records (the records with those IDs) are *already associated* and you're actually trying to change columns of those records themselves.

group = Group.find(...) # assume that group.users already includes user1.id
user1 = User.find(...)
user1.name = 'something different'
new_users = [user1]
group.users = new_users
group.save
# This won't change *anything*!!! Even though you've modified user1, *that change will be lost*. This is because the association [proxy] can *only* be used to change the association (associated records) itself.

In other words, group.users is equivalent to group.user_ids and can only be used to change *which* objects are associated...not any columns *of* those associated records!

Lance and I spent a couple hours tracking down why this behaves the way it does... And we found that it is because of how ActiveRecord::Associations::AssociationCollection#replace works...

      # Replace this collection with +other_array+
      # This will perform a diff and delete/add only records that have changed.
      def replace(other_array)
        other_array.each { |val| raise_on_type_mismatch(val) }

        load_target
        other   = other_array.size < 100 ? other_array : other_array.to_set
        current = @target.size < 100 ? @target : @target.to_set

        @owner.transaction do
          delete(@target.select { |v| !other.include?(v) })
          concat(other_array.select { |v| !current.include?(v) })
        end
      end


It's using the wrong foreign key in the join clause for your join table? Try :association_foreign_key !

          #  profiles LEFT OUTER JOIN profiles_hobbies ON profiles_hobbies.profile_id = profiles.id
          #           LEFT OUTER JOIN option_lists ON option_lists.id = profiles_hobbies.option_list_id
          #Mysql::Error: Unknown column 'profiles_hobbies.option_list_id' in 'on clause'
          #  Should be doing:
          #  Profile.find(:all, :limit => 10, :joins =>
          #         'join profiles_hobbies on profiles.id = profiles_hobbies.profile_id join option_lists on profiles_hobbies.hobby_id = option_lists.id')
          # Solved with:
          # has_and_belongs_to_many :hobbies, :join_table => :profiles_hobbies, :association_foreign_key => 'hobby_id'


ActiveRecord: Multiple joins on same table breaks find :include (ambiguous column)

          #   Profile.find(:all, :include=>[:hobbies, :majors])
          #LEFT OUTER JOIN profiles_hobbies ON profiles_hobbies.profile_id = profiles.id
          #LEFT OUTER JOIN option_lists ON option_lists.id = profiles_hobbies.hobby_id AND option_lists.`type` = 'Hobby' AND approved = 1
          #LEFT OUTER JOIN profiles_majors ON profiles_majors.profile_id = profiles.id
          #LEFT OUTER JOIN option_lists majors_profiles ON majors_profiles.id = profiles_majors.major_id AND majors_profiles.`type` = 'Major' AND approved =
          # Mysql::Error: Column 'approved' in on clause is ambiguous:

http://dev.rubyonrails.org/ticket/3574

Supposedly fixed it, but didn't seem to:

http://dev.rubyonrails.org/changeset/3776

Develop test case?

How do I create a user interface for editing a HABTM association with checkboxes?

http://wiki.rubyonrails.org/rails/pages/CheckboxHABTM

[How stuff works / Dissections] How do association accessor methods work, how do attribute accessor methods work, and what’s the difference (in ActiveRecord)?

Accessor Missing (http://errtheblog.com/post/20) (2006-08-22). Retrieved on 2007-02-15 22:35.

The question arose from a failed attempt to override an association accessor method. Something like this did not work:

class Story < ActiveRecord::Base
  belongs_to :author

  def author
    auth = super
    auth.name
  end
end

Obviously I want stories.author to return the name of a particular story’s author rather than the associated Author object. Remember that the super method calls the current method on the class parent. I’m trying to call the author method, then, on ActiveRecord::Base (hoping it will return the associated author object like normal). Rails, unfortunately, is having none of it:

>> story = Story.find(:first)
>> story.author
NoMethodError: super: no superclass method `author'

The truth’s that authors is not really a method_missing trick. It’s a real method defined on my Story class. Where does it get defined? When belongs_to is called. Like much of Rails’ sugar, belongs_to is a class method of ActiveRecord::Base. When you call it in a subclass, stuff happens.

The adventurous can peek into active_record/associations.rb around line 646 to see how belongs_to is defined. Here’s the cliffs: when belongs_to is called, ActiveRecord defines reader and writer methods on the calling class. Now it’s starting to make sense: author is added to my Story class when belongs_to is called. It’s not a method_missing trick and doesn’t exist on ActiveRecord::Base.

Accessor Missing (http://errtheblog.com/post/20) (2006-08-22). Retrieved on 2007-02-15 22:35.

That feels great. But I still have a question: what about, say, story.title? Does ActiveRecord define all of my attribute accessor methods in the same way it defines my association accessor methods? All told, attribute accessor methods are indeed method_missing magic. Get pumped: super will work when overriding them. class Story < ActiveRecord::Base belongs_to :author def title "This story's title is: " + super end end So close, so close…

>> story = Story.find(:first)
=> #<Story:0x279f9e0 ...>

>> story.title
=> "This story's title is: Accessor Missing"

["Accessor Missing" is the actual title of his article; this is not an error message!]








Libraries

Composite Primary Keys

Homepage: http://compositekeys.rubyforge.org/
Documentation: Forum


Project/Development: http://rubyforge.org/projects/compositekeys



License: MIT


Authors: Nic Williams


Composite Primary Keys (http://compositekeys.rubyforge.org/). Retrieved on 2007-05-11 11:18.


A model with composite primary keys would look like…

class Membership < ActiveRecord::Base
  # set_primary_keys *keys - turns on composite key functionality
  set_primary_keys :user_id, :group_id
  belongs_to :user
  belongs_to :group
  has_many :statuses, :class => 'MembershipStatus', :foreign_key => [:user_id, :group_id]
end

A model associated with a composite key model would be defined like…

class MembershipStatus < ActiveRecord::Base
  belongs_to :membership, :foreign_key => [:user_id, :group_id]
end

...

Membership.find(1,1)  # composite ids returns single instance
=> <Membership:0x39218b0 @attributes={"user_id"=>"1", "group_id"=>"1"}>



DrySQL

Name: DrySQL
Dynamic, Reflective, Invisible ORM for Ruby


Homepage: http://drysql.rubyforge.org/
Documentation: By example


Project/Development: http://rubyforge.org/projects/drysql
As listed in other directories: http://rubyfurnace.com/plugins/drysql
Depends on: ActiveRecord



Authors: Bryan Evans


http://www.infoq.com/articles/DrySQL - An article

http://rubyforge.org/projects/drysql

Define your DB constraints, validations, and associations in only one place: the DB.

http://drysql.rubyforge.org/getStarted.htm

DrySQL is a Ruby plug-in that extends ActiveRecord and applies the DRY principle to Object-Relational Mapping.

http://drysql.rubyforge.org/by_example.htm

DrySQL can generate the following validations based on the DB's information schema:

  • validates_numericality_of
  • validates_length_of
  • validates_inclusion_of (boolean columns only)
  • validates nullability_of
DrySQL can generate the following associations based on the DB's information schema:
  • belongs_to
  • has_many
  • has_one
  • has_many :through

Ways in which ActiveRecord sucks

Let me count the ways...

http://mysqldump.azundris.com/archives/72-Rubyisms.html Rubyisms - MySQL-dump

So, question: Most of these problems are fixable, right? Who's working on fixing them? When will they be fixed?

Some of these problems probably won't be fixed by the core team if they have different opinions about the way things should be. But that doesn't stop others from fixing them...




ActiveRecord  edit   (Category  edit)


Object-Relational Mapping  edit   (Category  edit) Category:Object-Relational Mapping

Category Object-Relational Mapping not found
 


http://www.b-simple.de/documents/download/6 6 (application/pdf Object)


{{{2}}} 

[[{{{2}}}]] [{{fullurl: {{{2}}}|action=edit}} edit]


{{:{{{2}}}}}

 


Controller-level: Controllers

Rails / Controller-level edit

How do I access my environment/ActiveRecord/my models from a (nearly) stand-alone command-line script?

For example, say you want to do some non-Web-based report that you run from the command line. Or a database maintenance script that you want to run regularly (so it wouldn't make sense to put it in a migration, since those are only expected to be run once). Or anything else you'd call from cron.

#!/bin/env ruby
$LOAD_PATH << "../"

require "config/environment"
require "app/models/book.rb"

result = Book.connection.select_all("select * from books")

Or: require File.expand_path(File.dirname(__FILE__) + "/../config/environment")

One could probably do something like this too (haven't tried it yet), which would automatically load the environment before executing the code that you pass to runner:

./script/runner "require 'send_mailing'"

$ ./script/runner -e production 'puts ENV["RAILS_ENV"]'
production

How do I access a controller from a stand-alone (runner) script?

Notice that this doesn't work:

$ ./script/runner "puts MainController.new"
/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/commands/runner.rb:27: /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:123:in `const_missing': uninitialized constant ApplicationController (NameError)
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:131:in `const_missing'
        from script/../config/../app/controllers/main_controller.rb:3
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:140:in `load'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:140:in `load'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:56:in `require_or_load'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:30:in `depend_on'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:85:in `require_dependency'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:98:in `const_missing'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:131:in `const_missing'
        from (eval):1
        from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `eval'
        from /usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/commands/runner.rb:27
        from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__'
        from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require'
        from /usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:147:in `require'
        from ./script/runner:3

But this does work:

]$ ./script/console
Loading development environment.
Welcome!
irb -> MainController.new
    => #<MainController:0xb7aefaa8>

So after examining the source of script/console and commands/console, I think I figured out what the difference was that allowed console to have access to it but not runner:

 12 libs =  " -r irb/completion"
 13 libs << " -r #{RAILS_ROOT}/config/environment"
 14 libs << " -r console_app"
 15 libs << " -r console_sandbox" if options[:sandbox]
 16 libs << " -r console_with_helpers"

This seems to work:

$ ./script/runner "require 'console_with_helpers'; puts MainController.new"
#<MainController:0xb78ee6dc>

Ugly, but it works.

How do I access url_for from a stand-alone (runner) script / from a mailer?

I don't know. I've tried to get it to work and failed.

http://lists.rubyonrails.org/pipermail/rails/2006-March/028892.html [Rails] Re: runner: url_for with mailers impossible

http://wrath.rubyonrails.org/pipermail/rails-core/2006-June/001652.html [Rails-core] The old deal with url_for in Mailer

This

puts MainController.new.url_for(:action => 'a')

causes this:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:490:in `url_for': You have a nil object when you didn't expect it! (NoMethodError)
The error occured while evaluating nil.rewrite  from ./script/whatever.rb:5

This:

http://wiki.rubyonrails.org/rails/pages/HowtoUseUrlHelpersWithActionMailer/ HowtoUseUrlHelpersWithActionMailer in Ruby on Rails

seems to suggest that maybe this would work:

include ActionView::Helpers::UrlHelper
puts url_for(:action => 'a')

but again we get:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:490:in `url_for': You have a nil object when you didn't expect it! (NoMethodError)
The error occured while evaluating nil.rewrite  from /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/url_helper.rb:27:in `send'
        from /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/url_helper.rb:27:in `url_for'
        from ./script/whatever.rb:7


How do I do something like puts from a controller?

Do I have to set it to a member variable, and then add <%= my_debug_output %> to the template??

Do I have to use a logger instead?

Can't I just say puts my_debug_output? Or render :text => my_debug_output? And have it added to the HTML that will be rendered?

That's one thing I miss about PHP. In PHP, you can put a echo/print statements everywhere to help you debug your code.

My solution: Usually I just use

raise whatever_object.inspect

Sometimes I'll use a global, something like this:

$output = ''
$output += 'some debug output'

And outputting <%= $output %> in my view. But really I should just be using the logger at that point.

What's the difference between redirect_to and render?

both render the view (.rhtml file).

Only redirect_to causes the action in the controller to be called. So if you are doing render and any set up needs to be done, you need to do it in the action method that is calling render or you need to explicitly call the action before you render the template for that action (confusing, I know!).

render keeps the local variables (such as error messages and the current, possibly invalid state of the objects being edited); redirect loses them

both of them keep and show the contents of flash

[need real example]

ActionMailer: Can use the helpers that are used by my normal (controller) views)?

Yes! Just put something like the following in your ActionMailer::Base subclass:

helper :application, :other

filters (before_filter, etc.)

meantime_filter

It's sort of like an around_filter, only cooler! (available as a plugin, I think)

after_filter

after_filter happens after the view, not after the controller (http://woss.name/2006/05/07/notes-from-a-rails-course/0)

Flash

Hey, how come my flash message is sticking around too long?

Normally the flash message will be available to the next action, whatever that is...

Maybe you're setting flash and then rendering and wanting your message to only appear for the current action.

Solution: Try setting flash.now[:error] = 'whatever' instead of flash[:error].

Reference: http://api.rubyonrails.org/classes/ActionController/Flash/FlashHash.html#M000142

Make it work from Ajax; add a fade effect; make it log errors

Rails Notification System (http://blog.andreasaderhold.com/2006/07/rails-notifications) (July 13, 2006). Retrieved on 2007-05-11 11:18.

Here’s a nice trick for building a cool notification system that uses the Rails flash array and can be used with Ajax and with normal actions. I’ve used this on two projects and it works just fine (not much that can go wrong anyways). First, put the following in your layout template:

<% flash.each do |key,value| -%>
  <h4 id="flash" class='alert <%= key %>'>
    <%= value %>
  </h4>
<% end -%>

[Incorrect! This will cause there to be more than one element with id="flash", which is invalid. I would make "flash" a class rather than an id. <h4 class='flash <%= key %>'>]

This gives you a dynamic flash thing, so you can use different type of notifications/alerts (i.e. falsh[:error], flash[:critical], etc.)

The styles for the default types error and notice could look something like this [...]:

h4.alert  {
   font-size: 0.8em;
   font-weight:bold;
   margin:0;
   padding:5px;
}
h4.error  {
   color:#fff; 
   background:#c00;
}
h4.notice {
  color:#060;
  background:#e2f9e3;
}

Ok, so far so good. This is quite standard. Because typing flash[:blah] = "Something" is boring and after all we want to send a message to the notification system and don’t want to care about arrays, it’s just logical we want to do something like notify :error, "Something went wrong". So let’s define a helper method in our application.rb:

def notify(type, message)
  flash[type] = message
  logger.error("ERROR: #{message}") if type == :error
end

Note, this also creates a log entry in case the type is :error. Now that’s it. You have a nice notification system, the CSS class names are named by the notification types and you have easy handler method to fire a note. Oh let it DRY baby. Bonus: Fading messages

It’s optional, but I like it. I don’t want the messages to stand in the layout all the time. It was a notification and that’s it. You would not want to have the credits of a movie scrolling on top of the screen all the time, would you? I don’t, so let’s just fade away the message by adding the following snippet to our application.js file:

/* fade flashes automatically */
Event.observe(window, 'load', function() { 
  $A(document.getElementsByClassName('alert')).each(function(o) {
    o.opacity = 100.0
    Effect.Fade(o, {duration: 8.0})
  });
});

This requires script.aculo.us, so don’t forget to include it. It softly fades out all flash messages over 8 seconds. You can extend/reduce the duration of course. Wait, what about those Ajax actions?

Helpers to rescue. Since we have the RJS stuff and helper methods are available to the page object, this is a no-brainer. Let’s define the notify function in application_helper.rb:

def notify(type, message)
  type = type.to_s  # symbol to string
  page.replace 'flash', "<h4 id='flash' class='alert #{type}'>#{message}</h4>" 
  page.visual_effect :fade, 'flash', :duration => 8.0
end

Ok, this makes a notify method avail so we can send the page object a message:

render :update do |page|
  # do your stuff here
  page.notify  :notice, "Successfully closed all windows" 
end

The last step is to extend your main flash rendering code to something like this:

<% if flash.empty? %>
  <h4 id="flash" class="alert" style="display:none"></h4>
<% else %>  
  <% flash.each do |key,value| -%>
    <h4 id="flash" class='alert <%= key %>'>
      <%= value %>
    </h4>
  <% end -%>
<% end %>

This ensures we always have a element with the ID “flash” rendered, so the notify call can update it.

How to make sure you don't overwrite an existing flash message

It bothers me how the prevailing convention is to set the flash value rather than appending to it: flash[:whatever] = 'My message'.

What if you have, say, some before_filter in your ApplicationController that may want to flash a message in certain cases, and your specific controller action also wants to flash a message? Whichever one sets the flash hash first "wins", silently overwriting the other message and preventing it from ever getting seen... bad!!

One possible solution would be to always append to the string:

flash[:notice] << 'My message'

But then your messages will run together. So you could try to remember (I say "try", because you know you'll forget some of the time) to put each message in an HTML paragraph:

flash[:notice] << '<p>My message</p>'

Instead of having each entry in the hash be a string, each entry should be an array of strings. Or something like that. So you'd have a list (array) of 0 or more error messages, 0 or more notices, etc.

flash[:notice] << 'My message'

You would have to change how you rendered it, though. To something like this:

<% flash.each do |flash_type, messages| -%>
  <h4 class='flash <%= flash_type %>'>
    <% messages.each do |message| -%>
      <p>
        <%= message %>
      </p>
  <% end -%>
  </h4>
<% end -%>

Should there be a standard list of flash "types" (error, notice, etc.)?

No, I think the application developers should be able to use whichever ones make sense to the application. I mean, they should be consistent themselves, but I'm not sure we need to adopt a Rails-community-wide standard here...

Here's one person's proposal:

standardizing rails flash messages [ruby [rails] [flash]] (http://snippets.dzone.com/posts/show/1348). Retrieved on 2007-05-11 11:18.

  • :notice for positive feedback (action successful, etc)
  • :message for neutral feedback (reminders, etc)
  • :warning for negative feedback (action unsuccessful, error encountered, etc)

I don't agree with it though. I would suggest using one of these conventions:

  • Convention 1 (simple):
    • :notice for positive or neutral feedback (action successful, etc)
    • :error for negative feedback (action unsuccessful, error encountered, etc.)
  • Convention 1 (more advanted):
    • :positive for positive feedback (action successful, etc)
    • :neutral for neutral feedback (reminders, etc)
    • :warning for somewhat negative feedback (warnings)
    • :error for very negative feedback (more fatal-type erorrs)

Information about the request: remote IP address

David Reynolds said:

Not sure if anyone else knows this, but request.env['REMOTE_ADDR'] doesn't return the correct visitor IP address when the user is behind a proxy or if the web server forwards requests to an internal server (one server on port 80 that forwards to a different port). In theses cases, request.remote_ip works way better. In Rails, you can apparently find the originating host even if they are proxy chaining.
Here's some more info: http://railsmanual.com/class/ActionController::AbstractRequest/remote_ip


File uploads

http://manuals.rubyonrails.com/read/chapter/57 Rails Cookbook: file upload forms

http://www.kanthak.net/opensource/file_column/ Sebastian Kanthak - FileColumn - easy handling of file uploads in Rails








View-level / Controller-level / params / Model-level interaction (form processing)

Validation and Securing Input

http://www.railsdiary.com/diary/validation_securing_form_input The Rails Diary | Validation and Securing Input

Things to consider when building form-processing code

  • Do you want the models to save even if there are validation errors?
    • If no, then be sure to check model.valid? to find out if there are errors and not check model.save
  • Do you have any fields that are required for the form, but not necessarily be the model and everywhere the model is used?
    • If yes, consider using a FormValidator object
  • Which model objects are being edited in your forms? You need to handle each of them.
  • Associations...


Updating associations from form input

      @company.update_services(params[:services])

  def update_services (services_hash)
    if services_hash
      services.clear
      services_hash.each { |service_id, value|
        services << Service.find(service_id.to_i)
      }
    end

You can just set the collection directly using the 'things_id =' or (?) 'things =' accessor/settor methods!


http://wiki.rubyonrails.com/rails/pages/HowtoUseFormOptionHelpers/:

Assigning to has_many or has_and_belongs_to_many collections If you want to have your multiple choice select assign to a has_many or has_and_belongs_to_many (habtm) collection on your object, you should specify the collection using the <collection name>_ids name. For example, if we have an AddressGroup object with a collection of Address objects called addresses, we would not use this: <select name="addressgroup[addresses][]" multiple="multiple"> but rather this: <select name="addressgroup[address_ids][]" multiple="multiple"> With either select statement the returned value is an array of Address object ids, not the actual array objects, which will fail if we tried to assign to addressgroup.addresses. If you try to do that you will get an error message like “Expected Address and got String”. Instead, we use the implicit address_ids= property (automatically added to an object when it has a has_many or habtm collection), which assigns to the collection based on a collection of IDs.


Forms for associations: my_model = MyModel.new(params[:my_model]) complains that the type passed to the association setter is of type String

[Caveat (category)][Setting has_many association (category)]

For example, you have:

Profile Model:
  habtm :hobbies
View:
  <%= select("profile", "hobbies", @hobbies.options_for_select, { :include_blank => true }) %>
Controller:
  @profile = Profile.new(params[:profile])

When you submit the form, it says "Hobby expected, got String".

/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_proxy.rb:134:in `raise_on_type_mismatch'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_collection.rb:118:in `replace'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations/association_collection.rb:118:in `replace'
/usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/associations.rb:895:in `hobbies='

Solution:

In this case, I made it extra difficult, because I'm using a single-select field (typically used for has_many associations, not habtm) to a habtm (usually a multi-select would be used).

If I were using a multi-select, this would be all I need to change:

View:
  <%= multi_select("profile", "hobby_ids", @hobbies.options_for_select, { :include_blank => true }) %>

In my case, however, I need to convert my single string value submitted in params[:profile][:hobby_ids] (for example, "3") into an array of strings (["3"]). So I did this (which could easily be thrown into the model logic):

    for key, value in params[:profile]
      if key.match /_ids/ and !value.is_a?(Array)
        params[:profile][key] = [value]
      end
    end

How does an object get converted to a field get converted to a param get converted to an object?

# "Model" object
class Address
  def line_1
    "123 Fake St."
  end
  def line_2
    "Apt. 42"
  end
end
@address = Address.new

# Helpers
text_field("address", "line_1")
text_field("address", "line_2")

# Produce essentially this HTML output:
<input type="text" name="address[line_1]"  />
<input type="text" name="address[line_2]"  />

# Produces these params:
"address"=>{"line_1"=>"123 Fake St.", "line_2"=>"Apt. 42"}}

params[:address][:line_1] = "123 Fake St."
params[:address][:line_2] = "Apt. 42"
<input type="text" name="address[][line_1]"  />
<input type="text" name="address[][line_2]"  />
# Not very useful
"address"=>{""=>{"line_1"=>"234", "line_2"=>"234"}

<input type="text" name="address[lines][]"  />
<input type="text" name="address[lines][]"  />
"address"=>{"lines"=>["123 Fake St.", "Apt. 42"]
params[:address][:lines[0] = "123 Fake St."
params[:address][:lines[1] = "Apt. 42"

How do I handle/prevent ActiveRecord::MultiparameterAssignmentErrors resulting from bad input from date_select?

http://i.nfectio.us/articles/2006/02/22/handling-invalid-dates-with-activerecord-date-helpers :

Rescue the exception in your controller. Iterate over the error details, removing from params all of the values that caused the error. Then send your modified params to the ActiveRecord model again.

Plugin:

http://wiki.rubyonrails.org/rails/pages/Validates+Multiparameter+Assignments :

Add a validates_multiparameter_assignments call in your model. Then it will add the errors to the model's errors collection rather than throwing an exception. Genius!
 






ActionMailer

ActionMailer: How do I get started?

Read the section on the Agile Rails book, or read the Rdoc.

./script/generate mailer OrderMailer confirm sent

OrderMailer.deliver_confirm(order)


ActionMailer: Can use the helpers that are used by my normal (controller) views)?

Yes! Just put something like the following in your ActionMailer::Base subclass:

helper :application, :other







View-level

Rails / View-level edit

Views: Miscellaneous

Having an initially-hidden "create" form that displays immediately when needed (the Inline Create Form)

[When Ajax doesn't work (category)] (An example of when you sometimes want to optimize your user interface layer by doing things inline instead of Ajax so that the user doesn't experience latency...) [When to use components (category)]/[When components are useful (category)]


Craig Ambrose. CRUD vs The Inline Create Form (http://blog.craigambrose.com/past/2006/8/26/crud-vs-the-inline-create-form/). Retrieved on 2007-05-11 11:18.


So, we both know the solution. The new comment form has to be written into the article show template. This is such a common technique that I’m sure I can get away with referring to it as a pattern, and putting it in capital letters. Behold the Inline Create Form. Ok, so it’s not all that exciting, but we’re all doing it, and in my opinion the implementation introduces some nasty smells that I’d like to try and minimise.

What About Controller Code to Initialise the Form?

Realistically, we need some code to initialise the form when it is first called. In this simple example, it would probably look like this (current_user is presumably a method on your application controller).

    @comment = Comment.new
    @comment.author = current_user

Now this is fairly minimal. We can stick it inside Articles.show, and the world isn’t going to end. If we end up using the form as a stand-alone page, we can stick it in the comments new action as well. However, I certainly have real word example where the code is more complex than this, and even if it is only a couple of lines of code, frankly this is wrong. Putting code to initialise the comment form, inside the article show action, rather than inside the comment new action, and for a reason only based on speeding up the load time of a user interaction is most definitely a bad smell.

The rails API docs suggest removing view/controller duplication using partials and filters. It’s true, we could move this controller code into a before_filter, and apply that filter to the article show action. However, that does not improve the design problem even a tiny bit. The only difference is that it obscures it. In agile development, we call this a deodorant. It doesn’t fix a bad smell in the code, it just hides it.

Rails Components Solve This Problem

The force is more powerful on the dark side. Have a look at how show.rhtml would be written if we used components.

    <%= link_to_function "Add a Comment", "Element.hide('add_comment_link'); Element.show('new_comment_form')", :id => 'add_comment_link' %>
    <div id="new_comment_form" style="display: none">
      <%= render_component :controller => 'comments', :action => 'new' %>
    </div>

A component is an inline render to a totally different rails action, and the controller code for that action gets called too. Hey, that’s exactly what we’re actually doing here! Anything else is just an optimisation. With the above code, the form initialisation code stays where it belongs (in the comments new action). In fact, this code would allow the comments controller to behave in ‘full page mode’ as well, providing it knew to render it’s layout if it isn’t called as a component, but to not render it if it is (and yes, this is easy to do). That gives us a non-Javascript fallback, which might be important for some people. For myself, I just like the elegance of the properly decoupled code.

Why Components are ‘Bad’

DHH doesn’t like components because he wants to build applications up from very cleverly designed building blocks, rather than sticking vertical slices together (my words, but I think that’s what he means). He has a point, but that isn’t really what we’re trying to do here. Or, if it is, the decision was made as soon as we decided that we wanted an inline form. If we really wanted to de-couple our controllers, we shouldn’t have inlined the form at all. If we decide that it’s a nicer experience for the user if we do inline it (and it is, which is why most apps do it), then we have already introduces the problem, and we need a solution. Of the examples I’ve presented so far, the component approach yields the nicest code.

However, components have speed issues. [...]



How do I link to a popup window?

<%= link_to( "Give us some feedback", {:controller => 'popup', :action => 'feedback'}, { :popup => ['new_window', 'height=650,width=450,scrollbars=yes'], :target => '_blank' } ) %>

As you can see, you use the :popup option of link_to. That generates a standard Javascript popup call. You can pass all the options that you'd normally use for such a thing, like hight and width.

I usually like to specify a target, too, so that just in case they have Javascript turned off, it will at least open in a new window.

Make sure to create a new, very simple layout for use by your popup windows -- because you probably don't want everything that your normal layouts provide.


How do I make an image into a link?

Answer: Sorta like this...

<%= link_to image_tag('next.gif'), :action => :next %>

<%= link_to image_tag('next.gif', :border => 0), :action => :next %>

<%= link_to( "<img src=" + image_path('logo.png') + ' />', { :controller => 'main' } ) %>

<%= link_to( "<img src=" + image_path('logo.png') + ' style="float: right;" />', { :controller => 'main' } ) %>

How do I make an image submit button?

image_submit_tag('next.gif')

Not with submit_tag (image_path(...)).


text_field or text_field_tag??

text_field_tag field_name, options

text_field object_name, field_name, options

How do I make a UI for editing multiple obects of the same class, and having an 'Add another _' button?

http://rails.techno-weenie.net/question/2006/8/24/multiple-records-from-one-form


[Used by View or Controller] Workaround for Rails bug: url_for flattens multi-dimensional params hashes

http://dev.rubyonrails.org/ticket/4947 "URL rewriter should support deeply nested parameters"

Solution: http://marklunds.com/articles/one/314

  def flatten_hash(hash = params, ancestor_names = [])
    flat_hash = {}
    hash.each do |k, v|
      names = Array.new(ancestor_names)
      names << k
      if v.is_a?(Hash)
        flat_hash.merge!(flatten_hash(v, names))
      else
        key = flat_hash_key(names)
        key += "[]" if v.is_a?(Array)
        flat_hash[key] = v
      end
    end
    
    flat_hash
  end
  
  def flat_hash_key(names)
    names = Array.new(names)
    name = names.shift.to_s.dup 
    names.each do |n|
      name << "[#{n}]"
    end
    name
  end
  
  def hash_as_hidden_fields(hash = params)
    hidden_fields = []
    flatten_hash(hash).each do |name, value|
      value = [value] if !value.is_a?(Array)
      value.each do |v|
        hidden_fields << hidden_field_tag(name, v.to_s, :id => nil)          
      end
    end
    
    hidden_fields.join("\n")
  end

How do I access the controller?

@controller gives you the actual controller instance

params[:controller] gives you its URL component/name

Can I call methods of the controller from my view?

You're not supposed to.

But it seems that you can by using @controller.

For example: <%= @controller.send(:some_protected_method?) %>

I think it's okay to do for debugging (only).

How to tie a yes/no select box to a boolean field in your model

View: 
To default to whatever is in the model by default:
<%= select_tag "person[is_bald]", options_for_select(["Yes", "No"], selected = (@person.is_bald? ? "Yes" : "No")) %>
To default to "Yes" if the column is nil in the model:
<%= select_tag "person[is_bald]", options_for_select(["Yes", "No"], selected = ((@person.is_bald.nil? || @person.is_bald?) ? "Yes" : "No")) %>

Model:
  def is_bald=(new)
    if new.is_a? String
      write_attribute :is_bald, (new == "Yes")
    else
      write_attribute :is_bald, new
    end
  end

content_for / CaptureHelper

Links

http://errtheblog.com/post/28 err.the_blog.find_by_title('Content for Whom?')

http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html

/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.3/lib/action_view/helpers/capture_helper.rb


capture(*args, &block)

Capture allows you to extract a part of the template into an instance variable. You can use this instance variable anywhere in your templates and even in your layout.

 <% @greeting = capture do %>
   Welcome To my shiny new web page!
 <% end %>


content_for(name, content = nil, &block)

content_for("name") is a wrapper for capture which will make the fragment available by name to a yielding layout or template.

Example:

  <% content_for("header") do %>
    alert('hello world')
  <% end %>

content_for :what / yield :what

"Beware that content_for is ignored in caches. So you shouldn’t use it for elements that are going to be fragment cached."

Caveat: content_for only works properly when called from the view or a helper called from the view

So if you have something in lib, or a model, that calls the helper "directly" ... it may assign this content to a @content_for_name variable in the wrong object!

[Haven't verified this theory.]

Caveat: The call to content_for must come before the call to yield

This displays "test!":

<% content_for :test do %>
  test!
<% end %>
<%= yield :test %>

This, however, displays nothing, because at the time that the yield is called, @content_for_test has not yet been set to anything (it evaluates to nil):

<%= yield :test %>
<% content_for :test do %>
  test!
<% end %>

Why this is a problem...

Template rendering appears to happen in the following order:

  1. yields
  2. current template

It's a problem with helpers, since the page is evaluated in the order it is laid out

<% content_for :b do %>
  Content for b
<% end %>
<% b = yield :b %>
<h1>Section A</h1>
<h1>Section B</h1>
<%= b %>


resource_on_demand test 1

This:

app/views/layouts/t.rhtml                                                                                                                  
<html>
<head>
  <%= require_on_demand %>
</head>
<body>
  <%= yield %>
</body>
</html>

app/views/t/index.rhtml                                                                                                                    
<%= render :partial => 'partial' %>

app/views/t/_partial.rhtml
I am a partial!
<% require_stylesheet 'my_stylesheet', 'media' => 'all' %>

Produced this:

<html>
<head>
  <link href="/stylesheets/my_stylesheet.css?1178226968" media="all" rel="Stylesheet" type="text/css" />
</head>
<body>
  I am a partial!
</body>
</html>


Using content_for to capture a string expression instead of a section of ERb

It looks like this can only be done outside of a view / ERb template.

When I put this in my view:

<% content_for(:foo1) do %>
  foo1
<% end %>
<%= yield :foo1 %>

<% content_for(:foo2) { 'foo2' } %>
<%= yield :foo2 %>

I only got "foo1" as output, not "foo1 foo2" as expected/desired.

The reason for this is that it detected an ERb buffer (_erbout) and used capture_erb_with_buffer instead of capture_block.

    # File vendor/rails/actionpack/lib/action_view/helpers/capture_helper.rb, line 56
56:       def capture(*args, &block)
57:         # execute the block
58:         begin
59:           buffer = eval("_erbout", block.binding)
60:         rescue
61:           buffer = nil
62:         end
63:         
64:         if buffer.nil?
65:           capture_block(*args, &block)
66:         else
67:           capture_erb_with_buffer(buffer, *args, &block)
68:         end
69:       end

That's usually not a problem, though, because when you're in an ERb template, you almost always want to use it to capture a section of your ERb template.

What if you want to call a helper from within one of your templates? Will it still have this problem? The answer is no, there is no problem.

Thus, I can do this:

# Helper:
module THelper
  def prepare_a_greeting_for(name)
    content_for(:greeting) { "Hello, #{name}!" }
  end
end

# View:
<% prepare_a_greeting_for('Tyler') %>
...
<%= yield :greeting %>

and it will happily print out "Hello, Tyler!"

Interestingly, it looks like somebody started to provide another way to supply a string to content_for, but never quite finished. Although content_for has a content argument, it doesn't seem to do anything. When I do this:

<% content_for(:foo3, 'foo3') %>
<%= yield :foo3 %>

I get this error:

ActionView::TemplateError (/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/helpers/capture_helper.rb:98:in `capture_block': You have a nil object when you didn't expect it!

        def capture_block(*args, &block)
          block.call(*args)
        end

It looks like they added the content argument to content_for in release 1.2.3 but didn't actually add any code that uses it. How ... silly.

r5335 | david | 2006-10-22 23:41:11
Fixed that setting RAILS_ASSET_ID to "" should not add a trailing slash after assets (closes #6454) [BobSilva/chrismear]

M trunk/actionpack/CHANGELOG
M trunk/actionpack/lib/action_view/helpers/asset_tag_helper.rb
M trunk/actionpack/lib/action_view/helpers/capture_helper.rb
M trunk/actionpack/test/template/asset_tag_helper_test.rb

svn diff --diff-cmd colordiff /home/tyler/code/rails/rel_1-2-3/actionpack/lib/action_view/helpers/capture_helper.rb -r 5334:5335

/home/tyler/code/rails/rel_1-2-3/actionpack/lib/action_view/helpers/capture_helper.rb
--- /home/tyler/code/rails/rel_1-2-3/actionpack/lib/action_view/helpers/capture_helper.rb     (revision 5334)
+++ /home/tyler/code/rails/rel_1-2-3/actionpack/lib/action_view/helpers/capture_helper.rb     (revision 5335)
@@ -89,7 +89,7 @@
       # named @@content_for_#{name_of_the_content_block}@. So <tt><%= content_for('footer') %></tt>
       # would be avaiable as <tt><%= @content_for_footer %></tt>. The preferred notation now is
       # <tt><%= yield :footer %></tt>.
-      def content_for(name, &block)
+      def content_for(name, content = nil, &block)
         eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)"
       end

When would you want to use content_for to capture a string (rather than a section of a ERb template -- that is, what gets output to _erbout)? The resource_on_demand plugin (http://svn.devjavu.com/liquid/resource_on_demand/lib/resource_on_demand.rb) is a good example of this. It is a helper that calls another helper (javascript_include_tag), which returns a string...

      def require_javascript( *list )
        @required_javascripts ||= []
        content_for( :on_demand_header_includes ){ javascript_include_tag( *(list - @required_javascripts ).uniq ) }
        @required_javascripts |= list
      end

content_for yield_with_default

graeme nelson: Dirty Views? Clean them up! (http://blog.imperialdune.com/2007/3/27/dirty-views-clean-them-up) (2007-03-27). Retrieved on 2007-05-03 11:22.

<html>
    <head>
    </head>

    <body>
        <div id="wrapper">
            <div id="header">
                <% yield :header %>
            </div>

            <div id="content">
                <%= yield %>
            </div>

            <div id="sidebar">
                <% yield :sidebar %>
            </div>

            <div id="footer">
                <% yield :footer %>
            </div>
        </div>
    </body>
</html>

And in your view the following:

  <% content_for :header do %>
       This would appear for the yield :header call
  <% end %>

  <% content_for :sidebar do %>
      This is the sidebar content
   <% end %>

  <% content_for :footer do %>
     This is the footer content
  <% end %>

   <%# the rest would just be the content for the plain yield call %>
    This is would be my main content

This is nice, but I wanted to find a way to supply default content and this is what I came up with.

  def yield_with_default(yield_on, content=nil, &block)
    yield_content = eval "@content_for_#{yield_on} || ''" 
    if block_given?(&block)
      concat(yield_content.empty? ? capture(&block) : yield_content, block.binding)
    else
      Binding.of_caller do |binding|
        concat(yield_content.empty? ? content : yield_content, binding)
      end
    end
  end

I placed this in my application_helper.rb file. And now I can add something like this to my view.

    # with a block
    <% yield_with_default :header do %>
        This is my DEFAULT header content
    <% end %>

   # without a block
   <% yield_with_default :header, "This would be my default content" %>


Textilize with syntax highlighting

http://rails.techno-weenie.net/tip/2005/11/29/textilize_with_syntax_highlighting






Helpers

Helpers: Ditch the controller-specific helpers!

That's just too many files to expect people to open up, check, refer to, etc.! Too many files!

Here's an alternative, proposed by zerohalo at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

I do find myself wondering whether I should put a helper that applies to a particular controller only in the controller file itself or in the helper file tied to the controller. I sometimes wonder whether the controller-specific helper files aren’t redundant, and that those helpers which are controller-specific should go in the controller, with those that are global in the application_helper file. Then you don’t have to look in both the controller and its helper file for stuff. (Same for models.)

Helpers can take blocks!

Helpers that take Blocks

[Open question] How do I make a helper function pretty-looking with lots of HTML like ERb templates?

Usually when I am motivated to make a helper it is by duplication in my view: I do the same thing twice and I'd rather call a method twice than have all that code duplicated twice. So the natural thing to want to do is to move that code -- that ERb code! -- from the view into the helper. Don't make me reformat it a lot; I want it to be ERb just like the view!

Can we use erb to create the helper function?? render_to_string()??

Could we use partials instead?

I hate building a string with "blah" + "blah" and return_value += "something"!

Ruby on Rails / Ugly helper function example

Maybe use render :inline?

# Renders "hello david"
render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }

Country select helper

http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#M000401







View-level: Associations (has_many, has_and_belongs_to_many)

How do I create a multiple select box for use in editing a has_and_belongs_to_many relationship?

http://lists.rubyonrails.org/pipermail/rails/2006-April/036962.html :

class User < ActiveRecord::Base
  # id, name fields
  has_and_belongs_to_many :groups
end

class Group < ActiveRecord::Base
  # id, name fields
  has_and_belongs_to_many :users
end

class UserController < AbstractApplicationController
  model :user
  def edit
    case @request.method
    when :get
      @user = User.find(params['id'])
      @available_groups = Groups.find_all - @user.groups
    when :post
      @user = User.find(params['user']['id'])
      @user.groups << Group.find(params['add_groups'])
      redirect_to :action => 'show', :id => @user.id
    end
  end
end

views/user/edit.rhtml
<%= form_tag :action => 'edit' %>
  <%= hidden_field 'user', 'id' %>
  <select name="add_groups[]" multiple="multiple">
    <%= options_from_collection_for_select @available_groups, 'id',
'name' %>
  </select>
</form>





Layouts

[Caveats (category)] Have to pass the layout name as a string

Do this:

layout 'main'

If you do this:

layout :main

, you'll get an "undefined method main" error...

[Problem (category)]: Can you nest one layout within another layout?

Useful, for example, when you want an "outer layout" that's shared by all per-controller layouts. This "outer layout" would include site-wide stuff, like the <html> tags, including some stylesheets, etc.

Example

views/layouts/master_layout.rhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>My site: <%= @title %></title>
  <%= stylesheet_link_tag "common", :media => "all" %>
</head>
<body>
  <%= yield %>
</body>
</html>

views/layouts/controller1_layout.rhtml (wrapped in master_layout):

<h1><%= @title %></h1>

<%= yield %>

<div id="Sidebar">
   ...
</div>

views/controller1/view1.rhtml (wrapped in controller1_layout)

How to call render/render_to_string from outside of a controller?

This problem was possed on http://lists.rubyonrails.org/pipermail/rails/2006-May/043642.html but I didn't see a solution posted there.

This does not work:

      renderer = ActionController::Base.new
      @my_string = renderer.render_to_string(:template => 'controller/template')
      @my_string = renderer.instance_eval { render_to_string('controller/template') }
      @my_string = renderer.send(:render_template,'controller/template')
      @my_string = RendererController.new.custom_render(:template => 'controller/template')

Those give errors such as:

The error occured while evaluating nil.[]=
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:994:in `add_class_variables_to_assigns'"

http://rails.techno-weenie.net/forums/1/topics/281 had two possible solutions: curl the URL of the template (don't like that idea, because then your templates have to be public), or call ERB directly.

ERb (Embedded Ruby)

Template loop detected: Template:Include with edit link







Rendering templates / calling helpers from outside of normal controller/web context

How do I access a helper from a controller or other non-view class (like -- gasp! -- a model)?

Inspired by: http://www.bigbold.com/snippets/posts/show/1799

Add this to your ApplicationController:

  # Make it possible to call helpers (the presentation layer) from controllers.
  def presentation
    Presentation.instance
    presentation.controller = self
    presentation
  end
  class Presentation
    include Singleton
    include ActionView::Helpers::TextHelper
    include ActionView::Helpers::TagHelper   # provides html_escape, which is used by url_for
    include ActionView::Helpers::UrlHelper
    attr_accessor :controller
    # add any other helpers you want to be able to use
  end

What are legitimate uses of this?

Okay, so some may object to the use of a helper in a controller or model, but sometimes, I don't see any other way to do it...

These are the legitimate uses I've come across so far:

  • You want to put more than just plain text in a flash message. For example, you want to include a link in the message.
    • flash.now[:error] = "Blah blah. Please visit #{presentation.link_to('this page', 'http://wherever.com')} for more information."
    • Or do you think that should go in a view instead (and use render_to_string)?

Please comment if you disagree with any of these or know of a better way, a way to avoid needing to do this.

Caveat (fixed): using link_to raises NoMethodError "The error occurred while evaluating nil.url_for"

Started out with this (nice and simple):

  # Make it possible to call helpers (the presentation layer) from controllers.
  def presentation
    Presentation.instance
  end
  module Presentation
    include Singleton
    include ActionView::Helpers::TextHelper
    include ActionView::Helpers::UrlHelper
    # add any other helpers you want to be able to use
  end

This is the code I had in my controller action:

flash.now[:notice] = "Please go to #{presentation.link_to('this page', :action => 'this_page')}."

But I got this error:

NoMethodError (You have a nil object when you didn't expect it!
The error occurred while evaluating nil.url_for):

Here is the backtrace:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/helpers/url_helper.rb:27:in `send'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/helpers/url_helper.rb:27:in `url_for'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_view/helpers/url_helper.rb:75:in `link_to'
app/controllers/the_controller.rb:363:in `the_action'

This is the offending line: url_helper.rb:27:

        url = @controller.send(:url_for, options, *parameters_for_method_reference)

So @controller is nil, is it? Ugh.

Normally that is an instance variable of the anonymous "view" class created by Rails (#<#<Class:0xb70d9334>:0xb70d930c>). In this case, however, it is trying to access it as an instance variable of ApplicationController::Presentation (#<ApplicationController::Presentation:0xb721a66c>).

Therefore, we need to make our Presentation class behave as closely as possible (or at least as closely as is necessary to get this to work) as the view class as possible. In particular, we need to cause it to have a @controller instance variable which evaluates to an actual controller (rather than nil).

At first I tried to make it so I could pass in a controller to the Presentation constructor:

  def presentation
    Presentation.instance(self)
  end
  class Presentation
    include Singleton
    def initialize(controller)
      @controller = controller
    end
  end

, but it looks like Klass.instance() does not pass along its arguments to Klass#initialize, as I would expect it to.

So I resorted to this less elegant solution:

  # Make it possible to call helpers (the presentation layer) from controllers.
  def presentation
    Presentation.instance
    presentation.controller = self
    presentation
  end
  class Presentation
    include Singleton
    include ActionView::Helpers::TextHelper
    include ActionView::Helpers::TagHelper   # provides html_escape, which is used by url_for
    include ActionView::Helpers::UrlHelper
    attr_accessor :controller
    # add any other helpers you want to be able to use
  end

How could we rewrite my example so that we didn't need to call a helper from a controller?

Easy enough. Just move that presentation code into a view "where it belongs" and render that view with a call to render_to_string:

flash.now[:notice] = render_to_string(:action => 'flash_message_for_foo_action', :layout => false)

flash_message_for_foo_action.rhtml:

Please go to <%= link_to('this page', :action => 'this_page') %>.

A ticket for what appears to be a similar problem: #6774 ((PATCH) console's helper object doesn't work with #link_to) - Rails Trac - Trac


Rails / Ajax

Rails / Ajax edit


Rails / Ajax  edit   (Category  edit)


http://svn.tylerrick.com/public/rails/examples/ajax/ajax_examples/

You can't use Ajax.Updater to update a table in IE!

Let me clarify... The following code works just fine in Firefox, but does not work in IE:

app/views/test/ajax.rhtml:
<table id="table_without_tbody">
  <tr id="tr1">
    <td>
      Static content
    </td>
  </tr>
</table>
<%= link_to_remote "add to :bottom of 'table'", :update => 'table_without_tbody', :url => {:action => 'an_ajax_action'}, :position => :bottom %>

app/controllers/test_controller.rb:                                                                                                     
class TestController < ApplicationController
  def an_ajax_action
    render :layout => false
  end
end

app/views/test/an_ajax_action.rhtml:
Content to be added via Ajax

Symptoms: Nothing happened when I clicked the link. The request showed up in the logs and there were no errors server side, but no changes in the DOM were visible. I didn't even get an alert saying "RJS Error" like some people have reported.

The solution is to update the tbody instead of the table directly:

app/views/test/ajax.rhtml:
<table id="table_with_tbody">
<tbody id="tbody">
  <tr id="tr">
    <td>
      Static content
    </td>
  </tr>
</tbody>
</table>
<%= link_to_remote "add to :bottom of 'tbody'", :update => 'tbody', :url => {:action => 'an_ajax_action'}, :position => :bottom %>
<%= link_to_remote "add :after 'tr'", :update => 'tr', :url => {:action => 'an_ajax_action'}, :position => :after %>

References:



Ajax effects: Drag and drop


Indicator icons

http://www.napyfab.com/ajax-indicators/ A bunch of (free -- I assume) animated indicator icons you can use to indicate that an Ajax call is currently in process / working in the background.


 


Since you apparently can't call render/render_to_string from outside of a controller, how do I render my templates?

You can use ERb directly, much like Rails does behind the scenes when it renders a template:

      template = ERB.new(File.readlines('app/views/dir/my_view.rhtml').join(''), nil, '-')
      html = template.result(binding)

how do I use helpers, like image_tag?

Story: (hide)

lib/some_class.rb:
class SomeClass
  include ApplicationHelper
  include ActionView::Helpers::AssetTagHelper
end

But then you get this error:

NoMethodError: undefined method `relative_url_root'
    /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:156:in `compute_public_path'
    /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:125:in `image_path'
    /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:140:in `image_tag'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:
152         def compute_public_path(source, dir, ext)
153           source  = "/#{dir}/#{source}" unless source.first == "/" || source.include?(":")
154           source << ".#{ext}" unless source.split("/").last.include?(".")
155           source << '?' + rails_asset_id(source) if defined?(RAILS_ROOT) && %r{^[-a-z]+://} !~ source
156           source  = "#{@controller.request.relative_url_root}#{source}" unless %r{^[-a-z]+://} =~ source
157           source = ActionController::Base.asset_host + source unless source.include?(":")
158           source
159         end
lib/some_class.rb:

class FakeRequest
  def relative_url_root; "http://example.com"; end
end
class FakeController
  def request; FakeRequest.new; end
end
class SomeClass
  include ApplicationHelper
  include ActionView::Helpers::AssetTagHelper
end

But then you get this error:

NoMethodError: undefined method `tag'
    /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:148:in `image_tag'
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_view/helpers/asset_tag_helper.rb:
148         tag("img", options)

Final version:

lib/some_class.rb:

class FakeRequest
  def relative_url_root; "http://example.com"; end
end
class FakeController
  def request; FakeRequest.new; end
end
class SomeClass
  include ApplicationHelper
  include ActionView::Helpers::AssetTagHelper
  include ActionView::Helpers::TagHelper
end


How do I access a helper from a controller or other non-view class (like -- gasp! -- a model)?

http://www.bigbold.com/snippets/posts/show/1799

Add this to your ApplicationController:

  def help
    Helper.instance
  end

  class Helper
    include Singleton
    include ActionView::Helpers::TextHelper
  end


Rails / Ajax

Rails / Ajax edit


Rails / Ajax  edit   (Category  edit)


http://svn.tylerrick.com/public/rails/examples/ajax/ajax_examples/

You can't use Ajax.Updater to update a table in IE!

Let me clarify... The following code works just fine in Firefox, but does not work in IE:

app/views/test/ajax.rhtml:
<table id="table_without_tbody">
  <tr id="tr1">
    <td>
      Static content
    </td>
  </tr>
</table>
<%= link_to_remote "add to :bottom of 'table'", :update => 'table_without_tbody', :url => {:action => 'an_ajax_action'}, :position => :bottom %>

app/controllers/test_controller.rb:                                                                                                     
class TestController < ApplicationController
  def an_ajax_action
    render :layout => false
  end
end

app/views/test/an_ajax_action.rhtml:
Content to be added via Ajax

Symptoms: Nothing happened when I clicked the link. The request showed up in the logs and there were no errors server side, but no changes in the DOM were visible. I didn't even get an alert saying "RJS Error" like some people have reported.

The solution is to update the tbody instead of the table directly:

app/views/test/ajax.rhtml:
<table id="table_with_tbody">
<tbody id="tbody">
  <tr id="tr">
    <td>
      Static content
    </td>
  </tr>
</tbody>
</table>
<%= link_to_remote "add to :bottom of 'tbody'", :update => 'tbody', :url => {:action => 'an_ajax_action'}, :position => :bottom %>
<%= link_to_remote "add :after 'tr'", :update => 'tr', :url => {:action => 'an_ajax_action'}, :position => :after %>

References:



Ajax effects: Drag and drop


Indicator icons

http://www.napyfab.com/ajax-indicators/ A bunch of (free -- I assume) animated indicator icons you can use to indicate that an Ajax call is currently in process / working in the background.


 



View-level: The end //

 









Error/exception handling

Rails / Exception handling edit

What does Rails do when you request a path for which there is no matching route?

In Rails 1.1.6, it responded with a HTTP/1.x 404 Not Found error, displaying the contents of public/404.html if it existed.

And everyone was happy. Because that made sense.

The new 1.2 behavior is to raise a nasty RoutingError (which then gets e-mailed to you along with all the other exceptions, if you're using ExceptionNotifier) and then show the user a nasty 500 error page.

This is not the desired behavior for me, for a couple reasons:

  • These types of errors are too easy for a user to trigger. There's no way to stop the user from going to http://yoursite.com/foo and triggering a (what should be a) 404.
    • As such, they are not usually "exceptions" that you care about.
    • As such, these probably-not-a-problem errors should not be e-mailed to the developer with the other errors which are usually much more urgent.
  • The user should receive an appropriate response from HTTP. 500 is not appropriate because that suggests that something went wrong on the server end, which it didn't. "404 Not Found" is the only appropriate response to give, because indeed, Rails was unable to find a matching route.

Apparently I'm not the only one to think this new behavior sucks...

http://brian.pontarelli.com/2007/01/14/handling-rails-404-and-500-errors/. Retrieved on 2007-05-11 11:18.

I spent a couple of hours trying to figure out how to handle 404 and 500 errors in Rails. This is not simple and actually really annoying. Hopefully future versions clean this up because right now it sucks pretty badly

How to fix this behavior in Rails 1.2.3

http://forum.textdrive.com/viewtopic.php?id=16694 suggests doing something like this in ApplicationController:

def rescue_action_in_public(exception)
  case exception
    when ActiveRecord::RecordNotFound, ActionController::RoutingError, ActionController::UnknownController, ActionController::UnknownAction
      #render_404
    else          
      #render_500
  end
end

However, if you are using the exception_notification plugin (and you should be!) (> r7360), then this is handled for you:

./lib/exception_notifiable.rb

    def exceptions_to_treat_as_404
      exceptions = [ActiveRecord::RecordNotFound,
                    ActionController::UnknownController,
                    ActionController::UnknownAction]
      exceptions << ActionController::RoutingError if ActionController.const_defined?(:RoutingError)
      exceptions
    end

    def rescue_action_in_public(exception)
      case exception
        when *self.class.exceptions_to_treat_as_404
          render_404

        else
          render_500

          ...

          ExceptionNotifier.deliver_exception_notification(exception, self,
            request, data)
      end
    end

So install exception_notification and be happy again!

Other good links:



Dissection: What does Rails do when an (otherwise unhandled) exception occurs?

Just look at the source for rescue_action(exception):

    # File vendor/rails/actionpack/lib/action_controller/rescue.rb, line 26
       def rescue_action(exception)
         log_error(exception) if logger
         erase_results if performed?
 
         if consider_all_requests_local || local_request?
           rescue_action_locally(exception)
         else
           rescue_action_in_public(exception)
         end
       end

It

  • logs it
  • displays it to the screen (maybe)

Rails makes the following assumptions:

  • During development, you want to see a page with detailed debug info. This is called a "local rescue" or a "rescue for the developer view" ([3]).
  • For everyone else in the world, you want to show the visitor a user-friendly but uninformitive message saying that there was an error (but not giving the details of this error).

To decide whether you are a developer or an "everyone else", it doesn't look at your environment ('development' or 'production'), like I might have expected, but at your IP address (by default, 127.0.0.1 is the only IP considered local by default -- which basically only includes the case when you are doing development with webrick). That could be useful, I suppose, if you were debugging an error remotely and wanted it to automatically show the developer view any time you connected from your home computer. But I'm still not sure I like it.... My opinion:

  • On the production site, the developers are going to want to always be notified about exceptions (via e-mail or whatever).
  • Deciding whether or not you should see extra, developer-only info based on your IP is (1) slightly unsafe, (2) not all that helpful (what if you have a dynamic IP at home or you travel a lot and could be connecting from any number of different IPs?).

See http://api.rubyonrails.org/classes/ActionController/Rescue.html .

I might find it useful to override local_request?() sort of like this:

def local_request?()
  if environment == "production" then false
  if environment == "development" then true
end

I just noticed that something like this is already possible via the environments/* files:

config/environments/development.rb:

# Show full error reports
config.action_controller.consider_all_requests_local = true

That will ensure that all requests done during development will have a local rescue (verbose errors).

(Question: Won't that happen anyway? When would the request be not from localhost?)

(Complaint: Why don't they name it for what it does rather than how it works? It should be called config.action_controller.show_full_error_reports, not config.action_controller.consider_all_requests_local! "local" doesn't mean much to me -- at least not what they think it means.)



Should exceptions be handled the same way during development as during production?

Well, it depends what you want to debug... The default behavior is to show a different error page (rescue_action_locally) during development, one that gives the full error details and a backtrace.

That's usually fine... unless perhaps you're testing the error handling itself and you want it to be identical to production.

In that case, this might be useful... (put this in app/controllers/application.rb)

  def rescue_action_locally(exception)
    rescue_action_in_public(exception)
    super
  end

For instance, right after you install exception_notification in an app, you may want to test that it actually works in development before sending it out into the wild (where it could potentially break horribly)...

Question: will that actually cause it to try to send exception e-mails to someone for every exception that is raised during development?



Errors: What's the difference between "Application error" and "Application error (Rails)"?

"Application error" is found in public/500.html and (I believe) is rendered by ... (??)

"Application error (Rails)", on the other hand, is defined here:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/rescue.rb:

      # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
      def rescue_action_in_public(exception) #:doc:
        case exception
          when RoutingError, UnknownAction then
            render_text(IO.read(File.join(RAILS_ROOT, 'public', '404.html')), "404 Not Found")
          else render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
        end
      end

It is also defined here, but this probably doesn't happen as often:

/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/dispatcher.rb

      # If the block raises, send status code as a last-ditch response.
      def failsafe_response(output, status, exception = nil)
        yield
      rescue Object
        begin
          output.write "Status: #{status}\r\n"

          if exception
            message    = exception.to_s + "\r\n" + exception.backtrace.join("\r\n")
            error_path = File.join(RAILS_ROOT, 'public', '500.html')

            if defined?(RAILS_DEFAULT_LOGGER) && !RAILS_DEFAULT_LOGGER.nil?
              RAILS_DEFAULT_LOGGER.fatal(message)

              output.write "Content-Type: text/html\r\n\r\n"

              if File.exists?(error_path)
                output.write(IO.read(error_path))
              else
                output.write("<html><body><h1>Application error (Rails)</h1></body></html>")
              end
            else
              output.write "Content-Type: text/plain\r\n\r\n"
              output.write(message)
            end
          end
        rescue Object
        end
      end

When would that get called instead of the normal 500 error? I don't know yet...

Normally, it looks like the ugly "Application error (Rails)" message is rendered for all "normal" exceptions (rescue_action_in_public in /usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/rescue.rb). This is surprising; I would have expected it to use 500.rhtml. (Why doesn't it??)

However, if you have the exception_notifiable plugin installed (which I recommend everyone do!), then rescue_action_in_public will be overridden to have the desired behavior. Yay!

./vendor/plugins/exception_notification/lib/exception_notifiable.rb:

  def render_500
    respond_to do |type|
      type.html { render :file => "#{RAILS_ROOT}/public/500.html", :status => "500 Error" }
      type.all  { render :nothing => true, :status => "500 Error" }
    end
  end

  def rescue_action_in_public(exception)
    case exception
      when ActiveRecord::RecordNotFound, ActionController::UnknownController, ActionController::UnknownAction
        render_404

      else
        render_500

        deliverer = self.class.exception_data
        data = case deliverer
          when nil then {}
          when Symbol then send(deliverer)
          when Proc then deliverer.call(self)
        end

        ExceptionNotifier.deliver_exception_notification(exception, self,
          request, data)
    end
  end



How do I set it up to e-mail me when an error message occurs?

See Exception Notifier plugin

How to I change the error pages for my application?

http://wiki.rubyonrails.com/rails/pages/HowtoConfigureTheErrorPageForYourRailsApp

 








How do I deal with dependencies?

There are several kinds of dependencies you may have:

  • gems (such as Rails itself)
  • svn:externals
  • plugins (often included via svn:externals but not necessarily)


Problem: Rails apps depending on gems

Problem: Rails apps depending on gems edit


GemInstaller

Homepage: http://geminstaller.rubyforge.org/index.html
Documentation: .




Depends on: RubyGems


Implementation language: Ruby



GemInstaller Documentation (http://geminstaller.rubyforge.org/documentation/documentation.html#using_geminstaller_from_other_ruby_apps). Retrieved on 2007-05-11 11:18.


Config File Syntax Summary This is an example config file template showing all the valid properties (boolean defaults capitalized):

---
defaults:
  install_options: [ any valid option for the RubyGems 'gem install' command ]
  check_for_upgrade: [ true | FALSE ]
  fix_dependencies: [ true | FALSE ]
  no_autogem: [ true | FALSE ]
  prefer_binary_platform:  [ TRUE | false ]
gems:
- name: [ gem name ]
  version: '[ any valid version specification for this gem, e.g. >= 1.0.0 ]'
  platform: [ any valid platform for this Gem and version specification ]
  install_options: [ any valid option for the RubyGems 'gem' command ]
  check_for_upgrade: [ true | FALSE ]
  fix_dependencies: [ true | FALSE ]
  no_autogem: [ true | FALSE ]
  prefer_binary_platform: [ TRUE | false ]
- name: [ another gem name ]
- name: [ yet another gem name ]
  (etc...)


Automatically Requiring Gems with the autogem Method

GemInstaller provides a GemInstaller.autogem class method which will automatically invoke RubyGems’ ‘gem’ method to add all of the gems in your GemInstaller config file(s) to the load path. [...]

...

...
GemInstaller.autogem('--config=/path/to/one/geminstaller.yml,/path/to/another/geminstaller.yml')
...

...

You can also use the “—exceptions” argument to cause autogem to throw an exception if it encounters any error. This is useful if you want to abort your application startup if any gems failed to load.

http://geminstaller.rubyforge.org/documentation/tutorials.html. Retrieved on 2007-05-11 11:18.


Using Common or Shared Config Files

GemInstaller supports multiple config files, with the last config files in the list overriding the previous ones. This means you could do any of the following:

  • Have a common config file across all your projects, which is shared via an svn:external, and a custom config file to override or add gems which are not in the common config.
  • Have a common config file across all development machines, shared on a windows share or NFS mounted drive, with machine- or platform-specific gems listed in local config files.
  • Have config files that are specific to certain environments such as development and test, which will not be included when GemInstaller is run in a demo or production environment.


You may have no idea what gems your app uses, or you might just want to create a GemInstaller config file with all the gems currently on your system, in order to install the same gems on a different system. This tutorial will show you how to use the --print-rogue-gems option to automatically create a GemInstaller config file.

$ geminstaller --print-rogue-gems > geminstaller.yml

That was easy, wasn’t it? This config file should specify the exact versions for all of the gems which are currently installed on your system.

Gotchas/Notes:

  1. ...
  2. The --print-rogue-gems option generates exact version specifications. This means that you won’t ever get any gem upgrades by running GemInstaller with the standard generated file. This may be good (an upgrade can never break your app unexpectedly). However, you may wish to modify some of the version specifications to allow for upgrades. If you do this, remember to set the check_for_upgrade config property to true, but be aware that this will cause GemInstaller to check the remote gem server for upgrades each time it runs, and cause it to run slow or fail if the server is unavailable.


Integrating/using with Rails

http://geminstaller.rubyforge.org/documentation/tutorials.html. Retrieved on 2007-05-11 11:18.


Integrating GemInstaller into Ruby on Rails

GemInstaller can be configured to automatically install all gems in the config file, and add them to the load path when Rails boots. This means that you can check out and run your application anywhere, without having to worry about manually ensuring that the required dependency gems are installed. This works with Webrick and Mongrel.

IMPORTANT NOTE: Currently, there is a bug with installing Mongrel and Rails gems on Rails startup. See Known Bugs for details.

First, you need a Rails app. I’ll let you handle this step on your own. See the Rails documentation for details.

Next, you need a geminstaller.yml config file. Create this under the Rails config directory. For details, see the documentation on the config file and the tutorial on bootstrapping your GemInstaller config with the --print-rogue-gems option.

Capistrano method

http://geminstaller.rubyforge.org/documentation/tutorials.html#running_geminstaller_from_capistrano. Retrieved on 2007-05-11 11:18.


GemInstaller can be hooked in as a capistrano task. This is a way to avoid having the boot.rb hacks run in [...] production, and possibly cause problems with app startup in these environments. It also is a way to avoid the current bug with Rails and Mongrel gems not being loaded correctly if they are upgraded during the app startup process. TODO: Finish writing this

boot.rb method

GemInstaller Documentation (http://geminstaller.rubyforge.org/documentation/documentation.html#using_geminstaller_from_other_ruby_apps). Retrieved on 2007-05-11 11:18.


Once you have your geminstaller.yml created, invoke the GemInstaller on app startup in your boot.rb. It should be placed right after the block which defines the RAILS_ROOT constant, as shown below: ...

Possible solution: Freeze/unpack all your gems

Gems on Rails

Homepage: http://drnicwilliams.com/2007/02/09/railsrally-2007-and-gemsonrails/


Project/Development: https://rubyforge.org/projects/gemsonrails/







http://drnicwilliams.com/2007/02/09/railsrally-2007-and-gemsonrails/. Retrieved on 2007-05-11 11:18.


rake gems:freeze   GEM=gem_to_freeze
rake gems:link     GEM=gem_to_link
rake gems:unfreeze GEM=gem_to_unfreeze_or_unlink
 


Depending on a specific version of Rails

http://wiki.rubyonrails.org/rails/pages/HowtoLockToSpecificRailsVersions

Some solutions for depending on a specific version of Rails:

  • Freeze Rails
  • Use Gem dependencies

Freezing/unpacking your gems into svn

Should you do it??

Some say that this violates Don't Repeat Yourself. I would tend to agree; I don't really like it.

Chad Woolley on http://weblog.rubyonrails.org/2006/3/31/freeze-is-cool-so-freeze-for-goodness-sake :


Please don’t force untold numbers of duplicate copies of Rails to be checked into SCM repositories the world over. This would be a Very Bad Idea. Take a lesson from Maven, using versioned external dependencies from a central repository is a proven and reliable practice. It can also be extended to encompass your own custom dependencies from a local repository.

How to freeze/unpack gems

http://blog.nanorails.com/articles/2006/03/28/freeze-all-your-ruby-gems-on-a-shared-host Freeze all your ruby gems on a shared host

 cd vendor/rails
 gem unpack activerecord
 gem unpack activesupport
 gem unpack actionpack
 gem unpack actionmailer
 gem unpack actionwebservice
 mv activerecord-1.13.2/ rails/
 mv actionmailer-1.1.5/ actionmailer
 mv actionpack-1.11.2/ actionpack
 mv actionwebservice-1.0.0/ actionwebservice
 mv activerecord-1.13.2/ activerecord
 mv activesupport-1.2.5/ activesupport

Using Rails code outside of normal web context: as cron script, command-line script, etc.

How do I call a controller action from a stand-alone command-line/cron (runner) script?

Just instantiate the controller?

This seems to work, at least from console (should work from runner too):

x = ReportController.new x.run_report

But... it doesn't render the view :-(

http://forums.site5.com/showthread.php?t=8063 how to run rails action via cron job? - Site5 Web Hosting Forums

There are some maintenance actions in my admin controller that I'd like to run via cron job daily, rather than manually via the browser. Any suggestions on how to do this?
script/runner is your best option
running by wgetting an action might results in "action timeout" for long jobs. the preferred way is to use script/runner through a cron job caller.

not really helpful:

http://blog.yanime.org/articles/2006/08/05/rails-calling-render-outside-your-controllers








Using Rails code outside of normal web context: How do I run things in the background / as daemons / as scheduled jobs?

(See also Distributed Ruby)


BackgrounDRb

BackgrounDRb edit


Homepage: http://backgroundrb.rubyforge.org/
Source code: svn://rubyforge.org//var/svn/backgroundrb
Project/Development: http://rubyforge.org/projects/backgroundrb/


Description: BackgrounDRb is a small framework that facilitates running background tasks in a separate process from Rails, thereby decoupling them from the request/response cycle. With DRb you can manage your tasks from Rails using hooks for progress bars or status updates to your users.






http://rubyforge.org/pipermail/backgroundrb-devel/2006-September/000350.html [Backgroundrb-devel] Could a BackgrounDrb worker do this?

http://backgroundrb.rubyforge.org/

BackgrounDRb is a ruby job server and scheduler. Its main intent is to be used with Ruby on Rails applications for offloading long-running tasks. Since a rails application blocks while servicing a request it is best to move long running tasks off into a background process that is divorced from the http request/response cycle.

BackgrounDRb tag on Ezra's blog

star_full.gif star_full.gif star_empty.gif http://www.infoq.com/articles/BackgrounDRb


...

scheduling via a yaml config file

Scheduling tasks in Ruby / Rails - igvita.com (http://www.igvita.com/blog/2007/03/29/scheduling-tasks-in-ruby-rails/) (2007-03-29). Retrieved on 2007-05-11 11:18.


scheduled_task:
  :class: :scheduled_task_worker
  :worker_method: :do_work
  :trigger_args:
    :start: <%= Time.now + 10.minutes %>
    :repeat_interval: <%= 1.hour %>
 



OpenWFEru

OpenWFEru edit


Homepage: http://openwferu.rubyforge.org/scheduler.html
Documentation: http://openwferu.rubyforge.org/scheduler.html RDoc
Source code: gem install openwferu-scheduler
Project/Development: http://rubyforge.org/projects/openwferu


Description: OpenWFEru being a workflow / business process engine needs a scheduler for tasks to execute later (at) and tasks to execute repetitively (cron).


License: BSD




Scheduling tasks in Ruby / Rails - igvita.com (http://www.igvita.com/blog/2007/03/29/scheduling-tasks-in-ruby-rails/) (2007-03-29). Retrieved on 2007-05-11 11:18.


scheduler = Scheduler.new
scheduler.start
 
# Simple usage
scheduler.schedule_every('2s') { p 'every 2 seconds' }
scheduler.schedule_every('5m') { p 'every 5 minutes' }
scheduler.schedule_every('1d') { p 'every day' }
 
# More fine-grained control + schedule task once.
scheduler.schedule_in('3d2m10s') do
    p 'after 3 days 2 minutes and 10 seconds stopping the scheduler and exiting...'
    scheduler.do_stop
end
 
# Cron-string scheduling. There is something for everyone here!
scheduler.schedule('1-60 * * * *') do
    p 'perform task'
end
 
scheduler.join

...

 


Cron

HowToRunBackgroundJobsInRails (http://wiki.rubyonrails.com/rails/pages/HowToRunBackgroundJobsInRails). Retrieved on 2007-05-11 11:18.


script/runner -e production "Model.do_something"

is by far the most stable way of running background processes in Rails.

Cron, when used with RoR, has the following shortcomings:

  • Significant startup resources required for each job
  • Lots of RAM to run simultaneous processes
  • Hard to start/stop/affect the background processes from within Rails.

Hacktivate » Manage cron from rails (http://www.vdomck.org/2007/06/29/mange-cron-from-rails/). Retrieved on 2007-05-11 11:18.


A quick and dirty yet tried and true method is to use cron to call script/runner that does everything we need. All I needed to do was write the crontab entries - but that means later deployments will require someone to do that. My approach was to use environment.rb to programmatically edit the crontab. ...

http://kylemaxwell.typepad.com/everystudent/2006/09/railscron_depre.html RailsCron Deprecation / DaemonGenerator

If you want bare-bones, stable, and functional, use daemon_generator. If you want messaging and features, use backgroundRb. I don't see value in RailsCron anymore.

http://kylemaxwell.typepad.com/everystudent/2006/08/after_writing_r.html Every Student: Announcing daemon_generator

http://svn.kylemaxwell.com/rails_plugins/daemon_generator/

http://svn.kylemaxwell.com/rails_plugins/daemon_generator/tags/rel-0.5.0/

http://wiki.rubyonrails.org/rails/pages/RunnerScript RunnerScript in Ruby on Rails

Note: These methods must be created within models. The runner script does Not load any controllers. Controllers are associated with an http request. An http request does not exist in the command line environment.

Where do I put this code??

Keeping the M, V, and C in MVC!


Keeping the views clean, and also the controllers, by refactoring

Jamis Buck (2006-10-18). Skinny Controller, Fat Model (http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model). Retrieved on 2007-05-03 12:02. (License: Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License)

When first getting started with Rails, it is tempting to shove lots of logic in the view. ... Not only is the above difficult to read (just you try and find the HTML elements in it), it also completely bypasses the “C” in “MVC”. Consider the controller and model implementations that support that view: ... Is it really any wonder that it is so tempting for novices to take this approach? They’ve got all their code in one place, and they don’t have to go switching between files to follow the logic of their program. [...] For various reasons, though, this is a very, very bad idea. MVC has been successful for many reasons, and some of those reasons are “readability”, “maintainability”, “modularity”, and “separation of concerns”. You’d like your code to have those properties, right? A better way is to move as much of the logic as possible into the controller. Seriously, isn’t that what the controller is for? It is supposed to mediate between the view and the model. Let’s make it earn its right to occupy a position in our source tree: ... Better! Definitely better. We dropped that big noisy chunk at the top of the template, and it’s more immediately obvious what the structure of the HTML file is. Also, you can see by reading the controller code roughly what kind of data is going to be displayed. However, we can do better. There’s still a lot of noise in the view, mostly related to conditions and computations on the model objects. Let’s pull some of that into the model:

# app/models/person.rb
class Person < ActiveRecord::Base
  # ...

  def name
    "#{last_name}, #{first_name}"
  end

  def age
    (Date.today - person.birthdate) / 365
  end

  def pseudo_id
    new_record? ? "new" : id
  end
end

<!-- app/views/people/index.rhtml -->
<% @people.each do |person| %>
  <div id="person-<%= person.pseudo_id %>">
    <span class="name"><%= person.name %></span>
    <span class="age"><%= person.age %></span>
  </div>
<% end %>

Wow. Stunning, isn’t it? The template is reduced to almost pure HTML, with only a loop and some simple insertions sprinkled about. Note, though, that this is not just a cosmetic refactoring: by moving name, age and pseudo_id into the model, we’ve made it much easier to be consistent between our views, since any time we need to display a person’s name or age we can simply call those methods and have them computed identically every time. Even better, if we should change our minds and decide that (e.g.) age needs to be computed differently, there is now only one place in our code that needs to change.

However, there’s still a fair bit of noise in the controller. I mean, look at that index action. If you were new to the application, coming in to add a new feature or fix a bug, that’s a lot of line noise to parse just to figure out what is going on. If we abstract that code into the model, we can not only slim the controller down, but we can effectively document the operation we’re doing by naming the method in the model appropriately. Behold:

# app/models/person.rb
class Person < ActiveRecord::Base
  def self.find_recent
    people = find(
      :conditions => ["added_at > ? and deleted = ?", Time.now.utc, false],
      :order => "last_name, first_name")
  end

  # ...
end

# app/controllers/people_controller.rb
class PeopleController < ActionController::Base
  def index
    @people = Person.find_recent
  end
end

Voila! Looking at PeopleController#index, you can now see immediately what is going on. Furthermore, in the model, that query is now self-documenting, because we gave the method a descriptive name, find_recent.

Be aggressive! Try to keep your controller actions and views as slim as possible. A one-line action is a thing of wonder, as is a template that is mostly HTML. It is also much more maintainable than a view that is full of assignment statements and chained method calls.

Another (lesser) nice side-effect of lean controllers: it allows respond_to to stand out that much more, making it simple to see at a glace what the possible output types are:

...

Like adopting RESTful practices, it may take some time to wrap your mind around the refactoring process, especially if you’re still accustomed to throwing lots of logic in the view. Just be careful not to go too far; don’t go putting actual view logic in your model. If you find your model rendering templates or returning HTML or Javascript, you’ve refactored further than you should. In that case, you should make use of the helper modules [...].

Other benefits of this approach are…

  • testing domain logic in models is simpler & clearer than testing it in controllers and views
  • it makes your Rails console much more useful
Like the web user interface (with its controllers and views), both tests and the console, should be considered as important clients to the domain model.

Thread about fragment caching:

Zsombor at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

If you want fragment caching to actually save work you need to call down to the database from the view. Best I could make in such cases is placing the call to model method in a helper. But this just feels less intuitive. Am I missing something?

Jamis at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

I’m not sure what you mean, Zsombor, about needing to call the DB from the view for fragment caching to work. Backpack uses fragment caching extensively and we don’t do any Model#find calls from the view there.

bodhi at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

I believe what Zsombor meant was that even if you cache your view output, the controller is still going to (redundantly) call Person.find_recent.

Zsombor at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

If Model#find calls are present in the view code either directly or indirectly trough helpers, then how can you benefit from fragment caching? There will be no database calls saved, and rendering html is usually fast leaving little to save … What am I missing?

Jamis at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

zsombor, I understand now what you are saying. The fragment caching we do only saves us the trouble of re-rendering complex layout—we don’t actually avoid DB queries. (Well, we avoid some, since we lazy-load the associations on the page, but every request for a page will hit the DB for that page object.) I still feel that hitting the DB from the view is nasty, but yah, I’m not sure how you’d work around the issue with fragment caching.


Where to put reusable display-ish logic: In the model, helper, or controller?

http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model

class Person < ActiveRecord::Base
  def pseudo_id
    new_record? ? "new" : id
  end
end

zerohalo at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

pseudo_id could be a helper, [but] I think it fits fine in the model. I’m sometimes unsure whether to put little functions like that as part of the model or as a helper. Personally, I think helpers are better for things that are used beyond one particular model.

Andrey Tarantsov at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

I don’t like the pseudo_id method, because it’s presentational. Some day or another, you decide to make a link out of that “new”. Or maybe you want it in bold face, or maybe you want to give it an id to replace later as a result of an AJAX call. Even without going that far, just consider internationalized UI (able to switch the language of the UI for each user individually). How do you know what language to give the word “new” in? That information is in a session probably, and models can’t (and shouldn’t ever desire to) access sessions. So, well, pseudo_id’s place is among helpers.

Jamis at http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model :

Actually, I disagree that pseudo_id is presentational. It is no more presentational than id is; it simply handles the case where the record has not been saved (record.id returns nil if the record has never been saved successfully).


Presenter objects

[1]

This controller implementation works fine; however, the results method can become a bear to test. To solve this issue my team began inserting another layer: a Presenter. The main responsibility of a Presenter is to expose data for the view to consume. After introducing the presenter the controller becomes much slimmer.

class StandardsController < ApplicationController
  
  def list
    @standards = Standard.find(:all)
  end
  
  def results
    @presenter = Standard::ResultsPresenter.new()
  end
  
end

The Standard::ResultsPresenter class handles aggregating all the required data.

...

After introducing this abstraction the Presenter can be tested in isolation.

class Standard::ResultsPresenterTest < Test::Unit::TestCase

  def test_total_standards
    Standard.expects(:find).with(:all).returns([1,2,3])
    assert_equal 3, Standard::ResultsPresenter.total_standards
  end
  
  def test_standards_for_hr
    standard_stubs = [stub(:department=>'hr'), stub(:department=>'other')]
    Standard.expects(:find).with(:all).returns(standard_stubs)
    assert_equal 1, Standard::ResultsPresenter.standards_for_hr.size
  end
  
  def test_standard_categories
    standard_stubs = [stub(:catagory=>'Ruby'), stub(:catagor=>'Ruby')]
    Standard.expects(:find).with(:all).returns(standard_stubs)
    assert_equal ['Ruby'], Standard::ResultsPresenter.standard_categories
  end 

end

We like this style because it's much cleaner than the previously required code that lived in the controller tests. However, there is overhead involved in generating this additional layer. Because of the overhead we generally don't add a presenter until we notice that testing has become painful in the controller test file.

[1]

The classic example for using a Presenter is a summary page. For example, if you buy books from Amazon, the final page has information about each book that you bought, tax information, shipping information, billing information, user account information, etc. In that scenario, I'd prefer to aggregate all that data in a Presenter. Presenters offer other opportunities for separation of concerns that models do not. For example, if you want to validate that the user entered two matching passwords or email addresses you must add 'virtual' attributes to your models. The same is true if you would like to create an acceptance attribute (to use validates_acceptance_of). You could argue that since these attributes are not persisted to the database that they should not live in the model. Additionally, Presenters that include the Validatable module can contain child objects whos e validations can participate in the presenter's 'valid?' method.

  1. 1.0 1.1 Jay Fields. Rails Model View Controller + Presenter? (http://blog.jayfields.com/2006/09/rails-model-view-controller-presenter.html). Retrieved on ~~~~~.

http://blog.jayfields.com/2006/09/rails-model-view-controller-presenter.html, posted by Luke Melia : 1:17 AM

I've handled similar issues by making my model add singleton methods to the array returned by model#find.

Rails / ModelFormatter plugin edit

ModelFormatter

Formatting stuff in and out of the models

Homepage: http://brad.folkens.com/formatting-data-in-rails-models
Source code: https://hattenschwetter.com/svn/hattenschwetter/rails_plugins/model_formatter/trunk


As listed in other directories: http://agilewebdevelopment.com/plugins/modelformatter




Authors: Brad Folkens


http://agilewebdevelopment.com/plugins/modelformatter

The ModelFormatter module allows you to easily handle fields that need to be formatted or stripped of formatting as the are set or retrieved from the database. You can designate one or more of your columns as "formatted columns" like in this example:

  class Widget < ActiveRecord::Base
    # Set an integer field as a symbol
    format_column :some_integer, :as => :integer

    # Specify the type as a class
    format_column :sales_tax, :as => Formatters::FormatCurrency
    format_column :sales_tax, :as => Formatters::FormatCurrency.new(:precision => 4)

    # Change the prefix of the generated methods and specify type as a symbol
    format_column :sales_tax, :prefix => 'fmt_', :as => :currency, :options => {:precision => 4}

    # Use specific procedures to convert the data +from+ and +to+ the target
    format_column :area, :from => Proc.new {|value, options| number_with_delimiter sprintf('%2d', value)},
                         :to => Proc.new {|str, options| str.gsub(/,/, '')}

    # Use a block to define the formatter methods
    format_column :sales_tax do
      def from(value, options = {})
        number_to_currency value
      end
      def to(str, options = {})
        str.gsub(/[\$,]/, '')
      end
    end

    ...
  end

Brad Folkens. Formatting data in Rails Models (http://brad.folkens.com/formatting-data-in-rails-models). Retrieved on 2007-05-03 14:04.

[...] We’ve got lots of database columns that need to be formatted/un-formatted. View helpers are great for formatting things on the way out, but what happens when users need to enter that data in the same format, or in an auto-detected format? To begin, take our Widget example, with a column called weight. Weight is just an integer and we want it formatted all pretty, so we use a helper. But later we decide the user needs to enter data using the same format, so the controller accepts the input and can strip it, or we can throw that in the model too. Eventually though, we end up with a lot of the same code, un-formatting things in a model or controller that we’ve formatted already in the view. Seems a little un-DRY to me… Ideally, we’d have this code formatting stuff on the way into and out of a model, as with something like the code below:

class Widget < ActiveRecord::Base
  include ActionView::Helpers::NumberHelper

  def formatted_weight
    number_with_delimiter weight
  end

  def formatted_weight=(value)
    weight = value.nil? ? nil : value.gsub(/,/, '').to_i
  end
end

Ok, not so bad – we’ve got some basic stuff in place to format an attribute in the model and then something to accept formatted text, un-format it and set the weight attribute.

...

Install the model_formatter:

script/plugin install -x \
  https://hattenschwetter.com/svn/hattenschwetter/rails_plugins/model_formatter/trunk/

Then we can change our Widget to the following:

class Widget < ActiveRecord::Base
  format_column :weight, :as => :integer
end

[...] Now, what happens when we want to change the way weight is formatted? Let’s pass some options.

format_column :weight, :as => :integer, :delimiter => '.'

Now, instead of formatting our weight with something like 1,425,321 we return 1.425.321. You can pass options like this to all the formatters (and custom formatters too, as you’ll see further down). For example, the :decimal formatter takes :precision and the :currency formatter just passes options on the number_to_currency – so all your freedom is preserved.

Each formatter is just a small class which extends Formatters::Format. The super class contains a very simple set of methods: initialize, from, and to. It names the formatters, by default, with the ‘formatted_’ prefix [It gives you both a formatted_weight reader and a formatted_weight= writer.]. [...]

So far, all we’ve used are the included formatters in the Formatters module, which are conveniently resolved with the symbolized shortcuts. So for example, :as => :integer expands to Formatters::FormatInteger. Fortunately, we can provide any Formatters::Format code to model_formatter in the same way as the shortcuts:

format_column :weight,
  :as => Formatters::FormatInteger.new(:delimiter => '.')

So there it is, the simple way to format things coming out of and going back into the model. We can customize the model_formatter even more though.

What if we wanted to provide our own formatting functionality? Easy – we can do this several different ways. Either provide a class like the above, or you can provide a Proc directly:

format_column :weight,
  :from => Proc.new {|value, options| number_with_delimiter(value) + 'lbs.'},
  :to => Proc.new {|str, option| str.gsub(/,/, '')}

[We can] do the same thing with a block.

format_column :weight do
  def from(value, options = {})
    number_to_delimiter value
  end
  def to(str, options = {})
    str.gsub(/,/, '')
  end
end

We already know we can do things like the following to our Widget instance:

>> widget.weight = 1000
=> 1000
>> widget.formatted_weight
=> 1,000
>> widget.formatted_weight = '1,100'
=> '1,100'
>> widget.weight
=> 1100

[...] We typically need to do things which access the same formatters, but not necessarily through an instantiated [model] object. When the format_column method accepts our :as => :integer option, it creates an instance of the integer formatter. If you’ve specified a Proc pair or a block, you still get the same benefits, since it will wrap your Procs or block and expose them in a similar way, using (in our :to and :from, or block custom example) a CustomFormatWeight class. With the block, you get the added benefit of optionally adding an initialize method. In any case, you can access this formatter class in the following way:

>> Widget.formatted_weight_formatter 1000
=> 1,000
>> Widget.formatted_weight_unformatter '1,100'
=> 1100

You can also access the options passed to the formatter directly, which can be very helpful to piggy-back meta attributes with an attribute. See the :display_size option:

>> Widget.formatted_weight_formatting_options
=> {:to=>nil, :from=>nil, :options=>{},
:prefix=>"formatted_", :as=>:integer, :display_size => '12pt',
:formatter=>#<Formatters::FormatInteger:0x2601110 @delimiter=",">,
:formatted_attr=>"formatted_weight", :attr=>"weight"}
 




Miscellaneous: Questions

Alias: Questions and Answers / Problems and Solutions

This category has been relegated to the lowly status of "secondary category". Please use the topic (ActiveRecord, Views, etc.) as the primary category.

Which log level should I use (config.log_level)?

What's the difference between boot.rb and environment.rb?

environment.rb itself requires boot.rb

# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')   

script/* seem to require boot.rb rather than environment.rb?

How come I have often included environment.rb directly from cron scripts, etc.? Should I change those to use boot.rb instead?

What does script/runner do?

What does the :environment rake task do?

When I'm in a controller, view, etc., what is self and what methods and variables are available to me?

In a view

<% p self.class %>

You might be surprised to find that this returns an anonymous class, not an instance of ActionView::Base (as I would have expected):

#<Class:0xb6b1fa10>

Here are the ancestors of this anonymous class:

<% p self.class.ancestors %>
[#<Class:0xb6b7a320>, #<Module:0xb6ba7424>, EditMenuHelper, #<Module:0xb6beb250>, ApplicationHelper, #<Module:0xb6e5ad88>, ActionView::Base, #<Module:0xb6ff1cc8>, ActionView::Helpers::QualitySmithMenuHelper, ActionView::Helpers::ResourceOnDemand, ActionView::Helpers::MenuDuJourHelper, ActionView::Helpers::UrlHelper, ActionView::Helpers::TextHelper, ActionView::Helpers::TagHelper, ActionView::Helpers::ScriptaculousHelper, ActionView::Helpers::PaginationHelper, ActionView::Helpers::NumberHelper, ActionView::Helpers::JavaScriptHelper, ActionView::Helpers::PrototypeHelper, ActionView::Helpers::JavaScriptMacrosHelper, ActionView::Helpers::FormTagHelper, ActionView::Helpers::FormOptionsHelper, ActionView::Helpers::FormHelper, ActionView::Helpers::DebugHelper, ActionView::Helpers::DateHelper, ActionView::Helpers::CaptureHelper, ActionView::Helpers::CacheHelper, ActionView::Helpers::BenchmarkHelper, ActionView::Helpers::AssetTagHelper, ActionView::Helpers::ActiveRecordHelper, ActionView::Partials, ActionView::Base::CompiledTemplates, ERB::Util, Object, Base64::Deprecated, Base64, PP::ObjectMixin, Kernel]

[Rails / Code dissections (category)]

Here's where the class is created:

/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/base.rb:

      def self.view_class
        @view_class ||=
          # create a new class based on the default template class and include helper methods
          returning Class.new(ActionView::Base) do |view_class|
            view_class.send(:include, master_helper_module)
          end
      end

As you can see, this new class is created as a subclass of ActionView::Base

We can easily confirm that by putting this in our view:

<% puts self.class.superclass %>

But it begs the question, why are they creating an anonymous subclass there? Why don't thy just instantiate ActionView::Base directly?

If anyone knows the answer to that, please clue me in. Meanwhile, I am stumped.

One of the side effects of having this anonymous class is that when you do debugging and start inspecting objects, you will see instances of anonymous classes and not even be able to tell what class the bloomin instance is unless you explicitly call self.class.superclass or self.class.ancestors. How annoying.

Here's a sample of what happens if you try to do pp self from within a view:

#<#<Class:0xb6c587ec>:0xb6c58760
 @_cookies=
  {"_backend_session_id"=>["1e6dd0819611df08b691db2527bd3511"],
   "_session_id"=>["74d1c8abcc26a68562475ab00c0d7ec6"]},
 @_flash={},
 @_headers={"cookie"=>[], "Cache-Control"=>"no-cache"},
 @_params={"action"=>"index", "controller"=>"edit_menu"},
 @_request=
  #<ActionController::CgiRequest:0xb69e3390
   @cgi=
    #<Mongrel::CGIWrapper:0xb69e3bc4
     @args=nil,
     @cookies=
      {"_backend_session_id"=>["1e6dd0819611df08b691db2527bd3511"],
       "_session_id"=>["74d1c8abcc26a68562475ab00c0d7ec6"]},
...

(This assumes you've applied this patch to pp so that you can actually call pp on that object without getting an ArgumentError: wrong number of arguments (1 for 0).)

Here's where the template class is instantiated: /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/base.rb:

      def initialize_template_class(response)
        raise "You must assign a template class through ActionController.template_class= before processing a request" unless @@template_class

        response.template = self.class.view_class.new(self.class.view_root, {}, self)
        response.redirected_to = nil
        @performed_render = @performed_redirect = false
      end


In a controller

If you do this from within your controller MyController:

puts self.class

, you will, not surprisingly, get this output:

MyController

If I assign an instance variable in my controller, how does it end up as an instance variable in my view??

When we set an instance variable in our controller, like this:

  @foo = 'woot!'

, we are setting an instance variable in an object of type MyController (ApplicationController, which is a subclass of ActionController).

So how does that cause an instance variable to be available in my view, where [[:Base]]?

/usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/base.rb

    # Holds the hash of variables that are passed on to the template class to be made available to the view. This hash
    # is generated by taking a snapshot of all the instance variables in the current scope just before a template is rendered.
    attr_accessor :assigns

    ...

      def add_variables_to_assigns
        unless @variables_added
          add_instance_variables_to_assigns
          add_class_variables_to_assigns if view_controller_internals
          @variables_added = true
        end
      end

      def add_instance_variables_to_assigns
        @@protected_variables_cache ||= Set.new(protected_instance_variables)
        instance_variables.each do |var|
          next if @@protected_variables_cache.include?(var)
          @assigns[var[1..-1]] = instance_variable_get(var)
        end
      end

We can confirm that our @foo instance variable is actually getting used there by putting some [debug output (category)] in add_instance_variables_to_assigns:

      def add_instance_variables_to_assigns
        @@protected_variables_cache ||= Set.new(protected_instance_variables)
        instance_variables.each do |var|
          next if @@protected_variables_cache.include?(var)
          puts "Assigning #{var} to @assigns[#{var[1..-1].inspect}]"
          @assigns[var[1..-1]] = instance_variable_get(var)
        end
      end
Assigning @_headers to @assigns["_headers"]
Assigning @url to @assigns["url"]
Assigning @session to @assigns["session"]
Assigning @_params to @assigns["_params"]
Assigning @foo to @assigns["foo"]               # <--
Assigning @template to @assigns["template"]
Assigning @_session to @assigns["_session"]
Assigning @_request to @assigns["_request"]
Assigning @flash to @assigns["flash"]
Assigning @params to @assigns["params"]
Assigning @headers to @assigns["headers"]
Assigning @_response to @assigns["_response"]
Assigning @request_origin to @assigns["request_origin"]
Assigning @_flash to @assigns["_flash"]
Assigning @cookies to @assigns["cookies"]
Assigning @variables_added to @assigns["variables_added"]
Assigning @action_name to @assigns["action_name"]
Assigning @response to @assigns["response"]
Assigning @request to @assigns["request"]
Assigning @_cookies to @assigns["_cookies"]
Assigning @before_filter_chain_aborted to @assigns["before_filter_chain_aborted"]

From, there I don't quite understand how it gets from ActionController::Base's @assigns into the instance of the ActionView subclass... but somehow ... it does.

Miscellaneous: Complaints

Messy backtraces with extraneous information

When you get a load error, there's alway so much cruft between the load error (displayed at the top) and the source of the load error (which is usually the only thing you care about!!!), which is usually about 5 levels lower (visually)

no such file to load -- /home/tyler/code/plugins/app_profiler/test/app/config/../../init.rb                     # useful!
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'                             # extraneous
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'                                          # extraneous
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:495:in `require'             # extraneous
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'    # extraneous
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:495:in `require'             # extraneous
/home/tyler/code/plugins/app_profiler/test/app/config/../vendor/plugins/app_profiler/init.rb:5:in `load_plugin' # useful!




Miscellaneous: Reloadability

Why does it not seem to get my changes until I restart the server?

If it's not derived from a standard Rails class (like ActiveRecord::Base), you might need to add this line to your class:

include Reloadable

This is usually the case for code you put in the lib/ directory! Expect to have to restart every time you make a little change there. Or make it Reloadable.

Modules, unfortunately, cannot include the Reloadable module.


How do I make modules reloadable too?

In other words, I'm tired of restarting my application just to test out the changes I just made to my module, every time I change a line of code!

http://www.mathewabonyi.com/articles/2006/07/28/class-module-reloadable-everything

(Haven't tried it yet)

http://dev.rubyonrails.org/ticket/5329

(Might be fixed in 1.2?)


So if there's just one Ruby process (the Rails dispatcher) constantly running in the background, wouldn't things like global variables and class/method definitions from one request cycle possibly carry over into the next and cause problems?

(Context: production environment, mostly; during development, you're starting and stopping the server a lot)

I think the answer is yes, theoretically, but Rails does its best to clean up after each request...

Here is some of the code I've found that does cleanup..

./vendor/rails/railties/lib/dispatcher.rb

    def dispatch(cgi = nil, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
      ...
    ensure
      reset_after_dispatch
    end

    # Reset the application by clearing out loaded controllers, views, actions,
    # mailers, and so forth. This allows them to be loaded again without having
    # to restart the server (WEBrick, FastCGI, etc.).
    def reset_application!
      ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)

      Dependencies.clear
      ActiveSupport::Deprecation.silence do # TODO: Remove after 1.2
        Class.remove_class(*Reloadable.reloadable_classes)
      end

      ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
    end

Don't see any mention of clearing out global variables, however...
















Blogs

Ruby on Rails blogs edit

http://weblog.jamisbuck.org/ star_full.gif star_full.gif star_full.gif

http://errtheblog.com/ star_full.gif star_full.gif star_full.gif

Loud Thinking by David Heinemeier Hansson star_full.gif star_full.gif star_full.gif

http://www.opensoul.org/ "the ethics of code | opensoul.org"


I'm Brandon Keepers, a web application developer that likes beautiful code, valid markup and adherence to standards. As a part of Collective Idea in Holland, Michigan, I practice Agile software development primarily using Ruby on Rails.

http://www.robbyonrails.com/ star_full.gif star_full.gif star_empty.gif


thoughts.sort_by{|t| t[:topic]}.collect

Eigenclass star_full.gif star_full.gif star_empty.gif

err.the_blog star_full.gif star_full.gif star_empty.gif

caboo.se star_full.gif star_full.gif star_empty.gif

Rick Olson star_full.gif star_full.gif star_empty.gif (Mephisto developer)

Josh Susser star_full.gif star_full.gif star_empty.gif

http://blog.codahale.com/

http://www.lukeredpath.co.uk/

http://pragdave.pragprog.com/pragdave/ruby_rails/index.html Ruby & Rails

http://glu.ttono.us/articles/category/rails

nubyonrails/topfunky

http://weblog.rubyonrails.com/

http://www.planetrubyonrails.org/tags/view/ruby

http://blog.innerewut.de/

http://blog.zenspider.com/

http://jayfields.blogspot.com/

http://blog.segment7.net/

http://www.softiesonrails.com/

http://codefluency.com/

http://www.mathewabonyi.com/

http://www.actsasflinn.com/articles/category/rails

http://woss.name/category/geekery/ruby-and-rails/

Ruby on Rails Blog

http://www.onrails.org/

[4]

http://blog.nbwd.co.uk/ Bamboo Blog http://www.drnicwilliams.com/ Dr. Nic Williams http://www.rubybyraeli.org/blog/ Ruby by ’Raeli http://blog.methodmissing.com/ Rails, Ruby & Prototype ramblings—Lourens Naudé http://www.fearoffish.co.uk/ Jamie van Dyke http://ozone.wordpress.com/ Olivier Ansaldi http://on-ruby.blogspot.com/ Pat Eyler http://www.bencurtis.com/ http://www.puneruby.com/blog/ PuneRuby http://www.railsenvy.com/ Rails Envy http://redhanded.hobix.com/ RedHanded http://www.rorsecurity.info/ RoR Security http://www.rubyfleebie.com/ Ruby Fleebie http://rubyunderground.org/ Ruby Underground http://webonrails.com/ Web On Rails http://web2withrubyonrails.gauldong.net/ Web 2.0 With Rails

http://blog.nicksieger.com/ Nick Sieger -- "Working with glue that doesn't set"

http://www.rubyinside.com/railstips/ Rails Tips

http://blog.teksol.info/ A Single Programmer's Blog (François)

http://interblah.net/ interblah.net—conflagration over configuration

http://slash7.com/ hoy

jeremyhubert.com


I am not a designer -- The personal website of Jeremy Hubert i turn pretty designs into great software This is a bunch of useless text that allows me to see what the words here would look like when I actually get around to writing some proper content. If I had written this and actually intended for it to be seen by people like you, I probably would have put a little bit more effort into it. Either way, this should do the trick.

http://mir.aculo.us/ fuchs

http://www.jvoorhis.com/ voorhis

http://iamrice.org/ tanner

http://www.atmos.org/ atmos

http://david.goodlad.ca/ goodlad

http://AlternateIdea.com / http://encytemedia.com/ "AlternateIdea: A self-proclaimed design ninja" (Justin is Co-founder and Interaction Super-hero for Active Reload, LLC., the company behind Lighthouse and Mephisto.)

http://jystewart.net/ jystewart.net : Reading, writing, web development

http://rails.co.za/ Rails Co ZA -- sharing development tips we'd like to remember

http://www.igvita.com/blog/ igvita.com

http://blog.brightredglow.com/

def euler(x); cos(x) + i*sin(x); end

http://scottstuff.net/blog

 


Blogs: Articles (Advice / Tips / Opinions)

things-you-shouldnt-be-doing-in-rails

http://glu.ttono.us/articles/2006/08/30/guide-things-you-shouldnt-be-doing-in-rails











Deployment

See Rails / Deployment



Post-deployment

My log files are getting huge! How do I set up log rotation?

Put something like this in your environment.rb:

RAILS_DEFAULT_LOGGER = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}.log", 50, 1.megabyte)

(http://schwuk.com/articles/2006/03/21/log-rotation-in-rails-apps)




Performance

See Rails / Performance




Debugging

Logger

Normally (and by that I mean in your controller) you can access the logger with the easy [helper method] named "logger":

logger.info "whatever you want to log"

However, there are places where that method isn't available: for example, if you have some class that is called by a runner. Or in your tests.

For those places, you can use this alternative...

http://www.robbyonrails.com/articles/2006/01/25/rails-logger-and-those-pesky-tests :

RAILS_DEFAULT_LOGGER




Troubleshooting

Rails / Troubleshooting edit

[Caveats (category)] Some hashes are not of type HashWithIndifferentAccess

[To do: rewrite this betterly]

Implication: You can't do hash[:key] and expect it to work, if hash actually has a key of 'key'.

Many Some things in Rails (such as params and session) return hashes of class HashWithIndifferentAccess... which means you can access the key as either :key or 'key' (you don't have to care which is the actual key)...

But don't become so used to that behavior that you forget that normal hashes don't behave that way. In other words, any libraries (stuff you stick in lib/) you write, or pretty much anywhere you are constructing hashes like this: {key => value} -- it's going to just be a plain old Hash.

Here's a (somewhat boring) illustration:

> ./script/console
irb -> HashWithIndifferentAccess.new({:a=>2})['a']
    => 2

irb -> HashWithIndifferentAccess.new({:a=>2}).class
    => HashWithIndifferentAccess

# But!:

irb -> {:a=>2}.class
    => Hash

irb -> {:a=>2}['a']
    => nil

Question: So what actually uses HashWithIndifferentAccess rather than Hash?

Not session, apparently. Here's how I concluded that... [Dissections (category)] First of all, if we simply look at the value of session.class in our controller, we may be somewhat surprised to see CGI::Session rather than some sort of flavor of Hash. "Doesn't session behave as a hash though?!" you ask? Yes, it does. But only because it implements [] and []= (in other words, it acts like a Hash, thanks to duck typing): /usr/lib/ruby/1.8/cgi/session.rb

    # Retrieve the session data for key +key+.
    def [](key)
      @data ||= @dbman.restore
      @data[key]
    end

    # Set the session date for key +key+.
    def []=(key, val)
      @write_lock ||= true
      @data ||= @dbman.restore
      @data[key] = val
    end

Anyway, @dbman.restore will give us an instance of type CGI::Session::ActiveRecordStore... which is still not a hash, but getting closer...

I put the following debug output in /usr/lib/ruby/gems/1.8/gems/actionpack-1.13.3/lib/action_controller/session/active_record_store.rb :

class CGI
  class Session
    class ActiveRecordStore
      class Session < ActiveRecord::Base
        ...
        # Lazy-unmarshal session state.
        def data
          @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
          puts @data.class
          p @data
          @data
        end

Then, when I loaded the page, I saw this output from my development web server:

Hash
{"flash"=>{}, :user=>1509}

So the class is clearly Hash.

As further confirmation, I did this in my view:

<%= session[:user].inspect %>

and got this back as output: 1509.

<%= session['user'].inspect %>

and got this back as output: nil.

What about params? Putting this in our view:

<%= params.class %>

tells us the answer very quickly: HashWithIndifferentAccess. That was easy!

Question: So how/where does Rails actually convert things to HashWithIndifferentAccess??

cannot remove Object::__ / How to debug Dependencies problems

About my environment:


> ./script/about
About your application's environment
Ruby version                 1.8.5 (i386-linux)
RubyGems version             0.9.2
Rails version                1.2.3
Active Record version        1.15.3
Action Pack version          1.13.3
Action Web Service version   1.2.3
Action Mailer version        1.3.3
Active Support version       1.4.2
 

Here was the initial symptom in my case:

> rake remote:show_tasks --trace
rake aborted!
cannot remove Object::General
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:426:in `remove_const'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:426:in `send'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:426:in `remove_constant'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:372:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:372:in `each'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:372:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:501:in `require'
/usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/kernel/require_local.rb:13:in `require_local'
/home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/rails.rake:3
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:493:in `load'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:493:in `load'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:493:in `load'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8:in `each'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:501:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:501:in `require'
/home/tyler/svn/glass.net/frontend/old/Rakefile:10
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load_rakefile'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1929:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/bin/rake:7
/usr/bin/rake:16:in `load'
/usr/bin/rake:16

I tried enabling logging, like this (environment.rb):

RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
Dependencies::log_activity = true

, but it was too verbose in some ways (displayed stuff I wasn't interested in) and not verbose enough in other ways (didn't show enough context -- so all the log messages ended up looking the same):

Dependencies: called new_constants_in(Object)
Dependencies: New constants:
Dependencies: called new_constants_in(Object)
Dependencies: New constants:
Dependencies: called new_constants_in(Object)
Dependencies: New constants:
...


So I ended up putting some more-specialized debug output in /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:


  def load(file, *extras)
    $requiring ||= []                     # <--
    $requiring.push file                  # <--
    Dependencies.new_constants_in(Object) { super(file, *extras) }
  rescue Exception => exception  # errors from loading file
    exception.blame_file! file
    raise
  end

  def require(file, *extras)
    $requiring ||= []                     # <--
    $requiring.push file                  # <--
    Dependencies.new_constants_in(Object) { super(file, *extras) }
  rescue Exception => exception  # errors from required file
    exception.blame_file! file
    raise
  end



  def new_constants_in(*descs)
    aborting = true
    begin
      yield # Now yield to the code that is to define new constants.
      aborting = false
    ensure
      requiring = $requiring.pop          # <--

      # Find the new constants.
      new_constants = watch_frames.collect do |mod_name, prior_constants|
        ...
      end.flatten

      log "New constants: #{new_constants * ', '}"

      if aborting
        puts '{-----------------'
        puts "Error while attempting to load '#{$requiring}'. Removing partially loaded constants (#{new_constants.inspect})..."
        p $!
        log "Error during loading, removing partially loaded constants "
        new_constants.each { |name| puts "removing #{name}"; remove_constant name }
        new_constants.clear
        puts '}-----------------'
      end
    end
    ...

Which produced:


...
{-----------------
Error while attempting to load 'xsd/xmlparser/xmlscanner'. Removing partially loaded constants ([])...
#<MissingSourceFile: no such file to load -- xmlscan/scanner>
}-----------------
{-----------------
Error while attempting to load '/home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks'. Removing partially loaded constants (["ForApps", "SharedRailsTasks", "General", "Database", "ForPlugins"])...
ArgumentError: wrong number of arguments (1 for 0)
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:49:in `namespace'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:49:in `included'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:36:in `class_eval'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:36:in `included'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:18:in `include'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:18:in `included'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:324:in `include'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/shared_rails_tasks.rb:324
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/kernel/require_local.rb:13:in `require_local'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/rails.rake:6
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8:in `each'
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /home/tyler/svn/glass.net/frontend/old/Rakefile:10
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load_rakefile'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1929:in `run'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/bin/rake:7
  /usr/bin/rake:16:in `load'
  /usr/bin/rake:16
removing ForApps
{-----------------
Error while attempting to load '/home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/rails.rake'. Removing partially loaded constants ([])...
NameError: cannot remove Object::ForApps
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_const'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `send'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_constant'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `each'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/kernel/require_local.rb:13:in `require_local'
  /home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/rails.rake:6
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8:in `each'
  /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
  /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
  /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
  /home/tyler/svn/glass.net/frontend/old/Rakefile:10
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load_rakefile'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1929:in `run'
  /usr/lib/ruby/gems/1.8/gems/rake-0.7.2/bin/rake:7
  /usr/bin/rake:16:in `load'
  /usr/bin/rake:16
}-----------------
...
...
rake aborted!
cannot remove Object::ForApps
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_const'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `send'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_constant'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `each'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
/usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/kernel/require_local.rb:13:in `require_local'
/home/tyler/svn/glass.net/frontend/old/config/../vendor/plugins/shared_rails_tasks/tasks/rails.rake:6
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8:in `each'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
/home/tyler/svn/glass.net/frontend/old/Rakefile:10
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load_rakefile'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1929:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/bin/rake:7
/usr/bin/rake:16:in `load'
/usr/bin/rake:16
 

The source of the problem / solution

The Module#namespace method I'd written in quality_extensions (quality_extensions/module/namespace) was conflicting with Rake's namespace method. So when I was calling namespace from my Rakefile, thinking that I was calling a method from the Rake library, I really wasn't -- I was calling a completely different method that "just happened" to have the same name...

Well, my solution in this case was to rename my namespace to something else (namespace_module) (fixed in quality_extensions-0.0.52), but I wish I didn't have to do that. Can't our two libraries just get along? I guess that's just one of the dangers of adding methods to important base classes like Module...

Other questions

This raises several other questions, though:

  • Why did it think General was a constant in Object when it's actually a constant in the SharedRailsTasks module?
  • Why did all those other files ...

utf8proc_native, db2/db2cli, oci8, oci8, sybsql, Win32API, memcache, redcloth, redcloth, bluecloth, bluecloth, html/tokenizer, tmail/base64.so, tmail/scanner_c.so, xml/parser, xml/parser, xmlscan/scanner, xmlscan/scanner ) fail to load? ...Not that it's ever caused this problem, where it caused it to try to remove constants and then the constant removal failed... It's probably been failing on these files before for a long time and I'd just never noticed it before because it fails silently and it's not important that it not fail... .

Why did it think ForApps was a constant in Object when it's actually a constant in the SharedRailsTasks module?

ForApps is clearly a constant within SharedRailsTasks, not within Object!

p SharedRailsTasks.constants
# => ["Database", "ForPlugins", "General", "ForApps"]

To answer that question, I pared down my Rakefile to just the minimum needed to reproduce this behavior...

Deleted all plugins. Created a new dummy plugin and moved my rake task code into it...

module MyModule
  def self.included(base_module)
    include MyModule::General
  end

  # The tasks defined in this module can be used by both apps and plugins.
  module General
    def self.included(base_module)
      raise 'An error'
    end
  end
end

include MyModule

I tried moving the contents of my plugin's rake file into the main Rakefile, but that was not sufficient to reproduce this behavior. It caused an error to be raised (of course):

rake aborted!
An error
/home/tyler/svn/glass.net/frontend/old/Rakefile:26:in `included'
/home/tyler/svn/glass.net/frontend/old/Rakefile:20:in `include'
/home/tyler/svn/glass.net/frontend/old/Rakefile:20:in `included'
/home/tyler/svn/glass.net/frontend/old/Rakefile:32:in `include'
/home/tyler/svn/glass.net/frontend/old/Rakefile:32
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1855:in `load_rakefile'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake.rb:1929:in `run'
/usr/lib/ruby/gems/1.8/gems/rake-0.7.2/bin/rake:7
/usr/bin/rake:16:in `load'
/usr/bin/rake:16

... but not this one, which is the error I get when that same code is moved to vendor/plugins/dummy/tasks/dummy_tasks.rake:

cannot remove Object::General
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_const'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `send'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:428:in `remove_constant'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `each'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:373:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:496:in `load'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8:in `each'
/usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:8
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:342:in `new_constants_in'
/usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb:505:in `require'
/home/tyler/svn/glass.net/frontend/old/Rakefile:9

In other words, that error only occurs when my rake tasks are loaded through this mechanism in /usr/lib/ruby/gems/1.8/gems/rails-1.2.3/lib/tasks/rails.rb:

Dir["#{RAILS_ROOT}/vendor/plugins/**/tasks/**/*.rake"].sort.each { |ext| load ext }

Well, I dug around in /usr/lib/ruby/gems/1.8/gems/activesupport-1.4.2/lib/active_support/dependencies.rb a bit more, trying to figure out why it wasn't working as expected, but I couldn't really figure it out...

It looks like this is where it got Object from...

    Dependencies.new_constants_in(Object) { super(file, *extras) }

so I'm not really sure how it's supposed to work. Oh well, I won't waste any more time on this one...


Application error (failed to start)

Check the log file.

Reasons for this error:

  • file script/../config/../tmp/sessions//ruby_sess.0f36e74cd2a06698 not readable (solution: delete cookie to force new session)
  • permissions
  • failed to include some required file
  • (production:) pretty much any exception

[ActiveRecord (category)]::ConnectionNotEstablished : may simply be the permissions on production.log!

At least that's how I fixed it in this case (2007-01-16):

$ ./script/console production
Loading production environment.
/usr/lib/ruby/1.8/logger.rb:527:in `initialize':Errno::EACCES: Permission denied - script/../config/../config/../log/production.log
/usr/lib/ruby/gems/1.8/gems/activesupport-1.3.1/lib/active_support/dependencies.rb:123:in `const_missing':NameError: uninitialized constant ExceptionNotifiable
>> User.find(:first)
ActiveRecord::ConnectionNotEstablished: ActiveRecord::ConnectionNotEstablished
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/connection_adapters/abstract/connection_specification.rb:225:in `retrieve_connection'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/connection_adapters/abstract/connection_specification.rb:78:in `connection'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:1046:in `add_limit!'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:1017:in `construct_finder_sql'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:924:in `find_every'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:918:in `find_initial'
        from /usr/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:380:in `find'
        from (irb):1

Tried this, but I didn't have permissions to modify the file:

$ chmod a+w log/production.log
chmod: changing permissions of `log/production.log': Operation not permitted

But I do have drwxrwxrwx on the log directory, so that must be sufficient permissions to move the file:

$ mv log/production.log log/production.log.20070116; touch log/production.log

(The already-running application continued to append to the old file (log/production.log.20070116). However, when I ran script/console production, the console logged to the new file (log/production.log).)

When I tried it this time, it worked!

$ ./script/console production
Loading production environment.
>> User.find(:first)
=> #<User:0xb7460604 ...>

Caveat: `alias_method': undefined method `[...]_with_deprecation' error

This is the result an [incompatibility (category)] between Rails' version of alias_method_chain and Facets' version.

Example of error:

/usr/lib/ruby/gems/1.8/gems/facets-1.8.54/lib/facets/core/module/alias_method_chain.rb:34:in `alias_method': undefined method `add_benchmarks_with_deprecation' for class `AppProfilerRequest' (NameError)

Current as of: Facets 1.8.54

[Workaround (category)]:

Method 1, which I think only works with 1.2.3

config/environment.rb:


# Make sure this gets added to $LOADED_FEATURES now so that it won't get loaded again.
require 'facets/core/module/alias_method_chain'  

# Override alias_method_chain (again!) with the Rails version (which is compatible with the Facets version)
load 'active_support/core_ext/module/aliasing.rb'   # This file only exists in Rails 1.2.3

# That should all be above the Initializer code in case one of your plugins [...]...

Rails::Initializer.run do |config|
  ...
end

Method 2

Another method of doing this (courtesy of Lance):

require File.join(File.dirname(__FILE__), 'boot')
# By this point, it appears that ActiveSupport has been loaded, so Rails's version of Module.alias_method_chain has already been defined.

saved_alias_method_chain = Module.method(:alias_method_chain)

Rails::Initializer.run do |config|
  ...
end

# Make sure this gets added to $LOADED_FEATURES now so that it won't get loaded again.
require 'facets/core/module/alias_method_chain'  

# Override alias_method_chain (again!) with the Rails version (which is compatible with the Facets version)
Module.send(:define_method, :alias_method_chain, saved_alias_method_chain)

Variations

Sometimes neither of those methods work. Sometimes you have to do the saving of the Rails method or the restoring thereof sooner or later...

I had a similar issue although the fix was a little different than the previous examples. I instead had to require the alias_method_chain and load the rails aliasing at the bottom of the environment.rb (Rails 1.2.3). The second fix also didn't work erroring with:
config/environment.rb:12:in `method': undefined method `alias_method_chain' for class `Class' (NameError)

Example:
config/environment.rb:

Rails::Initializer.run do |config|
  ...
end

require 'facets/core/module/alias_method_chain'  
load 'active_support/core_ext/module/aliasing.rb'

One time, I couldn't get any of these methods to work, so I copied the aliasing.rb method from Rails 1.2.3, threw it in lib/ and loaded that from my Rails 1.1.6 app (because I can't find 'def alias_method_chain' anywhere in the Rails 1.1.6 code!)...

Ticket

Ticket: http://rubyforge.org/tracker/index.php?func=detail&aid=9791&group_id=804&atid=3169

Caveat!: Why am I getting a "wrong number of arguments (2 for 0)" error?

One possible reason: you have a column named 'quote'.

http://rails.techno-weenie.net/question/2006/8/10/wrong-number-of-arguments-issue:

Make sure you don't have a column called "quote" in any of your tables. This will override the current ActiveRecord "quote" method. http://dev.rubyonrails.org/ticket/3628

[Minor (category)][Caveats (category)]: If you modify log/development.log while your development web server is running and then make a request, nothing will get logged.

[Is this reliably reproducible?]

Solution: restart the web server and try again.

Or don't modify the log while your server is running...

 


Unsorted

http://snippets.dzone.com/user/err Err's Code Snippets

http://snippets.dzone.com/tag/model

http://errtheblog.com/post/43 err.the_blog.find_by_title('Real Console Helpers')

http://www.railsweenie.com/forums/2/topics/758 Share Controller Helpers with a View - Rails Weenie

http://snippets.dzone.com/posts/show/1799 Using helpers inside a controller [rails] [helpers] [custom]

Facts about RailsRDF feed
Depends on ActiveRecord  +, and RubyGems  +
Implemented in Ruby  +
Licensed under MIT  +, and BSD  +
Description [Oops! No type defined for attribute]
Retrieved from "http://whynotwiki.com/Rails"
Ads
Personal tools