Agis on Rails

So what?

Rails Tips - Part 2

Finally found some time and -more importantly- the mood for another post. So here are some more Rails tips I wish I’d known earlier.

Tip #4: Silence Postgres Warnings

Are you annoyed by messages like these in your Terminal when you run your migrations?

1
2
NOTICE:  CREATE TABLE will create implicit sequence "notification_settings_id_seq" for serial column "notification_settings.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "notification_settings_pkey" for table "notification_settings"

Well these are notifications, rather than warnings, to inform us about the awesome stuff Postgres is doing for us. But what do they actually tell us?

Postgres has a numeric type called serial, which is similar to MySQL’s AUTO_INCREMENT. A serial column stores 4-byte integers combined with a sequence to automatically provide auto-incrementing values. That’s what the first message is about: Postgres is automatically creating a sequence to make the id column -that ActiveRecord will use- function.

On to the second message. This is what the official documentation says:

PostgreSQL automatically creates an index for each unique constraint and primary key constraint to enforce uniqueness. Thus, it is not necessary to create an index explicitly for primary key columns.

Pretty clear, Postgres is creating indexes for us, so it also informs us about that.

To the point: We can silence these notifications by adding this simple line under the environments we want, inside database.yml:

1
min_messages: WARNING

Fortunately, we won’t have to do this anymore in Rails 4!

Tip #5: Rails Application Templates

Many times I catch myself going through the same setup steps when starting new Rails apps: writing the same Gemfiles, same Rspec configs, .gitignores, rake tasks, YMLs etc. Wouldn’t it be sweet if we could save time by avoiding all this repetition?

Fortunately, Rails application templates lets us do just that: create templates that we can use when we create new apps. There’s the relatively unknown (still I wonder why) a DSL that we can use to build our own templates. Here’s one of mine for example:

a_template.rb view on GitHub
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

# Fully-armed application template
#
# Postgres, Thin, RSpec, Shoulda, Capybara, FactoryGirl (plus some tricks)
# https://github.com/Agis-/rails_templates/blob/master/full.rb

database_name = ask("What do you want to call the database?")

run "rm public/index.html"
run "mv README.rdoc README.md"

gem "pg"
gem "thin"

require_relative 'testing'

# Database configuration
run "cp -f #{File.expand_path File.dirname(__FILE__)}/lib/database.yml config/"
run "perl -pi -w -e 's/db_name/#{database_name}/g;' config/database.yml"
run "createdb #{database_name}"
run "createdb #{database_name}_dev"
run "createdb #{database_name}_test"
rake "db:create"

# Git
git :init
run "echo *.DS_Store >> .gitignore"
git add: "."
git commit: "-a -m 'Initial commit (full template used)'"

Pretty self-explanatory, isn’t it? The way you use it is with the Rails Generator:

1
rails new myapplication -m a_template.rb

Actually I’ve created my own repo where I collect my own templates: (https://github.com/Agis-/rails_templates). If you find yourself in the same position I suggest you do the same.

There’s also the documentation in the Edge Guides.

Tip #6: Quick Benchmarking with Benchmark.ms

As it usually happens with Rails, there’s a very convenient way to measure the performance of small bits of code, straight in the Rails console. So open up a console and try it:

1
Benchmark.ms { User.all } #=> 89.33299827575684

You get the response time in miliseconds. This comes from ActiveSupport::Benchmarkable along the rest of the benchmarking tools. Nice.

Well, that’s all.

PS. In case you missed it, here’s the first part of this post.

Validating Paperclip Image Dimensions in Rails

I love using Paperclip for handling image uploads in my Rails apps. It’s super easy to use and plays nice with Amazon S3. It even has validators for attachment’s content-type, filesize & presence.

What it lacks is a built-in validator for image dimensions, but I’ve found out that it’s not so hard to build one yourself. In my last project, BathLiving, I needed the user to upload images of specific dimensions. These images where going to be used in image sliders, thumbnails etc. I didn’t want to use ImageMagick’s resizing/scaling options since I wanted the quality of the image to remain intact. So I had to force the admins to create images with the proper width & height in the first place.

Suppose this is our model:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/models/product.rb

class Product < ActiveRecord::Base
  attr_accessible :title, :image

  has_attached_file :image, styles: { thumb: '298x120#' }

  validates_attachment :image, presence: true

  validate :image_dimensions

  private

  def image_dimensions
    required_width  = 200
    required_height = 200
    dimensions = Paperclip::Geometry.from_file(image.queued_for_write[:original].path)

    errors.add(:image, "Width must be #{width}px") unless dimensions.width == required_width
    errors.add(:image, "Height must be #{height}px") unless dimensions.height == required_height
  end
end

I believe the code speaks for itself here. Pretty simple, right? We use #from_file that Paperclip::Geometry gives us to retrieve the dimensions of the image that the user is trying to upload, in other words, the image that’s queued for write.

But there’s one major drawback in this implementation: it’s not reusable. What if we need to validate the dimensions in other models too? We’d have to do the same thing over and over in our models.

Custom validators to the rescue. It looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# app/models/dimensions_validator.rb

class DimensionsValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if record.send("#{attribute}?".to_sym)
      dimensions = Paperclip::Geometry.from_file(value.queued_for_write[:original].path)
      width = options[:width]
      height = options[:height]

      record.errors[attribute] << "Width must be #{width}px" unless dimensions.width == width
      record.errors[attribute] << "Height must be #{height}px" unless dimensions.height == height
    end
  end
end

First we check that the attachment is actually present (line 5). If it is, we perform the validations.

Now in our models we can simply do this:

1
validates :image, dimensions: { width: 200, height: 200 }

and Rails will automatically find our custom validator since it’s in the right place (app/models/) with the right name (dimensions_validator).

Now this implementation won’t work with ActiveRecord associations (eg. if the attachments are declared on a separate Image model), but for the common cases where the attachments and their parent records are in the same model it’s all good.

Do you have any suggestions, fixes or questions? Please let me know.

P.S. I was thinking to implement a built-in paperclip image_dimensions validator so you may want to wait a little before using this (@sikachu of thoughtbot liked the idea).

Hands-on Rack

Rack provides a minimal interface between webservers supporting Ruby and Ruby frameworks.

Our first project in the Code Reading group was Rack. At first I thought it was going to be a complex project to go through but Rack’s logic is really simple. This is an overview of how it actually works.

Credits goes to fellow Code Readers and especially @skade, @robodisco (many of these explanation are copied from their replies on our GitHub Organization). Some info are also taken from Sam Elliot’s post on Rack. You should refer to our forked repo for more info.

Indeed, the Rack API is as simple as it gets:

A Rack application is any Ruby object that responds to `call`. It takes exactly one argument, the environment hash, and returns an Array of exactly three values: The status, the headers, and the body.

Based on this, a simple Rack app could be this:

1
2
3
4
5
6
7
class MyApp
  def call(env)
    [200, {"Content-Type" => "text/plain"}, ["Hello world!"]]
  end
end

run MyApp

We can run the app by saving this class to a file named config.ru and use the rackup tool that Rack provides for easily running Rack apps. #run and #use are two of the methods that the Rack DSL provides:

1
$ rackup config.ru

This will boot Thin (or WEBrick if you don’t have Thin installed). You can override the server if you want. Point to http://0.0.0.0:9292 to see your app.

It gets simpler! Lambdas also respond to call (since they’re Proc objects), so in our rackup file we could just do:

1
run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }

builder.rb implements the simple DSL we can use to add Middlewares to the Stack. Now we can use use method to add Middlewares to the stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
class MyMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    env["some_header"] = "I'm a Middleware"
    @app.call(env)
  end
end

use MyMiddleware
run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK", env["some_header"]]] }

We can see in our browser that this adds the headers. The important thing here is to understand how the Middleware stack works. In our case, the Proc is the endpoint -our main application- and above it sits our new Middleware.

If we take a look at builder.rb (which is a key file to understanding the stack) we can see that #use along with #to_app builds an array of Procs that each take an app and builds another app out of it, by prepending a Middleware to it (a Rack app with a Middleware in front of it is a Rack app). Note Lines 25 & Line 31 which do the trick:

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
# Specifies middleware to use in a stack.
#
#   class Middleware
#     def initialize(app)
#       @app = app
#     end
#
#     def call(env)
#       env["rack.some_header"] = "setting an example"
#       @app.call(env)
#     end
#   end
#
#   use Middleware
#   run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
#
# All requests through to this application will first be processed by the middleware class.
# The +call+ method in this example sets an additional environment key which then can be
# referenced in the application if required.
def use(middleware, *args, &block)
  if @map
    mapping, @map = @map, nil
    @use << proc { |app| generate_map app, mapping }
  end
  @use << proc { |app| middleware.new(app, *args, &block) }
end

def to_app
  app = @map ? generate_map(@run, @map) : @run
  fail "missing run or map statement" unless app
  @use.reverse.inject(app) { |a,e| e[a] }
end

So if we had an imaginary stack like this

1
2
3
4
5
6
# top of the stack
use CoolMiddleware
use AnotherMiddleware
use YoMiddleware
run OurApp # the endpoint, our main app
# bottom of the stack

then the server in response to a request from a client, would go through the stack in reverse order. The first Middleware, “YoMiddleware” gets initialized with “OurApp”, thus creating a Rack app. Then “AnotherMiddleware” gets initialized with that Rack app that was just created and the same process is repeated for next Middlewares till the stack is finished.

It could look something like this:

1
2
3
yo_middleware = YoMiddleware.new(OurApp) # iteration 1
another_middleware = AnotherMiddleware.new(yo_middleware) # iteration 2
cool_middleware = CoolMiddleware.new(another_middleware) # iteration 3

Each Middleware in the stack knows the next one but nothing else. Control is passed by calling the next Middleware. Middlewares can run before or after an application, it depends on where the Middleware wants to work.

The real power comes when you add, remove, move Middlewares to the stack at will and the remaining Middlewares can not care less. The implementation is so simple yet clever that an endpoint looks exactly the same as an endpoint with a middleware in front of it.

Our next project in Code Reading will propably be Sinatra. Stay tuned.

More resources

Rails Tips - Part 1

Mostly like a note to myself, here are some tips that were proved useful to me over time.

Tip #1: Skipping Test::Unit

If you’re like me, you prefer RSpec over Test::Unit for its expressiveness and the awesome DSL it provides. Everytime you create a new app, you can use the -T option to skip all the folders and files related to Test::Unit.

1
rails new app -T

Note that this will not only skip the test directory, but also won’t create any new test files when you use the built-in controller & model generators.

Tip #2: Console Sandbox mode

Many times I need to insert some data in the database just for testing purposes (eg. to test model validations) and then I want them to vanish immediately.

The sandbox mode lets you do just this: You can create, save & destroy ActiveRecord objects and they’ll exist just as long as you terminate the console session.

1
rails c --sandbox

You can see that right before the session gets terminated, Rails automatically rolls back all the transactions made during the session.

Tip #3: Task lists using Rake notes

Rake provides you with a great tool you can use to implement your own task lists. Inside .rb and .erb files, you can add notes like this:

1
2
3
4
5
6
7
8
9
class Post < ActiveRecord::Base
  # FIXME: whitelist all the attributes that should be whitelisted
  attr_accessible :title, :content, :category

  # TODO: add validations

  # OPTIMIZE: improve the SQL
  scope :published, where(:published => true).order("publish_date DESC")
end

Then in your terminal you can use this command that will scan all of your app’s files and list all of your notes and where exactly are they (file and line):

1
rake notes

As you can see, there are already 3 built-in types: TODO, FIXME and OPTIMIZE and the command below lists all of them. If you want to see let’s say only the TODO notes, you do it like this:

1
rake notes:todo

However, you’re not limited to these 3 types. You can easily add your own like this:

1
2
3
4
5
class PostsController < ApplicationController
  # REMEMBER: you can use any title you want for your task
  def index
  end
end

Then, to see all the notes of this custom type you could do:

1
rake notes:custom ANNOTATION=REMEMBER

Thanks to JEG2 for pointing this out.

Soon more to come..