elfs: (Default)

From the announcements for Rails 3:

The upcoming version 3 of Ruby on Rails will feature a sexy new querying API from ActiveRecord. Here is an example:

User.order('users.id DESC').limit(20).includes(:items)

In other words, Rails is now Django.

Also:

  • Each application now has it’s own name space, application is started with YourAppName.boot for example, makes interacting with other applications a lot easier.
  • Rails 3.0 now provides a Rails.config object, which provides a central repository of all sorts of Rails wide configuration options.

In other words, Rails is now Django.

To be fair, these are huge improvements to Rails. They’ve needed to do these things for a long, long time. The separation of application namespaces is especially powerful– it’s what gives Django a massive chunk of it’s dynamism. It’s good to see that these great ideas, which have been in Django since version 2, have now made it into Rails, just as the Django people start grappling with their own version of Capistrano (Fabric) and their own deployment issues. Rails’ migration path has always been obvious, a pythonic value, while Django has two migration tools (South and Evolution), which is more a rubyish value, and the Django team has decided to leave migration tracks up to outside development teams may-the-best-solution-win.

So, we’ll see. I’m installing Rails 3 this morning, and who knows?  Maybe it’ll seduce me back to working with Rails again.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

I have a contract that I’m working on that requires I work with rails.  That, in itself, isn’t so bad.  But I think what bothers me most about rails can be summed up in one word: partials. For example, let’s say I have the following:

render :partial => 'employee', :collection => @employees

What this means is that the files _employee.rhtml, using the internal variable employee, will iterate multiple times over the collection employees. The “magic” here is that the internal variable and the partial name coincide. This is called, in rails, using convention over configuration.  And while it makes perfect sense, it is in some sense straitjacketing.  Yes, I know, people will tell me that the internal variable name can be changed with the :as symbol; that’s not the point.  Ruby is such a malleable language that rails almost seems anathemic to ruby in the first place: why use what is just about the most flexible language in the first place, and then create a set of conventions which must be memorized in order to make the thing go?

I kinda like ruby and rails, but they don’t seem to belong to one another.   It feels very much like a marriage with a mail-order bride.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

Ah, the bleeding edge.  It’s a war out there!

This morning, Facebook released fbwatir. I’ve just spent the past few hours knocking it around, and have come to the conclusion that it’s pretty mega-borked but it can be saved.

In fact, I now have it working with Cucumber and Firewatir. There are several major flaws in FBWatir, the biggest of which is that it assumes its own responsibility for the browser object. This is broken, and causes Cucumber to spawn an innumerable number of Firefox windows. Commenting out the browser invocation in FBWatir and putting into cucumber’s own env.rb file is much better.

I also discovered that there’s a bug in Firewatir 1.6.2 that assumes that “window zero” is always broken,  but Javascript indexes the windows starting with zero, so window zero is still a valid window ID.  Annoying as hell, but easily monkeypatched away.

I now have Cucumber working with FacebookConnect…

Given how little I know about Ruby, and given how my Ruby expert says I’m doing “inappropriate things” with the Ruby scope, that’s a freaking miracle.  We’re working together to make it work better.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

Ruby is just an excuse for people who once loved spaghetti code, who wanted to be “the smartest guy in the room,” and who wanted to be indispensible, to get back at everyone who loved Python and thought that there was finally a decent way to write disciplined code.

Everyone talks about how nice Ruby folks are.  Ruby is vengeance with a smile.  After spending several hours digging through a Rails app trying to figure out where the hell all of these invocations are coming from, where they’re defined, and what they do (the whole “we can arbitrarily redefine typographic symbols!” thing is insane!), I’ve just gotten to the point where I want to growl, “Wanna know how I got these scars?

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

So, remember my comment about unit testing yesterday?  This is what it looks like today. Here’s the features page:

Feature: Login
  In order to use the system
  As a customer
  I want to log in

  Scenario: Access page
    Given I am at the login page
    Then the page title should be "SecretProject"

  Scenario: Log in
    When I log in as "username" password "redacted"
    Then the page title should be "SecretProject - Project List"

And here’s the steps:

Given 'I am at the login page' do
  @browser.goto @options[:address]
end

Then /the page title should be "(.*)"/ do |title|
  @browser.title.should == title
end

When /^I log in as "([^\"]*)" password "([^\"]*)"$/ do |arg1, arg2|
  @browser.text_field(:name, 'username').set(arg1)
  @browser.text_field(:name, 'password').set(arg2)
  @browser.button(:xpath, "//input[@value='Login']").click
end

While I’m not entirely sold on the COBOL-esque nature of the unit tests, I respect the way cucumber makes you think about what you’re doing and generate scenarios and codify the steps to fulfilling those scenarios, all the while working well within the context of things like WATIR, Matchy, and so forth.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)

FireWatir is my preferred testing suite for developing web applications.   Watir is a domain-language that describes things you do with a web browser: navigate to this page, find this input field, type in this text, find this button and click on it.  Combinded with a unit testing framework like Ruby’s Unit::Test, and you can write scripts that perform the basic functions of any application website and report back on what works and what fails.

The basis of Test-Driven Design is simple, but getting it right seems a little weird.  You really do have to write the tests first.   So I’m going to start with the two absolutely simplest scripts you’ve ever seen.  I haven’t even run the application builder script (rails <project> or django-admin.py startproject <project>) and already I know two things that it has to do.  But before that, let me show you the boilerplate I use for my test-driven work.  Below is a script that I put into a directory in my application project called setup.rb:

END {$ff.close if $ff} # close ff at completion of the tests

require 'rubygems'
require 'firewatir'
require 'test/unit'
require 'test/unit/ui/console/testrunner'
require 'net/http'
require 'json'
require 'optparse'

$options = {:address => 'http://localhost:8000/'}
OptionParser.new do |opts|
  opts.banner = "Usage: example.rb [options]"

  opts.on("-a", "--address", "Base address to test") do |v|
    $options[:address] = v
  end
end.parse!

This script is meant to be called by all testing scripts in the system.  It does two things: defines all of the libraries that the tests will need, defines a default address where we expect to find the development server, and accepts an alternative address as needed.

FireWatir invokes and drives Firefox.  The END clause at the top of this script banishes the window in which the tests were run when FireWatir has run all of its scripts.

Now, here’s the most basic test you’ll ever run.  I keep this in a file called, surprise, basic_test.rb.  This test has an important side-effect, which I’ll discuss in a moment.

require 'setup'
class TC_Basic < Test::Unit::TestCase
  include FireWatir

  def test_01_server_presence
    $ff = FireWatir::Firefox.start($options[:address])
    sleep 2
    # Do this rather than an assert; the tests cannot run if the server
    # is not present.
    if ($ff.title =~ /Page Load Error/) then
      puts "Could not contact server"
      exit
    end
  end

  def test_02_homepage_presence
    $ff.goto($options[:address])
    assert($ff.title =~ /SecretProject/)
  end
end

This file imports the setup script, which in turn does all of the argument parsing and setting up of libraries and stuff.  So that’s all taken care of.   It also defines a single test case, and two tests within that test case.  The first test is not really a test at all; it does the invocation of Firefox, sending it the address of the server.  If it cannot contact the server, it does not “fail” as a test should, instead it halts the tests right there.

The second test is much more fundamental, and is an accurate test.  It sets one variable (the home page URL), and asserts one true thing about the operation (the page’s title is set correctly)

One thing to note about these tests: I haven’t actually written the code these tests will test yet. That’s important.  My job, now, is to make these tests pass.  Once I’ve done that, I will go on to other things: logging in and registration, and then user interaction.

Each test will define one thing the user can actually do: list the user’s documents, pick a document, edit some detail of the document, save the document, delete the document, etc. etc.  That’s what TDD is for.  TDD is part of your documentation: it tells future testers what you expected your program to do well.

This entry was automatically cross-posted from Elf's technical journal, ElfSternberg.com
elfs: (Default)
Ruby is officially cool. Even without Rails, I can totally see how this language could become addictive fast. I barely speak the language and am desperately thumbing through the nutshell book at warp speed and I manage to write a better, faster, and more secure version of Narrator, this one obeying the principle of "configuration, not integration!", in less than half an hour.

That's totally cool. The only question remaining is simple: does the lack of an interim on-disk format cause a performance hit compared to the python version?
elfs: (Default)
So, in my neverending quest to Learn New Stuff, I've been using my self-development time to teach myself Ruby, a new high-level programming language that was popular for a long time in Japan (and is often thought of as "the Japanese version of Perl"), but is now making its way into the U.S., mostly due to the success of Rails, an incredibly powerful application framework that makes shake-and-bake HTML and WSDL businesses easy.

One of the things that many programming languages try to hit is the "sweet spot" between obviousness and repetition: on the one hand, it should be absolutely clear to anyone even vaguely familiar with the language what is happening, and on the other, the programmer should never have to repeat himself or busy himself with excessive boilerplate.

Some languages, trying to hit this mark, are a bit verbose. Python is this way, in that it requires that every object use an explicit parameter, self, for every instance method and every instance variable. Python also creates explicit loop and iterator objects that make sense because everything is spelled out in the expression.

Ruby, in contrast, is as terse as can be, creating lisp-like implementations of iterated expressions that make my brain hurt. I've just gotten to the section on iterators and I stared at the the example (scroll down to the section "Ruby Compared with C++ or Java") for half an hour before it finally became clear what was happening.

It's just a difference of language: instance variables have an @ symbol in front of them, but instance methods don't. They're unadorned. Python is a language built around the idea that almost everything is a reference; Ruby around the idea the almost everything is a message. In Python, if you say object.foo, Python returns the value of the thing foo, which, if it's not a native value, is a reference to some other thing. In Ruby, if you say object.foo, it's a message to object saying "do message foo", which some rational defaults built into the root object type.

It's just that example, in which the yield message is used twice, refers to default behavior not obvious in the example, and contains no mention of the local variable which it is quite obviously using, that drives me crazy. When I finally understood it, I felt that mental twinge I get when trying to work in lisp. Either I have to work a whole lot more with this, in which case I will be a whole lot smarter when I'm done, or I'm never going to grasp it.

Profile

elfs: (Default)
Elf Sternberg

June 2025

S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Aug. 6th, 2025 12:50 pm
Powered by Dreamwidth Studios