Codebeerstartups

Every Dog has one Blog

Scaling Ruby on Rails by Caching Your Database Queries

Its pretty good to use active-record in ruby on rails, relationships, find_by methods, where queries everything makes your life much more simpler as a developer but sooner or later your application becomes slow. At that time you need to work on optimizations. There are lot of work around for optimizations and one of them in caching the queries. The company I work for is StudyPad and we have a product ie splashmath and that has over 9 millions users using it across multiple platforms. The amount of traffic that we get is pretty huge and caching one of the thing that helped us to solve out scaling problems. Here is quick idea to cache your queries in memcache.

Basic requirements

There are few requirements for this. First is the dalli gem and memcache installed on your machine.

Basic structure of the application.

We have lot of static/seed data which change very less and the queries on those things are pretty high. So first thing to handle that static data.

1
2
3
4
5
6
7
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skills < ActiveRecord::Base
  belongs_to :grade
end

So in order to get skills for a grade, its pretty simple

1
2
grade = Grade.first
skills = grade.skills

Now for that matter every time we are fetching skills for a grade its gonna hit the database and get the results. So its time to get this query and its results cached.

Assuming you have configured dalli gem easily and you have memcached up and running. Here is a simple method to cache the results

1
2
3
4
5
def cached_skills
  Rails.cache.fetch([self.class.name, id, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

So instead of fetching skills like

1
grade.skills

we are gonna get it like

1
grade.cached_skills

Spliting Seed Files Into Multiple File in Ruby on Rails

Last month I joined a new job after a break of almost 4 months. You can checkout the product and there we had a small problem that our seed file was growing very fast. So we did a small thing to maintain our seed file. Here is a small tip if you are having a massive seed file and its pretty easy to implement.

We can store all our seeds inside the folder db/seeds and inside the db/seeds.rb we write the following:

1
Dir[File.join(Rails.root, 'db', 'seeds', '*.rb')].sort.each { |seed| load seed }

We can sort the files alphabetically before loading them.

What if seed file ordering is important?

In that case we can use the trick migrations are using. Timestamp in front of file names to make sure there ordering is same. So instead of using timestamp you can add a serial number in front of file name like 01_grades.rb, 02_topics.rb, 03_skills.rb.

I hope this will help someone in case you are facing same issues.

Irreversible Migrations in Ruby on Rails

In Rails 2 in migrations there were only 2 methods: up and down. They are called on running migrations up and down respectively with rake tasks rake db:migrate and rake db:rollback.

Rails 3 produced us great method change which allowed us to write there code which creates a table and drops the table automatically on rolling migration back. But unfortunately there was a problem – you didn’t have opportunity to say “don’t run this peace of code on down but run this only on up”. So the solution was just to use old syntax. Since Rails 4 has released there is a feature to fix this situation.

In Rails 2

1
2
3
4
5
6
7
8
def up
  add_column :users, :active, :boolean
  User.update_all(active: true)
end

def down
  remove_column :users, :active
end

In Rails 3

1
2
3
def change
  add_column :users, :active, :boolean
end

In Rails 4

Rails 4 provides us one more useful way to write what we need in one place:

1
2
3
4
5
6
def change
  add_column :users, :active, :boolean
  reversible do |direction|
    direction.up { User.update_all(active: true) }
  end
end

Checkout the official documentation

Social Login Integration With All the Popular Social Networks in Ruby on Rails

I have seen lot of people writting pretty messy code to integrate social logins in rails or get confused to integrate multiple social logins in rails. In order to help them I have written a small application having integrations with Twitter + Facebook + Linkedin + Google + Github.

Here is the demo and here is the source code

I am using devise in this application. Few things to note in this:

  1. There are two models in this application a user model and an authorizations model and a user has_many authorizations.
  2. User can link to multiple social account from edit profile
  3. Once social accounts are linked, user can login from any connected social network.
  4. If user tries to create an account with a different social network while in loggedout state. System maps it to the same account if that email address exists in the database else it will create a new account.
  5. Twitter don’t provide email address of the user, nothing I can do about it.
  6. There are lot of other commented out gems in the Gemfile. Those are the gems that I use a lot in my various projects. I will strongly suggest you to checkout those gems.

How to replace and find keys to make this application work..

In the config directory there is a file ie social_keys.yml. You need to specify all the social keys there. Make sure have restarted the application after making any change in that file.

Here are the urls from where you can create an app and get the keys

Facebook:

URL: https://developers.facebook.com/apps

Settings:

Facebook Settings

Twitter:

URL: https://dev.twitter.com/apps

Settings: Enter call back url as : http://127.0.0.1:3000/twitter under settings tab once you create an application

Google:

URL: https://code.google.com/apis/console/b/0/?pli=1

Settings: Go to API Access and enter redirect url as http://localhost:3000/users/auth/google_oauth2/callback

Github:

URL: https://github.com/settings/applications/new

Settings:Application callback url should be http://localhost:3000/users/auth/github

Linkedin:

url: https://www.linkedin.com/secure/developer

No settings required by default.

Parallel Gem Installing Using Bundler

My bundler is running

No more waiting time. Bundler supports faster gem install as it adds support for parallel installation. You can pass in —jobs=SIZE (or -jSIZE) as a parameter to bundle install

How can I use this now?

Simple! Just install a prerelease version of Bundler, and you should be good to go:

1
gem install bundler --pre

So now the problem that bundler is taking too much time is long gone. Happy coding :)

Use Belongs_to With Presence Validator

Assume we have two models: User and Image. User has one image and image belongs to user. The code below:

1
2
3
4
5
6
7
class User < ActiveRecord::Base
  has_one :image
end

class Image < ActiveRecord::Base
  belongs_to :user
end

Now we want to add validation for image to check if user is there or not.

1
2
3
4
class Image < ActiveRecord::Base
  belongs_to :user
  validates :user, :presence => true
end

So by adding just one line whenever you are trying to save image object, it will fire a query with respect to the user_id to check weather that user exists or not. In case user doesn’t exits “image.save” with return an error.

Happy Hacking :)

Better Way to Parse User Info From Omniauth Hash

Lets assume we have to parse this hash. There can be case we are not getting the keys as a response. So this post is about handling those cases.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
auth = {
  :provider => 'facebook',
  :uid => '1234567',
  :info => {
    :nickname => 'jbloggs',
    :email => 'joe@bloggs.com',
    :name => 'Joe Bloggs',
    :first_name => 'Joe',
    :last_name => 'Bloggs',
    :image => 'http://graph.facebook.com/1234567/picture?type=square',
    :urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
    :location => 'Palo Alto, California',
    :verified => true
  },
  :credentials => {
    :token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
    :expires_at => 1321747205, # when the access token expires (it always will)
    :expires => true # this will always be true
  }
}

General Scenario

So in the best case scenario we will be having all the keys. So the code like this will work.

1
2
3
 user_email = auth[:info][:email]
  user_nickname = auth[:info][:nickname]
  user_location = auth[:info][:location]

Exceptional Scenario

Now think about this case when we don’t have location key in the response. So we got an hash like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 auth = {
    :provider => 'facebook',
    :uid => '1234567',
    :info => {
      :nickname => 'jbloggs',
      :email => 'joe@bloggs.com',
      :name => 'Joe Bloggs',
      :first_name => 'Joe',
      :last_name => 'Bloggs',
      :image => 'http://graph.facebook.com/1234567/picture?type=square',
      :urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
      :verified => true
    },
    :credentials => {
      :token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
      :expires_at => 1321747205, # when the access token expires (it always will)
      :expires => true # this will always be true
    }
  }

Now in this case

1
2
3
 user_email = auth[:info][:email]   # will work
  user_nickname = auth[:info][:nickname] # will work
  user_location = auth[:info][:location] # will return nil here...

As we all know there is no surety of the hash from the provider so we cant trust it and this ‘nil’ value can later on create issues as we start using it assuming everything is coming perfectly fine from provider.

So if you want to generate an error in this case when key is not found a simple way can be using statement modifiers ie

Long Integer Literals in Ruby

I am here to share a small tip that I just come to know about ruby. A simple question. How many zeros are there integer written below?

1
    100000000000

Difficult to count right? In normal day to day practice we generally use comma to separate the digits, for example in US standard we use comma every 3 digits. So number written above can be re-written as:

1
  100,000,000,000

Now same thing can be done with long integers in ruby using an underscore, something like this:

1
100_000_000_000

which is actually same as writing 100000000000, but using underscore makes things more easy to read;)

I hope you will find this technique somewhere useful ;)

Released the Code Running Codebeerstartups

I got couple of requests to release the source code running this blog as few people liked the theme :) Glad to hear that. I am sorry for being so late to release the code.If you want to download the code feel free to fork the repo from github. Design credits goes to my friend Taroon Tyagi.

Stack:

  • Heroku as a server
  • Octopress as bloggin platform
  • Couple of gems to make things working.

I hope people will find it useful. In case you face any problem to the this code up and running. Feel free to contact me.