Set Background and Header for a Form Created Using Formotion in RubyMotion

Setting background color

Formotion for Rubymotion makes it a breeze to create views with forms. I am building a rubymotion app and my login form uses formotion. I needed to set background color for my form and here is how you can set a background color to a form created using Formotion.

1
2
3
4
5
6
7
8
9
class LoginViewController < Formotion::FormController

  def viewDidLoad
    super
    view = UIView.alloc.init
    view.backgroundColor = 0x838E61.uicolor
    self.tableView.backgroundView = view
  end
end

After the login view is done loading, I’m creating a new UIView and setting its background color. Then this UIView object is set as the background view to formotion’s table view.

Setting header image

If you want to add some branding to the login form, you can add a image to the form’s header by adding the below code to viewDidLoad:

1
2
3
header_image = UIImage.imageNamed('header_image_name.png')
header_view = UIImageView.alloc.initWithImage(header_image)
self.tableView.tableHeaderView = header_view

We are creating a UIImageView and initializing it with the image we want to show in the header. Now, set the tableview’s tableHeaderView value to the UIImageView we created.

Cookies on Rails

How session data is handled in 3.2

If you generate a Rails application in 3.2 then ,by default, you will see a file at config/initializers/session_store.rb . The contents of this file is something like

1
Demo::Application.config.session_store :cookie_store, key: '_demo_session'

First thing this line is telling is to use cookie to store session information.

Second thing this line is telling is to use _demo_session as the key to store cookie data.

A single site can have cookies under different key. For example airbnb is using 14 different keys to store cookie data.

airbnb cookies

Now let’s see how Rails 3.2.13 stores session information.

In my 3.2.13 version of Rails application I added following line to create session data.

1
session[:github_username] = 'neerajdotname'

Then I visit the action that executes above code. Now if I go and look for cookies for localhost:3000 then this is what I see .

demo session

As you can see I have only one cookie with key _demo_session .

Deciphering content of the cookie

The cookie has following data.

1
2
3
BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTgwZGFiNzhiYWZmYTc3NjU1ZmVmMGUxM2EzYmEyMDhhBjsAVEkiFGdpdGh1Yl91c2V
ybmFtZQY7AEZJIhJuZWVyYWpkb3RuYW1lBjsARkkiEF9jc3JmX3Rva2VuBjsARkkiMU1KTCs2dXVnRFo2R2NTdG5Kb3E2dm5Bcl
ZYRGJGbjJ1TXZEU0swamxyWU09BjsARg%3D%3D--b5bcce534ceab56616d4a215246e9eb1fc9984a4

Let’s open rails console and try to decipher this information.

1
2
3
content = 'BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTgwZGFiNzhiYWZmYTc3NjU1ZmVmMGUxM2EzYmEyMDhhBjsAVEkiFGdpdGh1Yl91c2V
ybmFtZQY7AEZJIhJuZWVyYWpkb3RuYW1lBjsARkkiEF9jc3JmX3Rva2VuBjsARkkiMU1KTCs2dXVnRFo2R2NTdG5Kb3E2dm5BclZYRGJGbjJ1T
XZEU0swamxyWU09BjsARg%3D%3D--b5bcce534ceab56616d4a215246e9eb1fc9984a4'

When the content is written to cookie then it is escaped. So first we need to unescape it.

1
2
3
4
> unescaped_content = URI.unescape(content)
=> "BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTgwZGFiNzhiYWZmYTc3NjU1ZmVmMGUxM2EzYmEyMDhhBjsAVEkiFGdpdGh1Yl91c2V
ybmFtZQY7AEZJIhJuZWVyYWpkb3RuYW1lBjsARkkiEF9jc3JmX3Rva2VuBjsARkkiMU1KTCs2dXVnRFo2R2NTdG5Kb3E2dm5BclZYRG
JGbjJ1TXZEU0swamxyWU09BjsARg==--b5bcce534ceab56616d4a215246e9eb1fc9984a4"

Notice that towards the end unescaped_content has . That is a separation marker. The value before is the real payload. The value after is digest of data.

1
2
3
4
> data, digest = unescaped_content.split('--')
=> ["BAh7CEkiD3Nlc3Npb25faWQGOgZFRkkiJTgwZGFiNzhiYWZmYTc3NjU1ZmVmMGUxM2EzYmEyMDhhBjsAVEkiFGdpdGh1Yl91c2V
ybmFtZQY7AEZJIhJuZWVyYWpkb3RuYW1lBjsARkkiEF9jc3JmX3Rva2VuBjsARkkiMU1KTCs2dXVnRFo2R2NTdG5Kb3E2dm5BclZYRGJ
GbjJ1TXZEU0swamxyWU09BjsARg==", "b5bcce534ceab56616d4a215246e9eb1fc9984a4"]

The data is Base64 encoded. So let’s unecode it.

1
2
3
4
> Marshal.load(::Base64.decode64(data))
=> {"session_id"=>"80dab78baffa77655fef0e13a3ba208a",
    "github_username"=>"neerajdotname",
    "_csrf_token"=>"MJL+6uugDZ6GcStnJoq6vnArVXDbFn2uMvDSK0jlrYM="}

So we are able to get the data that is stored in cookie. However we can’t tamper with the cookie because if we change the cookie data then the digest will not match.

Now let’s see how rails matches the digest.

In order to create the digest rails makes of use of config/initializer/secret_token.rb . In my case the file has following content.

1
Demo::Application.config.secret_token = '111111111111111111111111111111'

This secret token is used to create the digest.

1
2
3
> secret_token =  '111111111111111111111111111111'
> OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get('SHA1').new, secret_token, data)
=> "b5bcce534ceab56616d4a215246e9eb1fc9984a4"

Notice that the result of above produces a value that is same as digest in earlier step. So if cookie data is tampered with then the digest match will fail. This is why it is absolute necessary that attacker should not be able to get access to secret_token value.

Did you notice that we can access the cookie data without needing secret_token. It means the data stored in cookie is not encrypted and anyone can see it. That is why it is recommended that application should not store any sensitive information in cookie .

Using cookie gives you more control

In the previous example we used session to store and retrieve data from cookie. We can directly use cookie and that gives us a little bit more control.

Using unsigned cookie

1
cookies[:github_username] = 'neerajdotname'

Now if we look at cookie stored in browser then this is what we see.

update
cookie

As you can see now we have two keys in our cookie. One created by session and the other one created by code written above.

Another thing to note is that the data stored for key github_username is not Base64encoded and it also does not have to separate the data from the digest. It means this type of cookie data can be tampered with by the user and the Rails application will not be able to detect that the data has been tampered with.

Now let’s try to sign the cookie data to make it tamper proof.

1
cookies.signed[:twitter_username] = 'neerajdotname'

Now let’s look at cookies in browser.

update
cookies

This time we got data with another key twitter_username . Another thing to notice is that cookie data is signed and is tamper proof.

When we use session then behind the scene it uses cookies.signed. That’s why we end up seeing signed data for key _demo_session .

Tampering signed cookie

What happens when user tampers with signed cookie data.

Rails does not raise any exception. However when you try to access cookie data then nil is returned because the data has been tampered with.

Security should be on by default

session , by default, uses signed cookies which prevents any kind of tampering of data but the data is still visible to users. It means we can’t store sensitive information in session.

It would be nice if the session data is stored in encrypted format. And that’s the topic of our next discussion.

Rails 4 stores session data in encrypted format

If you generate a Rails application in Rails 4 then ,by default, you will see a file at config/initializers/session_store.rb . The contents of this file is something like

1
Demo::Application.config.session_store :cookie_store, key: '_demo_session'

Also you will notice that file at config/initializers/secret_token.rb looks like this .

1
Demo::Application.config.secret_key_base = 'b14e9b5b720f84fe02307ed16bc1a32ce6f089e10f7948422ccf3349d8ab586869c11958c70f46ab4cfd51f0d41043b7b249a74df7d53c7375d50f187750a0f5'

Notice that in Rails 3.2.x the key was secret_token. Now the key is secret_key_base .

1
session[:github_username] = 'neerajdotname'

image

1
Cookie has following data.

RkxNUWo4NlBKakoyU1VqZWJIKzNaV0lQVVJwQjZhdUVTRnowVHppSVJ3Mk84TStoS1hndFZFNHlNaGw2RHBCc0ZiaEpsM0NtYTg4d nptcjFaQWVJbUdOaFh5MVlCdWVmSHBMNWpKbkRKR0JrSU5KZFYwVjVyWTZ3aUNqSWxJM1RTMkQybEtPUFE5VDFsZVJyakx0dFh3PT 0tLTZ5NGIreU00Z0MyNnErS29SSGEyZkE9PQ%3D%3D–3f2fd67e4e7785933485a583720d29ba88bca15f

1
Let's open <tt>rails console</tt> and try to decipher this information.

ruby content = ‘RkxNUWo4NlBKakoyU1VqZWJIKzNaV0lQVVJwQjZhdUVTRnowVHppSVJ3Mk84TStoS1hndFZFNHlNaGw2RHBCc0ZiaEpsM0NtYTg4d nptcjFaQWVJbUdOaFh5MVlCdWVmSHBMNWpKbkRKR0JrSU5KZFYwVjVyWTZ3aUNqSWxJM1RTMkQybEtPUFE5VDFsZVJyakx0dFh3PT 0tLTZ5NGIreU00Z0MyNnErS29SSGEyZkE9PQ%3D%3D–3f2fd67e4e7785933485a583720d29ba88bca15f’

1
2
When the content is written to cookie then it is escaped. So first we
need to `unescape` it.

ruby unescaped_content = URI.unescape(content) => “RkxNUWo4NlBKakoyU1VqZWJIKzNaV0lQVVJwQjZhdUVTRnowVHppSVJ3Mk84TStoS1hndFZFNHlNaGw2RHBCc0ZiaEpsM0NtYTg4d nptcjFaQWVJbUdOaFh5MVlCdWVmSHBMNWpKbkRKR0JrSU5KZFYwVjVyWTZ3aUNqSWxJM1RTMkQybEtPUFE5VDFsZVJyakx0dFh3PT 0tLTZ 5NGIreU00Z0MyNnErS29SSGEyZkE9PQ==–3f2fd67e4e7785933485a583720d29ba88bca15f”

1
2
Now we need <tt>secret_key_base</tt> value. And using that let's build
<tt>key_generator</tt> .

ruby secret_key_base = ‘b14e9b5b720f84fe02307ed16bc1a32ce6f089e10f7948422ccf3349d8ab586869c11958c70f46ab4cfd51f0d41043b7b249a74df7d53c7375d50f187750a0f5’ key_generator = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000) key_generator = ActiveSupport::CachingKeyGenerator.new(key_generator)

1
2
Our <tt>MessageEncryptior</tt> needs two long random strings for encryption. So let's
generate two keys for encryptor.

ruby secret = key_generator.generate_key(‘encrypted cookie’) sign_secret = key_generator.generate_key(‘signed encrypted cookie’) encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret)

1
Now we can finally decipher the data.

ruby data = encryptor.decrypt_and_verify(unescaped_content) puts data => neerajdotname “`

As you can see we need the secret_key_base to make sense out of cookie data. So in Rails 4 the session data will be encrypted ,by default.

How to migrate from Rails 3.x to Rails 4 without loosing cookie

Rails4 will transparently will upgrade cookies from unencrypted to encrypted cookies. This is a brilliant example of trivial choices removed by Rails.

Understanding Instance Exec in Ruby

procs have lexical scoping

Let’s start with a simple example

1
2
3
4
square = lambda { x * x }
x = 20
puts square.call()
# => undefined local variable or method `x' for main:Object (NameError)

So even though variable x is present, the proc could not find it because when the code was read then x was missing .

Let’s fix the code.

1
2
3
4
5
x = 2
square = lambda { x * x }
x = 20
puts square.call()
# => 400

In the above case we got the answer. But the answer is 400 instead of 4 . That is because the proc binding refers to the variable x. The binding does not hold the value of the variable, it just holds the list of variables available. In this case the value of x happens to be 20 when the code was executed and the result is 400 .

x does not have to a variable. It could be a method. Check this out.

1
2
3
4
5
6
square = lambda { x * x }
def x
  20
end
puts square.call()
# => 400

In the above case x is a method definition. Notice that binding is smart enough to figure out that since no x variable is present let’s try and see if there is a method by name x .

Another example of lexical binding in procs

1
2
3
4
5
6
7
def square(p)
   x = 2
   puts p.call
end
x = 20
square(lambda { x * x })
#=> 400

In the above case the value of x is set as 20 at the code compile time. Don’t get fooled by x being 2 inside the method call. Inside the method call a new scope starts and the x inside the method is not the same x as outside .

Issues because of lexical scoping

Here is a simple case.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person
  code = proc { puts self }

  define_method :name do
    code.call()
  end
end

class Developer < Person
end

Person.new.name # => Person
Developer.new.name # => Person 

In the above case when Developer.new.name is executed then output is Person. And that can cause problem. For example in Ruby on Rails at a number of places self is used to determine if the model that is being acted upon is STI or not. If the model is STI then for Developer the query will have an extra where clause like AND "people"."type" IN ('Developer') . So we need to find a solution so that self reports correctly for both Person and ‘Developer` .

instance_eval can change self

instance_eval can be used to change self. Here is refactored code using instance_eval .

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person
  code = proc { puts self }

  define_method :name do
    self.class.instance_eval &code
  end
end

class Developer < Person
end

Person.new.name #=> Person
Developer.new.name #=> Developer

Above code produces right result. However instance_eval has one limitation. It does not accept arguments. Let’s change the proc to accept some arguments to test this theory out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person
  code = proc { |greetings| puts greetings; puts self }

  define_method :name do
    self.class.instance_eval 'Good morning', &code
  end
end

class Developer < Person
end

Person.new.name
Developer.new.name

#=> wrong number of arguments (1 for 0) (ArgumentError)

In the above case we get an error. That’s because instance_eval does not accept arguments.

This is where instance_exec comes to rescue. It allows us to change self and it can also accept arguments.

instance_exec to rescue

Here is code refactored to use instance_exec .

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person
  code = proc { |greetings| puts greetings; puts self }

  define_method :name do
    self.class.instance_exec 'Good morning', &code
  end
end

class Developer < Person
end

Person.new.name #=> Good morning Person
Developer.new.name #=> Good morning Developer

As you can see in the above code instance_exec reports correct self and the proc can also accept arguments .

Conclusion

I hope this article helps you understand why instance_exec is useful.

I scanned RubyOnRails source code and found around 26 usages of instance_exec . Look at the usage of instance_exec usage there to gain more understanding on this topic.

Rex, Rexical and Rails Routing

Please read Journery into Rails routing to get a background on Rails routing discussion.

A new language

Let’s say that the route defintion looks like this.

1
/page/:id(/:action)(.:format)

The task at hand is to develop a new programming language which will understand the rules of the route definitions. Since this language deals with routes let’s call this language Poutes . Well Pout sounds better so let’s roll with that.

It all begins with scanner

rexical is a gem which generates scanner generator. Notice that rexical is not a scanner itself. It will generate a scanner for the given rules. Let’s give it a try.

Create a folder called pout_language and in that folder create a file called pout_scanner.rex . Notice that the extension of the file is .rex .

1
2
class PoutScanner
end

Before we proceed any further, let’s compile to make sure it works.

1
2
3
4
$ gem install rexical
$ rex pout_scanner.rex -o pout_scanner.rb 
$ ls
pout_scanner.rb pout_scanner.rex

While doing gem install do not do gem install rex . We are intalling gem called rexical not rex .

Time to add rules

Now it’s time to add rules to our pout.rex file.

Let’s try to develop scanner which can detect difference between integers and strings .

1
2
3
4
5
class PoutScanner
rule
  \d+         { puts "Detected number" }
  [a-zA-Z]+   { puts "Detected string" }
end

Regenerate the scanner .

1
$ rex pout_scanner.rex -o pout_scanner.rb

Now let’s put the scanner to test . Let’s create pout.rb .

1
2
3
4
5
require './pout_scanner.rb'
class Pout
  @scanner = PoutScanner.new
  @scanner.tokenize("123")
end

You will get the error undefined method `tokenize’ for #<PoutScanner:0x007f9630837980> (NoMethodError) .

To fix this error open pout_scanner.rex and add inner section like this .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PoutScanner
rule
  \d+         { puts "Detected number" }
  [a-zA-Z]+   { puts "Detected string" }

inner
  def tokenize(code)
    scan_setup(code)
    tokens = []
    while token = next_token
      tokens << token
    end
    tokens
  end
end

Regenerate the scanner by executing rex pout_scanner.rex -o pout_scanner.rb . Now let’s try to run pout.rb file.

1
2
$ ruby pout.rb
Detected number

So this time we got some result.

Now let’s test for a string .

1
2
3
4
5
6
7
8
9
 require './pout_scanner.rb'

class Pout
  @scanner = PoutScanner.new
  @scanner.tokenize("hello")
end

$ ruby pout.rb
Detected string

So the scanner is rightly identifying string vs integer. We are going to add a lot more testing so let’s create a test file so that we do not have to keep changing the pout.rb file.

Tests and Rake file

This is our pout_test.rb file.

1
2
3
4
5
6
7
8
9
10
11
12
require 'test/unit'
require './pout_scanner'

class PoutTest  < Test::Unit::TestCase
  def setup
    @scanner = PoutScanner.new
  end

  def test_standalon_string
    assert_equal [[:STRING, 'hello']], @scanner.tokenize("hello")
  end
end

And this is our Rakefile file .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'rake'
require 'rake/testtask'

task :generate_scanner do
  `rex pout_scanner.rex -o pout_scanner.rb`
end

task :default => [:generate_scanner, :test_units]

desc "Run basic tests"
Rake::TestTask.new("test_units") { |t|
  t.pattern = '*_test.rb'
  t.verbose = true
  t.warning = true
}

Also let’s change the pout_scanner.rex file to return an array instead of puts statements . The array contains information about what type of element it is and the value .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PoutScanner
rule
  \d+         { [:INTEGER, text.to_i] }
  [a-zA-Z]+   { [:STRING, text] }

inner
  def tokenize(code)
    scan_setup(code)
    tokens = []
    while token = next_token
      tokens << token
    end
    tokens
  end
end

With all this setup now all we need to do is write test and run rake .

tests for integer

I added following test and it passed.

1
2
3
def test_standalone_integer
  assert_equal [[:INTEGER, 123]], @scanner.tokenize("123")
end

However following test failed .

1
2
3
def test_string_and_integer
  assert_equal [[:STRING, 'hello'], [:INTEGER, 123]], @scanner.tokenize("hello 123")
end

Test is failing with following message

1
2
3
  1) Error:
test_string_and_integer(PoutTest):
PoutScanner::ScanError: can not match: ' 123'

Notice that in the error message before 123 there is a space. So the scanner does not know how to handle space. Let’s fix that.

Here is the updated rule. We do not want any action to be taken when a space is detected. Now test is passing .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PoutScanner
rule
  \s+
  \d+         { [:INTEGER, text.to_i] }
  [a-zA-Z]+   { [:STRING, text] }

inner
  def tokenize(code)
    scan_setup(code)
    tokens = []
    while token = next_token
      tokens << token
    end
    tokens
  end
end

Back to routing business

Now that we have some background on how scanning works let’s get back to business at hand. The task is to properly parse a routing statement like /page/:id(/:action)(.:format) .

test for slash

The simplest route is one with / . Let’s write a test and then rule for it.

1
2
3
4
5
6
7
8
9
10
11
12
13
require 'test/unit'
require './pout_scanner'

class PoutTest  < Test::Unit::TestCase
  def setup
    @scanner = PoutScanner.new
  end

  def test_just_slash
    assert_equal [[:SLASH, '/']], @scanner.tokenize("/")
  end

end

And here is the .rex file .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PoutScanner
rule
  \/         { [:SLASH, text] }

inner
  def tokenize(code)
    scan_setup(code)
    tokens = []
    while token = next_token
      tokens << token
    end
    tokens
  end
end

test for /page

Here is the test for /page .

1
2
3
def test_slash_and_literal
  assert_equal [[:SLASH, '/'], [:LITERAL, 'page']] , @scanner.tokenize("/page")
end

And here is the rule that was added .

1
 [a-zA-Z]+  { [:LITERAL, text] }

test for /:page

Here is test for /:page .

1
2
3
def test_slash_and_symbol
  assert_equal [[:SLASH, '/'], [:SYMBOL, ':page']] , @scanner.tokenize("/:page")
end

And here are the rules .

1
2
3
4
rule
  \/          { [:SLASH, text]   }
  \:[a-zA-Z]+ { [:SYMBOL, text]  }
  [a-zA-Z]+   { [:LITERAL, text] }

test for /(:page)

Here is test for /(:page) .

1
2
3
def test_symbol_with_paran
  assert_equal  [[[:SLASH, '/'], [:LPAREN, '('],  [:SYMBOL, ':page'], [:RPAREN, ')']]] , @scanner.tokenize("/(:page)")
end

And here is the new rule

1
  \/\(\:[a-z]+\) { [ [:SLASH, '/'], [:LPAREN, '('], [:SYMBOL, text[2..-2]], [:RPAREN, ')']] }

We’ll stop here and will look at the final set of files

Final files

This is Rakefile .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'rake'
require 'rake/testtask'

task :generate_scanner do
  `rex pout_scanner.rex -o pout_scanner.rb`
end

task :default => [:generate_scanner, :test_units]

desc "Run basic tests"
Rake::TestTask.new("test_units") { |t|
  t.pattern = '*_test.rb'
  t.verbose = true
  t.warning = true
}

This is pout_scanner.rex .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PoutScanner
rule
  \/\(\:[a-z]+\) { [ [:SLASH, '/'], [:LPAREN, '('], [:SYMBOL, text[2..-2]], [:RPAREN, ')']] }
  \/          { [:SLASH, text]   }
  \:[a-zA-Z]+ { [:SYMBOL, text]  }
  [a-zA-Z]+   { [:LITERAL, text] }

inner
  def tokenize(code)
    scan_setup(code)
    tokens = []
    while token = next_token
      tokens << token
    end
    tokens
  end
end

This is pout_test.rb .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'test/unit'
require './pout_scanner'

class PoutTest  < Test::Unit::TestCase
  def setup
    @scanner = PoutScanner.new
  end

  def test_just_slash
    assert_equal [[:SLASH, '/']] , @scanner.tokenize("/")
  end

  def test_slash_and_literal
    assert_equal [[:SLASH, '/'], [:LITERAL, 'page']] , @scanner.tokenize("/page")
  end

  def test_slash_and_symbol
    assert_equal [[:SLASH, '/'], [:SYMBOL, ':page']] , @scanner.tokenize("/:page")
  end

  def test_symbol_with_paran
    assert_equal  [[[:SLASH, '/'], [:LPAREN, '('],  [:SYMBOL, ':page'], [:RPAREN, ')']]] , @scanner.tokenize("/(:page)")
  end
end

How scanner works

Here we used rex to generate the scanner. Now take a look that the pout_scanner.rb . Here is that file . Please take a look at this file and study the code. It is only 91 lines of code.

If you look at the code it is clear that scanning is not that hard. You can handroll it without using a tool like rex . And that’s exactly what Aaron Patternson did in Journey . He handrolled the scanner .

Conclusion

In this blog we saw how to use rex to build the scanner to read our routing statements . In the next blog we’ll see how to parse the routing statement and how to find the matching routing statement for a given url .

Journey Into Rails Routing – an Under the Hood Look at How Routing Works

Following code was tested with edge rails (rails4) .

When a Rails application boots then it reads the config/routes.rb file. In your routes you might have code like this

1
2
3
4
5
6
7
Rails4demo::Application.routes.draw do
  root 'users#index'
  resources :users
  get 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
  get '/logout' => 'sessions#destroy', :as => :logout
  get "/stories" => redirect("/photos")
end

In the above case there are five different routing statements. Rails needs to store all those routes in a manner such that later when url is ‘/photos/5’ then it should be able to find the right route statement that should handle the request.

In this article we are going to take a peek at how Rails handles the whole routing business.

Normalization in action

In order to compare various routing statements first all the routing statements need to be normalized to a standard format so that one can easily compare one route statement with another route statement.

Before we take a deep dive into how the normalization works lets first see some normalizations in action.

get call with defaults

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  get 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
end

After the normalization process the above routing statement is transformeed into five different variables. The values for all those five varibles is shown below.

1
2
3
4
5
6
7
8
9
10
11
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007fd05e0cf7e8 
           @defaults={:format=>"jpg", :controller=>"photos", :action=>"show"},
           @glob_param=nil,
           @controller_class_names=#<ThreadSafe::Cache:0x007fd05e0cf7c0 
           @backend={},
           @default_proc=nil>>
conditions: {:path_info=>"/photos/:id(.:format)", :required_defaults=>[:controller, :action], :request_method=>["GET"]}
requirements: {}
defaults: {:format=>"jpg", :controller=>"photos", :action=>"show"}
as: nil
anchor: true

app is the application that will be executed if conditions are met. conditions are the conditions. Pay attention to :path_info in conditions. This is used by Rails to determine the right route statement. defaults are defaults and requirements are the constraints.

get call with as

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  get '/logout' => 'sessions#destroy', :as => :logout
end

After normalization above code gets following values

1
2
3
4
5
6
7
8
9
10
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f8ded87e740 
           @defaults={:controller=>"sessions", :action=>"destroy"},
           @glob_param=nil,
           @controller_class_names=#<ThreadSafe::Cache:0x007f8ded87e718 @backend={}, 
           @default_proc=nil>>
conditions: {:path_info=>"/logout(.:format)", :required_defaults=>[:controller, :action], :request_method=>["GET"]}
requirements: {}
defaults: {:controller=>"sessions", :action=>"destroy"}
as: "logout"
anchor: true

Notice that in the above case as is populate with logout .

root call

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  root 'users#index'
end

After normalization above code gets following values

1
2
3
4
5
6
7
8
9
10
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007fe91507f278 
           @defaults={:controller=>"users", :action=>"index"},
           @glob_param=nil,
           @controller_class_names=#<ThreadSafe::Cache:0x007fe91507f250 @backend={}, 
           @default_proc=nil>>
conditions: {:path_info=>"/", :required_defaults=>[:controller, :action], :request_method=>["GET"]}
requirements: {}
defaults: {:controller=>"users", :action=>"index"}
as: "root"
anchor: true

Notice that in the above case as is populated. And the path_info is / since this is the root url .

get call with constraints

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  #get 'pictures/:id' => 'pictures#show', :constraints => { :id => /[A-Z]\d{5}/ }
end

After normalization above code gets following values

1
2
3
4
5
6
7
8
9
10
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f8158e052c8 
           @defaults={:controller=>"pictures", :action=>"show"},
           @glob_param=nil,
           @controller_class_names=#<ThreadSafe::Cache:0x007f8158e05278 @backend={}, 
           @default_proc=nil>>
conditions: {:path_info=>"/pictures/:id(.:format)", :required_defaults=>[:controller, :action], :request_method=>["GET"]}
requirements: {:id=>/[A-Z]\d{5}/}
defaults: {:controller=>"pictures", :action=>"show"}
as: nil
anchor: true

Notice that in the above case requirements is populated with constraints mentioned in the route definition .

get with a redirect

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  get "/stories" => redirect("/posts")
end

After normalization above code gets following values

1
2
3
4
5
6
app: redirect(301, /posts)
conditions: {:path_info=>"/stories(.:format)", :required_defaults=>[], :request_method=>["GET"]}
requirements: {}
defaults: {}
as: "stories"
anchor: true

Notice that in the above case app is a simple redirect .

resources

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  resources :users
end

After normalization above code gets following values

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
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41a315c0 
           @defaults={:action=>"index", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41a31598 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users(.:format)", :required_defaults=>[:action, :controller], :request_method=>["GET"]}
defaults: {:action=>"index", :controller=>"users"}
as: "users"

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41a4ef80 
           @defaults={:action=>"create", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41a4ef58 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users(.:format)", :required_defaults=>[:action, :controller], :request_method=>["POST"]}
defaults: {:action=>"create", :controller=>"users"}
as: nil

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41b63790 
           @defaults={:action=>"new", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41b63768 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/new(.:format)", :required_defaults=>[:action, :controller], :request_method=>["GET"]}
defaults: {:action=>"new", :controller=>"users"}
as: "new_user"

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41a10550 
           @defaults={:action=>"edit", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41a10528 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/:id/edit(.:format)", :required_defaults=>[:action, :controller], :request_method=>["GET"]}
defaults: {:action=>"edit", :controller=>"users"}
as: "edit_user"

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41f31818 
           @defaults={:action=>"show", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41f317f0 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/:id(.:format)", :required_defaults=>[:action, :controller], :request_method=>["GET"]}
defaults: {:action=>"show", :controller=>"users"}
as: "user"

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d44a9bb70 
           @defaults={:action=>"update", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d44a9bb48 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/:id(.:format)", :required_defaults=>[:action, :controller], :request_method=>["PATCH"]}
defaults: {:action=>"update", :controller=>"users"}
as: nil

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d41b17480 
           @defaults={:action=>"update", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d41b17458 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/:id(.:format)", :required_defaults=>[:action, :controller], :request_method=>["PUT"]}
defaults: {:action=>"update", :controller=>"users"}
as: nil

app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007f9d439ddf68 
           @defaults={:action=>"destroy", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007f9d439ddf40 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/:id(.:format)", :required_defaults=>[:action, :controller], :request_method=>["DELETE"]}
defaults: {:action=>"destroy", :controller=>"users"}
as: nil

In this case I omitted requirements and anchor for brevity .

Notice that a single routing statement resources :users created eight normalized routing statements. It means that resources statement is basically a short cut for defining all those eight routing statements .

resources with only

Here we have following route

1
2
3
Rails4demo::Application.routes.draw do
  resources :users, only: :new
end

After normalization above code gets following values

1
2
3
4
5
app: #<ActionDispatch::Routing::RouteSet::Dispatcher:0x007fdf55043e40 
           @defaults={:action=>"new", :controller=>"users"}, @glob_param=nil, @controller_class_names=#<ThreadSafe::Cache:0x007fdf55043e18 @backend={}, @default_proc=nil>>
conditions: {:path_info=>"/users/new(.:format)", :required_defaults=>[:action, :controller], :request_method=>["GET"]}
defaults: {:action=>"new", :controller=>"users"}
as: "new_user"

Because of only keyword only one routing statement was produced in this case.

Mapper

In Rails ActionDispatch::Routing::Mapper class is responsible for normalizing all routing statements.

1
2
3
4
5
6
7
8
9
10
11
12
module ActionDispatch
  module Routing
    class Mapper
      include Base
      include HttpHelpers
      include Redirection
      include Scoping
      include Concerns
      include Resources
    end
  end
end

Now let’s look at what these included modules do

Base

1
2
3
4
5
6
7
8
9
module Base
  def root (options = {})
  end

  def match
  end

  def mount(app, options = {})
  end

As you can see Base handles root, match and mount calls.

HttpHelpers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module HttpHelpers
  def get(*args, &block)
  end

  def post(*args, &block)
  end

  def patch(*args, &block)
  end

  def put(*args, &block)
  end

  def delete(*args, &block)
  end
end

HttpHelpers handles get, post, patch, put and delete .

Scoping

1
2
3
4
5
6
7
8
9
10
module Scoping
  def scope(*args)
  end

  def namespace(path, options = {})
  end

  def constraints(constraints = {})
  end
end

Resources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module Resources
  def resource(*resources, &block)
  end

  def resources(*resources, &block)
  end

  def collection
  end

  def member
  end

  def shallow
  end
end

Let’s put all the routes together

So now let’s look at all the routes definition together.

1
2
3
4
5
6
7
8
Rails4demo::Application.routes.draw do
  root 'users#index'
  get 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
  get '/logout' => 'sessions#destroy', :as => :logout
  get 'pictures/:id' => 'pictures#show', :constraints => { :id => /[A-Z]\d{5}/ }
  get "/stories" => redirect("/posts")
  resources :users
end

Above routes definition produces following information. I am going to show info path info.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{ :path_info=>"/":path_info=>"/photos/:id(.:format)" }

{ :path_info=>"/logout(.:format)" }

{ :path_info=>"/pictures/:id(.:format) }

{ :path_info=>"/stories(.:format)" }

{ :path_info=>"/users(.:format), :request_method=>["GET"]}

{:path_info=>"/users(.:format)", :request_method=>["POST"]}

{:path_info=>"/users/new(.:format)", :request_method=>["GET"]}

{:path_info=>"/users/:id/edit(.:format)", :request_method=>["GET"]}

{:path_info=>"/users/:id(.:format)", :controller], :request_method=>["GET"]}

{:path_info=>"/users/:id(.:format)", :request_method=>["PATCH"]}

{:path_info=>"/users/:id(.:format)", :request_method=>["PUT"]}

{:path_info=>"/users/:id(.:format)", :request_method=>["DELETE"]}

How to find the matching route definition

So now that we have normalized the routing definitions the task at hand is to find the right route definition for the given url along with request_method.

For example if the requested page is /pictures/A12345 then the matching routing definition should be get ‘pictures/:id’ => ‘pictures#show’, :constraints => { :id => /[A-Z]\d{5}/ } .

In order to accomplish that I would do something like this.

I would convert all path info into a regular experssion and I would push that regular expression in an array. So in this case I would have 12 regular expressions in the array and for the given url I would try to match one by one.

This strategy will work and this is how Rails worked all the way upto Rails 3.1 .

Aaron Patterson loves computer science

Aaron Patterson noticed that finding the best matching route definition for a given url is nothing else but pattern matching task. And computer science solved this problem much more elegantly and this happens to run faster also by building an AST and walking over it.

So he decided to make a mini language out of the route definitions . After all the route definitions , we write , follow certain rules.

And thus Journey was born.

In the next blog we will see how to write grammar rules for routing definitions , how to parse and then walk the ast to see the best match .

Life of Save in ActiveRecord

Following code was tested with edge rails (rails4) .

In a RubyonRails application we save records often. It is one of the most used methods in ActiveRecord. In the blog we are going to take a look at the lifecycle of save operation.

ActiveRecord::Base

A typical model looks like this.

1
2
class Article < ActiveRecord::Base
end

Now lets look at ActiveRecord::Base class in its entirety.

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
module ActiveRecord
  class Base
    extend ActiveModel::Naming

    extend ActiveSupport::Benchmarkable
    extend ActiveSupport::DescendantsTracker

    extend ConnectionHandling
    extend QueryCache::ClassMethods
    extend Querying
    extend Translation
    extend DynamicMatchers
    extend Explain

    include Persistence
    include ReadonlyAttributes
    include ModelSchema
    include Inheritance
    include Scoping
    include Sanitization
    include AttributeAssignment
    include ActiveModel::Conversion
    include Integration
    include Validations
    include CounterCache
    include Locking::Optimistic
    include Locking::Pessimistic
    include AttributeMethods
    include Callbacks
    include Timestamp
    include Associations
    include ActiveModel::SecurePassword
    include AutosaveAssociation
    include NestedAttributes
    include Aggregations
    include Transactions
    include Reflection
    include Serialization
    include Store
    include Core
  end

  ActiveSupport.run_load_hooks(:active_record, Base)
end

Base class extends and includes a lot of modules. Here we are going to look at the four modules that have method def save .

1
2
3
4
5
6
7
8
9
10
11
12
13
module ActiveRecord
  class Base
    ......................
    include Persistence
    .......................
    include Validations
    ........................
    include AttributeMethods
    ........................
    include Transactions
    ........................
  end
end

include Persistence

Module Persistence defines save method like this

1
2
3
4
5
def save(*)
  create_or_update
rescue ActiveRecord::RecordInvalid
  false
end

Now lets see method create_or_update .

1
2
3
4
5
def create_or_update
  raise ReadOnlyRecord if readonly?
  result = new_record? ? create_record : update_record
  result != false
end

So save method invokes create_or_update and create_or_udpate method either creates a record or updates a record. Dead simple.

include Validations

In module Validations the save method is defined as

1
2
3
def save(options={})
  perform_validations(options) ? super : false
end

In this case the save method simply invokes a call to perform_validations .

include AttributeMethods

Module AttributeMethods includes a bunch of modules like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module ActiveRecord
  module AttributeMethods
    extend ActiveSupport::Concern
    include ActiveModel::AttributeMethods

    included do
      include Read
      include Write
      include BeforeTypeCast
      include Query
      include PrimaryKey
      include TimeZoneConversion
      include Dirty
      include Serialization
    end

Here we want to look at Dirty module which has save method defined as following.

1
2
3
4
5
6
7
def save(*)
  if status = super
    @previously_changed = changes
    @changed_attributes.clear
  end
  status
end

Since this module is all about tracking if a record is dirty or not, the save method tracks the changed values.

include Transactions

In module Transactions the save method is defined as

1
2
3
4
5
def save(*) #:nodoc:
  rollback_active_record_state! do
    with_transaction_returning_status { super }
  end
end

The method rollback_active_record_state! is defined as

1
2
3
4
5
6
7
8
9
def rollback_active_record_state!
  remember_transaction_record_state
  yield
rescue Exception
  restore_transaction_record_state
  raise
ensure
  clear_transaction_record_state
end

And the method with_transaction_returning_status is defined as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def with_transaction_returning_status
  status = nil
  self.class.transaction do
    add_to_transaction
    begin
      status = yield
    rescue ActiveRecord::Rollback
      @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
      status = nil
    end

    raise ActiveRecord::Rollback unless status
  end
  status
end

Together methods rollback_active_record_state! and with_transaction_returning_status ensure that all the operations happening inside save is happening in a single transaction.

Why save method needs to be in a transaction .

A model can define a number of callbacks including after_save and before_save. All those callbacks are operated within a transaction. It means if an after_save callback operation raises an exception then the save operation is rolledback.

Not only that a number of associations like has_many and belongs_to use callbacks to handle association manipulation. In order to ensure the integrity of the operation the save operation is wrapped in a transaction .

reverse order of operation

In the Base class the modules are included in the following order.

1
2
3
4
5
6
7
8
9
10
11
12
13
module ActiveRecord
  class Base
    ......................
    include Persistence
    .......................
    include Validations
    ........................
    include AttributeMethods
    ........................
    include Transactions
    ........................
  end
end

All the four modules have save method. The way ruby works the last module to be included gets to act of the method first. So the order in which save method gets execute is Transactions, AttributeMethods, Validations and Persistence .

To get a visual feel, I added a puts inside each of the save methods. Here is the result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> User.new.save
1.9.1 :001 > User.new.save
entering save in transactions
   (0.1ms)  begin transaction
entering save in attribute_methods
entering save in validations
entering save in persistence
  SQL (47.3ms)  INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", Mon, 21 Jan 2013 14:56:52 UTC +00:00], ["updated_at", Mon, 21 Jan 2013 14:56:52 UTC +00:00]]
leaving save in persistence
leaving save in validations
leaving save in attribute_methods
   (17.6ms)  rollback transaction
leaving save in transactions
 => nil

As you can see the order of operations is

1
2
3
4
5
6
7
8
9
entering save in transactions
entering save in attribute_methods
entering save in validations
entering save in persistence

leaving save in persistence
leaving save in validations
leaving save in attribute_methods
leaving save in transactions

Handling Money in Ruby

Do not use float for calculation

float is not good for precise calculation.

1
2
irb(main):001:0> 200 * (7.0/100)
=> 14.000000000000002

7 % of 200 should be 14. But float is returning 14.000000000000002 .

In order to ensure that calculation is right make sure that all the actors participating in calculation is of calss BigDecimal . Here is how same operation can be performed using BigDecimal .

1
2
3
4
irb(main):003:0> result = BigDecimal.new(200) * ( BigDecimal.new(7)/BigDecimal.new(100))
=> #<BigDecimal:7fa5eefa1720,'0.14E2',9(36)>
irb(main):004:0> result.to_s
=> "14.0"

As we can see BigDecimal brings much more accurate result.

Converting money to cents

In order to charge the creditcard using Stripe we needed to have the amount to be charged in cents. One way to convert the value in cents would be

1
2
amount  = BigDecimal.new(200) * ( BigDecimal.new(7)/BigDecimal.new(100))
puts (amount * 100).to_i #=> 1400

Above method works but I like to delegate the functionality of making money out of a complex BigDecimal value to gem like money . In this project we are using activemerchant which depends on money gem . So we get money gem for free. You might have to add money gem to Gemfile if you want to use following technique.

money gem lets you get a money instance out of BigDecimal.

1
2
3
amount  = BigDecimal.new(200) * ( BigDecimal.new(7)/BigDecimal.new(100))
amount_in_money = amount.to_money
puts amount_in_money.cents #=> 1400

Stay in BigDecimal or money mode for calculation

If you are doing any sort of calculation then all participating elements must be either BigDecimal or Money instance. It is best if all the elements are of the same type.

Executing Commands in Ruby

Ruby allows many different ways to execute a command or a sub-process. In this article we are going to see some of them.

backtick

backtick returns the standard output of the operation.

1
2
output = `ls`
puts "output is #{output}"

Result of above code is

1
2
3
$ ruby main.rb
output is lab.rb
main.rb

Note that backtick operation forks the master process and the operation is executed in a new process. However this is a blocking operation. The main application waits until the result of backtick operation completes.

If there is an exception in the sub-process then that exception is given to the main process and the main process might terminate if exception is not handled.

In the following case I am executing xxxxx which is not a valid executable name.

1
2
outut = `xxxxxxx`
puts "output is #{output}"

Result of above code is given below. Notice that puts was never executed because the backtick operation raised exception.

1
2
3
$ ruby main.rb
main.rb:1:in ``': No such file or directory - xxxxxxx (Errno::ENOENT)
 from main.rb:1:in `<main>'

To check the status of the backtick operation you can execute $?.success?

1
2
3
output = `ls`
puts "output is #{output}"
puts $?.success?

Notice that the last line of the result contains true because the backtick operation was a success.

1
2
3
4
$ ruby main.rb
output is lab.rb
main.rb
true

backtick returns STDOUT. backtick does not capture STDERR . If you want to learn about STDERR then checkout this excellent article .

You can redirect STDERR to STDOUT if you want to capture STDERR using backtick.

1
output = `grep hosts /private/etc/* 2>&1`

%x does the same thing as backtick. It allows you to have different delimeter.

1
2
output = %x[ ls ]
output = %x{ ls }

backtick runs the command via shell. So shell features like string interpolation and wild card can be used. Here is an example.

1
2
3
4
5
$ irb
irb(main):001:0> dir = '/etc'
=> "/etc"
irb(main):002:0> %x<ls -al #{dir}>.chomp
=> "lrwxr-xr-x@ 1 root  wheel  11 Jan  5 21:10 /etc -> private/etc"

system

system behaves a bit like backtick operation. However there are some differences.

First let’s look at similarities.

Just like backtick, system is a blocking operation. You can get the result of the operation using $?.success? . system operations are also executed in a subshell.

Now the differneces between backtick and system .

system eats up all the exceptions. So the main operation never needs to worry about capturing an exception raised from the child process.

1
2
output = system('xxxxxxx')
puts "output is #{output}"

Result of the above operation is given below. Notice that even when exception is raised the main program completes and the output is printed. The value of output is nil because the child process raised an exception.

1
2
$ ruby main.rb
output is

Another difference is that system returns true if the command was successfully performed ( exit status zero ) . It returns false for non zero exit status. Returns nil if command execution fails.

exec

exec replaces the current process by running the external command. Let’s see an example.

Here I am in irb and I am going to execute exec('ls').

1
2
3
4
5
6
$ irb
e1.9.3-p194 :001 > exec('ls')
lab.rb  main.rb

nsingh ~/dev/lab 1.9.3
$

I see the result but since the irb process was replaced by the exec process I am no longer in irb .

Behind the scene both system and backtick operations use fork to fork the current process and then they execute the given operation using exec .

Since exec replaces the current process it does not return anything if the operation is a success. If the operation fails then SystemCallError is raised.

sh

sh actually calls system under the hood. However it is worth a mention here. This method is added by FileUtils in rake. It allows an easy way to check the exit status of the command.

1
2
3
4
5
6
require 'rake'
sh %w(xxxxx) do |ok, res|
   if !ok
     abort 'the operation failed'
   end
end

popen3

If you are going to capture stdout and stderr then you should use popen3 since this method allows you to interact with stdin, stdout and stderr .

I want to execute git push heroku master programmatically and I want to capture the output. Here is my code.

1
2
3
4
5
6
require 'open3'
cmd = 'git push heroku master'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  puts "stdout is:" + stdout.read
  puts "stderr is:" + stderr.read
end

And here is the output. It has been truncated since rest of output is not relevant to this discussion.

1
2
3
4
5
stdout is:
stderr is:
-----> Heroku receiving push
-----> Ruby/Rails app detected
-----> Installing dependencies using Bundler version 1.2.1

The important thing to note here is that when I execute the program ruby lab.rb I do not see any output on my terminal for first 10 seconds. Then I see the whole output as one single dump.

The other thing to note is that heroku is writing all this output to stderr and not to stdout .

Above solution works but it has one major drawback. The push to heroku might take 10 to 20 seconds and for this period we do not get any feedback on the terminal. In reality when we execute git push heroku master we start seeing result on our terminal one by one as heroku is processing things.

So we should capture the output from heroku as it is being streamed rather than dumping the whole output as one single chunk of string at the end of processing.

Here is the modified code.

1
2
3
4
5
6
7
require 'open3'
cmd = 'git push heroku master'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  while line = stderr.gets
    puts line
  end
end

Now when I execute above command using ruby lab.rb I get the output on my terminal incrementally as if I had typed git push herokou master .

Here is another example of capturing streaming output.

1
2
3
4
5
6
7
require 'open3'
cmd = 'ping www.google.com'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  while line = stdout.gets
    puts line
  end
end

In the above case you will get the output of ping on your terminal as if you had typed ping www.google.com on your terminal .

Last example is about how to check if the command succeeded or not.

1
2
3
4
5
6
7
8
require 'open3'
cmd = 'ping www.google.com'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  exit_status = wait_thr.value
  unless exit_status.success?
    abort "FAILED !!! #{cmd}"
  end
end

popen2e

popen2e is similar to popen3 but merges the standard output and standard error .

1
2
3
4
5
6
7
8
9
10
11
12
require 'open3'
cmd = 'ping www.google.com'
Open3.popen2e(cmd) do |stdin, stdout_err, wait_thr|
  while line = stdout_err.gets
    puts line
  end

  exit_status = wait_thr.value
  unless exit_status.success?
    abort "FAILED !!! #{cmd}"
  end
end

In all other areas this method works similar to popen3 .

Process.spawn

Kernel.spawn executes the given command in a subshell. It returns immediately with the process id.

1
2
irb(main)> pid = Process.spawn("ls -al")
=> 81001

Redirect to Www for Heroku With SSL

If you are using heroku and if you have enabled https then site must be redirected to use www . It means all Rails applications should ensure that “no-www” urls are redirected to “www”.

In Rails3 it is pretty easy to do. Here is how it can be done.

1
2
3
4
5
6
7
8
Bigbinary::Application.routes.draw do

  constraints(:host => /^bigbinary.com/) do
    root :to => redirect("http://www.bigbinary.com")
    match '/*path', :to => redirect {|params| "http://www.bigbinary.com/#{params[:path]}"}
  end

end

Solr, Sunspot, Websolr and Delayed Job

Solr is an open source search platform from Apache. It has a very powerful full-text search capability among other things.

Solr is written in Java. And it runs as a standalone search server within a servlet container like Tomcat. When you are working on a Ruby on Rails application you do not want to maintain Tomcat server. This is where websolr comes in picture. Websolr manages the index and the Rails application interacts with index using a gem called sunspot-rails .

Getting started

1
2
# Gemfile
gem 'sunspot_rails', '= 1.3.3' # search feature

Here I am interested in searching products.

1
2
3
4
5
6
class Product < ActiveRecord::Base
  searchable do
    text :name, boost: 1.5
    text :description
  end
end

Using sunspot gem

1
rails g sunspot_rails:install

Above command creates config/sunspot.yml file. By default this file looks like following.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
production:
  solr:
    hostname: localhost
    port: 8983
    log_level: WARNING

development:
  solr:
    hostname: localhost
    port: 8982
    log_level: INFO

test:
  solr:
    hostname: localhost
    port: 8981
    log_level: WARNING

The way sunspot works is that after every single web request it updates solr about the changes that took place in the request. This is not desirable. To turn that off add auto_commit_after_request option to false in the config/sunsunspot.yml file.

I would also change the log_level for development to DEBUG . The revised config/sunspot.yml file would look like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
production:
  solr:
    hostname: localhost
    port: 8983
    log_level: WARNING
    auto_commit_after_request: false

development:
  solr:
    hostname: localhost
    port: 8980
    log_level: DEBUG
    auto_commit_after_request: false

test:
  solr:
    hostname: localhost
    port: 8981
    log_level: DEBUG
    auto_commit_after_request: false

Taking care of callbacks

In the above case anytime I create, update or destroy a product then as part of after_save callback solr commit commands are issued. Since after_save callbacks are part of ActiveRecord transaction, this slows up the create, update and destroy operation. I like all these operations to happen in background.

Here is how I handled it

1
2
3
4
5
6
7
8
9
class Product < ActiveRecord::Base
  searchable do
    text :name, boost: 1.5
    text :description
  end
  handle_asynchronously :solr_index, queue: 'indexing', priority: 50
  handle_asynchronously :solr_index!, queue: 'indexing', priority: 50
  handle_asynchronously :remove_from_index, queue: 'indexing', priority: 50
end

In the above case I used Delayed Job but you can use any background job processing tool.

In case of Delayed Job the higher the priority value the less is the priority. By bumping the priority value to 50, I’m making sure that emails and other background jobs are processed before solr work is taken up.

Problem with remove_from_index

In the above case the call to remove_from_index has been deferred to Delayed Job. However the record has already been destroyed. So when Delayed Job takes up the work it first tries to retrieve the record. However the record is missing and the background job fails.

Here is how we solved this problem.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Product < ActiveRecord::Base
  searchable do
    text :name, boost: 1.5
    text :description
  end
  handle_asynchronously :solr_index, queue: 'indexing', priority: 50
  handle_asynchronously :solr_index!, queue: 'indexing', priority: 50

  def remove_from_index_with_delayed
    Delayed::Job.enqueue RemoveIndexJob.new(record_class: self.class.to_s, attributes: self.attributes), queue: 'indexing', priority: 50
  end
  alias_method_chain :remove_from_index, :delayed
end

Add another worker named remove_index.rb .

1
2
3
4
5
6
7
8
9
class RemoveIndexJob < Struct.new(:options)
  def perform
    return if options.nil?
    options.symbolize_keys!
    record = options[:record_class].constantize.new options[:attributes].except("id")
    record.id = options[:attributes]["id"]
    record.remove_from_index_without_delayed
  end
end

Connecting to websolr

From the websolr documentation it was not clear that the sunspot gem first looks for an environment variable called WEBSOLR_URL and if that envrionment variable has a value then sunspot assumes that the solr index is at that url. If no value is found then it assumes that it is dealing with local solr instance.

So if you are using websolr then make sure that your application has environment variable WEBSOLR_URL properly configured in staging and in production environment.