Category Archives: Ruby On Rails

This guide contains the general guidelines to follow for making the upgrade easier.

 Introduction

Before attempting to upgrade an existing application, you should be sure you have a good reason to upgrade.The best way to be sure that your application still works after upgrading is to have good test coverage before you start the process.

If you don’t have automated tests, you’ll need to spend time manually exercising all the parts that have changed. Do yourself a favour and make sure your test coverage is good before you start an upgrade.

Rails generally stays close to the latest released Ruby version when it’s released:

  • Rails 3 and above require Ruby 1.8.7 or higher. Support for all of the previous Ruby versions has been dropped officially. You should upgrade as early as possible.
  • Rails 3.2.x is the last branch to support Ruby 1.8.7.
  • Rails 4 prefers Ruby 2.0 and requires 1.9.3 or newer.

The official Rails guide for upgrading from Rails 3.2 to 4.0 is very thorough. If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting one to Rails 4.0. However, if you’re using any non-standard gems, you’re mostly on your own. Some gems stopped being maintained before Rails 4 was released.

There is a gem “rails4_upgrade” which you can add to your Rails 3 project. It includes a Rake task that will help you with the upgrade. After adding the gem to your Gemfile run “bundle install”.

Step1. In your project root directory run the command “rake rails4:check”. Now instead of going through your currently bundled gems or Gemfile.lock manually, you get a report of what gems you need to upgrade.

Sample Output: 

Step2. Update the Gemfile with the newer version of Rails.

Step3. After resolving all the gem dependencies based on Step1 run “bundle install”. To find outdated gems there’s also “bundle outdated”, which you can run on any project.

Step4. Lastly, there’s the “rails:update” task, which you can use as a guideline as explained very clearly in this post to get rid of unnecessary code or monkey-patches, especially if your Rails 3 app was previously running on Rails 2

Step5. After everything done run the test cases and fix them as well.

Thank you for reading this article!

Overview

We will be setting up a Ruby on Rails development environment on Ubuntu 14.04.

The reason we’re going to be using Ubuntu is because the majority of code you write will run on a Linux server. Ubuntu is one of the easiest Linux distributions to use with lots of documentation so it’s a great one to start with.

Note: Before going to start installation please make sure that you have root privileges or not.

Installing Ruby Dependencies:The first step is to install some dependencies for Ruby.

   sudo apt-get update
   sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev     libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev
  

Next we’re going to be installing Ruby using rvm. You can install from source as well.

Install Ruby Using RVM:

    sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
    gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
    curl -sSL https://get.rvm.io | bash -s stable
    source ~/.rvm/scripts/rvm
    rvm install 2.2.3
    rvm use 2.2.3 --default
    ruby -v
   

Installing Rails Dependencies:Since Rails ships with many dependencies, we’re going to need to install a Javascript runtime like NodeJS. This lets you use Coffeescript and the Asset Pipeline in Rails which combines and minifies your javascript to provide a faster production environment.

    curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
    sudo apt-get install -y nodejs
   

We can use various Rails versions with each Ruby by creating gemsets and then installing Rails within those using the normal gem commands:

    rvm gemset create starterapp
    rvm 2.2.3@starterapp
    gem install rails -v 4.2.4
   
  • “rvm gemset create starterapp” command is to create a gemset name called starterapp.
  • “rvm 2.2.3@starterapp” command is to specify Ruby version and our new gemset.
  • “gem install rails -v 4.2.4” command is to install specific Rails version.

Now that you’ve installed Rails, you can run the rails -v command to make sure you have everything installed correctly:

     rails -v
     # Rails 4.2.4
   

The last step is to install Bundler.

     gem install bundler
    

Configuring Git:(If you already installed git please ignore this step)
We’ll be using Git for our version control system so we’re going to set it up to match our Github account. If you don’t already have a Github account, make sure to register. It will come in handy for the future.

Replace my name and email address in the following steps with the ones you used for your Github account.

    git config --global color.ui true
    git config --global user.name "YOUR NAME"
    git config --global user.email "YOUR@EMAIL.com"
    ssh-keygen -t rsa -b 4096 -C "YOUR@EMAIL.com"
  

The next step is to take the newly generated SSH key and add it to your Github account. You want to copy and paste the output of the following command and paste it here

    cat ~/.ssh/id_rsa.pub
  

Once you’ve done this, you can check and see if it worked:

    ssh -T git@github.com
  

You should get a message like this:

    Hi excid3! You've successfully authenticated, but GitHub does not provide shell access.
  

Setting Up MySQL:

Rails ships with sqlite3 as the default database. Chances are you won’t want to use it because it’s stored as a simple file on disk. You’ll probably want something more robust like MySQL or PostgreSQL.If you’re coming from PHP, you may already be familiar with MySQL.

You can install MySQL server and client from the packages in the Ubuntu repository. As part of the installation process, you’ll set the password for the root user. This information will go into your Rails app’s database.yml file in the future.

    sudo apt-get install mysql-server mysql-client libmysqlclient-dev
  

Installing the libmysqlclient-dev gives you the necessary files to compile the mysql2 gem which is what Rails will use to connect to MySQL when you setup your Rails app.

Setting Up PostgreSQL:For PostgreSQL, we’re going to add a new repository to easily install a recent version of Postgres.

    sudo sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main' > /etc/apt/sources.list.d/pgdg.list"
    wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add -
    sudo apt-get update
    sudo apt-get install postgresql-common
    sudo apt-get install postgresql-9.5 libpq-dev
  

The postgres installation doesn’t setup a user for you, so you’ll need to follow these steps to create a user with permission to create databases. Feel free to replace vijay with your username.

    sudo -u postgres createuser vijay -s

    # If you would like to set a password for the user, you can do the following
    sudo -u postgres psql
    postgres=# \password vijay
  

Final Steps: Let’s create your first Rails application:

    #### If you want to use SQLite (not recommended)
    rails new myapp

    #### If you want to use MySQL
    rails new myapp -d mysql

    #### If you want to use Postgres
    # Note that this will expect a postgres user with the same username
    # as your app, you may need to edit config/database.yml to match the
    # user you created earlier
    rails new myapp -d postgresql

    # Move into the application directory
    cd myapp

    # If you setup MySQL or Postgres with a username/password, modify the
    # config/database.yml file to contain the username/password that you specified

    # Create the database
    rake db:create

    rails server
  

Note: Sentence start’s with ‘#’ are comments not commands.

You can now visit http://localhost:3000 to view your new website. Now that you’ve got your machine setup, it’s time to start building some Rails applications.

If you received an error that said Access denied for user ‘root’@’localhost’ (using password: NO) then you need to update your config/database.yml file to match the database username and password.

Reference: gorails

Thanks for reading this Article. If you have any questions, feel free to post your comments and we’ll get back to you.

User registration and social logins are extremely vital for any web application. It is the first step towards building any app, as the most basic functionality on any app would be a ‘user registration’ or logging in using ‘social accounts’. But, building an app with even these features takes a lot of time. I’m afraid, at least a week. So, we found out a solution for this. Our hosted API service lets you build an app with creating and managing user accounts along with a social login registration.

In this blog post, we’ll walk you through a tutorial where you can learn how to build a Ruby on Rails app with our Rest API with basic functionalities like login/registration, edit profile and more. And the best part? It takes just 15 minutes only.

You can click here to know more about our API.

To interact with the NBOS API Server, I have created a Ruby gem called “wavelabs_client_api“. The source code and for an easy reference, the documentation of this gem are available here. This gem is published in rubygems.org under the MIT license.

Prerequisites
1.Install Ruby Version Manager(RVM).
2.Install the Ruby 2.2.3 & Rails 4.2.4 using RVM.

Detailed installation instructions are available here

Note: No database is required because our API server has got it covered.

Now, let us create the web application. Open a console and follow the instructions:.

 $>git clone https://github.com/nbostech/wavelabs-rails-client-api.git
 $>cd wavelabs-rails-client-api
 $>git checkout -b release-1 v0.1
 $>bundle install
 $>rails server

Open the browser and access your application.The home page would look like this.

starter_app_home_page

Now you can use the following functionalities using the above application:
1. Signup
2. Login and Logout
3. Edit Profile Information
4. Change Password
5. Upload Profile Picture
6. Login with Facebook
7. Login with Github
8. Login with LinkedIn
9. Login with Google plus
10. Login with Instagram

The live application is available at Heroku Server

And there, you are all set to go. You haven’t just built a Ruby on Rails with basic login and registration functionality within 15 minutes, you are also all set to launch it into the market.

If you have any questions, feel free to post your comments and we’ll get back to you.

 

Introduction
In my previous article I have explained how to create our own ruby gem using bundler. Writing own gem is a good idea but if you add test cases for your gem it will be useful for other developers to understand what your gem will do. In this article I am going to explain how to write test cases for our gem using Rspec & creating configuration file using generators.

Test Our Own Gem
Install all the dependencies listed in previous article. Open the wavelabs_client_api.gemspec file & add the following line:

    spec.add_development_dependency "rspec"
  

‘rspec’ is a testing framework gem. After adding above line save the file and run ‘bundle install’ from our gem root directory. It will install the ‘rspec’ gem. Once you install the gem initialize the spec/ directory (where specs will reside) with:

    $rails generate rspec:install
  

This adds the following files which are used for configuration:

  • .rspec
  • spec/spec_helper.rb
  • spec/rails_helper.rb

Now we are ready to write test cases for our gem. In spec_helper.rb file ‘require’ all the dependencies to test our gem.

    require 'wavelabs_client_api' 
    require 'byebug'

    require 'support/user_sign_up.rb'
    # and any other gems you need
   

And also you need to add if you have any ENV variables needed for your gem for testing. After configuration you need to create your spec files under /spec directory. You can create your own namespace depending on your choice. In each of your spec file needs to ‘require’ spec_helper.rb file. ANd follow the naming rspec convention for spec files. Following is my spec directory structure:

owngem_test1

Example Spec file:

   #File /spec/auth_api_spec.rb
    require 'spec_helper'

describe WavelabsClientApi::Client::Api::Core::AuthApi do

  let (:auth_api_obj) { WavelabsClientApi::Client::Api::Core::AuthApi.new}
  let (:auth_api) { WavelabsClientApi::Client::Api::Core::AuthApi}

  it "#check_connection?" do
    expect(auth_api.check_connection?).to be true
  end 

  it "#Check Constants of Auth API URIS" do
    expect(auth_api::AUTH_TOKEN_URI).to eq '/oauth/token'
    expect(auth_api::TOKEN_VALIDATION_URI).to eq '/api/oauth/v0/tokens'
    expect(auth_api::LOGIN_URI).to eq '/api/identity/v0/auth/login'
    expect(auth_api::LOGOUT_URI).to eq '/api/identity/v0/auth/logout'
    expect(auth_api::CHANGE_PASSWORD_URI).to eq '/api/identity/v0/auth/changePassword'
    expect(auth_api::FORGOT_PASSWORD_URI).to eq '/api/identity/v0/auth/forgotPassword'
  end

  it "#get_auth_token with client details" do
    res = auth_api_obj.get_auth_token("client_credentials", "oauth.client.r")
    expect(res[:status]).to eq 200
  end
  ....... 
  

You can find the original specs for wavelabs_api_client here. After adding all test cases use the ‘rspec’ command to run your specs:

    bundle exec rspec
   

By default the above will run all _spec.rb files in the spec directory. For more details about this see the RSpec spec file docs.

To run only a subset of these specs use the following command:

# Run only model specs
bundle exec rspec spec/models

# Run only specs for AccountsController
bundle exec rspec spec/controllers/accounts_controller_spec.rb

Specs can also be run via rake spec, though this command may be slower to start than the rspec command.

Creating generator
It’s time to add a generator for our own gem. wavelabs_client_api gem needs API server details to communicate. Using generator we can create migrations, views, controllers & configuration files ..etc.

If you’ve written a gem at some point you might want to have configuration options for those people using the gem. Luckily, this is relatively easy to do with a configure block.

The code inside your lib/[gemname].rb should look something like this:

    module WavelabsClientApi

  class << self
    attr_accessor :configuration
  end

  def self.configure
    self.configuration ||= Configuration.new
    yield(configuration)
  end

  class Configuration
    attr_accessor :api_host_url, :client_key, :client_secret
  end

end
  

The gist is here all we do is add a configure method that allows us to store whatever options we want inside of the of the Configuration class. In this case, we only have three variables to configure: api_host_url, client_key, client_secret. However, if we were to add more, we simple just add an attr_accessor for each of the configuration options.

Now from anywhere in our gem we can look at WavelabsClientApi.configuration.api_host_url if we want to see what that value is set to.

Additionally, this allows us to do something like the following in our config/initializers/wavelabs_client_api.rb to configure our gem before we use it:

   WavelabsClientApi.configure do |config|
     config.api_host_url = 'API_HOST_URL'
     config.client_key = 'API_CLIENT_KEY'
     config.client_secret = 'API_CLIENT_SECRET'
   end
  

Well, that’s nice, but what if we want to generate the config/initializers/wavelabs_client_api.rb for those that use our gem. Luckily, Rails provides us with generators.

Inside our lib folder we should create a generators folder and inside of that two new folders: [gem_name] and templates.

Inside the templates folder we should put our default initializer file, such that lib/generators/templates/wavelabs_client_api_initializer.rb looks something like the following:

   WavelabsClientApi.configure do |config|
     config.api_host_url = 'API_HOST_URL'
     config.client_key = 'API_CLIENT_KEY'
     config.client_secret = 'API_CLIENT_SECRET'
   end
  

Note that it’s extremely helpful to those using your gem to have comments about what certain options do right inside the generated initializer file.

Now we can write or generator to copy over the file when someone runs:

   rails g [gemname]:install
 

We create a lib/generators/wavelabs_client_api/install_generator.rb file to do so and it should look something like:

   module WavelabsClientApi
  module Generators
    class InstallGenerator < Rails::Generators::Base
      source_root File.expand_path("../../templates", __FILE__)
      desc "Creates wavelabs client api initializer for your application"

      def copy_initializer
        template "wavelabs_client_api_initializer.rb", "config/initializers/wavelabs_client_api.rb"

        puts "Install complete! Please make sure to set following ENV variables!"
        puts "ENV['API_HOST_URL'] = 'site url' "
        puts "ENV['CLIENT_ID'] = 'XXXXXX'"
        puts "ENV['CLIENT_SECRET'] = 'XXXXXXXXX' "
      end
    end
  end
end
 

Every single public method inside of this class will be executed as part of the generator process. The magic line that you really care about is this one here:

   template "wavelabs_client_api_initializer.rb", "config/initializers/wavelabs_client_api.rb"
  

This is what copies the contents of our initializer template over to the application’s initializers.

Conclusion

In this article I have covered how to write test cases for our own ruby gem using rpsec & how to create generator for the gem. Thanks for reading this article!

Introduction
In this article I am going to explain how to create our own ruby gem using bundler. Bundler is a tool for managing Rubygems dependencies in Ruby libraries. But remember, Bundler isn’t just for Rails!

Did you know that you can use Bundler for not only gem dependency management but also for writing our own gems? It’s really easy to do this and Bundler provides a couple of things to help you along this path.

Why we need a gem?
Why should we create a gem? Can’t we just throw in some code into our other library and use that instead? Sure, we can do that. But then what if we want to use the code elsewhere, or we want to share it? This is why a gem is perfect. We can code our library and gem separately from each other and just have the library require the gem. If we want to use the gem in another library, then it’s just a tiny modification rather than a whole slew of copying.

Also: Sharing is caring.

Getting Started
First we need to install ruby(< 1.8.7) language, rubygems(< 1.3.6) & bundler gems.This guide was made using version 1.10.6 of bundler. We can follow along with other versions, but we might not get the exact same output. To check which version of bundler we currently have, lets run the following command:

   $bundler -v
  

If necessary, we can update to the newest version of Bundler by running ‘gem update bundler’.

To begin to create a gem using Bundler, use the bundle gem command like this:

   $bundler gem wavelabs_client_api
 

We call our gem wavelabs_client_api because this gem is going to communicate with wavelabs API Server, such as login, signup, Social Login..etc. Stay tuned. we need to follow the gem naming conventions. For more details visit this link

This command creates a scaffold directory for our new gem and, if we have Git installed, initializes a Git repository in this directory so we can start committing right away. If this is your first time running the bundle gem command, you will be asked whether you want to include a the CODE_OF_CONDUCT.md and LICENSE.txt files with your project. The files generated are:

  • Gemfile: Used to manage gem dependencies for our library’s development. This file contains a gemspec line meaning that Bundler will include dependencies specified in foodie.gemspec too. It’s best practice to specify all the gems that our library depends on in the gemspec.
  • Rakefile: Requires Bundler and adds the build, install and release Rake tasks by way of calling Bundler::GemHelper.install_tasks. The build task will build the current version of the gem and store it under the pkg folder, the install task will build and install the gem to our system (just like it would do if we gem install’d it) and release will push the gem to Rubygems for consumption by the public.
  • CODE_OF_CONDUCT.md: Provides a code of conduct that you expect all contributors to your gem to follow. Will only be included if you chose to have it included.
  • LICENSE.txt: Includes the MIT license. Will only be included if you chose to have it included.
  • .gitignore: (only if we have Git). This ignores anything in the pkg directory (generally files put there by rake build), anything with a .gem extension and the .bundle directory.
  • wavelabs_client_api.gemspec: The Gem Specification file. This is where we provide information for Rubygems’ consumption such as the name, description and homepage of our gem. This is also where we specify the dependencies our gem needs to run.
  • lib/wavelabs_client_api.rb: The main file to define our gem’s code. This is the file that will be required by Bundler (or any similarly smart system) when our gem is loaded. This file defines a module which we can use as a namespace for all our gem’s code. It’s best practice to put our code in…
  • lib/wavelabs_client_api: here. This folder should contain all the code (classes, etc.) for our gem. The lib/wavelabs_client_api.rb file is there for setting up our gem’s environment, whilst all the parts of it go in this folder. If our gem has multiple uses, separating this out so that people can require one class/file at a time can be really helpful.
  • lib/wavelabs_client_api/version.rb: Defines a WavelabsClientApi module and in it, a VERSION constant. This file is loaded by the wavelabs_client_api.gemspec to specify a version for the gem specification. When we release a new version of the gem we will increment a part of this version number to indicate to Rubygems that we’re releasing a new version.

There’s our base and our layout, now get developing! Now our gem directory structure as follows:

own_gem1

Open the .gemspec file and add the necessary details like author, license & files to be bundle..etc. I have modified the wavelabs_client_api.gemspec file as follows:

lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'wavelabs_client_api/version'

Gem::Specification.new do |spec|
  spec.name          = 'wavelabs_client_api'
  spec.version       = WavelabsClientApi::VERSION
  spec.authors       = ['sekhar']
  spec.email         = ['sekhar@nbostech.com']

  spec.summary       = %q{Simple Ruby Client Api for Wavelabs API Server.}
  spec.description   = %q{Simple Ruby Client Api for Wavelabs API Server.}
  spec.homepage      = 'TODO: Put your gem's website or public repo URL here.'
  spec.license       = 'MIT'

  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
  # delete this section to allow pushing this gem to any host.
  # if spec.respond_to?(:metadata)
  #   spec.metadata['allowed_push_host'] = 'TODO: Set to 'http://mygemserver.com''
  # else
  #   raise &quot;RubyGems 2.0 or newer is required to protect against public gem pushes.&quot;
  # end

  spec.files         = Dir.glob(&quot;{bin,lib,spec}/**/*&quot;) + %w(LICENSE.txt README.md) + <code>git ls-files -z</code>.split('\x0')
  spec.bindir        = 'exe'
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ['lib']

  spec.add_dependency 'httmultiparty', '0.3.16'
  spec.add_dependency 'activemodel', '4.2.4'
  
  spec.add_development_dependency 'byebug'
  spec.add_development_dependency 'minitest'
  spec.add_development_dependency 'rspec'


  spec.add_development_dependency 'bundler', '~&gt; 1.10'
  spec.add_development_dependency 'rake', '~&gt; 10.0'
end

Note: There might be some formatting issues in above file in lines 24,37 & 38.

You need to add details to this file as per your requirement. If you didn’t already know this, the DSL for gem specifications is already pretty clean and straight-forward, there is no need to generate your gemspec using alternative tools.Your gemspec should run standalone, ideally with no additional dependencies. You can assume its FILE is located in the root of your project.

Here is the more documentation about .gemspec file.

As per my requirement I need httmultipart & activemodel gems as run time dependencies remaining gems are development dependencies. Now we can add our gem functionality under lib/wavelabs_client_api directory. If the functionality is not enough to fit into a single ruby file you can split the functionality by using ruby namespace/mixin concept.

After completing the functionality we need to require all the ruby files in lib/wavelabs_client_api.rb file as follows:

   require "wavelabs_client_api/version"

require "wavelabs_client_api/client/api/core/base_api"
require "wavelabs_client_api/client/api/core/auth_api"
require "wavelabs_client_api/client/api/core/users_api"
require "wavelabs_client_api/client/api/core/media_api"
require "wavelabs_client_api/client/api/core/social_api"


require "wavelabs_client_api/client/api/data_models/base_api_model"
require "wavelabs_client_api/client/api/data_models/login_api_model"
require "wavelabs_client_api/client/api/data_models/media_api_model"
require "wavelabs_client_api/client/api/data_models/member_api_model"
require "wavelabs_client_api/client/api/data_models/message_api_model"
require "wavelabs_client_api/client/api/data_models/social_accounts_api_model"
require "wavelabs_client_api/client/api/data_models/token_api_model"

module WavelabsClientApi
  # Your code goes here
end
  

In my gem I am using namespaces wavelabs_client_api/client/api/core to communicate with API Server and wavelabs_client_api/client/api/data_models to create & validate response objects. Now the gem directory structure as follows:

own_gem2

Stay Tuned! We are ready to release our first gem 🙂

Releasing the gem
If we haven’t already added git repository, we should add it & commit all the files for our repository:

    $git add .
    $git commit -m "The beginnings of the wavelabs_client_api gem"
  

This is because the wavelabs_client_api.gemspec file uses git ls-files to detect which files should be added to the gem when we release it.

The final step before releasing our gem is to give it a summary and description in the wavelabs_client_api.gemspec file.

Now we’re going to make sure that our gem is ready to be published. To do this, we can run rake build which will build a local copy of our gem and then gem install pkg/wavelabs_client_api-0.1.0.gem to install it. Then we can try it locally by running the commands that it provides. Once we know everything’s working, then we can release the first version.

To release the first version of our gem we can use the ‘rake release’ command, providing we have committed everything. This command does a couple of things. First it builds the gem to the pkg directory in preparation for a push to Rubygems.org.

Second, it creates a tag for the current commit reflecting the current version and pushes it to the git remote. It’s encouraged that we host the code on GitHub so that others may easily find it.If this push succeeds then the final step will be the push to Rubygems.org which will now allow other people to download and install the gem.

You’ll need to create an account on RubyGems.org. Visit the sign up page and supply an email address that you control, a handle (username) and a password.After creating the account, use your email and password when pushing the gem. (RubyGems saves the credentials in ~/.gem/credentials for you so you only need to log in once.)

If we want to release a second version of our gem, we should make our changes and then commit them to GitHub. Afterwards, we will bump the version number in lib/wavelabs_client_api/version.rb to whatever we see fit, make another commit to GitHub with a useful message such as “bumped to 0.0.2” and then run rake release again.

Conclusion:

In this article I have explained how to create our own ruby gem. I have created my first gem and it is available on rubygems.org. gem name is ‘wavelabs_clien_api’. Still I am refining my first gem so that it has many versions and some versions are yanked. Source code is available on github

I will cover how to write tests for our own gem & creating a generator in my next article. Thanks for reading this article!

Introduction

The Ruby I18n (shorthand for internationalization) gem which is shipped with Ruby on Rails (starting from Rails 2.2) provides an easy-to-use and extensible framework for translating your application to a single custom language other than English or for providing multi-language support in your application.

The process of “internationalization” usually means to abstract all strings and other locale specific bits (such as date or currency formats) out of your application. The process of “localization” means to provide translations and localized formats for these bits.

So, in the process of internationalizing your Rails application you have to:

  • Ensure you have support for i18n.
  • Tell Rails where to find locale dictionaries.
  • Tell Rails how to set, preserve and switch locales.

In the process of localizing your application you’ll probably want to do the following three things:

  • Replace or supplement Rails’ default locale – e.g. date and time formats, month names, Active Record model names, etc.
  • Abstract strings in your application into keyed dictionaries – e.g. flash messages, static text in your views, etc.
  • Store the resulting dictionaries somewhere.

How I18n in Ruby on Rails Works

Internationalization is a complex problem. Natural languages differ in so many ways (e.g. in pluralization rules) that it is hard to provide tools for solving all problems at once. For that reason the Rails I18n API focuses on:

  • providing support for English and similar languages
  • making it easy to customize and extend everything for other languages

As part of this solution, every static string in the Rails framework – e.g. Active Record validation messages, time and date formats – has been internationalized, so localization of a Rails application means “over-riding” these defaults.

The Overall Architecture of the Library:
The Ruby I18n gem is split into two parts:

  1. The public API of the i18n framework – a Ruby module with public methods that define how the library works
  2. A default backend (which is intentionally named Simple backend) that implements these methods

As a user you should always only access the public methods on the I18n module, but it is useful to know about the capabilities of the backend.

The Public I18n API

The most important methods of the I18n API are:

    translate # Lookup text translations
    localize  # Localize Date and Time objects to local formats
  

These have the aliases #t and #l so you can use them like this:

    I18n.t 'store.title'
    I18n.l Time.now
  

There are also attribute readers and writers for the following attributes:

    load_path         # Announce your custom translation files
    locale            # Get and set the current locale
    default_locale    # Get and set the default locale
    exception_handler # Use a different exception_handler
    backend           # Use a different backend
  

So, let’s internationalize a simple Rails application from the ground up.

Setup the Rails Application for Internationalization

There are just a few simple steps to get up and running with I18n support for your application.

Configure the I18n Module:

Following the convention over configuration philosophy, Rails will set up your application with reasonable defaults. If you need different settings, you can overwrite them easily.Rails adds all .rb and .yml files from the config/locales directory to your translations load path, automatically.

The default en.yml locale in this directory contains a sample pair of translation strings:

   en:
     hello: "Hello world"
   

This means, that in the :en locale, the key hello will map to the Hello world string. Every string inside Rails is internationalized in this way. You can use YAML or standard Ruby Hashes to store translations in the default (Simple) backend.

The I18n library will use English as a default locale, i.e. if you don’t set a different locale, :en will be used for looking up translations.

The translations load path (I18n.load_path) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you.

The default application.rb file has instructions on how to add locales from another directory and how to set a different default locale. Just uncomment and edit the specific lines.

  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
  # config.i18n.default_locale = :de
 

Optional: Custom I18n Configuration Setup
For the sake of completeness, let’s mention that if you do not want to use the application.rb file for some reason, you can always wire up things manually, too.

To tell the I18n library where it can find your custom translation files you can specify the load path anywhere in your application – just make sure it gets run before any translations are actually looked up. You might also want to change the default locale. The simplest thing possible is to put the following into an initializer:

   # in config/initializers/locale.rb
   # tell the I18n library where to find your translations
   I18n.load_path += Dir[Rails.root.join('lib', 'locale', '*.{rb,yml}')]
   # set default locale to something other than :en
   I18n.default_locale = :pt
 

Setting and Passing the Locale

If you want to translate your Rails application to a single language other than English (the default locale), you can set I18n.default_locale to your locale in application.rb or an initializer as shown above, and it will persist through the requests.

However, you would probably like to provide support for more locales in your application. In such case, you need to set and pass the locale between requests. We can set locale in three ways as follows:

1.Setting the Locale from the URL Params
2.Setting the Locale from the Domain Name
3.Setting the Locale from the Client Supplied Information

1.Setting the Locale from the URL Params

The setting part is easy. You can set the locale in a before_action in the ApplicationController like this:

   before_action :set_locale
   
   def set_locale
     I18n.locale = params[:locale] || I18n.default_locale
   end
  

This requires you to pass the locale as a URL query parameter as in http://example.com/books?locale=pt. (This is, for example, Google’s approach.) So http://localhost:3000?locale=pt will load the Portuguese localization, whereas http://localhost:3000?locale=de would load the German localization, and so on.

2.Setting the Locale from the Domain Name

One option you have is to set the locale from the domain name where your application runs. For example, we want www.example.com to load the English (or default) locale, and www.example.es to load the Spanish locale. Thus the top-level domain name is used for locale setting. This has several advantages:

  1. The locale is an obvious part of the URL.
  2. People intuitively grasp in which language the content will be displayed.
  3. It is very trivial to implement in Rails.
  4. Search engines seem to like that content in different languages lives at different, inter-linked domains.

You can implement it like this in your ApplicationController:

   before_action :set_locale
 
   def set_locale
     I18n.locale = extract_locale_from_tld || I18n.default_locale
   end
 
   # Get locale from top-level domain or return nil if such locale is not available
   # You have to put something like:
   #   127.0.0.1 application.com
   #   127.0.0.1 application.it
   #   127.0.0.1 application.pl
   # in your /etc/hosts file to try this out locally
   def extract_locale_from_tld
     parsed_locale = request.host.split('.').last
     I18n.available_locales.map(&:to_s).include?(parsed_locale) ? parsed_locale : nil
   end
 

3.Setting the Locale from the Client Supplied Information

In specific cases, it would make sense to set the locale from client-supplied information, i.e. not from the URL. This information may come for example from the users’ preferred language (set in their browser), can be based on the users’ geographical location inferred from their IP, or users can provide it simply by choosing the locale in your application interface and saving it to their profile. This approach is more suitable for web-based applications or services, not for websites.

3.1 Using Accept-Language

One source of client supplied information would be an Accept-Language HTTP header. People may set this in their browser or other clients (such as curl).

A trivial implementation of using an Accept-Language header would be:

   
    def set_locale
      logger.debug "* Accept-Language: #{request.env['HTTP_ACCEPT_LANGUAGE']}"
      I18n.locale = extract_locale_from_accept_language_header
      logger.debug "* Locale set to '#{I18n.locale}'"
    end
 
    private
      def extract_locale_from_accept_language_header
        request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first
      end
  

Internationalizing your Application

OK! Now you’ve initialized I18n support for your Ruby on Rails application and told it which locale to use and how to preserve it between requests. With that in place, you’re now ready for the really interesting stuff.

Let’s internationalize our application, i.e. abstract every locale-specific parts, and then localize it, i.e. provide necessary translations for these abstracts.

You most probably have something like this in one of your applications:

   # config/routes.rb
   Rails.application.routes.draw do
     root to: "home#index"
   end
  
    # app/controllers/application_controller.rb
    class ApplicationController < ActionController::Base
      before_action :set_locale
 
      def set_locale
        I18n.locale = params[:locale] || I18n.default_locale
      end
    end
  
    # app/controllers/home_controller.rb
    class HomeController < ApplicationController
      def index
        flash[:notice] = "Hello Flash"
      end
    end
  
    # app/views/home/index.html.erb

<h1>Hello World</h1>



<%= flash[:notice] %>

  

i18n_screen1

Adding Translations

Obviously there are two strings that are localized to English. In order to internationalize this code, replace these strings with calls to Rails’ #t helper with a key that makes sense for the translation:

  # app/controllers/home_controller.rb
  class HomeController < ApplicationController
    def index
      flash[:notice] = t(:hello_flash)
    end
  end
 
   # app/views/home/index.html.erb

<h1><%=t :hello_world %></h1>



<%= flash[:notice] %>

 

When you now render this view, it will show an error message which tells you that the translations for the keys :hello_world and :hello_flash are missing.

I18n_screen2

So let’s add the missing translations into the dictionary files (i.e. do the “localization” part):

  # config/locales/en.yml
  en:
    hello_world: Hello world!
    hello_flash: Hello flash!
 
  # config/locales/pirate.yml
  pirate:
        hello_world: Ahoy World
        hello_flash: Ahoy Flash
 

There you go. Because you haven’t changed the default_locale, I18n will use English. Your application now shows:

I18n_screen3

And when you change the URL to pass the pirate locale (http://localhost:3000?locale=pirate), you’ll get:

I18n_screen4

Note:

  • You need to restart the server when you add new locale files.
  • You may use YAML (.yml) or plain Ruby (.rb) files for storing your translations in SimpleStore.
  • YAML is the preferred option among Rails developers. However, it has one big disadvantage.
  • YAML is very sensitive to whitespace and special characters, so the application may not load your dictionary properly.
  • Ruby files will crash your application on first request, so you may easily find what’s wrong. (If you encounter any “weird issues” with YAML dictionaries, try putting the relevant portion of your dictionary into a Ruby file.)

Conclusion
I18n gem has some limitations & may not fulfill your requirements. We can pass variables to translation files and also pass different parameters for translation method(#t). For more information about I18n gem visit click on this Link

Hope it was useful! Thanks for reading this article.

Introduction

In Rails applications we can serve common assets like javascripts, stylesheets and images. I want to serve different assets for different domains in a single rails application. Say for example I have rails application with 2 domains like “blog.nbostech.com” and “wiki.nbostech.com”.

To do this we have many solutions in rails. Here I am going to explain a simple solution in three steps:

1. Create domain specific assets directories in rails application assets root.
2. Add logic in application controller to determine domain specific assets based on requested url.
3. Test the application in development environment by adding your domains in /etc/hosts file.

Step1:

In rails application the default assets directory look like this:

assets_directory1

Now I want to serve different assets for different domains. To do that create different sub directories for each domain under app/assets/stylesheets directory with domain names as follows: for example I have two domains “blogs.nbostech.com” and “wiki.nbostech.com”

1. app/assets/stylesheets/blogs
2. app/assets/stylesheets/wiki

After creating sub directories create one applicaton.css manifest file and related style sheets file. Now each domain specific sub directories have it’s own styles.

Step2:

We have created the domain specific styles in assets directory. In rails application requests are handled by controllers. The base controller is application controller for all other costume controllers. In application controller we need to parse the request and get the subdomain using before_action filter.

Now add the following logic to your application controller:

  # file app/controllers/application_controller.rb
  class ApplicationController < ActionController::Base
    .
    .
    before_action :set_them #Add this line
    .
    .
    #Add this method at the end of application controller
    def set_them
     if request.subdomain.present? && request.subdomain != 'www'
      @css_root = "#{request.subdomain}/application"
     else
      @css_root = "application"  
     end  
    end

   end   
  

In the above method we are parsing the request to get the subdomain. If the request has subdomain and the subdomain not equal “www” then we are setting the css_root instance variable to domain specific stylesheet manifest file path. Otherwise we are using default one. In our case say for example the request from “blog.nbostech.com” then the css_root instance variable set to @css_root = “blog/application”.

After adding above logic to application controller we need to add a condition in application layout to include style sheets. In rails application.html.erb is the default layout for the application. In this layout we will include all the javascript and stylesheet files. The default application layout header section looks like this:

   # File app/views/layouts/application.html.erb
   .
   .
    <head>
     <title>NbosRorOauthProvider</title>
     <%= stylesheet_link_tag   'application', 'data-turbolinks-track' => true %>
     <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
     <%= csrf_meta_tags %>
   </head>
  

Here stylesheet_link_tag & javascript_include_tag are helper methods in rails views. Above lines will include the manifest files of stylesheet & javascript. manifest files includes the information about all the related assets which means application.js under app/assets/javascripts include all the javascript related logic and application.css under app/assets/stylesheets include all the css related logic. Note that here file name extensions are not required if you are using rails helper methods.

As per our requirement we need to use different stylesheet manifest file for different domain. We already have an instance variable @css_root which holds the path of manifest file of domain specific in application controller. We can use that instance variable in our application layout. Only thing we need to replace ‘application’ parameter with @css_root in stylesheet_link_tag helper method as follows:

   #file app/views/layouts/application.html.erb
   .
   .
   .
   <%= stylesheet_link_tag  @css_root, 'data-turbolinks-track' => true %>
   .
   .
  

Step3:

Now we have created domain specific styles & added the back end logic. It’s time to test the functionality in development environment.

Rails application by default run on port 3000. we can access the application using http://localhost:3000 from browser. Here localhost is nothing but 127.0.0.1. To test our functionality we need to add the domain names to point 127.0.0.1 in etc/hosts(unix based OS) file. Go to the root directory of hosts file and open it in your favorite editor. I am using nano editor as follows:

  sudo nano hosts
 

It will prompt your root user password. After entering the root password it will open the hosts file. In that file add the following two lines:

   127.0.0.1  blog.nbostech.com
   127.0.0.1  wiki.nbostech.com
  

After adding above lines save and exit. Go to your rails application root directory and run the server “rails server”. Now we are ready to test our domain specific theming in development environment. Now the assets directory structure looks as follows:

domain_assets_structure

To demonstrate I am using different background image & font styles for two domains.

Open a browser and access “http://blog.nbostech.com:3000”. Out put is:

domain1

Open another tab and access “http://wiki.nbostech.com:3000”. Out put is:

domain2

you can observe the different background image and different font styles for two domains.

Conclusion

Above is a simple example for serve different themes for different subdomains. But let’s say if you have more than 20 subdomains then we need to create a directory for each subdomain under assets/stylesheets. So there is lot of code redundancy. To avoid this we need to follow DRY(don’t repeat yourself) approach in rails. I will cover that topic in my next article.

Thanks for reading this article. We hope this was useful!

Instruction

Creating API for a Rails application is very easy for a Ruby on Rails developer. But how other customers/clients will know whether the API is working fine or not without a client-side application. Is there any solution for this which I can document for API within the Rails application, The answer is yes we have many tools & approaches but I would prefer swagger UI.

In this article I am going to explain how to create Rails API documentation using swagger UI.

Prerequisites

To understand this article we need a rails application which already have an API. I am going to use my the application which I have created in my previous article

Step1: Install required gems

To Integrate swagger UI for Rails API I am using a gem called swagger-docs. Add this gem to your Gemfile in your application as follows:

   #File Gemfile
   .
   .
   # Add this line
   gem 'swagger-docs'
  

Save the file and run ‘bundle install’. It will install the swagger-docs dependent gems into your system.

Step2: Create a configuration file

After installing the gem create an initializer in config/initializers (e.g. swagger_docs.rb) and define your APIs:

  #File config/initializers/swagger_docs.rb
  Swagger::Docs::Config.register_apis({
  "1.0" =>  {
  	#:controller_base_path => "/app/controllers/api/v0",
    # the extension used for the API
    :api_extension_type => :json,
    # the output location where your .json files are written to
    :api_file_path => "public",
    # the URL base path to your API
    :base_path => "http://localhost:3000",
    # if you want to delete all .json files at each generation
    :clean_directory => true,
    # add custom attributes to api-docs
    :attributes => {
      :info => {
        "title" => "NBOS Rails Oauth provider application",
        "description" => "Rails API documention with Swagger UI.",
        "termsOfServiceUrl" => "http://nbostech.com/",
        "contact" => "contact@nbostech.com"
      }
    }
  }
})
 

Above is minimal configuration to generate API documentation.

Configuration options
The following table shows all the current configuration options and their defaults. The default will be used if you don’t supply your own value.

Option Description Default
api_extension_type The extension, if necessary, used for your API – e.g. :json or :xml nil
api_file_path The output file path where generated swagger-docs files are written to. public/
base_path The URI base path for your API – e.g. api.somedomain.com /
base_api_controller / base_api_controllers The base controller class your project uses; it or its subclasses will be where you call swagger_controller and swagger_api. An array of base controller classes may be provided. ActionController::Base
clean_directory When generating swagger-docs files this option specifies if the api_file_path should be cleaned first. This means that all files will be deleted in the output directory first before any files are generated. false
formatting Specifies which formatting method to apply to the JSON that is written. Available options: :none, :pretty :pretty
camelize_model_properties Camelizes property names of models. For example, a property name called first_name would be converted to firstName. true

Step3: Documenting a controller

Now it’s time to document our API controllers to generate json files. Now I am going to add the documentation for available methods in my registration API controller as follows:

   #File app/controllers/api/v0/registrations_controller.rb
   class Api::V0::RegistrationsController < Devise::RegistrationsController
      skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }
      respond_to :json

      #Add this line
      swagger_controller :registrations, "Registrations"

      #Add this swagger_api block
      swagger_api :create do
         summary "Creates a new User"
         param :form, :first_name, :string, "First name"
         param :form, :last_name, :string, "Last name"
         param :form, :email, :string, :required, "Email address"
         param :form, :password, :string, :required, "Password"
         param :form, :password_confirmation, :string, :required, "Password Confirmation"
         response :unauthorized
         response :not_acceptable
      end
     .
     .
     .
   end
  

So like this add swagger_api block & swagger_controller to required API which you have created. I have added for my sessions API controllers as follows:

  #File app/controllers/api/v0/sessions_controller.rb
  class Api::V0::SessionsController < Devise::SessionsController
    skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }
    skip_before_filter :verify_signed_out_user
    respond_to :json

    #Add this line
    swagger_controller :sessions, "Login and Logout"

    #Add this block
    swagger_api :create do
      summary "Login"
      param :form, :email, :string, :required, "Email address"
      param :form, :password, :string, :required, "Password"
      response :unauthorized
      response :not_acceptable
    end

    swagger_api :destroy do
      summary "Logout"
      param :path, :id, :integer, :required, "User ID"
      response :unauthorized
      response :not_acceptable
    end
  end

   .
   .
 

swagger_controller & swagger_api are helpers to generate swagger UI documentation. Following table will tells more about the DSL of swagger_api block:

Method Description
summary The summary of the API
notes (optional) The associated notes for the API
param Standard API Parameter
param_list Standard API Enum/List parameter.
response Takes a symbol or status code and passes it to Rack::Utils.status_code. The current list of status codes can be seen here: https://github.com/rack/rack/blob/master/lib/rack/utils.rb. An optional message can be added.

Step4: Generate json files

We have created required configuration & annotations for our API methods to generate JSON files. To generate JSON files file our API we have to run swagger rake task as follows from our rails root directory:

   rake swagger:docs
  

After running above rake task Swagger-ui JSON files should now be present in your api_file_path (e.g. ./public/api/v1) which we have configured in config/initializers/swagger_docs.rb file. The swagger-docs rake task (rake swagger:docs) then examines your controllers to determine which controllers should be documented for swagger-ui.

Additional logging for generation failures

Errors aren’t displayed by default. To see all error messages use the SD_LOG_LEVEL environment variable when running the rake task:

  SD_LOG_LEVEL=1 rake swagger:docs

Currently only constantize errors are shown.Errors are written to $stderr. Error logging methods can be found in Config and can be overridden for custom behaviour.

Now we can access the each json file from browser as “http://localhost:3000/api/v0/registrations.json” because they have generated as static assets in public directory.

Step5: Integration of Swagger UI view

Now it’s good idea to integrate swagger UI index view like sample petstore. To do this I have copied all the swagger UI related source files from here. Copy swagger-ui directory to under your rails project “public/api/api” directory and index.html file to “public/api”. Now all the swagger UI resource files will be available under “public/api/api” and swagger-ui home page under “public/api”. Our rails application public directory will look like this:

swagger_doc_directory_structure

Now we all are set. Ready to view the Swagger-UI documentation for our rails application API. IF you are running the the server stop and restart the rails server. Go to browser and try to access “http://localhost:3000/api/index”. Following are the screens. you can click on available links(show/hide, List operations, Expand Operations and Raw) on each resource(registrations, sessions):

swagger_ui_2

swagger_ui_1

swagger_ui_3

For more information about swagger UI and swagger-docs gem visit following websites:

swagger UI

swagger-docs gem

Thanks for reading this article. We hope this was useful!

Introduction

Allowing users to login with multiple authentication providers brings great benefits but also results in some annoying edge cases. For example, what happens when they login with one provider, logout and then login with another? What happens when they try to login with one having already logged in with another?

Typically authentication systems have a User model which handles most of the authentication logic but having multiple logins forces you to correctly separate the concepts of an Identity and a User. An Identity is a particular authentication method which a user has used to identify themselves with your site whilst a User manages data which is directly related to your site itself.

In my previous article I explained how to integrate social signup on Rails using devise and omniauth gems. In this article I am going to explain how to manage multiple social logins as a single account. Main goal of this article is as follows:

1. User can sign up as a normal user and link his facebook/google account from his profile.
2. User can sign up with any social networking and manage his profile as a normal user.
3. In above both scenarios user can link/unlink his social accounts from his profile page.

Prerequisites

I am using the application which i have already created in my previous article

The present application is storing the user information and oauth information(UID & provider) in a single table called users as follows:

after_adding_ominiauht_columns

Authentications and Users

Now it’s time to separate the user information & oauth information. To do this I am going to add another table called authentications which will store all oauth information. And I am removing oauth information from user table. First create a model to store oauth data from console(application root directory):

 rails g model authentication provider:string uid:string user_id:integer

It will create required files for the table authentication. Modify the authentication model as follows:

 #File app/models/authentication.rb
 class Authentication < ActiveRecord::Base
  belongs_to :user
  validates_presence_of :user_id, :uid, :provider
  validates_uniqueness_of :uid, :scope => :provider
  def provider_name
     provider.titleize
  end
end

I have added the user association and added a instance method which will return provider name in camelcase. Here we’re modeling very simple relationships and making sure that the Authentication has both a provider (e.g. “twitter” or “facebook”) and a uid (i.e. the external service ID). Next up, we’ll wire these models into our controller to create a real sign in process. Now add the association in user as well which means a user can have zero or many authentications.

 #File app/models/user.rb
 class User < ActiveRecord::Base
  .
  .
  .
  has_many :authentications # Add this single line
  .
  .
 end

Adding association from model level was completed. It’s time to add the authentication table and remove oauth information from user table. Now the authentication migration looks like this:

 #File db/migrate/20150907085047_create_authentications.rb
 class CreateAuthentications < ActiveRecord::Migration
  def change
    create_table :authentications do |t|
      t.string :provider
      t.string :uid
      t.integer :user_id

      t.timestamps
    end
  end
end

To remove oauth info from user table create a migration from console:

 rails g migration remove_provider_fileds_from_user

It will create one migration file under db/migrate. Open the file and add the following code:

 # File db/migrate/20150907085601_remove_provider_fileds_from_user.rb
 class RemoveProviderFiledsFromUser < ActiveRecord::Migration
  def change
    remove_column :users, :provider
    remove_column :users, :uid
  end
 end

Now we have done adding required migrations to modify database tables. Run migrations from console:

 rake db:migrate

It will create a table called authentications and removed the uid and provider columns form user table. The following is the domain model for current requirement.

oauth_user_domain_model

Signing Up/In

One of the nice things about external authentication is you can collapse the sign up and sign in process into a single step. What we’ll do here is:

1. When a user signs in, look for existing Authorizations for that external account.
2. Create a user if no authorization is found.
3. Add an authorization to an existing user if the user is already logged in.

Let’s work backwards for this functionality by adding the code we want to have to the controller. Create a controller called authentications from console:

  rails g controller authentications
 

It will create a controller and a directory under app/views. Add the following code to newly created controller:

 # File app/controllers/authentication_controller.rb
 class AuthenticationsController < ApplicationController
  def index
    @authentications = current_user.authentications if current_user
  end

  def create
    omniauth = request.env["omniauth.auth"]
    authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
    if authentication
      flash[:notice] = "Signed in successfully."
      sign_in_and_redirect(:user, authentication.user)
    elsif current_user
      current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
      flash[:notice] = "Authentication successful."
      redirect_to authentications_url
    else
      user = User.new
      user.apply_omniauth(omniauth)
      if user.save
        flash[:notice] = "Signed in successfully."
        sign_in_and_redirect(:user, user)
      else
        session[:omniauth] = omniauth.except('extra')
        redirect_to new_user_registration_url
      end
    end
  end

  def destroy
    @authentication = current_user.authentications.find(params[:id])
    @authentication.destroy
    flash[:notice] = "Successfully destroyed authentication."
    redirect_to authentications_url
  end

Now Add the following code to existing user model which will be used for registration process:

  # File app/models/user.rb
  class User < ActiveRecord::Base
   .
   .
   # Add the following methods
  def apply_omniauth(omniauth)
    authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
  end

  def password_required?
    (authentications.empty? || !password.blank?) && super
  end

  def existing_auth_providers
    ps = self.authentications.all

    if ps.size > 0
      return ps.map(&:provider)
    else
      return []
    end
  end
   .
   .
  end
 

we have created the authentications controller and updated the code in both authentications controller & user model. Add an index view for authentications controller which will show you the number of authentications of a user.

 #File app/views/authetications/index.html.erb
 <% "Sign In Options" %>

<% if @authentications %>
  <% unless @authentications.empty? %>
    <p><strong>You have linked these services with your account:</strong></p>
    <div class="authentications">
      <% for authentication in @authentications %>
        <div class="authentication">
          <%= image_tag "#{authentication.provider}_icon.png", size: "32x32"%>
          <div class="provider"><%= authentication.provider_name %></div>
          <div class="uid"><%= authentication.uid %></div>
          <%= link_to "X", authentication, :confirm => 'Are you sure you want to remove this authentication option?', :method => :delete, :class => "remove" %>
        </div>
      <% end %>
      <div class="clear"></div>
    </div>
  <% end %>
<% else %>
  <p><strong>Sign in through one of these services:</strong></p>
<% end %>

<p><strong>Add another service to sign in with:</strong></p>
  <%- current_user.class.omniauth_providers.each do |provider| %>
    <%- if !current_user.existing_auth_providers.include?(provider.to_s) %>
      <%= link_to omniauth_authorize_path(current_user.class, provider) do %>
          <%= image_tag "#{provider.to_s}_icon.png", size: "32x32" %>
      <% end %>
    <% end %>
  <% end -%>

<div class="clear"></div>

<% unless user_signed_in? %>
  <p>
    <strong>Don't use these services?</strong>
    <%= link_to "Sign up", new_user_registration_path %> or
    <%= link_to "sign in", new_user_session_path %> with a password.
  </p>
<% end %>

Now it’s time to override the devise registration controller. Following are the next steps:
1. create a registration controller and update the code.
2. copy the devise registrations views(create, edit) and change the code.
3. Tell the devise routes to use our registrations controller instead of it’s own controller.
4. Update the callback controller logic same as authentication controller.

step1: create a registration controller & views

From the console create controller called registrations:

  rails g controller registrations
 [/source]

 Add the following logic to the controller:
 #File app/controllers/registrations_controller.rb
 
  class RegistrationsController < Devise::RegistrationsController
  def create
    super
    session[:omniauth] = nil unless @user.new_record?
  end

  private

  def build_resource(*args)
    super
    if session[:omniauth]
      @user.apply_omniauth(session[:omniauth])
      @user.valid?
    end
  end
end
 

Next copy devise registration views from app/views/devise/registrations to app/views/registrations and update the code as follows.

  #File app/views/registrations/new.html.erb
  <div class="border-form-div">
<h2>Sign up</h2>

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
  <%= devise_error_messages! %>

  <p><%= f.label :email %><br />
  <%= f.text_field :email %></p>

<% if @user.password_required? %>
  <p><%= f.label :password %><br />
  <%= f.password_field :password %></p>

  <p><%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation %></p>
<% end %>

  <p style="text-align: center;"><%= f.submit "Sign up", :class => 'btn_login' %></p>
<% end %>

<%= render :partial => "devise/shared/links" %>
</div>
 
 #File app/views/registrations/edit.html.erb
 <div class="border-form-div">

<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, :class => "edit_user_form"}) do |f| %>
  <%= devise_error_messages! %>

  <p><%= f.label :email %><br />
  <%= f.text_field :email %></p>

  <p><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
  <%= f.password_field :password %></p>

  <p><%= f.label :password_confirmation %><br />
  <%= f.password_field :password_confirmation %></p>

  <p><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
  <%= f.password_field :current_password %></p>

  <p style="text-align: center;"><%= f.submit "Update", {:class => "btn_login"} %></p>
<% end %>

<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.</p>

<%= link_to "Back", :back %>
</div>

step2: Update the routes.rb

We have to tell the devise engine to use our custom registration controller. To do that add update the routes file as follows:

 #File config/routes.rb

 Rails.application.routes.draw do
 .
 .
  #update the existing devise route as follows
  devise_for :users, :controllers => {:registrations => 'registrations', :omniauth_callbacks =>  "callbacks"}

 #Add the following line
  post '/auth/:provider/callback' => 'authentications#create'
 .
 .
 end

Step3: Update the callback controller as follows:

  class CallbacksController < Devise::OmniauthCallbacksController
  def all
    omniauth = request.env["omniauth.auth"]
    authentication = Authentication.find_by_provider_and_uid(omniauth['provider'], omniauth['uid'])
    if authentication
      flash[:notice] = "Signed in successfully."
      sign_in_and_redirect(:user, authentication.user)
    elsif current_user
      current_user.authentications.create!(:provider => omniauth['provider'], :uid => omniauth['uid'])
      flash[:notice] = "Authentication successful."
      redirect_to authentications_url
    else
      user = User.new
      user.apply_omniauth(omniauth)
      if user.save
        flash[:notice] = "Signed in successfully."
        sign_in_and_redirect(:user, user)
      else
        session[:omniauth] = omniauth.except('extra')
        redirect_to new_user_registration_url
      end
    end
  end
  alias_method :facebook, :all
end
 

previously I have created a separate method for each provider. Now the functionality is almost same for all providers. I have created a common method called ‘all’ and created an alias for facebook provider. you can add the aliases for other providers as well. Here I am using only facebook signup.

All the coding part was done. Now we are going to test the application. Start the rails server from console and try to access it from browser “http://localhost:3000”. Following is the output. I have added some styles for layout as well.

link_social_login

Now a user can sign in using any of their social accounts and a User will automatically be fetched or created. The signup page look like this:

link_social_new_user

Here we have two scenarios:

1. Signup as a normal user and link the other social accounts.
2. Signup with any social network and edit his profile like a normal user.

Scenario one:

Now i am going to signup as a normal user by providing required information:

new_user_signup

After signup it will redirect to the application homepage. here it is products list page. we have to test the authentications of the newly created user. Try to access the link “http://localhost:3000/authentications” from browser. It will look like as follows:

authentications_index_page

From here the newly created normal user don’t have any authentications. He can link the available social accounts from here. If user clicks the facebook icon it will got he facebook login page. once user authenticated from facebook our backend logic will automatically link the facebook authentication to the already logged in user. now the page look like this:

authentication_links

Note: I have removed the UID from the screen for security reasons.

Now the user can login using his normal user account or login with his facebook account. But the user entity will same for both logins. Means this user have one authentication. If user want to unlink just click on the ‘x’ mark it will simple remove the facebook authentication from database.

Scenario two:

User can login with any provider without registering as a new user. Say for example a user want to login with his facebook account without providing any information like email.. In our application we have validation for email to create a new user. If user is trying to login with his FB account first time it will ask for an email after authenticated from FB. The screen will look like this:

first_time_social_login

Once user enter the email it will validate & create a new user and link that FB authentication with the user. Like this User can link available social services after login.

We hope this was useful!

Introduction

In this tutorial you’ll learn first how to build an authentication API that can allow external users to register, login and logout through JSON requests. After having successfully logged in, a user will receive an authentication token that could be used in following API requests to authorize the user, securing the access to your application’s resources.

In my previous article I have explained how to build a rails application with registration & social login here is the link http://blogs.nbostech.com/2015/08/loginregistration-social-signup-using-ruby-on-rails/ . I am going to continue this article with the application which I have created in the above post. Which means I am going to create API for that application.

Latest version of devise gem does not support token_authenticatable module. To work with token_authenticatable module i am using another gem called “devise-token_authenticatable”. Add this gem to your gemfile.

 gem "devise-token_authenticatable"

After adding this gem run following command under application root to update your gems.

 bundle install

After running bundle install uncomment the following line in the Devise initializer to enable the auth_token:

   # file: config/initializers/devise.rb
   # ==> Configuration for :token_authenticatable
   # Defines name of the authentication token params key
   config.token_authentication_key = :auth_token
 

Add the :token_authenticatable to the devise modules in the user model:

    class User < ActiveRecord::Base
      # Include default devise modules. Others available are:
      # :confirmable, :lockable, :timeoutable and :omniauthable
      devise :database_authenticatable, :registerable, :token_authenticatable,
             :recoverable, :rememberable, :trackable, :validatable ,
             :omniauthable, :omniauth_providers => [:facebook]
  

Now the configuration part was done. Next we need to add a column to user table by creating a migration file.

   rails g migration add_authentication_token_column_to_users
  

After creating the migration open that file add the following code:

    class AddAuthenticationTokenColumnToUsers < ActiveRecord::Migration
      def change
  	  add_column :users, :authentication_token, :string
  	  add_index :users, :authentication_token, :unique => true
      end
    end
  

Now run the migration to add the new column to users table in database:

     rake db:migrate
   

After running above migration user model looks like this:

token_based_auth

Now we are ready to configure our application to work with token based authentication. First add the following to user model after devise configuration:

     before_save :ensure_authentication_token
   

Next add the following methods to user model at the end of all methods.

     def ensure_authentication_token
        if authentication_token.blank?
          self.authentication_token = generate_authentication_token
        end
     end

    private

     def generate_authentication_token
       loop do
         token = Devise.friendly_token
         break token unless User.find_by(authentication_token: token)
       end
     end
   

Above code will ensure that each and every user must have a auth token every time a user created or existing user updated. Now the auth token setup was done.

API Sessions Controller (login and logout)

Let’s start coding the sessions controller that will be used by API to authenticate the users. I want to use a namespace and a version for our API, so its controllers will be under the app/controller/api/v0/ folder. Create a directory ‘api’ under controllers and create a directory called ‘v0’ under controllers/api/

The sessions controller has two actions: create for login and destroy for logout. The first accepts a user JSON object as POST data with an email and a password parameters and returns an auth_token if the user exists in the database and the password is correct. The logout action expects an auth_token parameter in the url.

  # file: app/controller/api/v0/sessions_controller.rb
  class Api::V0::SessionsController < Devise::SessionsController
    skip_before_filter :verify_authenticity_token,
                       :if => Proc.new { |c| c.request.format == 'application/json' }
    skip_before_filter :verify_signed_out_user                          

    respond_to :json

    def create
      warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
      render :status => 200,
             :json => { :success => true,
                        :info => "Logged in",
                        :data => { :auth_token => current_user.authentication_token } }
    end

    def destroy
      warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#failure")
      current_user.update_column(:authentication_token, nil)
      render :status => 200,
             :json => { :success => true,
                        :info => "Logged out",
                        :data => {} }
    end

    def failure
      render :status => 401,
             :json => { :success => false,
                        :info => "Login Failed",
                        :data => {} }
    end
  end
 

In the routes definition we need to add our namespace and the two login and logout routes.

    # file: config/routes.rb
    namespace :api do
      namespace :v0 do
        devise_scope :user do
          post 'sessions' => 'sessions#create', :as => 'login'
          delete 'sessions' => 'sessions#destroy', :as => 'logout'
        end
      end
    end
  

Test the login

Let’s create a user to test the login with, open the rails console with rails console in the command line and write:

 user = User.new(:first_name => 'test', :last_name => 'user', :email => 'user@example.com',    :password => 'secret1234', :password_confirmation => 'secret1234')

user.save

Close the console and fire up the rails server and in another command line use curl to invoke our new login API:

  curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v0/sessions -d "{\"user\":{\"email\":\"user@example.com\",\"password\":\"secret1234\"}}"
 

If everything went fine, you should see the last line saying this (the auth_token will be different):

  {"success":true,"info":"Logged in","data":{"auth_token":"JRYodzXgrLsk157ioYHf"}}
 

Test the logout

Using the auth_token that the API gave us back when we logged in and specifying the DELETE verb we can reset the authentication token of the user, logging him out.

  curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X DELETE http://localhost:3000/api/v0/sessions/\?auth_token\=JRYodzXgrLsk157ioYHf
 

The result will be a nice message informing us that we are logged out.

  {"success":true,"info":"Logged out","data":{}}
 

API Registrations Controller (register a new user)

The registrations controller extends the Devise one and has only one action, create. Here we can save the new user and automatically log it in, returning the auth_token associated. Our user can then start using the API already logged in after the registration.

  class Api::V0::RegistrationsController < Devise::RegistrationsController
    skip_before_filter :verify_authenticity_token,
                     :if => Proc.new { |c| c.request.format == 'application/json' }

    respond_to :json

    def create
      build_resource(sign_up_params)
      if resource.save
        sign_in resource
        render :status => 200,
               :json => { :success => true,
                          :info => "Registered",
                          :data => { :user => resource,
                                     :auth_token => current_user.authentication_token } }
      else
        render :status => :unprocessable_entity,
               :json => { :success => false,
                          :info => resource.errors,
                          :data => {} }
      end
    end

    def sign_up_params
      params.require(:user).permit(:email, :password, :password_confirmation, :first_name, :last_name)
    end
  end
 

Add the register route to the API namespace:

    # file: config/routes.rb
    namespace :api do
      namespace :v0 do
        devise_scope :user do
          post 'registrations' => 'registrations#create', :as => 'register'
          post 'sessions' => 'sessions#create', :as => 'login'
          delete 'sessions' => 'sessions#destroy', :as => 'logout'
        end
      end
    end
  

Test the registration

Using the code we just added, we can now register new users from the JSON API. Try it out opening a command line and pasting this code.

  curl -v -H 'Content-Type: application/json' -H 'Accept: application/json' -X POST http://localhost:3000/api/v0/registrations -d "{\"user\":{\"email\":\"user1@example.com\",\"first_name\":\"another\", \"last_name\":\"user\",\"password\":\"secret\",\"password_confirmation\":\"secret\"}}"
 

If everything went fine, you should see the last line saying this (the id, dates and auth_token will be different):

  {"success":true,"info":"Registered","data":{"user":{"id":3,"email":"user1@example.com","created_at":"2015-09-01T12:12:50.866Z","updated_at":"2015-09-01T12:12:50.966Z","provider":null,"uid":null,"first_name":"another","last_name":"user","authentication_token":"3XC3BJpNvKHDoXTZb4zJ"},"auth_token":"3XC3BJpNvKHDoXTZb4zJ"}}
 

You now have a modern user authentication system for your app, where users can sign in & sign up using an email address and a password using API.

We hope this was useful!