Default form values in Rails

Save on DeliciousShare on Facebook+1Pin it on PinterestSubmit to redditSubmit to StumbleUponShare on TumblrShare on Twitter

One of the problems I initially ran into using Rails was the problem of how the hell you set initial values for your forms? (For example a select menu with a default value chosen but which a user can change). It turns out that as long as you don’t try and set the values in your views, it’s easy and there are several solutions.

As an example I started off with a classic Post model, then moved on to Ghost and Roast to try out alternatives (well you’ve got to call your models something…) I generated an identical scaffold for each and then ran a migration e.g.

 
rails generate scaffold Post name:string title:string type_id:integer
 
rake db:migrate

1. Use model after_initialize

/app/models/post.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
class Post < ActiveRecord::Base
  attr_accessible :name, :title, :type_id
  validates :name,  :presence => true
 
  after_initialize :init
 
  private
    def init
      if self.new_record? && self.type_id.nil?
        self.type_id = 5
      end
    end
 
end

Edit the model and add an after_initialize callback. This callback is called after a new object is instantiated. The init method sets the default value, but only if the value has not been set yet.

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

2. Set the default in the controller

/app/controllers/ghosts_controller.rb

1
2
3
4
5
6
7
8
9
10
  # GET /ghosts/new
  # GET /ghosts/new.json
  def new
    @ghost = Ghost.new
 
    respond_to do |format|
      format.html # new.html.erb
      format.json { render :json => @ghost }
    end
  end

Using this alternative solution, you simply set the default value in the new method using @ghost.type_id = 5. This value will only ever be set before the new view for this controller is created.

1
2
3
4
5
6
7
8
9
10
11
  # GET /ghosts/new
  # GET /ghosts/new.json
  def new
    @ghost = Ghost.new
    @ghost.type_id = 5
 
    respond_to do |format|
      format.html # new.html.erb
      format.json { render :json => @ghost }
    end
  end

This solution is easy implement and has the advantage that what you are doing is obvious – the default isn’t tucked away where you might forget about it.

3. Set the default in the database

When the rails generate command was run, a migration was automatically created:

/db/migrate/20130209234708_create_roasts.rb

1
2
3
4
5
6
7
8
9
10
class CreateRoasts < ActiveRecord::Migration
  def change
    create_table :roasts do |t|
      t.string :name
      t.string :title
      t.integer :type_id
      t.timestamps
    end
  end
end

We can edit the migration to set a default value here e.g.

1
2
3
4
5
6
7
8
9
10
class CreateRoasts < ActiveRecord::Migration
  def change
    create_table :roasts do |t|
      t.string :name
      t.string :title
      t.integer :type_id, :default => 5
      t.timestamps
    end
  end
end

Conclusion

Here are three ways of setting the default values. What you choose to do is up to you, I think it is important that a solution is consistent with the rest of the code that you are working with (as long as it isn’t bad code). Maintainability and clarity shouldn’t be neglected and I subscribe to the school of thought that you should always keep things simple – when complicated problems crop up, write complicated solutions, but don’t complicate things for the sake of it.

Personally I would be disinclined to use the final method because I think it would be easy to forget about.

Creating a method in the model may be overkill most of the time, but if you are setting a lot of default values or your default values change with context, then setting the default values in the method makes sense to me.

These code examples were built with Ruby on Rails 3.2.9

Sinatra templates from params

Save on DeliciousShare on Facebook+1Pin it on PinterestSubmit to redditSubmit to StumbleUponShare on TumblrShare on Twitter

Having been playing about with Sinatra I’m finding it cropping up in all kinds of useful ways. I shouldn’t be too surprised though, as that is the whole raison d’etre of micro-frameworks.

When I get around to it I am writing a little Sinatra app, but for me it has a found a real niche helping to prototype or mess around with CSS and Javascript – it only takes a few seconds to create a little app to play around with.

Here is a minimal app.rb file. It will load templates from the (default) /views directory based on the :name parameter.

1
2
3
4
5
6
7
8
9
10
11
 
require 'rubygems'
require 'sinatra'
 
get '/' do
    erb :index
end
 
get '/map/:name' do
    erb params[:name].to_sym
end

The key here is to_sym – this is not Sinatra specific it is part of the Ruby language. to_sym converts a string into a symbol.

If the concept of a symbol is a little woolly read this for an excellent explanation. An easy (although probably incorrect or simplistic) is to think of a ruby symbol simply as an immutable name for something that can either be a string or an integer. Once created it cannot change.

Speed matters

Save on DeliciousShare on Facebook+1Pin it on PinterestSubmit to redditSubmit to StumbleUponShare on TumblrShare on Twitter

To my unending amazement, many people I’ve worked with simply don’t believe that speed matters as a factor in Search Engine rankings. I don’t understand why. It’s not like Google (and lets face it, we are talking about Google) are being coy about it. (see links)

When you work in an office with stupidly fast broadband, maybe you just forget? But you shouldn’t assume that the people looking at your websites do.

The bit that really bugs me is this peculiar ‘head in the sand’ attitude. It does take more than a few minutes of thinking to come up with any number plausible reasons why speed is important for Search Engine Optimisation (aside from the fact the Google say it is). Try it, take a few seconds to think.

For me though the reasons that scream out at me are User Experience and Money.

User experience

First off – I admit this isn’t really an SEO factor, is it? People are busy, they don’t want to spend valuable time waiting for a web page to load, at least I know I don’t. I have better things to do, and I’m not that weird.

But why would Google care? I can rationalise that they want people to look at Ads. And then click on them. Probably a good idea to make websites faster so people don’t get bored and do something else, or find the bit of content they are looking for before the ads have loaded (because the ads come last).

Time = Money

Google must index billions of pages per day. It stands to reason that the faster a site loads, the faster it can be indexed. I’m guessing that it costs about the same to run a server whether it is indexing million pages per day or 10 million – but if your business is based on data the faster you can chew through data the better – and therefore it also makes sense for Google to promote an internet that lets them index more, faster.

But we don’t have the time, it’s to hard…

No it isn’t. Of course, as with any work there is optimum point in terms of cost vs. benefit (and that will be different for every site).

My gut feeling (and I don’t have any firm evidence for this) is that speed becomes more important the more traffic you get. If you’re site gets 50 visitors per day, then in a sense it is what it is – what you need to focus on is link building and content. However if your site gets 5000 visitors per day, then the chances are you’ve got quality links and good content – so how do you improve? I think the answer is the speed and user experience.

A fast website either means no traffic, no load on your server, no pictures, no javascript – in short a bit of old school html text… or it if is a big site, it means you care. It means that you have taken the time to think about users, you’ve spent a bit of money on hardware or code. It means that you are a quality business and therefore it is a good bet that you have better quality data on your site. It means that people will pay more to be associated with your site.

How to make your site fast

Hell, that’s a bold statement. It’s different for everybody. Think about the usual suspects, too much JavaScript, un-optimised images, sloooow queries with no indexes.

There are a myriad of solutions, and you don’t have to have Prototype, Jquery and Mootools all running on the same page, choose a single framework at a time – you will be amazed.

  • Use a CDN.
  • Use a cache server like Varnish.
  • If you’re a bit lazy – try out mod_pagespeed (sometimes it’s fab, sometimes it’ll melt your server)
  • Be brave, leave the Apache comfort zone and use Nginx or Lighttpd
  • Use minify

Fringe benefits

Chances are you’ll end up using some kind of caching and you’ll start setting sensible expires headers for your static content. But just think you’ll never have to tell somebody at the end of the phone to clear their browser cache again. And remember, the client is not an idiot because their browser hasn’t magically detected that a style sheet or an image has changed, and the website they have paid for looks a bit rubbish.

(by the way I don’t always practice what I’ve just preached, but at least I feel bad about it)

http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html
http://www.mattcutts.com/blog/site-speed/
etc.

Quick debug function

Save on DeliciousShare on Facebook+1Pin it on PinterestSubmit to redditSubmit to StumbleUponShare on TumblrShare on Twitter

It’s not pretty and it’s not elegant, but we’ve all got a bag of quick and dirty functions that help make life just a little bit easier.

This is a simple print_r() but I finally got fed up of loosing my quick debug statements and so added the line name and file number from a stack trace (substitute var_export() to taste).

1
2
3
4
5
6
7
8
9
10
11
12
13
 
function pr($str){
 
        echo('<pre>');
        print_r($str);
        echo('</pre >');
 
        $d = debug_backtrace();
 
        echo $d[0]['file'] . '<br />Line: <b>' . $d[0]['line'] . '</b>';
        echo '<hr />';
 
}

Reorder a nested HTML list in PHP

Save on DeliciousShare on Facebook+1Pin it on PinterestSubmit to redditSubmit to StumbleUponShare on TumblrShare on Twitter

Recently I was working on website where I had to re-order a nested list (part of a navigation menu) – unfortunately I only had access to fragment of HTML so I couldn’t just manipulate the arrays from which it was built. The menu was compiled from various arrays and months within years sometimes came out all wrong.

So I thought, I’d just treat it as a bit of XML (which obviously it is) and re-order it using PHPs native XML handling classes. XML is one of those things that I use frequently, but never really do anything with, and finding a solution took me rather longer than I had expected. It is a mixture of simpleXML and XMLDom.

One of the main problems was the lack any real examples.

If anyone can suggest a more elegant solution, I would love to hear it.

Here is my code:

The UL to re-order

As you can see the month names are in the wrong order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ul id="menu">
    <li class="first"><a href="/news/q/date/2011/">2011</a></li>
    <li class="current last">
        <a href="/news/q/date/2010/">2010</a>
            <ul>
                <li class=""><a href="/news/q/date/2010/07/">July</a></li>
                <li class=""><a href="/news/q/date/2010/06/">June</a></li>    
                <li class=""><a href="/news/q/date/2010/11/">November</a></li>
                <li class=" last"><a href="/news/q/date/2010/10/">October</a></li>
                <li class=""><a href="/news/q/date/2010/09/">September</a></li>
                <li class=""><a href="/news/q/date/2010/08/">August</a></li>
                <li class="first"><a href="/news/q/date/2010/12/">December</a></li>
                <li class=""><a href="/news/q/date/2010/05/">May</a></li>
           </ul>
    </li>
</ul>

My (woeful) solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 
$xml = simplexml_load_string($string);
 
// pull a node tree as an Array out using simpleXML xpath
$trees = $xml->xpath('/ul/li/ul');
 
$array = array();
$order = array();
 
$i = 0;
 
// we only need to delve into XML if there are any nested <ul>s
if(isset($trees[0])){
 
	foreach($trees[0] as $var){
 
		// store each node in an indexed array
		$array[$i] = $var; 
		// store the month number in an index array
		// based on the text node value of the <a> tag
		$order[$i] = date('m', strtotime((string) $var->a)); 
 
		$i++;
	}
 
	// sort the month number array descending, but maintaining the keys
	arsort($order); 
 
	// create a new XML Dom object to manipulate stuff
	$dom = new DomDocument();
	// create a holder node <ul>
	$ul = $dom->createElement('ul');
 
	// iterate through the array of simpleXML objects 
	// based on the order in which their keys appear in the re-ordered array
	foreach($order as $key => $value){
		// get the simpleXML objects into a string
		$node = dom_import_simplexml($array[$key]);
		// get the string into an actual DOM node
		$node = $dom->importNode($node, true);
		// append
		$ul->appendChild($node);	
	}
 
	//$dom->appendChild($ul);
 
	// unset the contents of the original <ul> node that we have resorted
	$parent = $trees[0]->xpath( 'parent::*' );
	$parent[0]->ul = NULL;
 
	// turn our simpleXML object into a DOM object
	$ixml = dom_import_simplexml($xml);
	$new = new DOMDocument('1.0');
	$ixml = $new->importNode($ixml, true);
	$ixml = $new->appendChild($ixml);
 
	// fire up a DOM xpath object
	$xpath = new DomXpath($new);
	// pull a node tree out using simpleXML xpath
	$tree = $xpath->query('/ul/li/ul');
 
	// add our newly created DOM node conatining the re-ordered <ul> after the existing node
	$tree->item(0)->parentNode->appendChild($new->importNode($ul, true));
	// delete the original empty node
	$tree->item(0)->parentNode->removeChild($tree->item(0));
 
	echo $new->saveHTML();
 
} else {
 
	echo $string;
 
}