What's New in Edge Rails: DB Console 7

Posted by ryan
at 4:39 PM on Tuesday, May 13, 2008



For those of you that haven’t committed the database username and passwords for your many apps to memory yet, there comes some help from edge Rails – the addition of a dbconsole script that will automatically drop you into a database prompt using the info in config/database.yml configuration.

In an edge rails app that has a mysql db config:

script/dbconsole 
...
mysql>

And there I am, logged into the dev database in the mysql command prompt. No scrounging around for that app-specific username or db name or cat ing the config/database.yml file anymore.

tags: ruby, rubyonrails

"loaded_specs" Gem Error 3

Posted by ryan
at 3:30 PM on Tuesday, May 13, 2008



For those of you that have run into this gem error when running the latest rails:

./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `install_gem_spec_stubs': undefined method `loaded_specs' for Gem:Module (NoMethodError)
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `reject!'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:175:in `install_gem_spec_stubs'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:89:in `send'
        from ./script/../config/../vendor/rails/railties/lib/initializer.rb:89:in `run'
        from ./script/../config/boot.rb:46:in `load_initializer'
        from ./script/../config/boot.rb:38:in `run'
        from ./script/../config/boot.rb:11:in `boot!'
        from ./script/../config/boot.rb:109
        from script/dbconsole:2:in `require'
        from script/dbconsole:2

the solution is a quick and dirty:

gem update --system

Or, if you currently have rubygems v0.8.4 or earlier installed:

gem install rubygems-update
update_rubygems

Just a public service announcement from the “Ryan’s been bitten by this one” dept.

What's New in Edge Rails - In Japanese! 1

Posted by ryan
at 7:24 AM on Friday, April 11, 2008



Not to be outdone by his neighbor to the west, Kenji Kobayashi is maintaining japanese translations of the What’s New in Edge Rails series. Thanks for spreading the news, Kenji!

tags: ruby, rubyonrails

What's New in Edge Rails: UTC-based Migration Versioning 18

Posted by ryan
at 9:11 AM on Wednesday, April 02, 2008



Rails migrations are a somewhat contentious bunch. On one hand they provide a consistent way of provisioning your database, and on the other hand they like to conflict with each other if there’s a heavy stream of development due to a simplistic naming convention. Just in is a change that will name your migrations based on a more unique UTC-based timestamp instead of just a sequential ordering.

When you go to create your next migration you’ll see something like this:

1
2
> script/generate migration one
      create  db/migrate/20080402122512_one.rb

Instead of the migration file being named 001_one.rb it now has a more unique prefix that will less likely conflict with another migration somebody else happened to check-in around the same time.

Update: When you do an svn update and get new migrations, even if they have a timestamp previous to a migration you’ve already added and run in your own environment, rake db:migrate will intelligently apply all migrations that have not yet run. This eliminates the interleaved migrations issue that existed on the first rev of this feature (see comments).

Another change that accompanies this functionality is the addition of rake db:migrate:up and rake db:migrate:down tasks, which will let you run the up and down operations of an individual migration (that may have been added on an svn merge or update):

1
2
3
4
5
> rake db:migrate:up VERSION=20080402122523
== 20080402122523 Two: migrating ==============================================
-- create_table(:two)
   -> 0.0122s
== 20080402122523 Two: migrated (0.0124s) =====================================

This functionality was previously available as the enhanced migrations plugin, so if you’re on an older version of Rails you can still make your migrations less whiny.

tags: ruby, rubyonrails

What's New in Edge Rails: Gem Dependencies 15

Posted by ryan
at 4:43 PM on Tuesday, April 01, 2008



Rails plugins are great for many reasons, one being that they provide extra functionality without being an external dependency – they’re packaged right there with your application. Until recently, there was no way do programmatically define a Rails applications’ external gem dependencies and we were left with rolling our own gem dependency solutions.

That all changes with a nice way to define, and tie, gem dependencies to our Rails apps. In our environment.rb we have the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Rails::Initializer.run do |config| 

  # Require the latest version of haml
  config.gem "haml"

  # Require a specific version of chronic
  config.gem "chronic", :version => '0.2.3'

  # Require a gem from a non-standard repo
  config.gem "hpricot", :source => "http://code.whytheluckystiff.net"

  # Require a gem that needs to require a file different than the gem's name
  # I.e. if you normally load the gem with require 'aws/s3' instead of
  # require 'aws-s3' then you would need to specify the :lib option
  config.gem "aws-s3", :lib => "aws/s3" 
end 

So great – when your app loads up it will automatically find and require each of the gems you’ve listed. But what if you’re on a system that doesn’t have all of these gems installed, or you’ve just deployed to a fresh environment? There’s now a rake task that will install all the referenced config.gems on your target system:


rake gems:install

Before running this you can see what gem dependencies your app has by running rake gems.

However, this still doesn’t package these gem dependencies with the app, it only refers to system-dependent gems. If you want to pull these gems into your application source you can do so with the new rake gems:unpack task:

1
2
# Unpack all gems to vendor/gems
rake gems:unpack

You can also unpack individual gems:

1
2
# Unpack only the hpricot gem to vendor/gems
rake gems:unpack GEM=hpricot

This will unpack the gem into the vendor/gems/hpricot-0.5 directory which is automatically searched as part of the config.gem startup.

Your deployment strategy can now choose to automatically install required gems in each target environment or just package gems as part of the application source.

Update: Just added is the ability to build gems that have native extensions:


rake gems:build

tags: ruby, rubyonrails

What's New in Edge Rails: Partial Updates

Posted by ryan
at 3:45 PM on Tuesday, April 01, 2008



In lock-step with the recent dirty objects functionality comes the ability of ActiveRecord models to perform partial updates – which only saves the attributes that have been modified on updates.

For instance:

1
2
3
4
5
6
7
8
9
10
article = Article.find(:first)
article.title  #=> "Title"
article.subject  #=> "Edge Rails"

# Update one of the attributes
article.title = "New Title"

# And only that updated attribute is persisted to the db
article.save
  #=>  "UPDATE articles SET title = 'New Title' WHERE id = 1"

Note: Your updated_at/on magic fields will only be set if there are unsaved attributes that need persisting. If there are no changed attributes for the object being persisted then there won’t be any SQL updates made.

To enable this functionality, set partial_updates = true for each model you wish to take advantage of partial updates. To enable this system-wide add this line to your environment.rb or, better yet, in a config/initializer:


ActiveRecord::Base.partial_updates = true

Note: There’s currently a config/initializers/new_rails_defaults.rb file that has this setting, so edit that file if you have it.

tags: ruby, rubyonrails

What's New in Edge Rails: Dirty Objects 23

Posted by ryan
at 7:38 PM on Monday, March 31, 2008



The ability to track whether or not your active record objects have been modified or not becomes a lot easier with the new dirty object functionality of edge rails. It’s dead simple, and pretty slick, to use:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
article = Article.find(:first)
article.changed?  #=> false

# Track changes to individual attributes with
# attr_name_changed? accessor
article.title  #=> "Title"
article.title = "New Title"
article.title_changed? #=> true

# Access previous value with attr_name_was accessor
article.title_was  #=> "Title"

# See both previous and current value with attr_name_change accessor
article.title_change  #=> ["Title", "New Title"]

Beyond the clever attribute based accessor methods, you can also query to object directly for its list of all changed attributes. (Continuing from the previous example):

1
2
3
4
5
# Get a list of changed attributes
article.changed  #=> ['title']

# Get the hash of changed attributes and their previous and current values
article.changes  #=> { 'title' => ["Title", "New Title"] }

Note: Once you save a dirty object it clears out its changed state tracking and is once again considered unchanged.

1
2
3
article.changed?  #=> true
article.save  #=> true
article.changed?  #=> false

If you’re going to be modifying an attribute outside of the attr= writer, you can use attr_name_will_change! to tell the object to be aware of the change:

1
2
3
4
article = Article.find(:first)
article.title_will_change!
article.title.upcase!
article.title_change  #=> ['Title', 'TITLE']

And coming down the pipe is a feature that will make the most of this functionality – partial SQL updates that will only update attributes that have changed…

tags: ruby, rubyonrails

What's New in Edge Rails: Has One :through 3

Posted by ryan
at 11:59 AM on Monday, March 24, 2008



It would appear that has_one has finally grown up the stature of has_many and now has support for the :through option.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Magazine < ActiveRecord::Base
  has_many :subscriptions
end

class Subscription < ActiveRecord::Base
  belongs_to :magazine
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :subscriptions
  has_one :magazine, :through => : subscriptions, :conditions => ['subscriptions.active = ?', true]
end

The intermediate associations are properly handled when doing assignments as well:

1
2
3
@ryan.subscriptions #=> []
@ryan.magazine = Magazine.create(:name => 'Hustler')
@ryan.subscriptions #=> [<Subscription magazine_id: 1, user_id: 1 ...>]

tags: ruby, rubyonrails

What's New in Edge Rails: Has Finder Functionality 12

Posted by ryan
at 11:37 AM on Monday, March 24, 2008



It looks like Nick Kallen’s wildly popular has_finder plugin will be making its way into Rails 2.x in the form of named_scope. Observe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class User < ActiveRecord::Base
  named_scope :active, :conditions => {:active => true}
  named_scope :inactive, :conditions => {:active => false}
  named_scope :recent, lambda { { :conditions => ['created_at > ?', 1.week.ago] } }
end

# Standard usage
User.active    # same as User.find(:all, :conditions => {:active => true})
User.inactive # same as User.find(:all, :conditions => {:active => false})
User.recent   # same as User.find(:all, :conditions => ['created_at > ?', 1.week.ago])

# They're nest-able too!
User.active.recent
  # same as:
  # User.with_scope(:conditions => {:active => true}) do
  #   User.find(:all, :conditions => ['created_at > ?', 1.week.ago])
  # end

All the goodness you’ve come to love in has_finder is now available as named_scope – plus you get some extra goodies too. User.all is given to you for free as an alias for User.find(:all).

Advanced

For those with more discriminating needs, don’t forget some of these has_finder tidbits:

Passing Arguments

Pass in arguments to your named scopes to specify conditions (or other props) at run-time.

1
2
3
4
5
class User < ActiveRecord::Base
  named_scope :registered, lambda { |time_ago| { :conditions => ['created_at > ?', time_ago] }
end

User.registered 7.days.ago # same as User.find(:all, :conditions => ['created_at > ?', 7.days.ago])

Named Scope Extensions

Extend named scopes (in a similar fashion to association extensions).

1
2
3
4
5
6
7
8
9
10
class User < ActiveRecord::Base
  named_scope :inactive, :conditions => {:active => false} do
    def activate
      each { |i| i.update_attribute(:active, true) }
    end
  end
end

# Re-activate all inactive users
User.inactive.activate

Anonymous Scopes

You can also pass around scopes as first class objects using scoped (a named scoped provided to you for free) as a way to build hairy queries on the fly.

1
2
3
4
5
6
7
8
9
# Store named scopes
active = User.scoped(:conditions => {:active => true})
recent = User.scoped(:conditions => ['created_at > ?', 7.days.ago)

# Which can be combined
recent_active = recent.active

# And operated upon
recent_active.each { |u| ... }

named_scope is a truly great feature. If you haven’t started using it yet, do so. You won’t know how you lived without it. Major thanks goes out to Nick.

tags: ruby, rubyonrails

What's New in Edge Rails: Easier Timezones

Posted by ryan
at 7:59 PM on Thursday, January 24, 2008



The days of forcing time-zone support into your Rails apps with not one, but two plugins are over. It would appear that Rails now has its own way of dealing with timezones via a custom implementation (though it’s still based on the tzinfo gem).

Here’s the deal. Set the Time.zone variable to the local timezone. All further date manipulations will automatically reflect this local time while being saved to the database in UTC. Here’s what that will look like:

1
2
3
4
5
6
7
8
9
10
# Set the local time zone
Time.zone = "Pacific Time (US & Canada)"

# All times will now reflect the local time
article = Article.find(:first)
article.published_at #=> Wed, 30 Jan 2008 2:21:09 PST -08:00

# Setting new times in UTC will also be reflected in local time
article.published_at = Time.utc(2008, 1, 1, 0)
article.published_at  #=> Mon, 31 Dec 2007 16:00:00 PST -08:00

So how can we use this new timezone support in the real world – as in our Rails apps where you let users define their own timezone? We can do this using a before filter to set Time.zone, much in the same way you’re used to doing:

1
2
3
4
5
6
7
8
9
class ApplicationController < ActionController::Base

  before_filter :set_timezone

  def set_timezone
    # current_user.time_zone #=> 'London'
    Time.zone = current_user.time_zone
  end
end

Now your controller actions and views will automatically have their dates represented in the user’s timezone.

To set a default timezone for your app, do so in environment.rb:

1
2
3
Rails::Initializer.run do |config|
  config.time_zone = "Hawaii"
end

To get the current time in the currently set timezone you can use Time.zone.now:

1
2
# Instead of Time.now
Time.zone.now

At the end of the day you’ve got a timezone solution built into Rails that avoids needless dependencies and establishes a common practice for multi-timezone applications.

This article leave you wanting for more (it won’t offend me)? If so, check out Geoff Buesing’s incredibly detailed and thorough timezone writeup. It looks to be the first of a few tutorials by the guy who actually wrote this functionality.

tags: ruby, rubyonrails