1. Skip to navigation
  2. Skip to content

The ELC Community Blog

A knowledge exchange on Ruby on Rails and Agile Development

November 29, 2007

by Ryan Garver

Ruby on Rails, OpenSocial Container plugin 0.1.0

ELC Plugins

As promised the opensocial_container 0.1.0 is here! This is still very very young. But it supports some basic persistence and people profile requests and starts down the path of implementing the REST API. If you are already implementing the REST API on your own, the implementation of the OpenSocial Javascript API that I'm working on is designed to play nicely with that (the docs are not strict on that matter as it turns out).

My goal is to get a 1.0 release out soon. To do that the plugin must support the full 0.5 version of the opensocial JS API. It still have a bit to go, but so far the progress is accelerating, which is very good. Expect a few more minor version releases as I expand the API support.

To install:
ruby script/plugin install http://opensocial.rubyforge.org/svn/plugin/tags/0_1_0/opensocial_container

Notice: This is very new, and in many ways broken. By default there isn't much in the way of security in place with the generators so don't put any data in the system if you expect it to be private.

OpensocialContainer
===================

This plugin is designed to pull together all of the neccessary components involved in turning your application in to an OpenSocial container capable of hosting OpenSocial applications.  Beyond the raw functionality this plugin is being built to make the practice of presenting a secure and stable container simple and straightforward by adopting best practices as they are defined.

Reference Links
===============

http://code.google.com/apis/opensocial/
http://code.google.com/apis/gadgets/

Example
=======

In the config/routes.rb file:

ActionController::Routing::Routes.draw do |map|
  ...

  map.opensocial_container :contain
end

To add the "feeds" resources:
ruby script/generator opensocial

If you support the OpenSocial REST API already, then use this instead to avoid the pollution:
ruby script/generator opensocial_assets

And finally to embed the container in a ERb template use the helper:
<%= opensocial_container('http://www.last.fm/opensocial/myfavouritemusic.xml') %>

In the config/environment.rb you need to add
OpenSocialContainer::Configuration.person_class = 'User'

to get the support for people to be your "User" class.  The user needs to have:
def self.opensocial_id_column_name; 'id'; end

and should define a title:
def title; self.display_name; end

Caveats
=======
This plugin is in very early development and should be used with that in mind.  While hopefully not too buggy, it may be feature lacking in a number of respects.


Copyright (c) 2007 ELC Technologies, released under the MIT license
See Also: Release 0.0.1

November 27, 2007

by Ryan Garver

Ruby on Rails primer for Java developers

Yay! My article for the Java Developers Journal just hit the presses over the weekend. It's always nice to be alerted of these things through mailing lists. Thanks Gregory Seidman on rubyonrails-talk for paying attention. Read up and and feel free to holler at me.


Why Ruby on Rails Has Become a Popular "Next Platform":
A RoR Primer for Java Developers
http://java.sys-con.com/read/464389.htm
by Ryan Garver

November 27, 2007

by Ryan Garver

OpenSocial container plugin 0.0.1

I've just tagged off a 0.0.1 version of the opensocial_container plugin that I've been working on in bits and pieces for the last few weeks. This is a very early version, but it is under very active development (I've been steadily devoting more and more of my time to it). If you do decide to risk trying it let me know how it goes. I'd like to have a 0.1.0 release later this week, so if you have problems toss them in our RubyForge tracker.


The installation is pretty easy:
ruby script/plugin install http://opensocial.rubyforge.org/svn/plugin/tags/0_0_1/opensocial_container
Keep an eye on this. More to come soon!
README
OpensocialContainer
===================

This plugin is designed to pull together all of the neccessary components involved in turning your application in to an OpenSocial container capable of hosting OpenSocial applications.  Beyond the raw functionality this plugin is being built to make the practice of presenting a secure and stable container simple and straightforward by adopting best practices as they are defined.

Reference Links
===============

http://code.google.com/apis/opensocial/
http://code.google.com/apis/gadgets/

Example
=======

In the config/routes.rb file:

ActionController::Routing::Routes.draw do |map|
  ...

  map.opensocial_container :contain
end

To add the "feeds" resources:
ruby script/generator opensocial

To bootstrap the static assets you need to run the generator:
ruby script/generator opensocial_assets

And finally to embed the container in a ERb template use the helper:
<%= opensocial_container('http://www.last.fm/opensocial/myfavouritemusic.xml') %>

Caveats
=======
This plugin is in very early development and should be used with that in mind.  While hopefully not too buggy, it may be feature lacking in a number of respects.


Copyright (c) 2007 ELC Technologies, released under the MIT license

November 20, 2007

by stevend

Ultraviolet syntax highlighting in Mephisto

Being a mac rails user, I love textmate. Naturally, I went crazy when I found out that Ultraviolet is a syntax highlighting gem for ruby that reads textmate theme and textmate syntax files to create xhtml highlighted code. After using this in my tictactoe project, I integrated it into mephisto. Because I wanted to use the filter:code tag that's normally used by coderay, I did this by deleting all the files in the filtered_column_code_macro/lib plugin except code_macro.rb, which I replaced as follows

require 'uv'
class CodeMacro < FilteredColumn::Macros::Base
  def self.filter(attributes, inner_text = '', text = '')
    lang = attributes.delete(:lang) || "ruby_on_rails"
    theme = attributes.delete(:theme) || "cobalt"
    begin
      Uv.parse( inner_text, "xhtml", lang, false, theme) 
    rescue
        RAILS_DEFAULT_LOGGER.warn "UltraViolet Error: #{$!.message}"
        RAILS_DEFAULT_LOGGER.debug $!.backtrace.join("\n")
    end
  end
end

Then I copied in the xhtml CSS files for Ultraviolet xhtml rendering (as shown in the UV instructions) into my stylesheets directory. I included the themes I liked into my layout.liquid as follows:

<link rel="stylesheet" href="/stylesheets/uv/blackboard.css" type="text/css" media="screen" />
<link rel="stylesheet" href="/stylesheets/uv/dawn.css" type="text/css" media="screen" />
<link rel="stylesheet" href="/stylesheets/uv/cobalt.css" type="text/css" media="screen" />

Unfortunately,

   1  require "coderay"
exists in 2 places in the application, but I didn't need it anymore. The quick solution:
   1  touch lib/coderay.rb
! For each code block in your blog, you can change the theme (there's about 12 included themes):

import java.net;

public class Bob extends BobParent implements Bobliness {
  private final int SOMETHING=0;
  public static void main(String[] args) {
    System.out.println("Hello world!" + 5.5)
  }
}

UV comes with syntax highlighting for 158 languages:

actionscript, active4d_html, active4d_ini, active4d_library, active4d, ada, antlr, apache, applescript, asp, asp_vb.net, bibtex, blog_html, blog_markdown, blog_textile, blog_text, build, bulletin_board, cake, camlp4, cm, coldfusion, context_free, css_experimental, css, cs, csv, c, c++, diff, dokuwiki, dot, doxygen, d, dylan, eiffel, erlang, fortran, f-script, fxscript, greasemonkey, gri, groovy, gtdalt, gtd, haml, haskell, html-asp, html_django, html_for_asp.net, html_mason, html_rails, html, html_tcl, icalendar, inform, ini, installer_distribution_script, io, javaproperties, javascript_+_prototype_bracketed, javascript_+_prototype, javascript, java, jquery_javascript, json, languagedefinition, latex_beamer, latex_log, latex_memoir, latex, lexflex, lighttpd, lilypond, lisp, literate_haskell, logo, logtalk, lua, macports_portfile, mail, makefile, man, markdown, mediawiki, mel, mips, mod_perl, modula-3, moinmoin, mootools, movable_type, m, multimarkdown, objective-c, objective-c++, ocamllex, ocaml, ocamlyacc, opengl, pascal, perl, php, plain_text, pmwiki, postscript, processing, prolog, property_list, python_django, python, qmake_project, qt_c++, quake3_config, ragel, r_console, rd_r_documentation, regexp, regular_expressions_oniguruma, regular_expressions_python, release_notes, remind, restructuredtext, rez, r, ruby_experimental, ruby_on_rails, ruby, s5, scheme, scilab, setext, shell-unix-generic, slate, smarty, sql_rails, sql, ssh-config, standard_ml, strings_file, subversion_commit_message, sweave, swig, tcl, template_toolkit, tex_math, tex, textile, tsv, twiki, txt2tags, vectorscript, xhtml_1.0, xml_strict, xml, xsl, yaml, yui_javascript

Update: This was much harder to install on ELC's blog due to some issues with a regex library called Oniguruma (a required gem for UltraViolet), and my library load path. I finally found some help getting Ultaviolet installed. It turned out that our linux distro on EC2 (FC4 or FC5 I think) didn't include /usr/local/lib in your shared object library search path, and that's where Oniguruma installs by default. I also had to upgrade ruby from 1.8.4 -> 1.8.5, and reinstall Oniguruma and its gem (I'm fairly sure the ruby upgrade was required here).

Originally posted on flouri.sh

November 19, 2007

by Daniel Lunde

Creating new generator commands

Extending the built-in generator commands

The built-in generator commands will do most of what you would expect (like copy files, create directories, create a file based on a template, and generate migrations), but there are times when we need it do something a little more unique.

For instance, inserting a line (or lines) of code into a given file. In my case I needed to add a new entry into my crossdomain.xml. The following method will insert a new string either before or after a given string match.

This technique builds on the strategy that the scaffold generator uses to insert a map.resources into the routes.rb.

def file_insert(path, insert_me, sentinel, insert_before=true)
  fullpath = destination_path(path)
  content = File.read(fullpath)
  unless content.include? insert_me
    content = content.gsub /(#{Regexp.escape(sentinel)})/mi do |match|
      (insert_before) ? "#{insert_me}#{match}" : "#{match}#{insert_me}"
    end
    File.open(fullpath, 'wb') { |file| file.write(content) }
    logger.updated path
  else
    logger.identical path
  end
end

And here we make the change to the crossdomain.xml

def crossdomain()
  path = 'public/crossdomain.xml'

  domain_list =  "  <allow-access-from domain=\"#{dns_name}\" />\n"
  domain_list += "  <allow-access-from domain=\"*.#{dns_name}\" />\n"

  sentinel = '</cross-domain-policy>'

  file_insert(path, domain_list, sentinel)
end

Using those methods, updating the crossdomain.xml file is easy. And if you happen to re-run the generator with the same parameters, it reports it as identical and won't re-insert the string a second time.

Bringing it all together

So where do we put these methods? You can actually stick them in with your custom generator.

class MyCustomGenerator < Rails::Generator::NamedBase
  attr_reader :dns_name

  def initialize(runtime_args, runtime_options = {})
    super
    @dns_name = args.join.downcase
  end

  def manifest
    record do |m|
      # Add something ridiculous
      m.file_insert('public/500.html', "<h3>Ridiculous</h3>", "<body>", false)
    
      # Add crossdomain entry
      m.crossdomain
    end
  end
  
  protected
  
  # Add your custom methods here...
  def file_insert(...)
    
  def crossdomain()
end

Now if I could just write a generator to build my entire website...

November 16, 2007

by josh

Testing Libraries

Another way to make Test::Unit act like RSpec

It's really frustrated to try and increase your test coverage in libraries and helpers when you have to instantiate a controller first. So - don't.

To get around this and at the same time make your testing infrastructure easier to manage, you can create a separate directories in your test directory for testing your helpers.

mkdir test/helpers

Create an application_helper_test.rb file in that dir and put the typical stuff in it:

require File.dirname(__FILE__) + '/../test_helper'

class ArticlesHelperTest < Test::Unit::TestCase

end

Now make sure the Articles Helper is included:

class ArticlesHelperTest < Test::Unit::TestCase
  include ArticlesHelper
end

Now you can test that your helper methods directly:

module ArticlesHelper
  def article_title_helper(article)
    "#{article.title} - #{article.created_at.strftime("%Y %B %d")}"
  end
end
   1  
   2  def test_article_title_helper
   3    assert_equal 'Title - 2007 August 6', article_title_helper(article)
   4  end

The same goes for any library. I would argue that if you need to include the library tests in your application, don't put the library in lib. app/lib is ideal for vendor libraries, not local ones.

You'll need to make sure you modify your RCov Task appropriately to include this directory.

November 14, 2007

by stevend

Rendering views without a web request in rails

Why the heck do you want to do that?

Views are very well integrated into the rails framework, but they're only typically rendered when an http request comes in. ActionMailer is the main exception, but what if you want to render a view for use in another backend application? These days, document fragments are being used everywhere, and often times I'll need rendered HTML for a use other than just sending it back to the requesting user.

A solution: instantiate a controller and view

Controllers are just objects, and so are views. We can instantiate a controller, instantiate a view, then point the view to the controller and we're ready to go. The only think we're missing is the session and the request objects, but not every view needs those. I used this once for updating a facebook profile using a backgroundrb worker:

   1  
   2  class FakeView < ActionView::Base
   3    include SomeHelper
   4    include SomeOtherHelper
   5  end
   6  
   7  class FakeController < ActionController::Base
   8    def render_some_view
   9      action_view = FakeView.new(File.join(RAILS_ROOT, "app", "views"), {})
  10      action_view.instance_variable_set("@controller", self)
  11      markup = action_view.render(:partial => 'facebook/your_profile')    
  12    end
  13  end

By subclasses

   1  ActionView::Base
, you can mix helpers into the view class, making their methods available.

Another solution: use the test framework!

The rails TestProcess is the only place where views are rendered. If you really want to simulate a real experience when rendering a view, use the test process. First, you'll need an actual controller with an actual action you want to render in it, like this one:

   1  
   2  class FacebookController
   3    before_filter :login_required
   4    
   5    def your_profile    
   6    end
   7  end

Then, we create and instante a test as follows:

   1  
   2  class FacebookTest
   3    include ActionController::TestProcess
   4    attr_reader :response
   5    
   6    def initialize
   7      require_dependency 'application' unless defined?(ApplicationController)
   8      @controller = UserScheduleEntryController.new
   9      @request    = ActionController::TestRequest.new
  10      @response   = ActionController::TestResponse.new
  11    end
  12    
  13    def render_your_profile(user)
  14      @controller.instance_variable_set("@user", user) # bypass login required
  15      get :your_profile
  16      @response
  17    end  
  18  end
  19  
  20  test = FacebookTest.new
  21  test.render_your_profile(user)
  22  markup = test.response.body

The

   1  require_dependency
was something I threw in because backgroundrb didn't have some of the required classes loaded at that point, it may not be something you need in your application.

November 13, 2007

by josh

Can I Take a Test Drive?

Why don't you just take this lemon right now for 500 bucks?!

Most developers who don't employ Test Driven Development either don't know how to write tests, don't believe TDD is worth their time or haven't trained themselves to do so. Yes - it's like sucking your thumb or carrying your blanky around. You have to be trained to stop.

If you're of the group that believes driving your application's development through testing isn't worth your time, then you haven't taken the time to consider how much time you spend debugging - not only as you develop, but as your maintain and deploy.

If you're of the group that hasn't trained yourself yet, you're probably also of the group that doesn't know how to test (to some degree).

Here's some help:

When you find yourself writing:

def some_method

STOP RIGHT THERE! You've gone far enough thank you.

Open or create a corresponding test file and write:

def test_some_method_should_do_this_when_something

Now define some_method's functionality in the test before you write the function. Then go write the functionality.

The key is: Don't write the functionality until you know how to test it. If you don't know how to test it. You don't know what your functionality is yet.

This may be easy for all the public methods, but what about the protected and private ones? How am I supposed to test those?

Use .send(:some_private_method)

def test_some_method_should_do_this_when_something
  my_object.send(:some_private_method)
end

November 09, 2007

by Asa Wilson

Points and Velocity in Trac Reports

Most Agile methods use some sort of complexity metric to track the relative time necessary to complete a task or story. Whether you're using developer days or story points, you can use the severeties field in trac to monitor your progess. Here's how:

*The following should only need to be completed once per Trac system and will require admin rights.

Adding the Points to the system

1. Click on the "Admin" link in the upper right hand corner (if you don't see it, you don't have the correct permissions).

2. On the left hand side of the screen you will see a menu of links. Click on "Severities" under the Ticket System heading.

3. The interface here is pretty intuitive, just type a Point value into the text box on the right and click "Add." Repeat until done. You should probably add both "0" and "??" values for no-point-value bugs and unknown difficulties, respectively.

Adding the column to your reports

1. Click on "View Tickets" in the menu on the upper right.

2. Select whichever report you'd like to modify.

3. Click on the "Edit Report" button. This will take you to a form where you should see the underlying SQL for the report (Under the header "SQL Query for Report", conveniently enough).

4. Look for the "summary" item in the select clause of the query (if it's not there, just choose an appropriate position in the list). Insert the following text prior to the summary item:

severity AS points,

5. Click "Save Report"

Assuming you don't receive a SQL error, you should now see the original report with a "Points" column added.

New report: Velocity

Alright so this is all fine and good but now the real fun begins. Since you have a points field you can now use the Custom Reports in Trac to analyze your progress in relation to these points. A popular report in the Agile world is Velocity (or the number of points you complete per iteration). Without further hullabaloo lets get into it... (Again assuming you have Admin privileges or mad hacking skills)

1. Go to "View Tickets" on the Nav bar.

2. Click on "Create new report"

3. Name your report something creative like "Velocity Report".

4. Enter the following code into the SQL Query for Report:

SELECT t.milestone AS __group__,
      COUNT(id) AS tickets, SUM(severity) AS points
      FROM ticket t,enum p
      WHERE (resolution IS NULL or resolution NOT LIKE 'duplicate')
      AND p.name=t.priority AND p.type='priority'
      AND status = 'closed'
      GROUP BY t.milestone
      ORDER BY (milestone IS NULL), milestone DESC, (status = 'closed'),
      (-1)*p.value DESC

5. Click "Save report" and enjoy your new data harvesting masterpiece.

Well there you have it. "Points" added to all you reports and a new report which gives you a velocity per iteration. Way cool!

November 07, 2007

by jeff

Installing Freeimage + image_science on Leopard

So I upgraded to Leopard the other day (actually I did a clean install), and all is well until I attempted to start a rails project that uses image_science. image_science depends on Freeimage as I'm sure you're aware, and the Freeimage install was failing spectacularly. Google to the rescue! Here's how I got it to install:

From http://www.ruby-forum.com/topic/129554#578387

1. I started with a clean install of Leopard.
2. Install macports for 10.4
3. Install the xcode dev tools from the Leopard disk - *be sure to also install the 10.3 sdk from the xcode dev tools install*
4. sudo port install freeimage
5. cd /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_graphics_freeimage/work/FreeImage
and change this:

LIBRARIES_PPC = -Wl,-syslibroot /Developer/SDKs/MacOSX10.3.9.sdk
LIBRARIES_I386 = -Wl,-syslibroot /Developer/SDKs/MacOSX10.4u.sdk

to this

LIBRARIES_PPC = -Wl,-syslibroot /Developer/SDKs/MacOSX10.3.9.sdk/usr/lib
LIBRARIES_I386 = -Wl,-syslibroot /Developer/SDKs/MacOSX10.4u.sdk/usr/lib

6. sudo port install freeimage
7. sudo gem install -y imagescience
8. cd /Library/Ruby/Gems/1.8/gems/RubyInline-3.6.4/lib
9. edit inline.rb
look for the line
flags = @flags.join(' ')
and change it to
flags = @flags.join(' ') + ' -lruby'
10. remove ~/.ruby_inline

Some notes:

Step #5 pertains to the file Makefile.osx

Before attempting to install freeimage again in step #6, be sure to cd out of /opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_ports_graphics_freeimage/work/FreeImage I just did a "cd ~" before "sudo port install freeimage"

Big thanks and all credit to original posters Thomas Mango and Michael Steinfeld for this information.


home | services | Ruby on Rails Development | code | blog | company