Posts Tagged ‘ruby’

Learning Cucumber – With Dynamic types must come documentation

Thursday, May 21st, 2009

Finally pulled the trigger on Cucumber, which allows one to write human-readable “features” that are essentially acceptance test cases. You can then execute them by adding some glue code in a mish-mash of Ruby DSLs and verify functionality.

It took me quite a while to decide to get going on this because the examples and documentation that are available are extremely hypothetical and very hand-wavy. A lot of the information glosses over the fact that you are still writing code and you still need to know what the keywords are and what data you will be given. Arcane non-human-readable symbols are almost preferable when getting started, because you don’t get distracted by English. This is why Applescript drives me insane.

At any rate, I found this page, which was pretty helpful. It shows testing a rails app using, among other things, webrat (another tool severely lacking in documentation but that is, nonetheless, pretty awesome).

I’m writing a basic wiki (for science) and so I thought a good feature would be “home page shows all pages in sorted order”, so I wrote it up:

Feature: pages are sorted
 As anyone
 The pages should be listed on the home page in sorted order

 Scenario: Visit Homepage
   As anyone
   When I visit the homepage
   Then The list of pages should be sorted

Now, the webrat/cucumber integration provided by rails means that the “plain English” has to actually conform to a subset of phrasing and terminology or you have to write the steps yourself (the features are everything under “Scenario”). It’s not hard to do that, and it’s not hard to modify the default webrat steps, but it was a distraction intially.

Next up, you implement the steps and here is where the crazy intersection of Ruby DSLs really made things difficult. The first two steps were pretty easy (”anyone” didn’t require any setup, and webrat successfully handled “When I visit the homepage”):

Then /The list of pages should be sorted/ do
  response.should # I have no clue wtf to do here

A puts response.class and a puts response.methods gave me no useful information. I eventually deduced that since Cucumber is a successor/add-on/something to RSpec, perhaps should comes from RSpec. This takes a Matcher and webrat provides many. Specifically, have_selector is available that allows selecting HTML elements based on the DOM.

Then /The list of pages should be sorted/ do
  response.should have_selector("ul.pages")

Success! (sort of). My feature executing is all green, meaning the home page contains <ul class=”pages”>. have_selector also takes a block (totally undocumented as to what it is or does in the webrat documentation):

Then /The list of pages should be sorted/ do
  response.should have_selector("ul.pages") do |pages|
    # WTF is pages and what can I do with it?

A puts pages.class later and I realize this is a Nokogiri NodeSet. Now, I’m in business, though it would’ve been nice to be told WTF some of this stuff was and what I can/should do with it. At this point it was pretty trivial to select the page names from my HTML and check if they are sorted:

response.should have_selector('ul.pages') do |pages|
   page_names = []
   pages.should have_selector('li') do |li|
     li.should have_selector('.pagename') do |name|
       name.each do |one_name|
         page_names << one_name.content
   assert_equal page_names,page_names.sort

(For some reason assert_equal doesn't evaluate to true when it succeeds and the block evaluates to false and then Cucumber/RSpec/Webrat/Ruby Gods claim my page is missing the ul tag). My initial implementation walked the DOM using Nokogiri's API directly, because I didn't realize that should had been mixed in (on?) to the objects I was being given. I'm still not sure if using that is the intention, but it seemed a bit cleaner to me.

So, this took me a couple of hours, mostly because of a combination of dynamic typing and lack of documentation. I'm all for dynamic typing, and I totally realize that these are free tools and all that. I think if the Ruby community (and the dynamic typing community in general) wants to succeed and make a case that dynamic typing, DSLs, meta-programming and all this (admittedly awesome and powerful) stuff enhance productivity, there has to be documentation as to the types of user-facing objects.

Now, given Github's general awesomeness, I'm totally willing to fork a repo, beef up the rdoc and request a pull, however I'm not even sure whose RDoc I could update to make this clear. Just figuring out that the have_selector in response.should have_selector is part of webrat was nontrivial (I had to just guess that should was part of RSpec and that the Webrat::Matchers module was mixed in). This is a problem and it's not clear to me how to solve it.

That being said, I was then able to create three more features using this system in about 10 minutes, so overall, I'm really happy with how things are working. Certainly if this were Java, I'd still be feeding XML to maven or futzing with missing semicolons. So, it's a net win for me.

Git, GitHub, forking: the new hotness

Thursday, February 5th, 2009

While working on my Gliffy Ruby Client, I decided I wanted a better way to describe the command line interface. Finding nothing that was any good, I whipped up GLI and refactored my Giffy command line client to use it. While doing that, I finally got annoyed at technoweenie’s version of rest-client, and also noticed that the original author’s had totally changed interfaces. So, clicked the nice “Fork” button on GitHub to get my own copy and fixed the issues. But that’s not the cool part. The cool part is that I can change my Gliffy gem to depend on my rest-client implementation and, viola! No special instructions, no hacks, no nothing. This is a really cool thing that would be difficult with Subversion, impossible without RubyGems, and downright painful without GitHub.

Command line interface for Gliffy

Wednesday, January 14th, 2009

My command line interface for Gliffy is relatively complete. It works pretty well, though the error handling isn’t very clean. It’s written in Ruby (RDoc here) and can be used as a Ruby client for Gliffy.

I decided on Ruby since that would be the most fun and didn’t require learning a new programming language. I initially tried to make an ActiveRecord-style domain-based interface, but it was just too difficult and it was hard to see the real benefit. At the end of the day, I think integrating Gliffy into another application is a relatively simple thing, and a procedural interface would probably be the easiest way to do that. So, I modeled it after the PHP client library, more or less.

The command line interface uses the Ruby client library and provides just the basic functions I need:

> gliffy ls
321654 Some Diagram
987654 Some Other Diagram
> gliffy edit 321654
# Takes you to your browser to edit the diagram

I live on the command line, so this is much more expedient than logging into Gliffy and navigating the UI to edit a diagram.

I’m already feeling like providing access to the folders via the command line would be helpful (they are exposed in the Ruby client of course). Not sure how much the API will ultimately change (it’s in private beta now), but hopefully not too much.

Gliffy API private beta: what should I do?

Friday, December 12th, 2008

Gliffy hooked me up with access to the private beta of their API (which I helped design and implement). I create a PHP client and experimental MediaWiki plugin to validate the API while working for them, and now I want to get something else going in my spare time.

My first thought was to make a Ruby client, because I think it would be fun and relatively easy. But, I have to admit that a Wordpress plugin would be more useful to me personally. That being said, A Trac extension would be useful at work, since we are using Trac (which is python based, and I can’t say I’m too interestedin Python at the moment). I think if GitHub allowed git access to project wikis, it would be cool to allow easier integration of Gliffy diagrams to GitHub project wikis.

At any rate, I don’t have tons of time outside of work, so I want it to be something easily achievable, and also something Chris and Clint are not likely to work on themselves….

Why underscores might be better than camel case

Wednesday, December 10th, 2008

So, the “Ruby way” is to use underscores to delimit most identifiers, e.g. “add_months_to_date“, as opposed to the Java camel-case way of “addMonthsToDate“. This was initially something that irked me about Ruby, mostly because typing an underscore is kindof a pain (shift with the left hand and pinky with the other).

Now that I’ve started working, I’ve been reading a lot of code and realizing that code is more often read than written. Ultimately, camel case is a just lot harder to read (especially if you create meaningful method names like myself and my co-workers seem to do).

It’s pretty hard to defend:

Date calculatePersonDataUsageHistoryStartDate() {}

as more readable than:

def calculate_person_data_usage_history_start_date()

The underscores are like spaces, making the identifier a lot more readable. Of course, both are more readable than:

// Calculates the start date of the
// person's data usage history
time_ prsn_dt_uhst_st_dt(){}

This would never fly with Java (and, honestly, look a bit weird), but I’m no longer gonna curse the Ruby convention.

Ruby’s awesomely dangerous awesomeness

Friday, October 31st, 2008

While packaging my resume creator/formatter as a Ruby Gem, I noticed my command line parser was preventing my code from running. It has this hook that sets up an “auto run” sequence (your code just defines a main method):

def self.inherited(child_class)
  @@appname = caller[0][/.*:/][0..-2]
  @@child_class = child_class
  if @@appname == $0

the problem is that $0 from gem is the /usr/bin location and @@appname is the actual location in the Gem repository. Not sure why this check was there, but I had a problem.

Not feeling like overhauling the command line interface code and not being able to patch OptionParser, I opted instead to use the dangerous-sounding ruby feature of replacing the method with my own implementation:

class CommandLine::Application
  class << self
    alias old_inherited inherited

    def inherited(child_class)
      @@appname = caller[0][/.*:/][0..-2]
      @@child_class = child_class
      normalized_appname = @@appname.gsub(/^.*\//,"");
      normalized_dollar0 = $0.gsub(/^.*\//,"");
      if normalized_appname == normalized_dollar0

Now that's pretty cool. My code just dynamically patches the broken Gem! The power of open source and dynamic languages. Of course, this feature allows so much awful code to be written....

Ruby and dead simple code coverage

Tuesday, October 14th, 2008

I haven’t used a code coverage tool for Java, but in my spare time I’ve been working up some Ruby code (mostly to learn the language). I’m using Test Driven Development, which is slightly simpler with Ruby than with Java (mostly due to Ruby’s interpreted nature).

I had heard about rcov and decided to give it a shot. Within literally 5 minutes I had it installed and a report showing my tests were not covering all my code! Amazing. I could then easily see exactly where I need to to test and, sure enough, found some bugs that would’ve gone unnoticed.

Even the best tool with Java would’ve required some painful ant tweaking (not to mention hopes and prayers that it worked with TestNG). I already cannot imagine writing tests without being able to view the coverage….

Getting Rake’s PackageTask to depend on generated files

Wednesday, September 17th, 2008

Been playing with Rake lately and decided to use it to package up the PHP Client Library for the Gliffy integration API. Didn’t seem to make sense to use ant for something that amounts to creating a tarball. make would be appropriate here, too, but I figured it would be cool to use Rake and there’s not really much harm in doing so.

A large annoyance is Rake::PackageTask. This is a seemingly handy task that creates tars, zips, etc. and is pretty useful. It’s not a task in and of itself, but it creates the :package task:"gliffy-php-client",GLIFFY_VERSION) do |p|
    p.need_tar = true
    p.need_zip = true
    p.package_files = SRC_FILES + EXAMPLE_FILES + DOC_FILES

Unfortunately, this doesn’t do what it seems to do. DOC_FILES is the list of documentation files output by phpDocumentor and are not checked into version control. The syntax of the PackageTask makes it appear that the code in the block will run when the :package task executes, however this is not the case. This code is initialization code. So, I tried:

# DOC_DIR is the dir generated by phpdoc, a task elsewhere
# uses this to kick off phpdoc
task :package => DOC_DIR

The result is that the tarball and zip files are created and then the documentation is generated. The reason is that creates a set of tasks and the actual creation of the tarball/zip file is done via a file task, upon which :package is dependent. So, the real dependency I created was:

task :package => "" "gliffy-php-client.tgz" DOC_DIR

Examining the source code, a task named for the directory created by :package is created. This task is dependent on the package_files set up in the constructor. So this is the task I need to use:

# Have to keep a reference to the PackageTask object
package_task ="gliffy-php-client",GLIFFY_VERSION) do |p|
    p.need_tar = true
    p.need_zip = true
    # Executed BEFORE any other tasks; DOC_FILES don't exist  yet
    p.package_files = SRC_FILES + EXAMPLE_FILES

file package_task.package_dir_path => DOC_DIR

    system("phpdoc #{PHP_DOC_ARGS}");
    doc_files = + "/**/**");
    # Have to add these files to the package_task file list
    package_task.package_files = package_task.package_files + doc_files

This is definitely a hack, because I’m depending on the internal implementation of the PackageTask. It really needs a facility for including generated files. In make, I could just send the directory DOC_DIR to tar and it would pick up everything. In Ant, I’d probably have to spawn another ant, since ant sets all property vaules at startup time.