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
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
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.
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.
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
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.
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.
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.
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! :)
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.
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.