Wednesday, November 10, 2010

Ruby 1.9.2, rvm, and Sinatra follies

Previous: Ubuntu 10.04 upgrade: Summary

I recently reported success with installing Ruby Version Manager (rvm). It appeared to install and function correctly; or at least it didn't report any errors. I was able to get my local Instiki up and running with no difficulty.

I didn't do much with ruby since then, up until three days ago. I spent the next two evenings struggling to get things to work. Online resources are plentiful but unhelpful. It seems no one else has experienced the same issues as I have. That in itself suggests pibcak. I readily admit to noob status, but unfortuantely that's not enough information to get things working again.

In hindsight I would say I asked for trouble by trying to use three unfamiliar technologies at the same time: Ruby 1.9.2, rvm, and Sinatra. I saw several bewildering behaviors whose root causes I was unable to pin down:
  • require was sometimes able to locate gems and sometimes not; GEM_PATH was correct; I realized the current directory was no longer included in the ruby load path by default, and changed code accordingly; no luck.
  • It seemed as if public methods inherited by a subclass were treated as private methods at runtime.
  • In theory, you can write a Sinatra app file as a plain script and it will inherit from Sinatra::Application silently; alternatively, you can write the file as a ruby class that extends Sinatra::Base, and it will work the same. I found that with the former method, Sinatra recognized its routes and functioned as expected; when the main Sinatra app file was a ruby class, no routes were ever recognized. By the same token, cucumber and rspec only worked against a class file, and not against the plain script version of the same Sinatra file. So, I had a choice between being able to run cukes and specs, or being able to run the app.
I lacked the experience with these technologies to understand what was happening, or where the problem(s) were coming from.

I decided to eliminate the suspects one by one. I removed rvm and all traces of ruby from my system and installed ruby 1.8.7 the old-fashioned way, and got my little side projects up and running in that environment (as they had worked before). That gave me a baseline for exploring the new items, which I still want to learn about and use.

I began my investigation with Sinatra, running it under the (to me) more-familiar ruby 1.8.7. I observed the same odd behavior regarding the main app file, and it became obvious at that point that the public/private issue had to do with Sinatra and not with ruby 1.9.2. It seems to have something to do with the way Sinatra inserts its magical code under the covers.

I found a gem called cucumber-sinatra that generates a skeleton directory structure and an env.rb file tailored to work with Sinatra. Rather than trying to write my own env.rb from scratch, as I had been doing, I let cucumber-sinatra do its thing. Still no luck: It was either run the app or run the cukes, but not both.

The env.rb file generated by cucumber-sinatra looked like this:
# Generated by cucumber-sinatra. (Wed Nov 10 16:38:55 -0500 2010)
ENV['RACK_ENV'] = 'test'

require File.join(File.dirname(__FILE__), '..', '..', 'lib/my_app.rb')
require 'capybara'
require 'capybara/cucumber'
require 'rspec'

Capybara.app = MyApp

class MyAppWorld
include Capybara
include RSpec::Expectations
include RSpec::Matchers
end

World do
MyAppWorld.new
end
This still had the same odd behavior regarding inherited public methods. I was getting better at using Gemfile and bundle, but was no closer to having a usable environment. I found a github page by Aslak Hellesoy that had a slightly different sample env.rb file for cucumber-sinatra. It looked like this:
app_file = File.join(File.dirname(__FILE__), *%w[.. .. app.rb])
require app_file
# Force the application name because polyglot breaks the auto-detection logic.
Sinatra::Application.app_file = app_file

require 'rspec/expectations'
require 'rack/test'
require 'webrat'

Webrat.configure do |config|
config.mode = :rack
end

class MyWorld
include Rack::Test::Methods
include Webrat::Methods
include Webrat::Matchers

Webrat::Methods.delegate_to_session :response_code, :response_body

def app
Sinatra::Application
end
end

World{MyWorld.new}
All that "app_file" business near the top of the file pertains to the plain script style of a Sinatra app file; that is, not a subclass of Sinatra::Base. I was happy to see the example because it suggested a way to set things up such that both cucumber and a web server could understand the file. Using this as an example, I set up an env.rb file for a small test app like this:
ENV['RACK_ENV'] = 'test'
app_file = File.join(File.dirname(__FILE__), '..', '..', 'lib/my_app.rb')
require app_file

Sinatra::Application.app_file = app_file

require 'rspec/expectations'
require 'rack/test'
require 'webrat'

Webrat.configure do |config|
config.mode = :rack
end

class MyWorld
include Rack::Test::Methods
include Webrat::Methods
include Webrat::Matchers

Webrat::Methods.delegate_to_session :response_code, :response_body

def app
Sinatra::Application
end
end

World{MyWorld.new}
With this env.rb file, I can run
bundle exec cucumber features
and I can run the app in a web browser. I'm not crazy about it and I don't fully understand how it works, but at least it works and I can proceed with development while gradually learning more about Sinatra.

I posted a question on Twitter about these problems, and three people offered help. But they're not at my beck and call, and I need to learn about these things anyway.

Now that I have a working ruby 1.8.7 environment again, my next step will be to install rvm and bring up a 1.8.7 environment, then to ensure everything that's working now works the same way in that environment. That will give me a basis to explore ruby 1.9.2 with a bit more clarity about where the differences lie. Once I get rvm working properly, I can install 1.9.2 under its control without losing my functional 1.8.7 environment. All good; but it will take this noob some time to get there.

Next: Success with rvm and ruby 1.9.2

No comments:

Post a Comment