rails models are views?

Rails appears pretty strict about separation of the abstraction layers that make up its notion of a web application: models, view and controllers. If you were to suggest calling a presentation method, such as url_for, in your model, the stoic Rails advocate will have an allergic reaction. However, Rails thinks nothing of rendering a model directly as a view, such as:

format.json { render :json => @products }

Now, one might argue that this is controller code and the controller is allowed to interpret the model as a view. The controller’s job is to mediate this interaction. However, I feel that it is a dangerous shortcut, made even more so by how hard to seems to be to override. Perhaps the json implementation is simply incomplete.

In xml, this strange controller pattern is easily corrected by providing an xml view. The xml builder syntax is particularly readable, and it is easy to design your XML API effectively.

I haven’t found an equivalent for json. I tried to use a JSON API today to no avail. My model included image data which breaks when auto-rendered in JSON. What I really wanted was to include a URL instead of the image data, which I implement neatly in my xml.builder view:

xml.instruct!
xml.products("type"=>"array") do
  @products.each do |product|
    xml.product do
      xml.sku product.sku
      xml.name product.name
      xml.brand product.brand
      xml.img_url url_for(:controller => :products, :action => :show, :format=>:png, :id => product.id, :only_path => false)
    end
  end
end

The problem is that I want a similar view in JSON. The to_json API leads me to put this logic in my model (gasp!). In fact, the ActiveRecord::Serialization docs give an example of providing a method to generate JSON instead of a literal attribute. The example is of a “permalink” which seem suspiciously like something that belongs is the view layer.

  konata.to_json(:methods => :permalink)
  # => {"id": 1, "name": "Konata Izumi", "age": 16,
        "created_at": "2006/08/01", "awesome": true,
        "permalink": "1-konata-izumi"}

Today’s solution was to go back to using my comfortable old XML API, but I would prefer to consume JSON from the other side. I wonder if anyone is working on a JSON builder or if there is some clear solution that I haven’t yet stumbled upon.

Posted in code | 6 Comments

ruby unit test frameworks

In preparation for teaching Ruby in a class with test first teaching. I decided to evaluate a few test frameworks. I thought initially to use Test::Unit, since it seemed easy to understand and ships with Rails. Wolfram Arnold argued that Test::Unit would burden the new folks with legacy. Alex Chaffee also advocated RSpec, but other friends from the Twittervese had good things to say about shoulda. Some folks declared it to be simply a matter of taste.

Even so, I wanted to make an informed decision and refine my palette for Ruby tools, so I wrote a simple exercise in each of Test::Unit, Shoulda and RSpec.

Test::Unit

require 'test/unit'
require 'pig_latin'

class PigLatinTest < Test::Unit::TestCase
    include PigLatinTranslator

    def test_simple_word
        s = translate("nix")
        assert_equal("ixnay", s)
    end

    def test_word_beginning_with_vowel
        s = translate("apple")
        assert_equal("appleay", s)
    end

    def test_two_consonant_word
        s = translate("stupid")
        assert_equal("upidstay", s)
    end
end

With the above code saved as "test_pig_latin.rb" you run it by simply executing it with Ruby.

$ ruby test_pig_latin.rb
Loaded suite test_pig_latin
Started
FFF
Finished in 0.01091 seconds.

  1) Failure:
test_simple_word(PigLatinTest) [test_pig_latin.rb:9]:
<"ixnay"> expected but was
<"translation">.

  2) Failure:
test_two_consonant_word(PigLatinTest) [test_pig_latin.rb:19]:
<"upidstay"> expected but was
<"translation">.

  3) Failure:
test_word_beginning_with_vowel(PigLatinTest) [test_pig_latin.rb:14]:
<"appleay"> expected but was
<"translation">.

3 tests, 3 assertions, 3 failures, 0 errors

Shoulda

Notice in the code below that Shoulda is simply and extension to Test::Unit. The PigLatinTest also subclasses Test::Unit::TestCase, just as the example above; however, the code inside the test case looks substantially different (and more readable in my opinion). You can actually mix Shoulda tests (below) with regular TestCase test methods (above) in the same TestCase. This is an advantage to Shoulda over RSpec if you have a codebase that already has lots of unit tests; however, I have also used RSpec and Test::Unit in the same project (you just have to remember to 'rake test' and 'rake spec').

require 'rubygems'
require 'shoulda'
require 'pig_latin'

class PigLatinTest < Test::Unit::TestCase
  include PigLatinTranslator

  context "#translate" do

    should "translate a simple word: nix" do
      s = translate("nix")
      assert_equal("ixnay", s)
    end

    should "translate a word beginning with a vowel: apple" do
      s = translate("apple")
      assert_equal("appleay", s)
    end

    should "translate a two consonent word: stupid" do
      s = translate("stupid")
      assert_equal("upidstay", s)
    end

  end
end

With the code above saved as "test_shoulda_pig_latin.rb" you use the same process as above by just executing the file with ruby.

$ ruby test_shoulda_pig_latin.rb
Loaded suite test_shoulda_pig_latin
Started
FFF
Finished in 0.008268 seconds.

 1) Failure:
test: #translate should translate a simple word. (PigLatinTest)
 [test_shoulda_pig_latin.rb:12:in `__bind_1251676444_52936'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a simple word. ']:
<"ixnay"> expected but was
<"translation">.

 2) Failure:
test: #translate should translate a two consonent word. (PigLatinTest)
 [test_shoulda_pig_latin.rb:22:in `__bind_1251676444_58860'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a two consonent word. ']:
<"upidstay"> expected but was
<"translation">.

 3) Failure:
test: #translate should translate a word beginning with a vowel. (PigLatinTest)
 [test_shoulda_pig_latin.rb:17:in `__bind_1251676444_59935'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `call'
 /Library/Ruby/Gems/1.8/gems/thoughtbot-shoulda-2.10.2/lib/shoulda/context.rb:351:in `test: #translate should translate a word beginning with a vowel. ']:
<"appleay"> expected but was
<"translation">.

3 tests, 3 assertions, 3 failures, 0 errors

RSpec

require "pig_latin"

describe "#translate" do
  include PigLatinTranslator

  it "should translate a simple word" do
    s = translate("nix")
    s.should == "ixnay"
  end

  it "should translate a word beginning with a vowel" do
    pending
    s = translate("apple")
    s.should == "appleay"
  end

  it "should translate a two consonent word: stupid" do
    pending
    s = translate("stupid")
    s.should == "upidstay"
  end

end

The code above is saved in a file called "pig_latin_spec.rb" and run it using the 'spec' command. You will need to have installed the rspec gem (sudo gem install rspec).

$ spec pig_latin_spec.rb
F**

Pending:

#translate should translate a word beginning with a vowel (TODO)
./pig_latin_spec.rb:11

#translate should translate a two consonent word: stupid (TODO)
./pig_latin_spec.rb:17

1)
'#translate should translate a simple word' FAILED
expected: "ixnay",
     got: "translation" (using ==)
./pig_latin_spec.rb:8:

Finished in 0.035728 seconds

3 examples, 1 failure, 2 pending

Conclusion

I like RSpec best since I find the output to be most readable. I love the pending keyword, which allows me to set up the tests as an exercise for the class with only one test failing. I find it helps focus on exactly one test and one failure. I considered going with Shoulda because the tests are just as readable as RSpec, even if the output takes some learning to read, because of my initial thought that Test::Unit held less magic. However, on closer inspection, I realized that Test::Unit has one significant magical incantation: you merely declare a class and when that class is defined, it runs the test. This seemed not the kind of topic I would want to teach in an intro class. Even some experienced programmers might struggle with understanding the mechanism that allows such a construct to function. I concluded that all of the test frameworks require serious magic, and picked RSpec since I found it to be most usable for test writing and analysis of the output.

Caveat: this exercise was for pure Ruby. In Rails, I wonder if Shoulda tests would be more concise, making them easier to write and read and, therefore, making it worth the steeper learning curve on reading the output.

Posted in code | 12 Comments

rails admin interface roundup

After my recent ActiveScaffold post, I heard about several newer alternatives from Jaime Flournoy, Mike Gunderloy, and some more web surfing. I evaluated four plugins for admin UI, using the following methodology:


rails xxx_simple
cd xxx_simple/
./script/generate scaffold Task title:string notes:text complete:boolean
rake db:migrate

plus whatever annotations to the code the plugin needed. Then I ran a little script to generate 500 records:

500.times do |counter|
  `curl -X POST -d "Another thing #{counter}More text with #{counter} thing" -H "Content-Type: application/xml" http://localhost:3000/tasks.xml`
end

They all support related models, but I only have screen shots from my simple test. I’ve listed them below with the ones I liked best at the top.

typus

Typus is the one I’m moving forward with. It has a clean interface and has nice configuration options. You can configure which columns are displayed and which are searched (with a nice UI touch of displaying the search criteria under the search box). It is actively maintained with quite a few contributors and a responsive google group. I really like how relationships are displayed (for which I don’t have a picture, sorry). The only drawback (for me) is that it has its own auth and I don’t really want to introduce a separate set of admin users for the project I am working on, and I’ll be looking into making a change to support my own auth.  By default, it adds it’s own typus_users table, but this could be a plus for some.


script/plugin install git://github.com/fesplugas/typus.git
script/generate typus
rake db:migrate
./script/server

Now visit http://localhost:3000/admin and you will be prompted for your email address, from which it will automatically create the first admin user (pretty slick). The default UI looks like this:


I ran into just one glitch where Rails reported "A copy of ApplicationController has been removed from the module tree but is still active!" but it was easily fixed. The error didn't happen in my simple project, but did in my real app. Francesc Esplugas has looked into it and so far can't reproduce it.

admin_data

I really liked admin_data. The simplicity of the install was breath-taking:

ruby script/plugin install git://github.com/neerajdotname/admin_data.git
sudo gem install will_paginate
./script/server

that's it. Now visit http://localhost:3000/admin_data and you'll see the following interface:


I didn't try it, but I really like the admin_data approach to integrating with the application's authentication: Add the following lines of code in an initializer at ~/config/initializers/admin_data.rb


# authorization check to see if the data should be shown to the user
ADMIN_DATA_VIEW_AUTHORIZATION = Proc.new { |controller|
   controller.send("admin_logged_in?") }
# authorization check to see if the user should be allowed to update the data
ADMIN_DATA_UPDATE_AUTHORIZATION = Proc.new { |controller| return false }

streamlined

Streamlined is nice, but not as pretty as ActiveScaffold. Not compatible with Rails 2.3. This and active_scaffold seem to be a little older than typus and admin_data and require you to modify your code similarly. I thought it nice that it provided its own admin layout. In my simple test I applied the series of steps and nested route as with active_scaffold.

class MyNiftyController < ApplicationController
  layout 'streamlined'
  acts_as_streamlined

...[anything else you want to do]
end


active_scaffold

See my previous post for details. This seems to be the grand-daddy of this genre of plugins and has a very active google group. I liked this plugin when I first tried it, but it hung when I applied it to my real app. Also, @jamieflournoy notes that he didn't like the UI for editing related models as much as he did Streamlined.

Posted in code | 9 Comments

getting started with activescaffold

The ActiveScafold plugin for Rails promises to be a huge time saver.  In just a few easy steps, you can create a full web interface for your database, complete with inline editing and fold out panels.  Of course, it helps to have some grasp about what it is doing or you can get stuck like I did this morning.  I’m no expert (yet), but since it is so very cool, I wanted to share what I’ve learned (with the help of Sean Dick and Ivan Storck at tonight’s SFRuby Hack session).

After installing the plugin, there are just 3 lines of code that magically generate the HTML pages, but the trick is knowing where to put them. There’s a nice intro on the github wiki that outlines common use cases:

  • Prototyping
  • Admin Interfaces
  • Embedded, Widget-Style
  • Data-Heavy Applications

The use case that led me to ActiveScaffold today was the creation of an admin interface.  I’m working on a website and the end user stuff is pretty nice, but there are a bunch of tables where the data needs a little love… no one wants to launch the site without at least a few corrections in the data and it is crazy to either delay the launch while we build an admin interface or have an engineer make corrections with sql updates.  Enter ActiveScaffold: a way to allow admins to make the changes they need with very little software development.  (Later I expect we’ll need to add some fancy bits to the admin interface, but ActiveScaffold promises to be configurable and extensible enough when the time comes and the key point is that I don’t expect to need those features this week.)

ActiveScaffold for Admin

Make a little app for this experiment:

rails active_scaffold
cd active_scaffold
./script/generate scaffold Task title:string notes:text  complete:boolean
rake db:migrate

Install the plugin, which is compatible with Rails 2.3.2 (yay!) and previous versions of rails (if you install  a specific revision)

./script/plugin install git://github.com/activescaffold/active_scaffold.git

Now we have an app that lets you create, view, edit and delete tasks. This is the end-user app, you could edit the views and remove controller actions to prevent editing, deleting and/or creation. We want to leave this interface as is, but create a separate set of pages to allow an administrator to view, create, modify and delete tasks.

Sean came up with the idea of using routes with a namespace to facilitate this. Here’s what we came up with:

In config/routes.rb add the following code:

map.namespace :admin do |admin|
   admin.resources :tasks
end

Create a copy of /app/views/layouts/tasks.html.erb and call it admin.html.erb (in same folder), then add the following lines inside the <head> tag:

<%= javascript_include_tag :defaults %>
<%= active_scaffold_includes %>

Create app/controllers/task_controller.rb:

class Admin::TasksController < TasksController
   layout "admin"
   active_scaffold :task
end

Check it out:

http://localhost:3000/admin/tasks


and when you click edit:

Posted in code | 9 Comments

openlaszlo simple post code

Here’s a little form for sending an XML post in OpenLaszlo. This is a useful little snippet of code, since as if you get one little content header or something wrong, Rails will be unforgiving with its powerful, yet strict implementation of REST.

What the app looks like (enter text in the top, click the button, response is printed in the lower box):

The code:

<canvas title="Test Post" proxied="false">
  <dataset src="http:/projects.xml" name="ds"
    ondata="response.setAttribute('text'this.childNodes[0].serialize())"/>
  <simplelayout spacing="4"/>
  <edittext id="postdata" multiline="true" width="400" height="200"
     text='&lt;project&gt; &lt;title&gt;XXX&lt;/title&gt; &lt;/project&gt;'/>
  <button text="post">
    <handler name="onclick">
    ds.setQueryParam("lzpostbody", postdata.text);
    ds.setAttribute("querytype", "POST");
    ds.setHeader("Content-Type", "application/xml")
    ds.doRequest();
  </handler>
  </button>
  <edittext id="response" multiline="true" width="400" height="280"/>
</canvas>

Posted in code | Leave a comment

simple web services with rails

Rails enables web services by default, which is pretty awesome, and I’ve been relying on that for a while. It is pretty nifty how Rails will magically parse XML post parameters, create an in-memory object and then save that object to the database without your having to write one line of code. However, when the magic fails it can be pretty hard to debug. I found it useful to run basic tests on the command line using curl (hearing the voice of Zach Moazeni in my head saying: “test your assumptions.”)

Below is a writeup of the set of curl commands and sample output for testing the default Rails XML REST APIs. This can serve as a cheat sheet for the experienced or an introduction for folks new to rails who want a basic understanding of the default webservice APIs.

Create an app, by typing the following commands into your terminal:


$ rails basic_app
$ cd basic_app
$ ./script/generate scaffold project title:string description:text
$ rake db:migrate
$ ./script/server

In Rails 2.3, you also need to added the following line to the top of app/controllers/projects_controller.rb (This will allow external access to the APIs.) You can make this change while the server is running, btw.


skip_before_filter :verify_authenticity_token

Leave that window open where you can see it, since it will output useful stuff from the log. Then in another terminal window, experiment with the following commands to interact with your application APIs.

Create

POST /projects.xml

Create a project object based on the XML representation given in the post body and save in the projects database table.

$ curl -X POST -d "<project><title>Awesome</title><description>This is an awesome project.</description></project>" -H "Content-Type: application/xml" http://localhost:3000/projects.xml

<?xml version="1.0" encoding="UTF-8"?>
<project>
  <created-at type="datetime">2009-06-21T10:13:43-07:00</created-at>
  <description>This is an awesome project.</description>
  <id type="integer">6</id>
  <title>Awesome</title>
  <updated-at type="datetime">2009-06-21T10:13:43-07:00</updated-at>
</project>

Index

GET /projects.xml

This returns a list of all of the projects in the database with an automatically generated XML representation.

$ curl http://localhost:3000/projects.xml<?xml version="1.0" encoding="UTF-8"?>

<projects type="array">
<project>
  <created-at type="datetime">2009-06-21T10:13:19-07:00</created-at>
  <description>This is an awesome project.</description>
  <id type="integer">1</id>
  <title>Awesome</title>
  <updated-at type="datetime">2009-06-21T10:13:19-07:00</updated-at>
</project>
<project>
  <created-at type="datetime">2009-06-21T10:13:43-07:00</created-at>
  <description>New information here</description>
  <id type="integer">2</id>
  <title>Awesome</title>
  <updated-at type="datetime">2009-06-21T10:49:21-07:00</updated-at>
</project>
</projects>

Show

GET /projects/1.xml

This returns an xml representation of the project with id #1

$ curl http://localhost:3000/projects/1.xml<?xml version="1.0" encoding="UTF-8"?>

<project>
  <created-at type="datetime">2009-06-21T10:45:19-07:00</created-at>
  <description>This is an awesome project.</description>
  <id type="integer">8</id>
  <title>Awesome</title>
  <updated-at type="datetime">2009-06-21T10:45:19-07:00</updated-at>
</project>

Update

PUT /projects/1.xml

This modifies the project with id #1

curl -X PUT -d "<project><description>New information here</description></project>" -H "Content-Type: application/xml" http://localhost:3000/projects/1.xml

Posted in code | 4 Comments

rails plugin view helpers and rspec

RSpec for View Helpers

  • To test a view helper, you need to test the plugin in the context of a rails app
  • To verify that you can put the following code into your spec_helper.rb (or just use rspec-plugin-generator by Pat Maddox which will generate this code for you)
    begin
      require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
    rescue LoadError
      puts "You need to install rspec in your base app"
      exit
    end
  • By convention, helper examples live in spec/helpers/ then their dependencies will get magically included.   David Chelimsky notes

    All directories under spec/ are potentially magic – rspec will always
    look at spec/:directory/:filename and see if it has an example group
    type registered for whatever directory is. So if there *is* a foo
    example group type, it will be used for any spec file in spec/foos

  • If you put helpers elsewhere, then you need to declare their type (or manually include dependencies)
    describe AppletHelper, :type => :helper do
  • To send params in your test, you can just define them before calling your view helper.  For example:
    describe DogHelper do
      it "should bark" do
        params[:debug] = "foo"
        html = helper.bark
        html.should have_tag("script")
      end
    end

What do you do when some behavior depends on RAILS_ENV?

  • It’s not a good practice to switch around RAILS_ENV in the test framework
  • Better idea is use a method to check it and then stub that out
  • Here’s an outline:  gist from Zach
  • Here’s what I did: EnvChecker, code and example

Stub tip

If you stub something and it appears not to be working, check when your module, class or method is loaded or created.  Dynamic languages can be challenging.  (thanks David)

References

For more information, see

These lessons learned while developing RSpec examples for a bug fix in openlaszlo_plugin with help from Zach Moazeni, RailsBridge mentor, and David Chelmsky, on the RSpec forum.

Posted in code | Leave a comment

bash context

Sometimes it takes quite an effort to be a “lazy programmer” (as in Larry Wall’s virtues of a programmer). I’ve been trying to optimize my workflow as I run into things that make for repetitive typing. I find when I can type less, I have more cycles to think. One of my favorite tools in laziness is bash scripting, but I’m still learning its ins and outs.

The problem: Lately I’ve been switching between subversion trees in OpenLaszlo. (Someday I want to convince the team to switch to git, but I’m not sure that would even solve the problem completely, since I also like having multiple webapps installed.) So I’ve got trunk checked out in different directories: a clean one for testing, one I keep for code reviews, a sandbox for whatever I’m working on at the moment, etc. Every time I switch OpenLaszlo contexts, I need to redefine $LPS_HOME environment variable and cd into that directory. Should be simple, huh?

BRANCH="trunk"
[ -n "$1" ] && BRANCH="$BRANCH-$1"

echo "BRANCH=$BRANCH"

export LPS_HOME=/Users/sarah/src/svn/openlaszlo/$BRANCH
cd "/Users/sarah/src/svn/openlaszlo/$BRANCH"
pwd

Except this didn’t work. It seemed that ‘cd’ had no effect and $LPS_HOME wasn’t modified. WTF? Lacking someone to look over my shoulder in my new solo work situation, I emailed my friend Scott Evans from whom I have learned much bash lore. He emailed me the following:

1) use “.” to run the script, which runs it in your shell process instead
of in a child one.

2) use a bash function instead — these run in your current environment.
try something like this in your bashrc/bash_profile:

trunk() {
 echo "dollar 1: ${1}"
 if [ -n "${1}" ] ; then
   export LPS_HOME=/Users/sarah/src/svn/openlaszlo/trunk-${1}
 else
   export LPS_HOME=/Users/sarah/src/svn/openlaszlo/trunk
 fi

 cd $LPS_HOME
 pwd
}

Note that I didn’t set something like $TRUNK there — since the thing is running in your shell environment, it’s good practice not to potentially step on existing variables, or leave any variables defined after the fact.

Thanks Scott! Posting here so that I will always remember what I learned today and maybe some other folk will find it helpful.

Posted in code | 1 Comment

git submodule gotcha

I anticipate enjoying git submodules for all of the various and sundry dependencies for my project. However there is a bit of a gotcha with my specific use case. It’s seems I’m not alone, but I thought I’d write this up for others who may into the same thing (and for my own future reference).

The issue happens when you have a directory that is checked into git and you decide to delete the directory of checked in files and add a submodule instead. Seems like a common use case to me, but perhaps the creators of git submodules didn’t thinks so. This happened to me in the move from subversion to git. Initially, all of the plugins were accessed as svn externals; however they were not available via git and I wanted to do one migration at a time, so I just checked the source of all of them into vender/plugins. Later, I dug up references to various git repositories where the plugins are now available, and created subrepositories — awesome, right?

The gotcha comes when I switch branches back and forth between the “master” (no submodules, but with checked in source) and “dev” (where my submodule work is). Here’s what happens:

$ git checkout master
error: Untracked working tree file 'vendor/plugins/acts_as_list/lib/active_record/acts/list.rb' would be overwritten by merge.

Here’s my workaround…
To go back to a branch w/o submodules

$ rm -rf vendor/plugins
$ git checkout master
$ git checkout vendor/plugins

To go back to a branch w/ submodules

git checkout dev
git submodule update --init

Update: another gotcha, when I tried to merge dev into master I got “fatal: cannot read object… It is a submodule!” found the answer here and it worked for me:

git merge -s  resolve

For the record, I’m working with git version 1.6.1

Posted in code | 3 Comments

rails routes

I just went through a fabulous tutorial by Daryn Holmes about routes. It is written for 2.0.2, but I found that nothing had changed for 2.2. I already had a good feel for how routes worked, but stepping through the details was very helpful.

Key points:

  • By default controllers render the view that has the same name of the action invoked.
  • index is the default action

Debugging Tips:
You can quickly see where a route ends up by typing this into irb:

>> rts = ActionController::Routing::Routes
>> rts.recognize_path("/")
=> {:controller=>"albums", :action=>"index"}

You can also go in the reverse direction to see what URL would be generated by a route. To see what URL an action will end up at:

>> rts.generate(:controller=>'albums',:action=>'index')
=> "/music"
Posted in code | 2 Comments