Sarav's Weblog

Technical Articles for RoR Developers

Category Archives: ROR

Rails 3 – AuthLogic implementation – A basic tutorial

Last week I had a chance to implement authlogic for one of my projects. Initially I planned to go with Devise – Flexible authentication solution for Rails. It can be easily to plug and play. But I had a different intent/requirement to go with authlogic.

This tutorial will explain about, AutuhLogic implementation with rails 3. I’m just going to go over the basic commands/files that you need to get the framework up and working. Please refer to this link “https://github.com/binarylogic/authlogic” for a depth information. Reading required 🙂 I believe you have already installed rails 3 and other required gems. Here we go..

Auth Logic Basic Set up – Installation and configuration
Install authlogic gem

$ [sudo] gem install authlogic

create new application

$rails new auth_logic_implementation

Include the gem in the gem file #Gemfile

gem “authlogic”

database connectivity
I’ve used “mysql” for database connectivity. Change the database settings based on your mysql settings.

development:
adapter: mysql
database: auth_logic_development
host: 127.0.0.1
username: root
password: root

create database – run the following command

$rake db:create

Working with Models
create user and user_session models.

$rails g model user_session
$rails g model user

User session models will take care of the user sessions. In order to achieve this you have to inherit user_session model from AuthLogic.(ie AuthLogic instead of ActiveRecord)”

class UserSession < Authlogic::Session::Base
end

Then, in the user model, you have to tell Authlogic that its the model that you wand to use for logging in and out.

class User < ActiveRecord::Base
  acts_as_authentic do |c|
  end # block optional
end

and then update the migration files

 class CreateUserSessions < ActiveRecord::Migration
 def change
    create_table :user_sessions do |t|
      t.string :session_id, :null => false
      t.text :data
      t.timestamps
    end

    add_index :user_sessions, :session_id
    add_index :user_sessions, :updated_at

  end
end

#

class CreateUsers <ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string    :name,                :null => false, :default => ''
      t.string    :login,             :null => false
      t.string    :crypted_password,    :null => false
      t.string    :password_salt,       :null => false
      t.string    :email,               :null => false
      t.string    :persistence_token,   :null => false
      t.string    :single_access_token, :null => false
      t.string    :perishable_token,    :null => false                   

      t.integer   :login_count,         :null => false, :default => 0
      t.integer   :failed_login_count,  :null => false, :default => 0
      t.datetime  :last_request_at
      t.datetime  :current_login_at
      t.datetime  :last_login_at
      t.string    :current_login_ip
      t.string    :last_login_ip                                           

      t.timestamps
    end
  end
end

Make sure you have a model that you will be authenticating with. Since we are using the User model it should look something like:

class User < ActiveRecord::Base

  acts_as_authentic do |c|
    c.login_field = 'email'

  end # block optional

end

Do the migration

$rake db:migrate

Application controller and helper methods

class ApplicationController < ActionController::Base
  protect_from_forgery 

  #filter_parameter_logging :password, :password_confirmation # there are underscores :-|

  helper_method :current_user_session, :current_user

  private
    def current_user_session
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end

    def require_user
      logger.debug "ApplicationController::require_user"
      unless current_user
        store_location
        flash[:notice] = "You must be logged in to access this page"
        redirect_to new_user_session_url
        return false
      end
    end

    def require_no_user
      logger.debug "ApplicationController::require_no_user"
      if current_user
        store_location
        flash[:notice] = "You must be logged out to access this page"
       # redirect_to home_index_path
        return false
      end
    end

    def store_location
      #session[:return_to] = request.request_uri
    end

    def redirect_back_or_default(default)
      redirect_to(session[:return_to] || default)
      session[:return_to] = nil
    end

end

user_sessions controller, view, and routes

Controller

$ rails g controller user_sessions

class UserSessionsController  [:new, :create]
  before_filter :require_user, :only => :destroy

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_back_or_default account_url(@current_user)
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = "Logout successful!"
    redirect_back_or_default new_user_session_url
  end
end

Views

<h1>Login</h1>
# app/views/user_sessions/new.html.erb
<%= form_for @user_session, :as => :user_session, :url => { :action => "create" } do |f| %>
   <%= render "shared/error_messages", :target => @user_session %>
  <%= f.label :email %><br />
  <%= f.text_field :email %><br />
  <br />
  <%= f.label :password %><br />
  <%= f.password_field :password %><br />
  <br />
  <%= f.check_box :remember_me %><%= f.label :remember_me %><br />
  <br />
  <%= f.submit "Login" %>
<% end %>

Users and User Registration

$ rails g controller users

class UsersController < ApplicationController    before_filter :require_user, :only => [:show, :edit, :update]

  def new
    @user = User.new
  end

  def create
    @user = User.new(params[:user])

    # Saving without session maintenance to skip
    # auto-login which can't happen here because
    # the User has not yet been activated
    if @user.save
      flash[:notice] = "Your account has been created."
      redirect_to signup_url
    else
      flash[:notice] = "There was a problem creating you."
      render :action => :new
    end

  end

  def show
    @user = current_user
  end

  def edit
    @user = current_user
  end

  def update
    @user = current_user # makes our views "cleaner" and more consistent
    if @user.update_attributes(params[:user])
      flash[:notice] = "Account updated!"
      redirect_to account_url
    else
      render :action => :edit
    end
  end

end

Views

#app/views/users/_form.html.erb

<%= render "shared/error_messages", :target => @user %>

<p>
<%= form.label :name %><br />
<%= form.text_field :name %>
</p>
<p>
<%= form.label :email %><br />
<%= form.text_field :email %>
</p>
<p>
<%= form.label :password, form.object.new_record? ? nil : "Change password" %><br />
<%= form.password_field :password %>
</p>
<p>
<%= form.label :password_confirmation %><br />
<%= form.password_field :password_confirmation %>
</p>

#app/views/users/new.html.erb
<h1>Register</h1>

<%= form_for @user do |f| %>
<%= render :partial => "form", :object => f, :locals => { :user => @user } %>
<%= f.submit "Register" %>
<% end %>

# app/views/users/show.html.erb
<p>
<b>Email:</b>
<%=h @user.email %>
</p>

<p>
<b>Login count:</b>
<%=h @user.login_count %>
</p>

<p>
<b>Last request at:</b>
<%=h @user.last_request_at %>
</p>

<p>
<b>Last login at:</b>
<%=h @user.last_login_at %>
</p>

<p>
<b>Current login at:</b>
<%=h @user.current_login_at %>
</p>

<p>
<b>Last login ip:</b>
<%=h @user.last_login_ip %>
</p>

<p>
<b>Current login ip:</b>
<%=h @user.current_login_ip %>
</p>

<%= link_to 'Edit Account', edit_account_path %>

#app/views/shared/_error_messages.html.erb
<% if target.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(target.errors.count, "error") %> prohibited this record from being saved:</h2>
<ul>
<% target.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

Routes

#config/routes.rb
  resources :user_sessions

  match 'login' => "user_sessions#new",      :as => :login
  match 'logout' => "user_sessions#destroy", :as => :logout

  resources :users  # give us our some normal resource routes for users
  resource :user, :as => 'account'  # a convenience route

  match 'signup' => 'users#new', :as => :signup

  root :to => 'users#new'

$rake routes

    user_sessions GET    /user_sessions(.:format)          {:controller=>"user_sessions", :action=>"index"}
                  POST   /user_sessions(.:format)          {:controller=>"user_sessions", :action=>"create"}
 new_user_session GET    /user_sessions/new(.:format)      {:controller=>"user_sessions", :action=>"new"}
edit_user_session GET    /user_sessions/:id/edit(.:format) {:controller=>"user_sessions", :action=>"edit"}
     user_session GET    /user_sessions/:id(.:format)      {:controller=>"user_sessions", :action=>"show"}
                  PUT    /user_sessions/:id(.:format)      {:controller=>"user_sessions", :action=>"update"}
                  DELETE /user_sessions/:id(.:format)      {:controller=>"user_sessions", :action=>"destroy"}
            login        /login(.:format)                  {:controller=>"user_sessions", :action=>"new"}
           logout        /logout(.:format)                 {:controller=>"user_sessions", :action=>"destroy"}
            users GET    /users(.:format)                  {:controller=>"users", :action=>"index"}
                  POST   /users(.:format)                  {:controller=>"users", :action=>"create"}
         new_user GET    /users/new(.:format)              {:controller=>"users", :action=>"new"}
        edit_user GET    /users/:id/edit(.:format)         {:controller=>"users", :action=>"edit"}
             user GET    /users/:id(.:format)              {:controller=>"users", :action=>"show"}
                  PUT    /users/:id(.:format)              {:controller=>"users", :action=>"update"}
                  DELETE /users/:id(.:format)              {:controller=>"users", :action=>"destroy"}
          account POST   /user(.:format)                   {:controller=>"users", :action=>"create"}
      new_account GET    /user/new(.:format)               {:controller=>"users", :action=>"new"}
     edit_account GET    /user/edit(.:format)              {:controller=>"users", :action=>"edit"}
                  GET    /user(.:format)                   {:controller=>"users", :action=>"show"}
                  PUT    /user(.:format)                   {:controller=>"users", :action=>"update"}
                  DELETE /user(.:format)                   {:controller=>"users", :action=>"destroy"}
           signup        /signup(.:format)                 {:controller=>"users", :action=>"new"}
             root        /                                 {:controller=>"users", :action=>"new"}

Start the application

$ rails server

Visit http://localhost:3000 – default root path set to signup. Otherwise you can hit /signup to register an account! Then log out by going to /logout, and try logging back in at /login.

Note: Dont forget to delete the public/index.html

References
The majority of the content was taken from the Authlogic tutorial on github.
and thanks to http://joelgreutman.com/2011/03/13/rails-3-and-authlogic-basic-setup

Understand Rails Authenticity Token!

What happens:
When the user views a form to create, update, or destroy a resource, the rails app would create a random authenticity_token, store this token in the session, and place it in a hidden field in the form. When the user submits the form, rails would look for the authenticity_token, compare it to the one stored in the session, and if they match the request is allowed to continue.

Why this happens:
Since the authenticity token is stored in the session, the client can not know its value. This prevents people from submitting forms to a rails app without viewing the form within that app itself. Imagine that you are using service A, you logged into the service and everything is ok. Now imagine that you went to use service B, and you saw a picture you like, and pressed on the picture to view a larger size of it. Now, if some evil code was there at service B, it might send a request to service A (which you are logged into), and ask to delete your account, by sending a request to http://serviceA.com/close_account. This is what is known as CSRF (Cross Site Request Forgery).

If service A is using authenticity tokens, this attack vector is no longer applicable, since the request from service B would not contain the correct authenticity token, and will not be allowed to continue.

Notes: Keep in mind, rails only checks POST, PUT, and DELETE requests. GET request are not checked for authenticity token. Why? because the HTTP specification states that GET requests should NOT create, alter, or destroy resources at the server, and the request should be idempotent (if you run the same command multiple times, you should get the same result every time).

Lessons: Use authenticity_token to protect your POST, PUT, and DELETE requests. Also make sure not to make any GET requests that could potentially modify resources on the server.

Courtesy: Faisal and stack overflow

Apache Thrift with Ruby – A basic Tutorial

Thrift is a software framework for scalable cross-language services development. It combines a software stack with a code generation engine to build services that work efficiently and seamlessly between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, and OCaml.

Originally developed at Facebook, Thrift was open sourced in April 2007 and entered the Apache Incubator in May, 2008.

Thrift is arguably one of the best lightweight systems for cross-language programming using code-generation, RPC, and object serialization. Designed from the ground up by the Facebook development team it enables seamless integration of the most commonly used languages (Ruby, Perl, C/C++, Haskell, Python, Java, and more) via a set of common RPC libraries and interfaces.

In this post I’ll outline how to setup and connect a thrift client in ruby

To install thrift

gem install thrift

Steps:

  • Defining a thrift service
  • Run the server script(ruby)
  • Run the client script(ruby)

Defining a service in Thrift

The core goal of Thrift is to enable efficient and reliable communications across multiple languages. All datatypes and services are defined in a single language-neutral file and the necessary code is auto-generated for the developer by the thrift compiler.

One of the great things about thrift is that it simplifies the communication between a client and server to a simple .thrift file. This file describes the data structures, and functions available to your remote service. In this tutorial, we use a simple calculator mechanism.

Define a struct and a service

	enum BinaryOperation {
		ADDITION = 1,
		SUBTRACTION = 2,
		MULTIPLICATION = 3,
		DIVISION = 4,
		MODULUS = 5
	}

	/** Structs are the basic complex data structures. They are comprised of fields 
	   * which each have an integer identifier, a type, a symbolic name, and an 
	   * optional default value. */
   
	struct ArithmeticOperation {
		1:BinaryOperation op,
		2:double lh_term,
		3:double rh_term,
	}

	/* Structs can also be exceptions, if they are nasty. */
	exception ArithmeticException {
		1:string msg,
		2:optional double x,
	}


	service Calculator {

		/**
		* A method definition looks like C code. It has a return type, arguments,
		* and optionally a list of exceptions that it may throw. Note that argument
		* lists and exception lists are specified using the exact same syntax as
		* field lists in struct or exception definitions.
		*/
		
		double calc(1:ArithmeticOperation op) throws (1:ArithmeticException ae),
		
		
		/**
		* This method has an oneway modifier. That means the client only makes
		* a request and does not listen for any response at all. Oneway methods
		* must be void.
		*
		* The server may execute async invocations of the same client in parallel/
		* out of order.
		*/
		
		oneway void run_task()
	}

Open a empty file and create service(copy the above code) and save it with .thrift extension(eg:calc.thrift)

We’ve defined two different functions: remote calculationand an asynchronous method call. To generate the required code, we simply call the thrift generator:

# Generate C++, Ruby, and Python implementations
# generated code will be in ‘gen-cpp’, ‘gen-rb’, ‘gen-py’ folder

thrift --gen rb calc.thrift

The beauty of this approach is, of course, the ability to mix and match implementations of services: server may be written in C++, but we can access it seamlessly via a Python, Java, or a Ruby client. Thrift will take care of the communications links, object serialization, and socket management!

Implementing a Thrift powered server in Ruby

A very simple example of interaction with a calculator server application whose actions are facilitated by thrift. Both the client and server negotiate on the common interface defined by calc.thrift.

# include thrift-generated code
$:.push('../gen-rb')

require 'thrift'
require 'calculator'

# provide an implementation of Calculator
class CalculatorHandler
	def initialize()
		#nothing goes here
	end

	def calc(val)
		lh_term = val.lh_term
		rh_term = val.rh_term
		case val.op
			when 1 #Addition
				lh_term+rh_term
			when 2 #subraction
				lh_term-rh_term
			when 3 #multiplication
				lh_term*rh_term
			when 4 #division
				lh_term/rh_term
			when 5	#modulas
				lh_term%rh_term
		end
	end
	
	def run_task()
		puts "Kicking off the task"
	end
end

# Thrift provides mutiple communication endpoints
#  - Here we will expose our service via a TCP socket
#  - Web-service will run as a single thread, on port 9090

handler = CalculatorHandler.new()
processor = Calculator::Processor.new(handler)
transport = Thrift::ServerSocket.new(9090)
transportFactory = Thrift::BufferedTransportFactory.new()
server = Thrift::SimpleServer.new(processor, transport, transportFactory)
puts "Starting the Calculator server..."
server.serve()
puts "done."

To expose our thrift service, we wrap it into a TCP socket listening on port 9090. From this point on, Thrift takes over all communications, serialization and handling of the incoming requests. Because the protocol is identical in every language, the client may be written in any language of choice.

Building the Ruby Client

You may have the client program in any language which supports Thrift.

In similar fashion, we can build a Ruby client for any Thrift service with just a few lines of code. To interface with our server implementation above, refer the following code:

# include thrift-generated code
$:.push('../gen-rb')

require 'thrift'
require 'calculator'

begin
	port = ARGV[0] || 9090

	transport = Thrift::BufferedTransport.new(Thrift::Socket.new('localhost', 9090))
	protocol = Thrift::BinaryProtocol.new(transport)
	client = Calculator::Client.new(protocol)

	transport.open()

	ar = ArithmeticOperation.new()    
	ar.op = BinaryOperation::ADDITION
	ar.lh_term = 99
	ar.rh_term = 3
	
	# Run a remote calculation
	result = client.calc(ar)  #it accessing the ruby server program method calc via thrift service
	puts result.inspect
	
	#Run a Async call
	client.run_task()
	
	transport.close()
rescue
	puts $!
end

And there you have it, a simple thrift client. I hope you think about thrift next time you need to interface between 2 applications, or require some kind of client->server model for your system. Thrift makes it very fast and convenient to share data across different programming languages and makes it easy/flexible to develop to a shared interface contract. Cheers!

Download Files
calc.thrift
ruby_server
ruby_client

Summary:

  • Download the calc.thrift and place it into the new folder(name it whatever you like. Eg:- thrift-ruby)
  • cd thrift-ruby
  • thrift –gen rb calc.thrift (it will create the auto generated ruby code. Dont change anything on the auto generated code)
  • create the folder named “rb” and place the ruby_server.rb and ruby_client.rb
  • run server program first. $ruby ruby_server.rb (it will start the server)
  • then run the client program in new cmd prompt. $ruby ruby_client.rb

References:

Making beautiful graphs with Ruby

I’m evaluating some charting tools (free) to be used with RoR web
application. Those should be easy to use and yet have some elegant
features. At the moment I’m using Open Flash chart. But I’m on
evaluating following

New source path for ruby gems

If you would had chance to view any ruby news in recent days , you would have come to know that github is no more going to host gems any-more. Ofcourse github will continue to host current gems (if I am not wrong that too for an year only)

Ok. No worries. From now onwards gemcutter is going to host ruby gems. so we all would need to update our ruby gem source path. commands to do so are as follows:

sudo gem update –system
sudo gem install gemcutter
gem tumble

This will add gemcutter as your primary RubyGem source. I think in near future all gems will be available at gemcutter, not rubyforge or github.
Follow above mentioned steps, to make sure you install proper version of gem.

blackbook gem (Contact Importer gem for Ruby on Rails)

Blackbook automates the nitty-gritty of importing contacts from various services and files and exporting them as VCard, XML, or simple Hash. Utilize those contacts from services like AOL, GMail, Yahoo Mail, Hotmail or CSV to help your social networking site become GIGANTIC overnight! You‘ll be able to get big and sell for millions before anyone figures out it‘s just like every other social network.

Websitehttp://contentfree.rubyforge.org/blackbook/

FEATURES/PROBLEMS:

The current list of supported services and file types:

Import:

  • AOL
  • CSV files
  • Gmail
  • Hotmail
  • Yahoo! Mail

Export:

  • Simple hash (default)
  • Vcard
  • XML

If you create an additional importer or exporter – or simply find a bug – please consider submitting it as a patch to the project so the community can all benefit from your hard work and ingenuity.

SYNOPSIS:

# An example of fetching Gmail contacts – by default, returns an array of hashes with :name and :email

  contacts = Blackbook.get :username => 'me@gmail.com', :password => 'whatever'

# or returning XML

  contacts = Blackbook.get :username => 'me@gmail.com', :password => 'whatever', :as =>  :x ml

# or importing from a CSV file

  contacts = Blackbook.get :csv, :file => #<File:/path/to/file.csv>

REQUIREMENTS:

  • Mechanize and its dependencies, for interacting with online providers
  • Fastercsv for reading CSV, Mechanize >= 0.7.0 for page scraping

INSTALL:

  • sudo gem install blackbook


Paypal on Rails – ActiveMerchant tips

Anyone who has looked into using ActiveMerchant and PayPal together knows it is quite a challenge. ActiveMerchant is great, but there is no documentation for using it with Paypal. Here are some tips to help you get it working:

Here is a page with some good references.

Setting up Paypal

The paypal website is a bit tough to navigate and they don’t make it obvious to figure out what are all the things you need to do to get it all setup.

  1. You will need a sandbox (developer) paypal account to test your set up. Sign up here
  2. The sandbox does not send emails, so you will have to use the Sandbox Email Tab to validate your sandbox account email address.
  3. Launch the sandbox
  4. Create a sandbox business account. and go through the normal paypal verification stuff. (It does not use your real bank account.)
  5. Go to the Merchant Services tab and signup for a Website Payments Pro Account.
  6. Verify your merchant account. You will be asked to enter you SSN number, use 111 + six random numbers. If you get an error message, try a different six numbers, the fake ssn must be unique in the sand box.
  7. Under the My Account tab and the Profile subtab and the Billing Agreements link, make sure you accept the Agreement.
  8. Same for the PayPal Monthly Billing link.
  9. Follow these instructions to create your API Certificate. Do not make the API Signature. You only need to do the “Step 1. Generate Certificate” part.
  10. Make sure you save the username & password given and download and save the pem file.

Paypal should now be set up. You will have to go through and do all the same stuff for your real paypal account, once your development is done and working.

Setting up ActiveMerchant

  1. Install ActiveMerchant by following the instructions, you will need to install the money gem, which ActiveMerchant requires.
  2. Rename the pem file to cert_key_pem_dev.txt and put it in your config dir.
  3. Add the following code to your environments/development.rb:
  4. 
    config.after_initialize do
      ActiveMerchant::Billing::Base.gateway_mode = :test
      ActiveMerchant::Billing::PaypalGateway.pem_file = File.read(RAILS_ROOT + '/config/cert_key_pem_dev.txt')
    end
    $PAYPAL_LOGIN = ''
    $PAYPAL_PASSWORD = ''
    
  5. Use the actual username and password in the previous code (don’t use the empty strings 🙂
  6. Do the same for the environments/test.rb
  7. Do the same for the environments/production.rb, except remove the line: ActiveMerchant::Billing::Base.gateway_mode = :test and use the cert_key_pem_prod.txt file (which you can get through your real paypal account (renaming to cert_key_pem_prod.txt ).
  8. Do something like this in your code:
  9. 
    
    amount = 1000 #$10.00
    credit_card = ActiveMerchant::Billing::CreditCard.new(
        :type       => 'visa',
        :number     => '4242424242424242',
        :month      => 8,
        :year       => 2009,
        :first_name => 'Bob',
        :last_name  => 'Bobsen',
        :verification_value=> '123'
    )
    
    flash[:error] = credit_card.errors and return unless credit_card.valid?
    
    billing_address = {
        :name     => "John Smith",
        :address1 => '123 First St.',
        :address2 => '',
        :city     => 'Los Angeles',
        :state    => 'CA',
        :country  => 'US',
        :zip      => '90068',
        :phone    => '310-555-1234'
    }
    
    gateway = ActiveMerchant::Billing::PaypalGateway.new(:login=>$PAYPAL_LOGIN, :password=>$PAYPAL_PASSWORD)
    
    res = gateway.authorize(amount, credit_card, :ip=>request.remote_ip, :billing_address=>billing_address)
    
    if res.success?
        gateway.capture(amount, res.authorization)
        flash[:notice] = "Authorized"
        redirect_to somewhere_url
    else
        flash[:error] = "Failure: " + res.message.to_s
    end
    
  10. Make sure State and Country are two character code.
  11. The credit card errors is actually a hash, not an array (like AR.errors), so you will need a helper to display the error.

Last Tip

  1. When testing with paypal, you must use port 80: ./script/server -p 80