Thursday 19 January 2017

Extract values from hash or array with .dig method in ruby 2.3

Suppose that we have a hash some think like this -

place ={
   country:{
      state:{
         district:"Katihar"
      }
   }
}


For access the district value we are doing like this -

place[:country][:state][:district]

It is working fine but there is a problem, suppose that if the state key is nil than it will return something like this-

#NoMethodError: undefined method `[]' for nil:NilClass

We can handle above error in rails by doing like this -

place.try(:[], :country).try(:[], :state).try(:[], :district)

But this is looking somewhat ugly isn't it?

Now, here comes ruby 2.3 with .dig method. The new #dig method can look for deeply nested keys-

place.dig(:country, :state, :district)

If any of the attempts to access a nested key is nil, the output will be nil.

Sunday 15 January 2017

User authentication Api with Devise.


In this blog we will create sign-up and login Api with devise. For this we will override registration controller and session controller of devise. For this you can follow devise documentation.

So, in Gemfile -

gem 'devise'

Then run bundle install

As we know that we need to run the devise generator

$ rails generate devise:install

Now, configure the default Devise modules with User model

$ rails generate devise User

For overriding devise's controller, create your custom controllers using the generator

$ rails generate devise:controllers User

We have to generate auth_token for every user for authentication

So, in User.rb

class User < ApplicationRecord

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable


  #==== Callbacks =======================================================
  before_create :generate_auth_token


  def generate_token
    SecureRandom.urlsafe_base64
  end


  private

  def generate_auth_token
    if self.auth_token.blank?
      begin
        self.auth_token = generate_token
      end while User.exists?(auth_token: self.auth_token)
    end
  end

end

We have to override routes also.

In roures.rb

Rails.application.routes.draw do
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  devise_for :users, controllers: {
                       registrations: 'users/registrations',
                       sessions: 'users/sessions'
                   }

  devise_scope :user do
    root to: "users/sessions#new"
    post '/sign-up' => 'users/registrations#create'
    post '/sign-in' => 'users/sessions#create'
  end
end


Now, we will override registration controller -

So, in users/registrations_controller.rb-

class Users::RegistrationsController < Devise::RegistrationsController

=begin
********************************************************************
  POST /resource.json
  params -
      {
       "user":{
          "first_name":"xyz",
          "last_name":"abc",
          "phone_number":"0987654321",
          "email":"xyz@xyz.com",
          "password":"12345678",
          "password_confirmation":"12345678"
       }
     }
********************************************************************
=end
  def create
    resource = User.new(configure_sign_up_params)
    respond_to do |format|
      if resource.save
        format.json { render json: {resource: resource}, status: :created }
      else
        format.json { render json: {error: resource.errors.full_messages}, status: :unprocessable_entity }
      end
    end
  end


  protected

  # If you have extra params to permit, append them to the sanitizer.
  def configure_sign_up_params
    params.require(:user).permit(:email, :first_name, :last_name, :phone_number, :password, :password_confirmation)
  end

end

Above code will return user obejct as json -


{
  "resource": {
    "auth_token": "NywmeVFFU1eyZE_BGBA4og",
    "id": 1,
    "first_name": "xyz",
    "last_name": "abc",
    "phone_number": "0987654321",
    "created_at": "2017-01-12T09:57:18.000Z",
    "updated_at": "2017-01-12T10:03:00.000Z",
    "email": "xyz@xyz.com"
  }
}

For login we have to override sessions_controller.rb

class Users::SessionsController < Devise::SessionsController

  #====== Filters ===============================================
  prepend_before_action :require_no_authentication, :only => [:create]
  skip_before_action :verify_signed_out_user, if: -> { request.format.json? }

=begin
*********************************************************
  POST /sign-in.json
  params - {"user":{"email": "xyz132@xyz.com", "password": "12345678"}}
*********************************************************
=end
  def create
    respond_to do |format|
      resource = User.find_for_database_authentication(email: params[:user][:email])
      if resource and resource.valid_password?(params[:user][:password])
        resource.auth_token = resource.generate_token
        format.json { render json: {resource: resource}, status: :ok }
      else
        format.json { render json: {error: 'Sign in not successful'}, status: :unprocessable_entity }
      end
    end
  end

end

On successful it will also return user object as josn -

{
  "resource": {
    "auth_token": "NywmeVFFU1eyZE_BGBA4og",
    "id": 1,
    "first_name": "xyz",
    "last_name": "abc",
    "phone_number": "0987654321",
    "created_at": "2017-01-12T09:57:18.000Z",
    "updated_at": "2017-01-12T10:03:00.000Z",
    "email": "xyz@xyz.com"
  }
}


Note that In api authentication we should not generate session for user because it should handle at front-end.

If you have web app also than you have to create session for user and use respond with html too. 


Thursday 5 January 2017

Fetching recent sold products and their sold quantity of ebay's seller through Ruby On Rails


For fetching recent sold products of ebay's seller, we will call GetSellerTransactions api of Trading Api.

As we know that ebay provide Trading Api's.Trading API follows eBay’s traditional listing model, where all aspects of a listing (description, product details, quantity and price, etc.) are created and managed.

So, we will integrate Trading Api's in our application. For this please follow my previous blog :

Authentication for ebay's seller and use Trading Api for getting seller information.

From above mentioned blog you can get ebay client. Which is necessary to call any api's of Trading Api.

Now, we will call GetSellerTransactions api through this ebay client.

def get_recent_sold_items
  begin
    inference_hash = {}
    recent_sold_items = @ebay_client.get_seller_transactions.payload
    transactions = recent_sold_items[:transaction_array][:transaction] rescue []
    transactions.each do |transaction|
      item = transaction[:item] rescue nil
      if item
        inference_hash[item[:item_id]] ||= {sold_count: 0, last_sold_at: (Time.now - 10.years)}
        inference_hash[item[:item_id]][:last_sold_at] = transaction[:created_date] if transaction[:created_date] > inference_hash[item[:item_id]][:last_sold_at]
        inference_hash[item[:item_id]][:sold_count] = (inference_hash[item[:item_id]][:sold_count] + item[:selling_status][:quantity_sold].to_i)
      end
    end
  rescue => e
    Rails.logger.debug("Exception occurred while fetching/get_recent_sold_items: #{e.message}")
    nil
end

From above method you will get recent sold product and their sold quantity.