Category: Tutorials

  • Download an image file using wget

    wget -O filename.png "http://some.url/folder/some_image.png"
    

    And to upload the same file using Capistrano:

    task :something do
      put File.read("filename.png"), "#{deploy_to}/remote_filename.png"
    end
    
  • How to filter model data before rendering in Streamlined

    One question that seems to come up a lot on the Streamlined mailing list is how best to filter the records that are displayed in the list view using a certain set of conditions. There are several ways of doing this, but the simplest way is to use Rails’ built-in scoping methods in conjunction with an around filter.

    For example, say we have a Task model and a TaskController that is Streamlined-enabled. The TasksController will display a list of all Tasks in the system by default. What if we only want to display Tasks for the currently logged-in user? Something like this would work:

    around_filter :scope_by_user, :only => [:list, :index]
    
    def scope_by_user
      Task.find_with_user_scope(current_user) { yield }
    end
    

    Once this code is added to the TaskController, any calls to Task#find within the associated actions, views, and helpers will be scoped. Only tasks belonging to the current user will be listed.

    What if we still want to perform an unscoped Task#find within an action? We can eliminate the scoping for a specific block of code using the #with_exclusive_scope method. It looks like this:

    Task.with_exclusive_scope { code_that_should_not_be_scoped }
    

    And that, folks, is how easy it is to filter lists in Streamlined.

  • Using named routes in Streamlined addition files

    For you Streamlined users out there, here’s an easy way to include named routes in your Streamlined addition modules. Previously, you had to hard code the URLs like so:

    module OrderAdditions
      def name_link
        link_to user.name, "/users/show/#{user.id}"
      end
    end
    
    Order.class_eval do
      include OrderAdditions
      include ActionView::Helpers::UrlHelper
      include ActionView::Helpers::TagHelper
    end
    
    Streamlined.ui_for(Order) do
      list_columns :name_link
    end
    

    By including Rails 2’s new ActionController::UrlWriter module, you can access any named or RESTful routes you’ve defined in routes.rb:

    module OrderAdditions
      def name_link
        link_to user.name, user_url(user.id)
      end
    end
    
    Order.class_eval do
      ...
      include ActionController::UrlWriter
      default_url_options[:host] = APP_HOST
    end
    

    It would be nice for this to get baked into Streamlined somehow so UrlWriter automatically gets included, but until that happens this is a good way to get rid of those pesky hard-coded URLs.

  • Layout assertion added to test_spec_on_rails

    After contacting Rick with my patch, he offered to give me commit access to the project. I took him up on the offer, so the layout assertion is now in there.

    test_spec_on_rails is now on Git so unless you’re running Edge Rails you can’t use script/install to grab it. My suggestion is to clone from Git into vendor/plugins and then svn:ignore the .git file:

    git clone git://github.com/pelargir/test_spec_on_rails.git vendor/plugins/test_spec_on_rails
    svn propset svn:ignore .git vendor/plugins/test_spec_on_rails
    
  • Add layout checking to test_spec_on_rails

    test_spec_on_rails is a plugin that adds Rails-specific assertions to test_spec, allowing you to do nifty things like this:

    it "should render with foo template" do
      get :some_action
      template.should.be "foo"
      # equivalent to assert_equal "foo", @response.template
    end
    
    it "should display foo on page" do
      get :some_action
      page.should.select "p", "foo"
      # equivalent to assert_select "p", "foo"  
    end
    

    One thing test_spec_on_rails is missing, though, is the ability to check the layout that a template renders with. For example, it would be nice to do this:

    it "should render with foo layout" do
      get :some_action
      layout.should.be "foo"
      # equivalent to assert_equal "foo", @response.layout
    end
    

    I’ve submitted a patch to Rick Olson that adds this capability. I’m not sure if or when it will get incorporated into the plugin though, so I’m posting the patch here for anyone who might need it.

    To apply, right-click on the link and download the file into your vendor/plugins/test_spec_on_rails folder, then run this command from inside the same folder:

    patch -p0 < add_layout.diff
    

    Assuming your plugin isn't checked out as an external, you'll be able to commit the changes to your own repository. You can then start using the new hotness as described above. Enjoy!

    Update: Rick made me a committer so I was able to add this myself.

  • Fix for fixture_replacement2 when using default methods

    I’ve been using the excellent fixture_replacement plugin for several months now and greatly prefer it over traditional fixtures (yes, even foxy fixtures). fixture_replacement2 adds even more goodness to the party. However, I ran into a problem today when trying to use default_xxx methods in my example_data.rb file:

    module FixtureReplacement
      attributes_for :course do |c|
        c.name = "Geometry"
        c.transcript = default_transcript
      end
      
      attributes_for :transcript do |e|
        e.name = "Joe's Transcript"
      end
    end
    

    In “fixture_replacement language” this says that new courses should receive a default name of “Geometry” and should also receive a new transcript with a default name of “Joe’s Transcript.” The example above follows the format given in the fixture_replacement README. When I attempt to use this config file in my Rails 2.0.2 project, though, I get this error:

    NameError: undefined local variable or method `default_transcript' for FixtureReplacement:Module
    at top level in example_data.rb at line 7
    ...
    

    Why can’t it find default_transcript? I have no idea. Digging into the source code reveals that default_transcript is added to the FixtureReplacement module at runtime, but for some reason it doesn’t ever show up as being available for calling. I don’t have the time or inclination to dig further, but I did find a workaround by changing:

    c.transcript = default_transcript
    

    to…

    c.transcript = c.default_transcript
    

    This makes fixture_replacement happy and all my tests pass. I submitted a patch so hopefully this wrinkle will be ironed out soon. But this should get you by until then.

  • Generating database migrations with Merb

    I’ve been futzing with the goodness that is Merb lately. It’s nifty. Really nifty. The documentation is pretty rough though. I’ll be posting some of my discoveries here. Maybe it will help someone who, like me, was a tad lost early on.

    One of the first things that confused me was migrations. Merb has script/generate just like Rails, but script/generate --help didn’t list a generator for migrations. When I attempted to generate a migration, I was promptly greeted with this pleasantness:

    $ script/generate migration CreateUsers
    Started merb_init.rb ...
    Loading Application...
    Compiling routes..
    Loaded DEVELOPMENT Environment...
    Couldn't find 'migration' generator
    

    Turns out that Merb doesn’t support databases by default. The Merb-specific ActiveRecord plugin has to be installed and switched on before migrations become available.

    (There are several ORM plugins to choose from as documented in the README, but I’ve had very little success getting the others to work so far.)

    Generate your Merb project:

    sudo gem install merb
    merb my_project
    cd my_project
    

    Install the ActiveRecord plugin thusly:

    sudo gem install merb_activerecord
    

    Now open up config/dependencies.rb and uncomment the following line:

    use_orm :activerecord
    

    The first time you run script/generate after doing this, you’ll get:

    Started merb_init.rb ...
    No database.yml file found in my_project/config.
    A sample file was created called database.sample.yml for you to copy and edit.
    

    Copy config/database.sample.yml to config/database.yml and customize as needed. (Don’t forget to create the test and development databases.)

    Now generate your first migration:

    script/generate migration CreateUsers
    

    And you’re off to the races.

  • Use helpers in your Rails PDF templates

    The Rails PDF plugin is a dandy little thing. It wraps the PDF::Writer library for Ruby, allowing PDF templates to be defined like any other Rails view (with a .rpdf extension, of course).

    One thing it doesn’t do is allow access to the parent controller’s helper from within a PDF template. It’s easy enough to patch the plugin to accomplish this, though.

    First, open up vendor/plugins/railspdf/lib/railspdf.rb and take a gander. This is the section we’re interested in:

    ...
    class PDFRender < ActionView::Base
      def initialize(action_view)
        @action_view = action_view
      end
      ...
    

    PDF templates get rendered in the context of this class. All we need to do to gain access to the controller's helper here is include the helper from inside the class initializer, like so:

    ...
    class PDFRender < ActionView::Base
      def initialize(action_view)
        @action_view = action_view
        prefix = action_view.controller.class.to_s.gsub(/Controller/, '')
        self.class.send(:include, "#{prefix}Helper".constantize)
      end
      ...
    

    We first calculate the prefix of the helper based on the class of the controller assigned to the view. Then we include the helper in the current class by constantizing the resulting string.

    We're almost done. Since ApplicationHelper should be available to every controller and helper in the system, let's include it here for good measure:

    ...
    class PDFRender < ActionView::Base
      include ApplicationHelper
      
      def initialize(action_view)
        @action_view = action_view
        prefix = action_view.controller.class.to_s.gsub(/Controller/, '')
        self.class.send(:include, "#{prefix}Helper".constantize)
      end
      ...
    

    And we're done! Piece of cake. Now we can reference methods from inside our controller's helper. We can, of course, modify this hack somewhat so that the same helper (something like "PdfHelper" maybe) is included for all of our templates. This enables us to isolate our PDF-specific helper methods in a single module.

    And before you ask, yes, it's nasty to be modifying the plugin directly. There is a way to inject this patch into the PDFRender class without touching anything in the plugin... but I've gotta leave something for you to figure out! :)

  • Hey, flexmock and unit_record, play nice!

    So you’re savvy with flexmock, a fine Rails plugin that lets you create mock objects in your tests. You’ve been coding up a boatload of fine tests that are elegant in their isolation thanks to your super mocks. They all run fine, but they’re kind of slow so you go Googling for techniques to speed them up.

    You stumble across a wonderful plugin called unit_record that can dramatically speed up your unit tests by disconnecting them from the database. You install it, run your tests, and wham, you get a barrage of error messages like this:

    ActiveRecord is disconnected; database access is unavailable in unit tests.
    

    There could be one or two things going on here. The first possibility is that one or more of your unit tests are still attempting to access the database via a finder or a fixture. This is the most likely case if you’re only getting a few errors. If you’re getting an error for every single unit test in your suite, though, then something else is going on: flexmock is not playing nicely with unit_record.

    The root of the problem is that unit_record attempts to turn off transactional fixtures for your unit tests when you run them, but flexmock hijacks unit_record by defining its own alias chains for setup/teardown.

    I got around this problem by updating my unit_test_helper.rb file to include the following:

    class Test::Unit::TestCase
      self.use_transactional_fixtures = false
    end
    

    It’s unfortunate this has to be done, but it’s a better option than digging into the flexmock plugin itself… especially if you plan on upgrading to a new version of flexmock anytime soon. Don’t forget that you should be disconnecting ActiveRecord in your unit_test_helper.rb as well. This is what my entire file looks like:

    require File.dirname(__FILE__) + '/../test_helper'
    
    require 'unit_record'
    ActiveRecord::Base.disconnect!
    
    class Test::Unit::TestCase
      # Unit_record is trying to do this for us, but since we're
      # using flexmock's test case it patches the wrong class.
      self.use_transactional_fixtures = false
    end
    

    Hope this helps someone who was as equally stumped as I was. Happy testing!

    Update on 12/1/07: After chatting with Jim Weirich (the creator of flexmock) and doing some more tweaking, it became obvious that the problem is not with flexmock but lies somewhere else. I removed all references to flexmock from my code and still had trouble. The transactional fixture switch seems to be getting set between test runs, but I can’t locate where it’s happening. If anyone else finds a solution to this problem, please let us know by posting a comment here.

  • Automating file uploads with SSH and Ruby

    Are you fairly new to Ruby programming? Do you want to learn more about how Ruby can be used as the “glue” to script various libraries into harmonious cooperation? Look no further…

    InfoQ recently published an article I wrote last year about how to automate file uploads to a web server with Ruby’s Net::SSH and Net::SFTP libraries. It’s a fairly lengthy tutorial with plenty of code samples.

    Looking back, the script I created could pretty easily be reproduced in Capistrano today. But isn’t it sometimes more fun (not to mention educational) to write something from scratch? I sure think so.