codedecoder

breaking into the unknown…


Leave a comment

pagination missing rails_admin view

Recently, I have integrated, the current version of rails_admin 0.4.9 and found that, everything working fine except, the pagination link. The pagination link not appearing on any of the index view of the rails_admin. I tried hard to google the solution, but failed and finally posted my problem here on stack overflow. But, since no answer coming from stack overflow, I tried to dig deeper in the problem myself. So this is how I followed

first thing first, check the code, which render the rails_admin index view. my target file is rails_admin-0.4.9/app/views/rails_admin/main/index.html.haml  of the rails_admin gem. On the first look, itself I got the problem, they have put the pagination within if else condition, in their this recent commit

if @objects.respond_to?(:total_count)

.....pagination code...

else

......without pagination code......

end

Infact, this change has resulted in a known issue for rails_admin reported here . The thread also have the solution. So what is happening due to the above change. The code is trying to check, if the object have total_count method defined on it or not. The gem owner has introduced this change to prevent “undefined method total_count” . unfortunately, it solve the error at the cost of escaping pagination. So now, we know the cause. The Solution is to define total_count method on the object . The objects here is returned from will_paginate gem. so we are going to add the total_count method to it through monkey patching . monkey patching is nothing but adding new methods or overriding the existing one, by maintaining same hierarchy of class and module in your application. Say, to solve our problem, we will monkey patch will_paginate module as below.

create a file with any name say will_paginate_patch.rb in /config/intializer folder of your app, and add below line to it.

if defined?(WillPaginate)
  module WillPaginate
    module ActiveRecord
      module RelationMethods
        def per(value = nil)
          per_page(value)
        end
        def total_count()
          count
        end
      end
    end
    module CollectionMethods
      alias_method :num_pages, :total_pages
    end
  end
end

so, if you see the /lib/will_paginate/active_record.rb file of will_paginate gem, you can see that, they already have per_page and count method defined in it, but there is no total_count or per method in it, so the above patch will add the method and solve our problem.


Leave a comment

rails_admin custom logout link

rails_admin generally work with devise. Its logout link simply destroy the user session. But in my application, although I am using devise, the actual authentication is done on OPENAM. If user is authenticated on OPENAM, I used to sign_in the user with devise. Similarly, the logout link on my application go to my custom action, where I destroy the user session on OPENAM and then call devise sign_out method to destroy the devise user session.

So basically, on rails_admin, I want to change the logout url from “/users/sign_out” which call devise  sessions#destroy action to “/logout“, which will call my custom logout method.

Unfortunately, I do not find any configuration option provided by rails_admin to this. I do find a thread here , which talked of the configuration option introduced for logout, but some how it do not worked for me, may be the changes are not present in current rails_admin 0.4.3.

The workaround, is to replace the link on page load for rails admin. You can add custom js or css to rails_admin. It is explained here .

In our case to change, the rails_admin logout link follow the below steps :

STEP 1: create rails_admin js override file

add ui.js file at below location in your application –

app/assets/javascripts/rails_admin/custom/ui.js # any js code in this file will override, those set in rails_admin

STEP 2 : write the js code to replace the logout link.

jQuery(function(){
      $(document).ready(function(){
      #It will check that the page do contain the link to be replaced
      if($("a[href='/users/sign_out']").length > 0){ 
      $("a[href='/users/sign_out']").attr("href", "/logout")
      }
    })
 })

that’s all, now whenever user click on logout link, it will take him to your custom logout action.

STEP 3: Adding Static links to Rails Admin

Though not related to logout link, I want to mention it that you can add any static link to rails Admin Navigation.

It is explained here .


23 Comments

Sample API consumer or API client

The people who make API call are API consumer or API client. Those who build the API to expose there application, making it interact with other application are called API provider. I have created a sample API in this post . Any other application(the consumer) can make call to this API to register, update or delete the user on API provider database.

You can see that the API provider has documented the url, authentication, expected data type and data required to fulfil your request. Provided, you fulfil, those criteria  , you can write your API consumer code in any language. Here, we will stick to Ruby.

Ruby has inbuilt library, to manage , making API calls, retrieving the returned response and parsing it to  reuse it in your own application. Below are those library.

http://ruby-doc.org/stdlib-1.9.3/libdoc/net/http/rdoc/Net/HTTP.html

http://rubydoc.info/gems/rest-client/1.6.3/RestClient

You may directly, use the above two library, to write your API consumer class. But, being part of ruby community, you do not need to go in much depth 🙂 as a no of Gem are available, there : – rest-client , faraday , httparty . I am not in position, to make a comment on which one is best or advantage and disadvantage of them, as I have tried only rest-client.

Now Let us make a Application which will consume the API,  I have developed in this post. The API, provide functionality to manage user, so in our application we will not store user in our own database, but will register it , update and delete them with the API provider.

I am assuming that, you all have implemented users controller at some point of time, where you have implemented index , new, create, edit, update and destroy method. Here, the view, routes and everything remain same, only the Users controller code will change, as now, it will interact, not with your Databse, but with the API provider Database .

Let us create our Rails Apllication, which will consume the user management API

STEP 1: generate the rails app

we will call it api_consumer

$ rails new api_consumer

STEP 2: add rest-client gems to your Gemfile

gem ‘rest-client’

run bundle install on the terminal

$ bundle install

STEP 3: generate the user controller

$ rails g controller users

STEP 4: add the routes for users resource in your routes

ApiConsumer::Application.routes.draw do
resources :users
end

STEP 5: Write the users controller code.

Users controller will have, the same actions and flow as you have seen in your, users controller, except that, here you interact with API provider database, thorugh its API call. IF you see the API provider document, you can see that they have provided you the authentication, url and expected data format.

You just have to make a call to these URL and handle the returned data in your application. API now a days return, data in XML or json format in general, but in case they return data in some other format say SOAP, then you have to adjust your code accordingly. The API provider whose service we are going to use return XML data by default i,e if you do not provide format in URL, they will return XML data. But, I found it easy to handle json data, So while making the call, we will specifically ask for json data.

So below is our controller code :

NOTE : see the use of get post put and delete call of the REST client. It will decide which action of API provider will be called for certain URL

class UsersController < ApplicationController
  require 'rest_client'

  USERNAME = "myfinance" # needed to access the APi
  PASSWORD = "credit123" # needed to access the APi
  API_BASE_URL = "http://localhost:3000/api" # base url of the API

  def index
    uri = "#{API_BASE_URL}/users.json" # specifying json format in the URl
    rest_resource = RestClient::Resource.new(uri, USERNAME, PASSWORD) # It will create
    new rest-client resource so that we can call different methods of it
    users = rest_resource.get # will get back you all the detail in json format, but it
    will we wraped as string, so we will parse it in the next step.
    @users = JSON.parse(users, :symbolize_names => true) # we will convert the return 
    data into array of hash.see json data parsing here
  end

  def new

  end

  NOTE :  see below how we are passing the payload and content type while making the post call
  def create
    uri = "#{API_BASE_URL}/users"
    payload = params.to_json # converting the params to json
    rest_resource = RestClient::Resource.new(uri, USERNAME, PASSWORD)
    begin
      rest_resource.post payload , :content_type => "application/json"
      flash[:notice] = "User Saved successfully"
      redirect_to users_path # take back to index page, which now list the newly created user also
    rescue Exception => e
     flash[:error] = "User Failed to save"
     render :new
    end
  end

  def edit
    uri = "#{API_BASE_URL}/users/#{params[:id]}.json" # specifying format as json so that 
                                                      #json data is returned 
    rest_resource = RestClient::Resource.new(uri, USERNAME, PASSWORD)
    users = rest_resource.get
    @user = JSON.parse(users, :symbolize_names => true)
  end

  def update
    uri = "#{API_BASE_URL}/users/#{params[:id]}"
    payload = params.to_json
    rest_resource = RestClient::Resource.new(uri, USERNAME, PASSWORD)
    begin
      rest_resource.put payload , :content_type => "application/json"
      flash[:notice] = "User Updated successfully"
    rescue Exception => e
      flash[:error] = "User Failed to Update"
    end
    redirect_to users_path
  end

  def destroy
    uri = "#{API_BASE_URL}/users/#{params[:id]}"
    rest_resource = RestClient::Resource.new(uri, USERNAME, PASSWORD)
    begin
     rest_resource.delete
     flash[:notice] = "User Deleted successfully"
    rescue Exception => e
     flash[:error] = "User Failed to Delete"
    end
    redirect_to users_path
   end
 end

STEP 6 : Create the corresponding views to the controller action

index.html.erb

It will list all the user detail returned by the API, it will as usual, provide “create new user” link to create a new user, also each record will have a Edit | Delete link. clicking the edit link will take the user to edit page where they will modify there detail and the Delete action will delete the user.

<%= link_to "Create New User", new_user_path %>
<%if @users.present? %>
  <p>Below is the list of users</p>
  <table width="100%">
    <tr>
      <td>ID</td>
      <td>first_name</td>
      <td>last_name</td>
      <td>email</td>
      <td>Action</td>
    </tr>
    NOTE : the @users object in our index action is array of hash, which is generated 
    when be passed return data to JSON.parse method. If you handled data in some other
    way, modify the code below accordingly. say for xml data you have to use something like
    u.xpath("id").text instead of u[:id]
    <%@users.each do |u|%>
    <tr>
      <td><%=u[:id]%></td>
      <td><%=u[:first_name]%></td>
      <td><%=u[:last_name]%></td>
      <td><%=u[:email]%></td>
      <td>
        <%= link_to "Edit", edit_user_path(u[:id]) %> | 
        <%= link_to "Delete", user_path(u[:id]), :method => :delete %>
      </td>
    </tr>
   <%end%>
  </table>
<%else%>
  <p>No User is Found</p>
<%end%>

new.html.erb

Here, as usual, user will fill up his detail and on submit it will go to create action, which will call create action of API to create the new user.

<%= form_tag users_path, :method => :post do %>
   email : <%=text_field_tag :email%><br />
   first_name :<%=text_field_tag :first_name%><br />
   last_name :<%=text_field_tag :last_name%><br/><br/>
   <%=submit_tag "save"%>
<%end%>

edit.html.erb
User will modify, there detail and submit to update action, which in turn update the record on API provider data base.

<%= form_tag user_path, :method => :put do %>
   email : <%=text_field_tag :email, @user[:email]%><br />
   first_name :<%=text_field_tag :first_name, @user[:first_name]%><br />
   last_name :<%=text_field_tag :last_name, @user[:last_name]%><br/><br/>
   <%=submit_tag "update"%>
<%end%>

STEP 7 : see it working

both the API provider and API consumer are sample application running on my own local machine. I have fixed the port 3000 for the API provider application. so start the API provider application on 3000, which is by default

$ cd to_api_provider_folder

$ rails s # will start the API provider service ast 3000

NOTE : for API consumer code to work, The API provider application must be up and running

Start the API consumer application on some other port, say 3030

$ cd api_consumer

$ rails s -p 3030

Go to the index page of the application:

http://localhost:3030/users # it will show you all users with edit | delete link and also a create new user link. So remaining things are self explainatory, try it out  yourself


1 Comment

escaping special characters & < etc in xml

some charecter like & , > or < etc have special meaning in xml structure. So If you are generating xml payload from user inputs for some API call, you must take care of these characters, or the generated xml payload will become corrupt and break your application.

This is sample code from one of my library which generate xml payload from user input to submit the content to an other API

 
require 'rest_client'
require 'base64'

module LoanPath
  class Application
    def apply_credit(detail)
      details=detail.symbolize_keys
      uri = "http://mybank.com/esb-sunpower/outbound/SubmitForCreditService"
      payload =<<-eos
      <soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
      <soap11:Body>
        <Request>
           <Payload>
               <customer>
                  <id>#{details[:CustomerID]}</id>
                  <firstName>#{details[:first_name]}</firstName>
                  <lastName>#{details[:last_name]}</lastName>
                  <street>#{details[:street]}</street>
                  <city>#{details[:city]}</city>
                  <state>#{details[:state]}</state>
               </customer>
          </Payload>
        </Request> 
      </soap11:Body>
     </soap11:Envelope>
     eos
     rest_resource = RestClient::Resource.new(uri, {:user => "arun", :password => "secret", 
                                                   :timeout => 90 s, :open_timeout => 90s})
     rest_resource.post payload, :content_type => "application/xml"
     end
  end
end

I trigger, apply credit method in my controller as below

LoanPath::Application.new.apply_credit(params) # params contain value filled by user for name city state etc.

Now, say if a user fill the street address as “23 & main street”, the application will crash due to internal server error, which is basically due to generation of wrong XML.

The solution is to, replace all the special character with there correct XML notation

"&" with "&amp";
"<"; with "&lt";  
">" with "&gt"; 
and so on.....

Well, you do not need to do much hard work. You just need to encode all the users value By using REXML Text module.

NOTE : ruby have its own library called REXML to do all the stuff with XML. just look up this document whenever, you need some solution for your XML need.

So the correct code will look like this

require 'rest_client'
require 'base64'

module LoanPath
  class Application
    def apply_credit(detail)
      details=detail.symbolize_keys
      uri = "http://mybank.com/esb-sunpower/outbound/SubmitForCreditService"
      payload =<<-eos
      <soap11:Envelope xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/">
      <soap11:Body>
        <Request>
           <Payload>
               <customer>
                  <id>#{details[:CustomerID]}</id>
                  <firstName>#{REXML::Text.new(details[:first_name], false, nil, false)}</firstName>
                  <lastName>#{REXML::Text.new(details[:last_name], false, nil, false)}</lastName>
                  <street>#{REXML::Text.new(details[:street], false, nil, false)}</street>
                  <city>#{REXML::Text.new(details[:city], false, nil, false)}</city>
                  <state>#{REXML::Text.new(details[:state], false, nil, false)}</state>
               </customer>
          </Payload>
        </Request> 
      </soap11:Body>
     </soap11:Envelope>
     eos
     rest_resource = RestClient::Resource.new(uri, {:user => "arun", :password => "secret", 
                                                   :timeout => 90 s, :open_timeout => 90s})
     rest_resource.post payload, :content_type => "application/xml"
     end
  end
end

So basically, you need to replace the value of the node with below method of RXML. see the detail documentation here

REXML::Text.new(“your string”, false, nil, false)

Below, are the description of the parameter above.

First Argument

“your string”  is the first argument and will hold your string you want to escape. we careful that your string can be empty but not nil

1.9.3p194 :022 > REXML::Text.new( nil, false, nil, false) # will throw below error  for nil
NoMethodError: undefined method `type’ for nil:NilClass

Also, the first argument should be a string. Not apply it to integer

REXML::Text.new( 1234, false, nil, false)
NoMethodError: undefined method `type’ for 1234:Fixnum

Second Argument

It have boolean value for white space, if set true, white space will be retained, for false it will be removed.

1.9.3p194 :018 > REXML::Text.new( “&     arun”, true, nil, false) # see that white space is retained in the string
=> “&     arun”

Third Argument:

Keep it nil or pass the parent object. I always keep it nil, have never tried with parent object

Fourth Argument:

raw (nil) This argument can be given three values.

If true, then the value of used to construct this object is expected to contain no unescaped XML markup, and REXML will not change the text.

1.9.3p194 :011 > REXML::Text.new( “Iam <&”, false, nil, true ) # setting last argument true will not escape < & , so throw error
RuntimeError: Illegal character ‘<‘ in raw string “Iam <&”

If this value is false, the string may contain any characters, and REXML will escape any and all defined entities whose values are contained in the text.

1.9.3p194 :011 > REXML::Text.new( “Iam <&”, false, nil, true )  # setting last argument false will escape < &  etc
“Iam <&”

If this value is nil (the default), then the raw value of the parent will be used as the raw value for this node. If there is no raw value for the parent, and no value is supplied, the default is false.

NOTE : always set the fourth argument to false if want to escape the special characters