Category Archives: Ruby

Why Rails and Their Committers Are Awesome

I love keyword arguments when they were introduced in Ruby 2.0, and use them every chance I get. As I was hacking around with ActiveJob today, and thought it would be cool to pass keyword arguments into the ActiveJob#perform_later method.

I thought it should work. It didn’t.

You get an  ArgumentError: wrong number of arguments (1 for 0) error. The reason is the hash is converted to string keys upon serialization.

So I was thrilled when I did a bit of searching to see if anyone else had the same issue. Nope. Looked at the ActiveJob::Arguments module. Passing keyword arguments are not supported. I actually get to report a real feature request to Rails!

So here is my ActiveJob should support passing of keyword arguments to perform method.

This this is where the magic began… within a few minutes Sean Griffin (@sgrif) picked up on the issue report. I went to lunch, planning to fork ActiveJob and start hacking away when I got back. But Sean beat me to it. Sean added the ability to pass keyword arguments to ActiveJob jobs, and even backported it to Rails 4.2.1.

Within an hour, it was all done. Check out the full commit.

Want a reason to love Rails and their committers? This is one, of many.

Thank you Sean.

Posted in Rails, Ruby. Tagged with , .

Small Revelation – FactoryGirl, build_stubbed, associations, and let

Not really a problem, but something I didn’t really look out for because all my tests were passing… and hey, if tests are passing, I sleep well.

TL;DR Dumped let{}, removed FactoryGirl associations, back to before(:all) and instance variable/FG.build pattern = super speedy tests. 
TL;DR.1 And read the docs.

Background

I need to test a fairly complex permission matrix at the model level. To properly test it, I need to build out several complete company structures, with a multitude of users at various authorization levels. And 200+ actual tests.

Being a fan of rspec’s let{}, I decide to use it copiously. 40 lines worth.

In these tests, I don’t really need to have persisted records, so using FactoryGirl.build_stubbed fits the bill here.

let(:company) { FactoryGirl.build_stubbed(:company)}
… an so on, 39 times. 

What I Found

I thought one particular spec was running a little slow. So I converted the FactoryGirl.build to the shiny new FactoryGirl.build_stubbed. No difference in time. So I decided to watch the test.log. Well, I quickly saw what was happening… it was persisting thousands of records.

But wait. I was using FactoryGirl.build_stubbed. Where were all these records coming from?

Problem #1

In most of my FactoryGirl factories, I was using ‘association :area‘, ‘association :store‘, and so on. Didn’t think much about these until yesterday.

Turns out that FactoryGirl will build/create and save those associated factories, even if you are using FactoryGirl.build or FactoryGirl.build_stubbed. Learned something new there. Honestly, I didn’t expect this behavior, but I understand why. Shoulda read the docs.

Now, the easy way around this is to pass into a FactoryGirl.create/build/build_stubbed a nil for the association, if it is not needed. Ala:

FactoryGirl.build_stubbed(:store, company: fg_company, area: nil)

Now it won’t build out the associated area. Alas, I had forgotten just one of these nil associations. And at the worst possible relationship level, the bottom. So every time one factory was built, it create the entire supporting structure above. Thus, every hit to an let(:invoice) builds an entire company structure from one single FactoryGirl.build_stubbed(:invoice) call.

But it get’s worse.

Problem #2

I love the let{}. But to be honest, I never read the docs on it. Well, I did read them yesterday. Relavent line being (emphasis added):

The value will be cached across multiple calls in the same example but not across examples.

Uh-oh. Let{} is like a before(:each). Which is what most specs need. But I don’t, not for this spec. I’m never modifying the AR records, just testing some methods within models, which don’t modify AR instances.

Resulting Big Problem

Ok, not really a problem. But certainly very, very inefficient.

By forgetting to nil an association in a FactoryGirld.build_stubbed, and with let{} recreating an entire company structure, to the database, for every 200+ test. Well, you get the picture. It’s slooooow. 22 seconds worth of slow.

Solution

You know I wouldn’t drag you along this far without a solution.

  1. Just remove all the ‘association :model‘ statements from all FactoryGirl definitions. I know they are handy, but I want CONTROL over my factories. And just one small mistake can make a spec run many X-times longer.
  2. Remove the let{} and replace with the good ol’ instance variable/build pattern.
  3. Move all the instance variables into a before(:all).
before(:all) do
  @company = FactoryGirl.build_stubbed(:company)
  @store = FactoryGirl.build_stubbed(:store, company: company)
  … and so on, 38 times.
end

Note for step #1. It caused me to refactor some other specs as well. This turned out to be a good thing, as I was able to speed up several other specs, and add some clarity to those specs that required building out a company structure.

Results

2.5 seconds. Not too shabby.

After the refactoring for all tests, I dropped ~30 more seconds off the entire suite.

 

Hope this helps someone else out there improve the speed of their specs too.

Posted in Rails, Ruby. Tagged with , , .

Sinatra-Tailer: a small app for viewing server log files

UPDATE (Jan 9, 2015): I have not updated this project in year. While it may work, I doubt it. If you would like to take over the project, leave me a comment and we’ll get in touch.

I was reading Jason Seifer’s: Offline Gem Server Rdocs, which is an apache/passenger served Sinatra app that allows you to view the Rdocs of installed gems without using gem server. Nice. So I installed it on our sandbox server for all to enjoy.

But it got me thinking, there is another think I like to keep an eye on on our servers… log files. Oh, and I was looking for good excuse to play around with Sinatra. So, “with a one, and-a-two, and-a-three…” we have Sinatra-Tailer.

You can read all about it on the github page, but in short it simply performs a tail and displays the last X lines of the log file.

[sourcecode language=’bash’]
tail -n /path/to/my/log/file.log
[/sourcecode]

Features

  1. refreshes the list every X seconds, set by the user
  2. only one config.yml file to edit
  3. supports file globs, so you can grab a whole list of file with one line
  4. specify the number of lines to show
  5. completely unobtrusive js, because I’m cool?

Requirements

Sinatra framework

      (tested on 0.9.1.1, earlier may work)

I whipped this up pretty quickly so I’m sure there are a few bugs. There is some testing for a few unit tests, but nothing functional.

One word of warning, if you want to put this on a production server my recommendation is to put it on a separate port (like 9876) and for heaven’s sake, at a minimum use http basic authentication. From the sinatra readme:

Enjoy!

Posted in Ruby. Tagged with , , , .

Apache Virtual Hosts, Rails, Rack, Passenger on Local Net

This one has been bugging me for a couple of years and I just didn’t want to put in the time and testing to nail it down. But tonight was my night.

The Problem

Here in my development palace I have a couple of laptops that I use for development and an Ubuntu (hardy) server as a Rails/Rack sandbox that I run with Passenger. While I have always been able to serve up either a Rails or Sinatra project, I typically have 3 or 4 going at the same time. But I had to access them with urls like: http://192.168.0.2/project1 and http://192.168.0.2/project2.

So, what’s the problem, eh?

Well, for starters, you can’t do anything concerning subdomains. That’s a show stopper for many apps.

Second, it really messes with your environment variables like [“PATH_INFO”]. So if you do any routing and you expect the base url to be ‘/’, it won’t be. The root url will be ‘/projectx’. Bummer.

The Solution

To remedy the issues with non-tld virtual hosts (also called sub URIs?) we need to add the domains to the laptops and server, then add the virtualhosts on the server for apache. This means that I can now goto http://project1.ubuntu.remote url to get to my rails/rack app.

For reference, I am using the following:

On Each Laptop

These instructions are for Mac OS X, but should be identical for your favorite flavor of *nix. We are going to add some friendly names to our ubuntu server using it’s ip address (192.168.0.2). In this example we will be able to access the root web documents using http://ubuntu.remote, and one of our Sinatra projects using http://project1.ubuntu.remote.

Important! Don’t use a name ending in ‘.local’. While it did work for me, I read about a few instances where some systems will not resolve domain names ending in .local outside the local machine. Also, don’t change anything else in your /etc/hosts file except to add the lines at the end.

[source language=’python’]
mate /etc/hosts

# add the following line to the END of your /etc/hosts file and save
# note: there is a character between the ip address and each host name
192.168.0.2 ubuntu.remote project1.ubuntu.remote
[/source]

On The Server: Hosts File

Nothing will work until we tell the server machine that it can accept incoming traffic from our new friendly names. Similar to on the mac, we need to edit our /etc/hosts file but we will add a slightly different line. In this case we are telling the server that incoming traffic from ‘ubuntu.remote’ and ‘gems.ubuntu.remote’ are just names for the localhost (127.0.0.1).

[source lang=’shell’]sudo pico /etc/hosts

# add the following line to the end of our /etc/hosts file and save
# you can added after your existing 127.0.0.1 line if you wish
127.0.0.1 ubuntu.remote project1.ubuntu.remote
[/source]

On The Server: VirtualHosts

As a final step we will need to add or change the virtual host definitions for your rails/rack project. Depending on your Apache setup or Linux distro, the default enabled site may be slightly different and you may have to adapt your virtual host file to your default setup.

First, in my /etc/apache2/apache.conf file, I have the following line near the end of the file. This was to eliminate a warning every time apache was started/restarted. It may affect your virtual host default, but shouldn’t, so add it if you get errors.

[source lang=’shell’]
#added near end of /etc/apache2/apache.conf
ServerName localhost
[/source]

Next we need to check our default virtual host setup to see how the NameVirtualHost is defined. We will duplicate that in each new virtual host that we add. The parts we are looking is the NameVirtualHost and VirtualHost near the top. They could be ‘NameVirtualHost *’ and ‘<VirtualHost *>’ or ‘NameVirtualHost *:80’ and ‘<VirtualHost *:80>’. See the pattern? We need to use the same definition in our new virtual files.

Let’s check our default virtual host setup:

[source lang=’shell’]
cd /etc/apache2/sites-enabled
ls # your are looking for the name of your default site file

total 0

lrwxrwxrwx 1 root root 36 2009-02-27 18:49 000-default -> /etc/apache2/sites-available/default

cat /etc/apache2/sites-enabled/000-default

NameVirtualHost *

  ServerAdmin webmaster@localhost
# snipped several lines #

[/source]

On my system the NameVirtualHost is ‘*’, so that is what we need to use for the new virtual host. In this case, I am going to add a virtual host file for a rack based project. It’s a Sinatra app, but that doesn’t matter as this will work for any rack based project, even a rails/rack project (since rails 2.2.2?).

Also, I like using the Apache2 system to enable and disable sites. It’s clean, easy to use, and if any apache admin comes along they will know exactly what is going on.

It’s also work mentioning that you should disable or delete any existing virtual host files that you were using previously.

[source lang=’shell’]cd /etc/apache2/sites-available
sudo pico gems

# enter this as your virtual host setup

ServerName project1.ubuntu.remote

DocumentRoot “/path/to/your/application/project/public”

RackEnv production

Order allow,deny
Allow from all

# save the file and reload apache

sudo /etc/init.d/apache2 reload
[/source]

The Fun Part

Assuming you saw no errors when you reloaded apache, you should be able to go to your new urls see both the root files and your rack application.

http://ubuntu.remote
http://project1.ubuntu.remote

Dyn-O-Mite!

Bonus Points!

For extra credit, let’s say you have admin access to your router, and you are one of those gotta-have-control-of-everything dudes (or dudettes) so you installed a 3rd party firmware. You’re in luck! You can probably add some custom entries in your DNS and you don’t have to edit the /etc/hosts files on your laptop.

I installed Tomato firmware on my Linksys wireless router. Tomato uses Dnsmasq with allows you to set internal host names.

Maybe I’ll cover that in another post.

Posted in apache, Rails, Ruby. Tagged with , , , , .

Ruby on Rails Ubuntu Server VM with VMPlayer

I had to setup a Ubuntu (8.04) Server virtual machine and get Rails up-to-speed on it this morning, so I thought I would document my steps for those who find the need to repeat. Here is what I needed:

What you need to start:

Continue reading

Posted in Rails, Ruby. Tagged with , , .

Ruby: Send Email Reminders from iCal Calendar

This is going to be long, so hunker down for a good read.

Problem: Our Cub Scout den leader sends email reminders to all the parent about upcoming events. I decided to setup a gCal (google calendar) will all the events so that other parents could just check the calendar as they wish, or subscribe to the ical file to integrate with their own calendar. But several of the parents don’t have calendars (shame, shame) and everyone still liked the idea of receiving email reminders. So I decided to write a ruby script to email reminders automatically two days before the event.

Solution: Ruby is a great language to handle this. Especially since there are gems to handle many of the functions we need. Some of the gems we will be using are as follows:

Continue reading

Posted in Ruby. Tagged with , , , , .