Tech Talk

Bytefull of Tech Talk (http://tech.bytefull.com)

Update …

My first Rails app went online on 1st Sep (more on that later), and I was subsequently busy with those ‘trivial’ changes that drain your time and energy … I also have a looong list of feature requests to work through.

My other excuse for being inactive is that I’ve been working on 2 static sites and it is sooo boring :) What little free time I had, I spent writing a wordpress theme for my chess blog !

I’ll profile the rails app, soon … I promise !

November 25th, 2007 | ahsan | No Comments | Other

Little Things about Ruby Strings

Here are some things about Ruby Strings that may have escaped your attention - if you were introduced to Ruby via Rails

Efficiency

Since Ruby scans double-quoted strings for variables and escape sequences, it’s more efficient to use single-quotes for raw strings:

@sentence = 'Jackdaws love my big sphinx of quartz'
redirect_to :index => 'action'

Pretty Please, with sugar on top …

Split your strings across multiple lines. No backslashes required (sorry, couldn’t resist that !).

sql_query = "SELECT [column]
                 FROM [table]
                 WHERE [condition]
-- sql comment goes here"

or

# Q = double-quotes, embed variables 
# q = single-quotes
sql_query = %Q{SELECT @columnname
                 FROM @tablename
                 WHERE @conditions}
# You may use [ < or (as delimiters instead of {

ASCII code

To obtain the ASCII code for a character, prefix it with a question-mark:

puts ?B
# => 66

Convert it back to a string with:

puts 66.chr
# => "B"

Concatenation

Use the << operator to concatenate strings. Where += or + create a new copy, << appends to an existing string. Hence, it is more efficient:

existing << " end"

Instead of:

existing += " end"

(source)

Index a string with a regexp

You can test a string like this:

email = "spamandbakedbeans@skit.org"
if email[/@/]
  puts "I found the first @"
end

And this replaces the first match

string = "jackdaws love my big sphinx of quartz"
string[/a/] = 'A'
# => jAckdaws love my big sphinx of quartz

This replaces the second match

string = "jackdaws love my big sphinx of quartz"
string[/(j).+(z)/, 2] = "Z"
# => jackdaws love my big sphinx of quartZ

Remember, the regexp matches the entire string

Convert hex to integer (sort of)

Given a string representation of a hexadecimal, String.hex returns the corresponding integer:

puts "0x7b".hex # you can omit 0x if you wish
# => 123

Multiply a string

puts "<br />" * 3
# => "<br /><br /><br />"
August 6th, 2007 | ahsan | 2 Comments | Rails, Ruby

Handy Rake tasks for your TODOs

Here’s a rake task to list TODOs in all *.rb files under a Rails application’s app directory, and another to open them in VIM (or your preferred editor):

require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
 
namespace :todo do
  desc 'List TODOs in all .rb files under app/'
  task(:list) do
      FileList["app/**/*.rb"].egrep(/TODO/)
  end
 
  desc 'Edit all TODOs in VIM' # or your favorite editor
  task(:edit) do
      # jump to the first TODO in the first file
      cmd = 'vim +/TODO/' 
 
      filelist = []
      FileList["app/**/*.rb"].egrep(/TODO/) {|fn,cnt,line| filelist << fn}
 
      # will fork a new process and exit, if you're using gvim
      system("#{cmd} #{filelist.sort.join(' ')}") 
  end
end

Put the above code in a .task file under lib/tasks/, and invoke them with rake todo:list, and rake todo:edit respectively.

Sample Output of rake todo:list:

app/models/xxxx_transaction.rb:13: # TODO: rewrite this ...
app/controllers/membership_controller.rb:98:  # TODO: raise an exception
July 18th, 2007 | ahsan | No Comments | Code, Rails

Testing a goldberg-enabled app

Goldberg is a great plugin for user/role authentication, with simplistic CMS features and an administrative interface. It wraps itself around your application without adding a single line of code to it.

The Dark Con

But for a plugin that makes authentication so easy, testing really sucks. To boot, a goldberg-enabled application is currently very unintuitive to deploy (more on that in a separate post).

Once goldberg is installed, your functional/integration tests will fail because goldberg intercepts all url requests and redirects you to /goldberg/auth/login. Fret not, here’s what to do.

Loading Goldberg’s data into the test environment

Goldberg provides two handy rake tasks:

  • rake goldberg:dump_bootstrap which dumps your goldberg specific data to yaml files under vendor/plugins/goldberg/db/
  • rake goldberg:load_bootstrap loads the data, from the yaml files that dump_bootstrap created, into your current database environment.

Dig into the second task and you’ll see that it uses a GoldbergMigration.load_bootstrap method.

Add this to both your functional and integration tests:

def setup
  GoldbergMigration.load_bootstrap
end

This loads the goldberg-specific data into your test environment. (of course, you need to dump it at least once using rake goldberg:dump_bootstrap !)

Integration Tests

In your integration tests, add the above code to your setup method, and use the below code in your test methods.

def test_index
 
    get "/goldberg/auth/login"
    assert_response :success
    post "/goldberg/auth/login", :login => {:name => "yourusername", :password => "yourpass"}
    follow_redirect!
    assert_response :success
 
    # do your testing here
    get "yourcontroller/index"
    assert_response :success
    assert_template "yourcontroller/index"
end

Functional Tests

Put the following into your setup method and they’ll run fine.

def setup
    get 'goldberg/auth/login' # otherwise @request.session will be empty
    @ouruser = Goldberg::User.find(:first, :conditions => 'name="yourusername"')
    Goldberg::AuthController.set_user(@request.session, @ouruser.id)
end

The downside to this is that calling GoldbergMigration.load_bootstrap for every test method slows down your tests, as it reads the .yml files from disk everytime. Awful, I know. But I can’t find a better way.

Don’t forget

Before you run your tests, don’t forget to create the proper goldberg tables in your test database: run ‘rake RAILS_ENV=test goldberg:plugin_migrations‘. Also, I am assuming you have used goldberg’s admin interface to create a user to test with - you have, haven’t you ?

Live Free and DRY Hard

For my project, I created two helper methods in test_helper.rb called goldberg_setup and goldberg_migrate. The first method has the single line (GoldbergMigration.load_bootstrap) and the second one has the code that the functional tests require.

For those who know not

The goldberg plugin, currently at version 0.2.1, is under active development, and the authors are active at the Goldberg community forum.

Thanks to unclecheese whose travails preceded mine.

July 16th, 2007 | ahsan | No Comments | Code, Rails

Link: Guide to Rails+Nginx+Mongrel on Ubuntu Feisty Fawn Server

I found a nice guide (view as a pretty pastie) to deploying rails, mongrel and nginx on an Ubuntu Feisty Fawn Server. Its not verbose, but it is pretty helpful if you’re deploying your first rails app.

Be sure to customize it to your requirements.

July 13th, 2007 | ahsan | No Comments | Rails

Hack: Redefine a rails model for a rake task

Well, this is weird.

I wrote a rake task to import data from a csv file (output from a legacy system) into my development database for testing. The catch was that I wanted to extend my model class by adding a custom method only in the rake file.

require File.expand_path(File.dirname(__FILE__) + "/../../config/environment")
 
class Customer < ActiveRecord::Base
  def my_custom_method
  end
end

But here, for some reason, rake tries to define a _new_ activerecord model, not redefine the existing model I’ve written. So, what I tried was to precede the class definition with:

c = Customer.new # which really goes to waste

and it worked ! Rake now recognizes that I’m trying to add a method to my existing rails model !

June 17th, 2007 | ahsan | No Comments | Code, Rails, Ruby

Initial experience of Safari for Windows

I tried out Apple’s Safari for Windows (beta). It’s not bad at all.

However, I did uncover the annoyance of being unable to minimize it by clicking on the taskbar ! You have to either use the shortcut CTRL+M or Windows+D. Worse, when you maximize it, the window is ‘restored‘ instead of being maximized. I hope they fix this in the final release.

I would like:

  • To view RSS item headings in a menu as in firefox. Safari opens an admittedly nifty Feed Viewer with all the feed items. But I’d really like to see the headlines at a glance.
  • To use CTRL+TAB to cycle through the tabs instead of the given shortcut, which is CTRL+{ or CTRL+}. On my keyboard, this entails pressing three keys: CTRL + SHIFT + [
  • A lighter theme ? The current one is too dark and gray. An option to switch to the Macintosh theme would be nice.
  • To be able to click on an item in the auto-completion menu for the address bar. You have to press ENTER in Safari.
  • The status bar to be visible, by default, upon installation.

Otherwise, I like Safari.

  • Compared to IE, Safari is swift (to the naked eye).
  • Font smoothing is configurable.
  • There is an amnesiac mode called Private Browsing where, like TorPark, Safari does not record anything in the History or Downloads and form entries are not saved in AutoFill.
  • History items are accessible date-wise from a recursive menu (History -> ‘Wednesday, June 13th 2007′ -> Item …. ), which, in my opinion, is far easier than opening a sidebar and navigating a tree menu.
  • I haven’t noticed any website rendering incorrectly, so far.

I need Firebug at work, so I’ll probably stay with Firefox. I’m on Linux at home, so I don’t have the option of Safari there.

That said, I’ll definitely give Safari’s final release another chance.

June 13th, 2007 | ahsan | 1 Comment | Other

Rails oddity: created_at not auto-updated in fixtures

Today I discovered the following Rails oddity.

Models created in a test fixture don’t have their created_at attributes auto-updated. They need to be specified like this:

created_at: <%=Time.now.to_s(:db)%>

Thanks to toretore on #rubyonrails (irc.freenode.net) for this tip.

June 12th, 2007 | ahsan | 1 Comment | Code, Rails

Django Templates for Rails ?

I know this is perverse but I wish the Django Template language was available for Rails.

Beat this:

{%extends ‘base’%} {%block leftnav} a link {%endblock%} {% block content %} this is the content {% endblock %} {% block footer%} another link{%endblock%}

If you leave out a ‘block’ it will pick up the default content from its parent ‘base’ template. Everything between the blocks is rendered as per the parent template. In short, you can ‘extend’ the bits you want, or just render the parent template. A more detailed explanation is available here.

Compared to that, Rails lacks nested layouts. You can only have one level of inheritance: application-wide. There’s a nested_layout plugin that provides the required functionality, but it isn’t as elegant as the above Django example.

I’ve been looking at liquid, which looks similar to Django’s Templates, but it doesn’t seem to be documented well. I’m now looking at HAML, which seems promising, but I may just be too deep into the project to change now. I may have to survive on partials !

June 6th, 2007 | ahsan | 3 Comments | Other

style2inline.rb (for html newsletters)

Dreamweaver CS 3 has a facility to rearrange inline styles in a <style> tag. Well, here’s a utility that does the opposite: it converts css in style tags to inline css. Yeah, I hear you. Why would anyone want to do that ?!

Sending HTML Newsletters is a nightmare. Most of the email clients, bar Eudora Light, support css. However, not all properties are supported, and Gmail, one of the major web-based email clients, supports only inline styles.

You need Ruby, and Hpricot installed to run this. Duh, it runs on windows too !

EDIT: fixed a bug & added NOTES

Here’s the code:

#!/bin/env ruby
# style2inline.rb - v0.2
# http://tech.bytefull.com
 
# NOTES:
# CSS must not contain any comments
# the last attribute-value pair must end in a semi-colon, example:
#  a {font-family: Verdana; font-size: 18pt;}
 
# uncomment this if you have hpricot installed as a gem
# require 'rubygems'
require 'hpricot'
 
def style_to_inline(filename)
  doc = open(filename) {|f| Hpricot(f)}
  css = ""
  elems = doc.search('head/style')
  # combine the content of all style tags
  elems.map {|x| css &lt;&lt; x.inner_html}
  # remove the style tags - we don't need them in our output doc
  elems.remove
  # collapse all newlines
  css.gsub!("\n", " ")
  # Regexp by Nick Franceschina
  # http://regexlib.com/REDetails.aspx?regexp_id=916
  dec_block = '((\s*([^,{]+)\s*,?\s*)*?){((\s*([^:]+)\s*:\s*([^;]+?)\s*;\s*)*?)}'
  regexp = Regexp.new(dec_block, Regexp::IGNORECASE)
  results = css.scan(regexp)
  results.each do |res|
    res[0].split(',').each do |css_sel|
      doc.search(css_sel.strip).each do |elem|
        if elem.elem? # added in version 0.2,
                      # to avoid operating on a Hpricot::Text element
          if elem[:style] then
            # append to the existing style information
            elem[:style] = elem[:style] + res[3]
          else
            # add a style attribute
            elem[:style] = res[3]
          end
          # we don't need the class attribute
          elem.remove_attribute(:class)
        end
      end
    end
  end
# output
  File.open("s2i_#{filename}", 'w') {|file| file.write(doc) }
end
 
ARGV.each {|x| style_to_inline(x)}

Invoke it with ruby style2inline.rb [FILENAME], and it will output a file of the same name prepended with ’s2i’, e.g s2i-filename.html.

June 3rd, 2007 | ahsan | No Comments | Code, Ruby

« Previous Entries