State Machine Simulator
April 1st, 2008
If you need to create a wizard, with forward and back buttons for several different steps, a great way to do it is to use the acts_as_state_machine plugin.
But for just two or three steps, loading a whole plugin seems like overkill. Instead of loading a whole plugin, you can simulate the same functionality by dropping next! and previous! methods into your model like so:
# State Machine
# Guilt-free stateful modeling!
# These two simple methods simulate all the goodness of
# acts_as_state_machine without the added fat of an included plugin.
def next!
next_step = case self.state
when "step1"
"step2"
when "step2"
"step3"
when "step3"
"step3"
end
self.update_attribute(:state, next_step)
end
def previous!
previous_step = case self.state
when "step1"
"step1"
when "step2"
"step1"
when "step3"
"step2"
end
self.update_attribute(:state, previous_step)
end
def current_step
@current_step ||= self.state
end
attachment_fu on Edge Rails
February 26th, 2008
Speaking of Edge Rails… attachment_fu will break because Edge has extracted callbacks out into a separate module.
To prevent unnecessary pain and suffering, see http://blog.methodmissing.com/2008/1/19/edge-callback-refactorings-attachment_fu/
Just paste that whole blob at the bottom of your attachment_fu.rb file and you’re good to go.
check_box broken in Rails 2.0.2?
February 26th, 2008
I was having an awful time submitting a form with a check_box. No matter what I did, it would only return “0” or “false”, never “1” or “true”, no matter how hard I mashed the mouse button on the check box.
<%= check_box :fund, :tax_deductible %> <%= f.label :tax_deductible %>
I finally tried moving to edge Rails (from Rails 2.0.2), and the problem went away. Anyone else experience this?
Introducing Donor Tools
December 14th, 2007
With a little bit of fanfare, we recently announced Donor Tools, a new donor management app for non-profits.
I used to work at a small non-profit, and I was never satisfied with the software that was available to us. It all seemed either too hard to use, too clunky, too slow, or 20 years old. Ever since then I dreamed of making a killer app for non-profits – one that’s easy to use, has just a few features that every non-profit needs (and not a lot of extra features that only a few need), helps non-profits get to know their donors better, and is affordable.
We’ve been writing code for Donor Tools since July, and it’s exciting to watch it take shape. There’s still a lot to be done. If you’d like to keep an eye on the development progress, be sure to subscribe to the news feed.
Pretty soon we’ll need some beta testers. If you know of any nonprofit organizations that might be interested in helping us beta test, please invite them to subscribe to the mailing list on www.donortools.com
Radiant Factory
November 29th, 2007
Say hello to Radiant Factory, our new theming service for Radiant CMS.
Basically, Radiant Factory is a theming service for Radiant CMS. We take your design and content and turn it into a series of layouts, snippets, and pages that you (or your client) can manage with the fabulous Radiant content management system.
I’ve fallen in love with Radiant’s easy, straightforward website administration. Honestly, I always hated content management and website update tasks until Radiant came along. The idea behind Radiant Factory is simple: share the love. Radiant Factory is my way of helping web designers provide an even better product to their clients.
So check it out and let me know what you think. Your feedback is appreciated!
Sitepoint Rails Book Giveaway
November 27th, 2007
Only five days left to get a free copy of Sitepoint’s Build Your Own Ruby on Rails Web Applications
It’s a pretty good book for beginners – very complete; over 400 pages.
WebAppers
June 26th, 2007
WebAppers – Only the Best and Free Resources for Web Application Developers
Where has this been all my life?
has_one :through
June 20th, 2007
Via: http://idm.s9.xrea.com/ratio/2006/08/04/000496.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class ActiveRecord::Associations::HasOneThroughAssociation < ActiveRecord::Associations::HasOneAssociation private def construct_sql @finder_sql = "#{@reflection.through_reflection.table_name}.#{@reflection.through_reflection.primary_key_name} = #{@owner.quoted_id}" @finder_sql << " AND (#{conditions})" if conditions end def find_target @reflection.klass.find(:first, :select => @reflection.options[:select] || "#{@reflection.table_name}.*", :conditions => @finder_sql, :order => @reflection.options[:order], :include => @reflection.options[:include], :joins => "LEFT OUTER JOIN #{@reflection.through_reflection.table_name} " + " ON #{@reflection.through_reflection.table_name}.#{@reflection.source_reflection.primary_key_name} " + " = #{@reflection.table_name}.#{@reflection.klass.primary_key}" ) end end module ActiveRecord::Associations::ClassMethods def has_one_with_through(association_id, options = {}) if options[:through] reflection = create_has_one_through_reflection(association_id, options) association_accessor_methods(reflection, ActiveRecord::Associations::HasOneThroughAssociation) else has_one_without_through(association_id, options) end end alias_method :has_one_without_through, :has_one alias_method :has_one, :has_one_with_through private def create_has_one_through_reflection(association_id, options) options.assert_valid_keys( :class_name, :foreign_key, :remote, :conditions, :order, :include, :dependent, :counter_cache, :extend, :as, :through ) create_reflection(:has_one, association_id, options, self) end end |
CSSEdit with Rails
June 19th, 2007
I love CSSEdit. It has to be one of the prettiest OS X programs out there, and it makes editing CSS a breeze.
The one thing that has annoyed me though has been using the override feature when developing with Rails. Rails likes to append an asset id on every CSS, JavaScript, and image file that it serves. If I have screen.css, Rails would embed it like screen.css?123456789. So every time I would reload the page in CSSEdit, the style sheet override would be broken, because the asset id would be different.
There is a fast and wonderfully simple solution. Just add this line in config/environments/development.rb:
ENV["RAILS_ASSET_ID"] = "" |
Voila! No more asset id in development mode. No more broken overrides.
Credit: MacRabbit Blog
Asking Models for their URL
May 22nd, 2007
[UPDATE] This article accidentally got deleted, along with all the comments. Shame on my trigger-happy mouse finger, which clicked “OK” without reading the contents of the delete confirmation. So, thanks to Jeff Whitmire who found the text of the article in his RSS feed. Sorry to those who commented – there were some good thoughtful ones.
In my Rails projects, I sometimes have trouble remembering all my complex routes. Not only that, it’s a bummer to have to type out that long line every time I need to generate a URL.
So instead, I let the models tell me their URLs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Article < ActiveRecord::Base belongs_to :category include ActionController::UrlWriter default_url_options[:host] = "www.example.com" def url category_article_url(self.category, self) end def path category_article_path(self.category, self) end end |
Now I can say @article.url, which will generate
http://www.example.com/categories/123/articles/456-my-article
or @article.path, which will give me just the path:
/categories/123/articles/456-my-article
Back From RailsConf
May 22nd, 2007
Fantastic conference. I wish I had more to say, but I’m still trying to untangle my brain and get back into the swing of work.
On a related note, I must be the world’s last Twitter adopter:
cap production deploy
May 7th, 2007
This is a great little tip if you use Capistrano 2.0 to deploy to multiple servers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
task :production do role :web, "web.example.com" role :app, "app.example.com" role :db, "db.example.com", :primary => true set :deploy_via, :remote_cache set :start_mongrel, "/path/to/mongrel_cluster start" set :stop_mongrel, "/path/to/mongrel_cluster stop" set :restart_mongrel, "/path/to/mongrel_cluster restart" end task :staging do role :web, "staging.example.com" role :app, "staging.example.com" role :db, "staging.example.com", :primary => true set :deploy_via, :checkout set :mongrel_rails, "/path/to/mongrel_rails" set :start_mongrel, "#{mongrel_rails} start -d -p #{application_port} -e production -P #{current_path}/log/mongrel.pid -l #{current_path}/log/mongrel.log -c #{current_path}" set :stop_mongrel, "#{mongrel_rails} stop -P #{current_path}/log/mongrel.pid" set :restart_mongrel, "#{mongrel_rails} restart -P #{current_path}/log/mongrel.pid" end namespace :deploy do desc "Restart mongrel" task :restart, :roles => :app do sudo restart_mongrel end desc "Start mongrel" task :start, :roles => :app do sudo start_mongrel end desc "Stop mongrel" task :stop, :roles => :app do sudo stop_mongrel end end |
Capistrano 2.0 gives you powerful new options using namespaces and tasks. As you can see, we set up special tasks for staging production, and then set our variables for each environment there.
If you’re familiar with Capistrano deploy.rb file, then the role lines should be pretty self-explanitory. We just set up different roles for the staging environment and the staging environment.
Now, on my particular setup, I use mongrel_cluster in production, but plain old Mongrel in staging, just to keep things simple. In order to accommodate the two different commands, I just assign the command to appropriate variables inside the :staging and :production tasks, and then call the variables from sudo.
Now you can do things in your different environments by calling
1 2 3 4 |
cap production deploy # Deploy to production cap staging deploy # Deploy to staging cap production deploy:restart # Restart mongrels on production cap staging deploy:update_code # Update code |
You’ll also notice that I used a different deploy_via in each environment. In production, I want to deploy_via :remote_cache because it is much quicker than a full svn export. But on my staging server, I want to be able to cheat and run svn update occasionally so that I don’t always have to do a full deploy.
Theoretically, you could have any number of different environments, and any combination of different variables for each environment. Really though, the simpler the better – with a staging server, the point is to get as close as possible to emulating the actual environment of the production server. For those little variations, this setup works great.
- Original tip by Ezra on Google Groups
- See Geoffrey’s Capistrano introduction at http://nubyonrails.com
- Capistrano 2.0
Easy Printable Pages with Rails
March 5th, 2007
Print-friendly pages require either code forking or magic tricks with CSS, right? Not with Rails. You barely have to modify your code to make this work. You will need to be using Rails’ web service support with respond_to blocks.
Fun part first. Just add a line to your respond_to block, like this:
1 2 3 4 |
respond_to do |format| format.html format.print { render :layout => "print" } end |
Next, you’ll need a layout for your print-friendly page. Create a new layout called print.rhtml, and create the associated print stylesheet(s). I’ll wait while you do that.
Now you’ll need to add a mime type for the print format. You can register a new mime type with your server. But a quicker way to do this is to add this line to your environment.rb file:
Mime::Type.register "text/html", :print |
Make sure to restart your application so that the mime type is actually loaded.
Now the money shot – add a link to your new print-friendly page:
<%= link_to "Print this", {:format => "print"} %> |
That’s it! No code forking, nothing too hard, and easy to maintain. And to top it all off, you get neat looking URLs like www.example.com/pages/123.print.
Turn off Rails' Layouts for AJAX Requests
December 20th, 2006
Here’s a quick snippet for your ApplicationController:
layout proc{ |c| c.request.xhr? ? false : "application" }
HTTP requests using get and post will be processed normally, but AJAX requests will be rendered with no layout. This saves you from having to put render :layout => false every time you do an AJAX request.
