YAML to Ruby hash to CoffeeScript object

In one of my projects I found myself in the odd position where I had data in a YAML file, that needed some processing done on and then being inserted into a CoffeeScript file.

Now I could’ve just done a YAML to JSON conversion, but seeing as I had the intermediate Ruby processing steps and because I really wanted the output to be CoffeeScript as I was likely to have to work and manipulate it further later anyway, and would want to make changes to the CS directly instead of parsing the whole beast again.

So after loading the YAML into a Ruby hash and manipulating it appropriately I needed to do the conversion to CoffeeScript.

This is my 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
      defaultProc = Proc.new do |output|
        print output
      end

      #input is a Ruby hash
      #spaces is a prefix string of spaces used for whitespace significance
      #proc acts on the output
      def HashToCS.convert(input, spaces, proc=defaultProc)
        if input.is_a? String
          proc.call spaces + '"' + input + '"' + "\n"
        elsif input.is_a? Array
          proc.call spaces + "[\n"
          input.each do |a|
            convert(a, spaces + "  ", proc)
          end
          proc.call spaces + "]\n"
        elsif input.is_a? Hash
          proc.call spaces + "{\n"
          input.each do |k, v|
            proc.call spaces + "  #{k}:\n"
            convert(v, spaces + "    ", proc)
          end
          proc.call spaces + "}\n"
        else
          proc.call spaces + input.to_s + "\n"
        end
      end

usage:

1
2
3
4
5
    proc = Proc.new do |output| 
      coffee_script_file.puts output
    end

    HashToCs.convert(ruby_hash, "", proc)

I am using it from a rather intricate Thor script to create a data file for my coffeescript app to act on.
More on using Thor to manage intricate application builds in a later post.

Setup Mac for CoffeeScript development

For my pet projects I mainly use CoffeeScript these days.

I recently decided to do some of my mobile app development on my old mac book pro, so after installing Snow Leopard I had to do the following to get started.

Snow leopard comes preinstalled with Ruby, although I would suggest installing RVM, and Ruby 1.9.2 – there are plenty examples of doing that elsewhere. This is not a guide for Ruby development, so I’ll show the minimal required Ruby. Expect a future article on how I use Ruby and the Middleman gem to do mobile app development and using the same source to build and maintain multiple apps that share some core functionality.

First step is to make sure you have XCode installed. and specifically, be sure to have the version that includes iOS SDK. This would most likely mean that you have to download the whole 4.2+Gb. I tried downloading it from the Apple dev center a few times, only to have the download fail after 1.5~Gb . I then resorted to downloading a torrent. If you want to do development for an Apple mobile device, then you need this. You will need XCode regardless, various Ruby, Node.js and other tools/libraries rely somewhat on it.

Homebrew: Is a great package management tool for OSX. We’ll use it to install Node.js, but you’ll use it for loads of other stuff too, once you know about it.

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"

Node.js : Server-side Javascript.

1
2
brew install node
curl http://npmjs.org/install.sh | sh

CoffeeScript: awesome language that releases the power by taking the pain out of Javascript.

1
npm install -g coffee-script

See if things work.

1
coffee -v

I’ve recently been introduced to the excellent Sublime Text 2 text editor. I know everyone has their own favourite text editor; mine was Textmate. ST2 just does everything so much nicer than any text editor i’ve tried before.

Here’s how to set it up for CoffeeScript editing:
Download it from here

Install it, then in the console:
cd ~/Library/Application\ Support/Sublime\ Text\ 2/Packages/
git clone git://github.com/jashkenas/coffee-script-tmbundle CoffeeScript
ln -s “/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl” /usr/local/bin

I usually use the Railscast colour scheme textmate bundle, or the Twilight colour scheme. In both I just change the comments colour to be more striking.

You can create your own custom build system recipes to compile/translate CoffeeScript file from Sublime Text 2 directly. I don’t currently use this as I prefer to use the console. I might create a build system to use in the future once my project gets a bit more complex.

:franc

Cooler #truncate

  • Posted on March 04, 2011

The default Rails’ #truncate method is really useful when doing things with long reams of text. Recently I needed a version that grabbed at least :length characters, instead of at most. Basically, :boundary needed to be greedy and I wanted to be able to specify a regex instead of a string if the mood caught me.

Here’s my 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
def rtruncate(long_string, opts={})
  text = long_string.to_s.strip
  return "" if text == ""

  defaults = {
    :boundary => /\w*/,
    :length   => 30,
    :omission => "..."
  }  

  options = defaults.merge(opts)

  return text if text.length <= options[:length]

  result = text.scan(Regexp.new(".{#{ options[:length] }}#{ options[:boundary] }")).first
  result << options[:omission] unless result.size == text.size
  result  
end

> numbers = "one two three four five"
> rtruncate(numbers, :length => 10)

#=> "one two three..."

> rtruncate(numbers, :length => numbers.length - 1)
#=> "one two three four five"

Yay!

Turning my MDSL plugin into a gem

For the past year+ I’ve been doing client work for Tom Locke’s Artisan
Technology
web development consulting
firm.

The app I’ve been working on allows a user to perform various standard business
intelligence queries on a dataset using a pretty cool AJAX front-end.

We’re using MultiDimensional eXpression Language (MDX) to write our BI queries
in. We then execute the queries against our database engine using the
excellent and open source
Mondrian project.

When I was originally handed the project, all queries were built using
super general MDX snippets with several “REPLACE_ME_WITH_DATE_CLAUSE” tokens
that would get replaced or removed as needed for a particular query.

Also, once we executed a query against the database using Mondrian, the
results obviously came back as Mondrian (Java) objects with all the lovely
readability that that language is known for. Getting to the actual data in the
mondrian results turned out to be something like:

1
2
3
4
5
6
7
# to get the column headers
column_headers mondrian_result.get_axes(0).get_positions.map &:get_caption

# to get the row headers
row_header     mondrian_result.get_axes(1).get_positions.map &:get_caption

# getting at the actual grid values will make your eyes bleed so it is omitted

Even then, you couldn’t refactor this very cleanly as not all queries returned
rows, so get_axes(1) would throw errors and gah! it was really messy!

This was not fun. I spent a couple of days trying to figure out a.)
what the hell is this MDX thing and what’s it do? and b.) how am I going to
figure out what text snippets go where and what placeholder needs to get
gsub’ed (or was that just sub?) with what other snippet.

Then I approached Tom with a pretty radical idea: give me a while and I’ll rip out all this text
manipulation stuff and replace all MDX strings with calls to ruby objects.

Tom, being damn awesome, answered exactly as you would hope!

So `git checkout -b heaven` and boom! I started hacking about. I had a rough
idea of what I wanted…

  1. I don’t want to write MDX in my classes
  2. A quagmire of a java object is not ‘results’, I want ruby objects that make
  3. sense.

The first version was called MDXBuilder, a utility class that provided a
pretty basic DSL for writing MDX queries and returned a simple hash with
values for rows, columns and grid. This was already a big improvement. So much
so, in fact, that I later did a complete rewrite that allowed you to define
your entire mondrian schema using ruby classes, then run a rake task to
generate your mondrian datamart definition XML (which sucks doing by hand,
trust me.) Think an ‘ORM’-like abstraction for business intelligence queries using
Mondrian. (ORM isn’t quite the right pattern though as BI queries don’t return
objects, but rather result sets.)

Anyway, I wrote it as a plugin and we’ve been using it and steadily improving it for
months now and every day I get happier and happier using it.

However, the fact that it’s a plugin has always gnawed at me. It needs to be
a gem. Also, I currently use inheritance on classes:

1
2
3
class SalesDimension < MDSL::Dimension
  ...
end

But since I saw the newer ruby ORMs move to mixins I’ve been in love with the
idea…it makes so much more sense.

So my next task is to:

  1. Turn MDSL plugin into a gem
  2. Use include MDSL::SomeClass instead of inheriting from it

I hope to find time to work on this soon, so follow along if you’re
interested. For the next few posts I’ll be looking at cool patterns that I
find in some of the existing gems that do things I’d like to copy in mine.

Cool little python script for merging / swapping fields in a CSV file

  • Posted on December 18, 2010
  • Tagged python

I had tons of fun writing a little python script to extract fields from CSV files the other day. I’ve only used python once before during a CS olympiad, so feel free to critique!

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import sys
import getopt
import re
import csv

def help_message():
    print '''awesome_csv.py -- 
Options: -h     -- displays this help message
         -i     -- input file
         -o     -- output file
         -f     -- fields (merge fields with: \%,1,2:\ 3,4,5 where "\%" represents a counter). 
         -s     -- number of lines to skip'''
    sys.exit(0)

skip_lines = 0

try:
    options, xarguments = getopt.getopt(sys.argv[1:], 'hi:o:f:s:')
except getopt.error:
    print 'Error: You tried to use an unknown option or the argument for an option that is required was missing. Try using -h'
    sys.exit(0)

for a in options[:]:
    if a[0] == '-h':
        help_message()

for a in options[:]:
    if a[0] == '-i' and a[1] != '':
        infile = a[1]
        break
    elif a[0] == '-i' and a[1] == '':
        print '-i expects an input file name as argument'
        sys.exit(0)

for a in options[:]:
    if a[0] == '-o' and a[1] != '':
        outfile = a[1]
        break
    elif a[0] == '-o' and a[1] == '':
        print '-o expects an output file name as argument'
        sys.exit(0)

for a in options[:]:
    if a[0] == '-f' and a[1] != '':
        fields = a[1]
        break
    elif a[0] == '-f' and a[1] == '':
        print '-f expects a list of output fields as argument'
        sys.exit(0)

for a in options[:]:
    if a[0] == '-s' and a[1] != '':
        skip_lines = int(a[1])
        break
    elif a[0] == '-s' and a[1] == '':
        print '-s expects the number of lines to skip as argument'
        sys.exit(0)



ifile  = open(infile, "rb")
reader = csv.reader(ifile)

ofile  = open(outfile, "wb")
writer = csv.writer(ofile, delimiter=',', quotechar='"', quoting=csv.QUOTE_ALL)

lines_skipped = 0
line_number   = 0

def repl(matchobj):
    return row[int(matchobj.group(0)) - 1]

for row in reader:
    if lines_skipped < skip_lines:
        lines_skipped += 1
    else:
        line_number += 1
        new_row = []

        for field in fields.split(","):
            new_field = re.sub(r'\d+', repl, field)
            new_field = re.sub(r'\%', str(line_number), new_field)

            new_row.append(new_field)

        writer.writerow(new_row)

ifile.close()
ofile.close()

print "Success! Python is awesome."

Used as:

1
2
3
4
5
6
7

echo "one,two,three" > test.csv

python parse.py -i test.csv -o result_with_merged_fields.csv -s 1 -f 1,2,3,2:\ 3,1:\ 2

cat result_with_merged_fields.csv
one,two,three,two: three,one: two

Happy!

Fragment caching with lambda's

Here’s some commonly seen Rails controller code:

Your controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class PostsController < ApplicationController

  def index
    @posts = get_posts

    respond_to do |format|
      format.html
      format.js  { render :json => @posts}
      format.xml { render :xml  => @posts}
    end
  end

  private

  def get_posts
    # some complex query that
    # returns some posts, say...
  end

end

Technically the #get_posts method should be in the Post model,
but bear with me, I’m trying to prove a point.

index.html.erb

1
2
3
4
5
<h2>Posts!</h2>

<% @posts.each do |post| -%>
  <h3><%=link_to(post) %></h3>
<% end -%>

So that’s pretty standard. The site’s been doing great and everyone who is
anyone is checking out the index page!

Unfortunately your server is getting steadily dragged down and after
some profiling you realize that the main bottleneck is the #get_posts
method.

Not all is lost, you’ve heard that caching can help! Sadly the rest of your
page (that is, various bits of your layout) is quite dynamic and cannot be
cached globally so page and action caching isn’t going to help.

Luckily Rails supports fragment caching which allows you to cache a specific
bit of ERB. So your view becomes:

index.html.erb

1
2
3
4
5
6
7
<h2>Posts!</h2>

<% cache do -%>
  <% @posts.each do |post| -%>
    <h3><%=link_to(post) %></h3>
  <% end -%>
<% end -%>

So that looks great, you profile again and notice that from the second
request onwards, the server is indeed breathing a little easier…but
not nearly as much as you expected.

The reason is that the database queries are still being run in ‘#getposts’,
and the posts assigned to @posts. The fragment caching just skips looping
through the @posts array, replacing the entire ‘cache do … end’ block
with the HTML that got generated during the previous request.

This leads people to unhappy action like this:

The associated helper

1
2
3
4
5
6
7
8
module PostsHelper

  def get_posts
    # some complex query that
    # returns some posts, say...
  end

end

Your controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PostsController < ApplicationController

  def index
    respond_to do |format|
      format.html
      format.js  { render :json => get_posts}
      format.xml { render :xml  => get_posts}
    end
  end

  private

  def get_posts
    # some complex query that
    # returns some posts, say...
  end

end

index.html.erb

1
2
3
4
5
6
7
<h2>Posts!</h2>

<% cache do -%>
  <% get_posts.each do |post| -%>
    <h3><%=link_to(post) %></h3>
  <% end -%>
<% end -%>

Don’t get me wrong, this works. Also, in this rather simple case you can
use Rails’ #helper_method function to remove the duplication.

However, something worth looking at when you run up against more involved
problems of this kind is to use lambda’s for lazy evaluation in the view:

Your controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PostsController < ApplicationController

  def index
    @posts_finder = find_posts

    respond_to do |format|
      format.html
      format.js  { render :json => @posts_finder.call}
      format.xml { render :xml  => @posts_finder.call}
    end
  end

  private

  def find_posts
    Proc.new do
      # some complex query that
      # returns some posts, say...
    end
  end

end

index.html.erb

1
2
3
4
5
6
7
<h2>Posts!</h2>

<% cache do -%>
  <% @posts.call.each do |post| -%>
    <h3><%=link_to(post) %></h3>
  <% end -%>
<% end -%>

That way @posts is lazily evaluated and you’ve cleanly removed your
bottleneck.

Let me know what you think!

Gustav

Disqus

  • Posted on November 25, 2010
  • Tagged blog

I’ve seen Disqus being used around the web and finally integrated it.

Use Babushka to set up an Ubuntu 10.04 Server to run a Rails app

UPDATE: These instructions no longer work. Babushka went through a growth spurt and lots of breaking changes were made. I decided to wait for the project to settle down a bit before updating this post. I believe it is stabilizing so I’m going to update this soon. Be sure to check back here within the next week!

Basic setup

Most of this is copied from the Zero to Hero with Babushka screencast. Watching it is highly recommended
before you start fiddling with Babushka!

For this tutorial I assume that you’ve intalled a bare bones Ubuntu 10.04 Server and you’re logged into your user account.

First we’re going to set up babushka itself:

1
bash -c "`wget -O - babushka.me/up`"

This will bootstrap Babushka and proceed to install ruby, irb and various other useful packages.
When prompted for whose deps you’d like to install, enter ‘gpaul’.

Next we’re going to have Babushka do some base system config by installing ssh, curl, htop and some other apps that come in handy at times:

1
babushka system

Application setup

From here onwards it’s all up to you. You can install mysql, postgres, nginx, etc. You can also add more babushka-deps from github if you see
something you like or fork Ben Hoskings’ Babushka-Deps repo and add your own deps.

For this tutorial I’m going to use Ben’s Nginx and Postgres deps and get a sample rails app (Enki) going.

Ben uses an interesting approach: he sets up a user account for the app named after the domain. Sounds pretty cool so let’s go that route.
Remember: when asked for your username, enter ‘your-domain-name.tld’ (eg. enkiblog.com) Also, when asked for home dir base enter ‘/var/www’ or ‘/src/http’, whichever one you prefer to host your apps out of.

1
babushka 'user exists'

Set up a password for your new user:

1
sudo passwd your-domain.tld

Now become the new user:

1
su - your-domain.tld

Sweet! Now for some real action. We’re going to set up a git repo our app is going to reside in at. When asked, I put it in ~/current to keep with convention.

1
babushka 'passenger deploy repo'

The post-receive hook it adds is pretty cool: every time you push to this repo, the post-receive hook will refresh the code and restarts the passenger instance…have a look:

1
2
3
4
5
6
cat ~/current/.git/post-receive

cd ..
env -i git reset --hard
mkdir -p tmp
touch tmp/restart.txt

On your local machine, cd to your rails app’s root and add the repo on the server as a remote repository:

1
2
3
git remote add server your-domain.tld@your-ip-address:~/current
git push server master
... wait for it to finish pushing ...

Once its finished, your code will be on the server in the ~/current repo you created.

Now that we’ve set up the code on the server, we need to set up the rails app itself. Easy enough with Babushka, answer the prompts as you go:

1
babushka 'rails app'

Congrats, you can hit http://your-domain.tld and see your app hosted and running off postgres and nginx.

Sharing a session between subdomains

http://guestbook.co.za/ is also available at http://www.guestbook.co.za/. We wanted our users to remain logged in if they’re using GuestBook without the ‘www.’ subdomain and then enter it or visit the site from a link.

To do so we needed to share the session between www.guestbook.co.za and guestbook.co.za. This sounds really difficult but thankfully all it takes is prepending the domain name of your cookie with a period (.)

Here’s how to do it in Rails:

1
2
3
4
5
6
# In your production.rb file
config.action_controller.session = {
  :session_domain => ".your-domain.tld",
  :session_key    => "_yourapp",
  :secret         => "somehugesecretkeythathastobemorethanthirtycharacterslong"
}

Thanks for the original post I got this from!

Upgrade a show/hide toggle to use jQuery/Ajax

A friend of mine – Auke – is building a web application. The user has a secondary level of navigation that she
can show/hide at any time.

The app doesn’t include any javascript yet so clicking ‘Show Navigation’ currently reloads the page.
Auke rightly feels that this is a clunky solution and makes testing difficult. Here’s how to switch it around.

Before

1
2
3
4
5
6
7
8
9
<% if navigation_should_be_shown? %>
  <div id="navigation">
    <!-- Some links and stuff -->
  </div>

  <a href="/toggle-me-baby">Hide Navigation</a>
<% else %>
  <a href="/toggle-me-baby">Show Navigation</a>
<% end %>

After

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
<head>
.....
  <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js"></script>

  <script type="text/javascript">
    function updateToggleText() {
      if ($("#navigation").is(":visible")) {
        $("#toggle").text("Hide Navigation");
      } else {
        $("#toggle").text("Show Navigation");
      }
    }
    
    $(document).ready(function() {
      $("#toggle").click(function() {
        $.post({url:  "/toggle-me-baby", function(response) {
          if (response == 'toggled!') {
            $("#navigation").toggle();
    
            updateToggleText();
          } else {
            alert("There was a problem toggling the navigation!");
          }
        });
      });
    });
  </script>
  
.......
</head>

<!-- Replacing the code from above -->
<% if navigation_should_be_shown? %>
  <div id="navigation">
    <!-- Some links and stuff -->
  </div>

  <a id="toggle" href="/toggle-me-baby">Hide Navigation</a>
<% else %>
  <div id="navigation" style="display:none">
    <!-- Some links and stuff -->
  </div>

  <a id="toggle" href="/toggle-me-baby">Show Navigation</a>
<% end %>

I haven’t tested this yet but it should do the trick. :)

Gustav

Enki seems nice

  • Posted on June 15, 2010

I’ve tried a couple of blogging engines but wasn’t a big fan of any of them. Thus far enki seems nice though.

~ Gustav Paul