Rails & Slim - A match made in haven

Rails is about doing thins elegantly, with minimal code, in an extensible fashion. Slim is a template engine that transpile to HTML. Its minimal, elegant and fast. It makes boring HTML fun to write and easy to read! This document includes some helpful information about the world of Slim.

Installing

Slim has 2 install candidates. slim and slim-rails. If you are not on Rails, use the former. It will need some setup and config this document doesn’t cover.

Add slim-rails to your Gemfile and bundle.

# Gemfile
gem 'slim-rails'

This should do the trick. But for older versions I think you need to configure Rails to use Slim as the default template engine. If that is the case, do the following.

# config/application.rb
class Application < Rails::Application
  # Add the following
  config.generators do |g|
    g.template_engine :slim
  end
end

Converting HTML / ERB to Slim

What if you already have your views in ERB? Or what if your designer provided you with HTML. This is more common than you think. Fortunately there is a tool for the rescue. It is html2slim. While the name suggests it converts HTML, it works fine for both HTML and ERB.

# Gemfile
group :development do
    gem 'html2slim'
end

This works fine 90% of the time flawlessly. Some times small portions of the conversion fails when it mistakes and adds Ruby in to some HTML attributes. I wanted to fix this long ago, but really didn’t find the time. Just delete the embedded Ruby and you will be fine.

Conclusion

I consider Slim a must in every new project I start. Then there is Haml. It takes a different approach to Slim. Many dislike HAML. How ever if you have an existing project in HAML, stick to it. Otherwise just jump shift to Slim!

Happy hacking!

Rails - ActiveRecord - Includes vs Joins

Joining tables is a common scenario in a project. We have to join tables for few reasons and there are corresponding joins in the database level. Inner join, full outer join, left outer join, right outer join and cross join are them. How would you do it in Rails / ActiveRecord?

While you can leverage raw SQL, rails provides 2 neat ways to join tables. includes and joins.

joins()

  • lazy loading
  • to access attributes of relation, additional sql queries will be made
  • uses SQL INNER JOIN to join tables
  • all the benefits and pitfalls of INNER JOIN

includes()

  • eager loading
  • minimal number of queries to fetch all data
  • can access relation data without additional sql queries
  • uses SQL LEFT OUTER JOIN
  • all the benefits and pitfalls of LEFT OUTER JOIN
  • chain it with joins() and you have an INNER JOIN

Practical use

You have 3 models in a SaaS application. User, Role and Organization. User has many Roles and Role belongs to `Organization.

  1. Get all roles with or without users/organizations (left outer join)

     Role.includes(:users, :organizations)
    

    Keep in mind that not always will the included table data exist. You may want to use &. or .try() to skip nil situations.

  2. Get all roles with users/organizations

     Role.includes(:users, :organizations).joins(roles: :organizations)
    

    includes() uses LEFT OUTER JOIN but when you pair it with joins() it becomes an INNER JOIN which is the trick to get the desired result set.

  3. Query through relation ships, you only need Role data

     Role
       .joins(:users, :organizations)
       .where(
         users: { active: true },
         organizations: { active: true }
       )
    

    Keep in mind that if you now do a role.organization.name you will have to pull the additional data using a new query. This is true if lets say in a view you iterate through every role and print users name, organization name etc. In that case just through an includes() which will use eager loading and fetch all data at once.

I think this 3 use cases cover the average uses of joins() and includes().

Custom Ubuntu Vagrant Base Image

I live in Linux, but not every one in my team GivingFire.com does. Windows has its uses, but when it comes to Rails development, its not a good idea. Windows 10 WSL (Windows Subsystem for Linux) is a great step, but its still not there. But one can always use a virtual machine and vagrant makes integration super easy. This is how you would make your own Vagrant image.

You need to first use an existing image. I used the one provided by Canonical them self. It doesn’t follow the proper specs of vagrant, but I don’t care.

#~ cd /path/to/your/project
#~ vagrant init ubuntu/xenial64

Make necessary changes to the vagrant file…

#~ vagrant up
#~ vagrant ssh

Now as I said, the box by Canonical is having some issues. You can either create a new vagrant user with password or change the current password. If you don’t do this you will get stuck with “Authentication failure” and unable to login after creating an VM using the box.

#~ sudo passed ubuntu

Now do your installations and configuration within the virtual machine. Make sure you test your application before we continue to the next step.

Now it is time to minimize it and make it in to a box that you can distribute. Lets clean up apt, zero out and clear the bash history.

#~ sudo apt-get clean
#~ sudo dd if=/dev/zero of=/EMPTY bs=1M
#~ sudo rm -f /EMPTY
#~ cat /dev/null > ~/.bash_history && history -c && exit

Now we can make the box and add it to the list of boxes.

#~ vagrant package --output project.box
#~ vagrant box add project project.box

Now lets try it out.

#~ vagrant destroy
#~ vagrant init project

If you didn’t create a vagrant user with password vagrant which is the default you will have to explicitly mention it in the vagrant file.

Vagrant.configure("2") do |config|
  config.vm.box = "gf"

  config.vm.network "forwarded_port", guest: 3000, host: 80

  config.ssh.username = "ubuntu"
  config.ssh.password = "password"

  config.vm.provider "virtualbox" do |vb|
    vb.memory = "1024"
  end
end

Then you can do vagrant up and then ssh to the box using vagrant ssh. It will prompt the password.

Once you know its working, distribute it how ever you see it fit.

Blacklist - Tom Keen - Goodbye friend!

I have been a fan of The Blacklist. It is a great show including a great cast and wonderful story telling. I have always admired Tom Keen (Ryan Eggold) but today is went down putting a great show.

I would like to thank Ryan for the great entertainment provided throughout the 5 seasons of the show. It is sad that you had to go for the evolution of the story line.

The death of Tom would be a new beginning for Elizabeth Keen (Megan Boone). She was already beginning to be Reddingtons child and I believe she will turn out to be like her father now on. It is interesting to see how the story will play out.

Rails Service Objects - Why and How

There seems to be some confusion about the role of the ‘controller’ and ‘model’ among many Rails developers. Today I was inspecting the code of a project I am planning to take over by January 2018. One controller almost had 500 lines of code and a model had 1300 lines of code, a ton of fat methods with really ugly naming.

I believe controllers and models should be skinny. Any business logic should go to the service objects and any validation logic should go to form objects. To show how serious I am on the topic, lets work on a simple website with a contact form.

class ContactsController < ApplicationController
  def show
    # Ok, I am thinking of a form object here. It can be a modal.
    # Doesn't really matter.
    @contact_form = ContactForm.new
  end

  def create
    # What to do here?
  end

  private

  def contact_params
    # For starters, that * down there is the splat operator.
    params.require(:contact_form).permit(*ContactForm::PERMITTED_ATTRIBUTES)
  end
end

While the job at hand is really simple, we can have a create action like this

def create
  @contact_form = ContactForm.new(contact_params)
  if @contact_form.valid?
    ContactMailer.notify_admin(
      @contact_form.email,
      @contact_form.first_name,
      @contact_form.last_name,
      @contact_form.message
    ).deliver_later

    ContactMailer.notify_user(
      @contact_form.email,
      @contact_form.first_name
    ).deliver_later

    flash[:success] = 'Message sent!'
    redirect_to root_path
  else
    flash[:warning] = 'Message not sent, check form for errors!'
    render :show
  end
end

Its simple enough, but why is business logic in the controller? Lets clean it up!

def create
  @contact_form = ContactForm.new(contact_params)
  if @contact_form.valid?
    ContactService.process(@contact_form)
    flash[:success] = 'Message sent!'
    redirect_to root_path
  else
    flash[:warning] = 'Message not sent, check form for errors!'
    render :show
  end
end

Really? You want me to spoon feed you too? Ok! Alright!

class ContactService
  attr_reader :contact_form

  def initialize(contact_form)
    @contact_form = contact_form
  end

  def process
    # blah!
  end

  def self.process(contact_form)
    new(contact_form).process
  end
end

I have been said “Who uses process? Use run!”. Now let me point out why I use #process instead of #run.

ContactService.process(@contact_form)

The above sounds “Contact service process contact form”. If it was run, “Contact service run contact form”. I don’t know about you but it doesn’t sound right to me.

Thats it, happy hacking!