Thursday, December 31, 2015

Programmers and Obsessive-Compulsive Disorder: a hint for beginners



I think we, programmers, all have felt, one way or another, that compulsion to put things in a certain order.

Identation is an example. In the last 30 years programming, I've met just a few programmers who couldn't care less about identation. None of them is still a programmer by now. It seems this feeling about organizing program's lines is essential when it comes to be a good programmer or not.

It is kind of a positive Obsessive-Compulsive Disorder. And I say positive because keeping things organized helps a lot in programming.

Have you ever felt unconfortable when you add a column to a table using a migration and then you have things like

  create_table "courses", force: :cascade do |t|
    t.integer  "user_id",           limit: 4
    t.string   "title",             limit: 100,   null: false
    t.datetime "created_at",                      null: false
    t.datetime "updated_at",                      null: false
    t.binary   "short_description", limit: 1024
    t.binary   "long_description",  limit: 65535 
  end

Here you clearly see that columns short_description and long_description are out of order. And, worst of all, they are going to be out of order in your table. I mean out of order because I assume all columns should come before created_at and updated_at. This is so natural to me that I can't even explain why is that so. But it is! Maybe it is really Obsessive-Compulsive Disorder.

The good news for beginners is: you can safely edit your db/schema.rb to fix this!

In spite of what Rails writes in this file by default, you can safely edit db/schema.rb, as long as you do nothing more than changing the order of some lines inside a create_table.

In the case above one may edit db/schema.rb and write


  create_table "courses", force: :cascade do |t|
    t.integer  "user_id",           limit: 4
    t.string   "title",             limit: 100,   null: false
    t.binary   "short_description", limit: 1024
    t.binary   "long_description",  limit: 65535 
    t.datetime "created_at",                      null: false
    t.datetime "updated_at",                      null: false
  end

Then, the point here is:

  1. Generate all your migrations
  2. Run rake db:create:all
  3. Run rake db:migrate
  4. Edit db/schema.rb and place all columns in the correct order
  5. Run rake db:drop:all && rake db:create:all && rake db:schema:load
And voilĂ ! There you have all your columns, in all your tables, in the correct order, no matter how you created them.


Monday, December 28, 2015

Phusion Passenger x Rails Composer



I host my customers' websites in VPS servers rented from HOST1PLUS (http://host1plus.com), and I use Apache webserver with Phusion Passenger module to do so.

Up  to this point, nothing so special. But a few days ago I started testing Rails Composer (https://github.com/RailsApps/rails-composer). Everything worked great while I was using my development environment with Webrick, but when I finally tried to publish a small test app in one of my VPS servers, nothing worked.

Rails Composer offers the option to install better-errors, a wonderful gem. It displays a details' screen with all about your errors, which is great, considering that debugging requires all information available sometimes. And when it comes to web apps, you may say this again, and again...

Well, I installed better-errors and when I first tried to run my app I got just the dump screen with error details!

It said, sumarizing, that gem nokogiri could not be loaded. Strange, 'cause I had executed 'bundle install' and knew all gems were there. I followed the procedure to empty my gemset and then installed everything again. Nothing... Same error.

I tried almost everything I could imagine. Then, suddenly an idea came to my mind.

It happens that Rails Composer uses the strategy of creating a private gemset for each web app. The gemsets are stored under .rvm directory and we have always a global gemset for each Ruby version installed and as many private app gemsets as you create.

But be careful!

It seems that Phusion Passenger does NOT recognize private app gemsets. I tried almost everything, as I said before, including changing permissions. Nothing helped! But when I changed back to global gemset it all worked fine.

In order to move back you just have to edit .ruby-gemset file, which is under your app root directory. Remove whatever is written there and write just the word 'global' (no quotes, please).

After doing this, just run 'bundle install' again and you'll see all your gems which were installed in the private genset being installed again, now in the global gemset.

In the next article I'll discuss the advantages and problems of having private gemsets for each app. 

See you there.


Thursday, December 3, 2015

A small trick to use AJAX with Rails



Reloading the whole page at every new request is not a good solution sometimes. But that's exactly what Rails does by default, as we all know. Ajax, as always, is a good solution to prevent this and Rails has its way to use Ajax.

I'm going to talk here about another way to use ajax inside rails, circunventing a small problem.

Let's assume I have a fragment like this

app/views/customer/fragment.html.erb
<div>
  <h1>Just a small test</h1>
</div>

and I what this to be displayed inside a certain area in my page whose id is "ajax-content-area". Something like this.

app/views/customer/main_customer_area.html.erb
<html>
  <body>
    <button class='btn' onclick='ajax_call_fragment();'>Click to show fragment below</button>
    <div id="ajax-content-area"></div>
  </body
</html>

Let's also assume main_customer_area.html.erb is already in my screen. It's been renderized by a call to customer#main_customer_area, according to Rails standards. Now I want to call my fragment.html.erb and place it inside the corresponding <div>. For doing so I create a route:

config/routes.rb:
get '/customer/ajax/fragment', to: 'customer#fragment'

and a method fragment

app/controller/customer.rb
def fragment
end

Now, the final piece:

app/assets/javascripts/application.js
function ajax_call_fragment() {
  var displayArea = $('#area-content-area');
  var myUrl = '/customer/ajax/area';  
  $.ajax( {
    url: myUrl,
    success: function(ret) {
      displayArea.html(ret);
    },
    error: function(ret) {
      displayArea.html("<h3>Put your error message here...</h3>");
    }
  })  
}

Now thar we are ready, when you click the button in main_customer_area, something really bad happens!

Your fragment really appears, but it comes wrapped in the main layout!

As you said nothing, Rails will proceed ad always. It will receive the ajax request for '/customer/ajax/area'; will validate the route and, as said in config/routes.rb, will invoke method fragment at Customer controller. the method says nothing, so Rails will follow its usual procedures. Will search for a view named fragment.html.erb inside app/views/customer/. It is there and then Rails will render it inside the main layout and send it back to browser. Ajax will receive it and put it inside the desired <div> object.

Everything all right, but not what you wanted in the screen. Yeah... because your fragment will be wrapped by the mais layout! You'll have it as a copy of you page inside your page. Banners, menus... all pieces inside your main layout will duplicate.

Of course you may create a new (almost) empty layout, say

app/views/layouts/emptylayout.html.erb
<%= yield %>

and rewrite your method fragment as:

app/controller/customer.rb
def fragment
  render layout: 'emptylayout'
end

This will surely work. But you'll add unnecessary complexity to yout app, creating a new layout for doing nothing.

I'll show you an easy way to achieve the same without this.

Rewrite your method fragment as

app/controller/customer.rb
def fragment
  render layout: nil
end

Yes, you may do such thing as render a nil layout. The result is exactly what you want. Rails will pick the corresponding view and pass it away, since it was told not to wrap it in a layout!

This, of course, is not a suggestion to forget all Rails support to Ajax. It's just another cool way to get things done and a new information: nil layouts are possible!

You never know when this kind of thing may be necessary.