Sunday 27 November 2016

Infinite or Endless Scrolling pagination in Rails with Kaminari

In this blog we will implement infinite scrolling with kaminari gem. Infinite scrolling appends more content to the end of the page before the viewer gets to it, so they can just keep scrolling unabated and the application doesn't need to initially load a bunch of records only a small percent of viewers will ever scroll down to.

Add 'kaminari' to your Gemfile -


gem 'kaminari'

Run the jQuery generator to download the necessary files and setup your Rails app to use jQuery.

$ rails generate jquery:install

In Controller -

def index
  @projects = Project.order(:created_at).page(params[:page])
end


In index.html.erb

<h1>Listing projects</h1>

<table id="projects">
  <thead>
    <tr>
      <th>Title</th>
    </tr>
  </thead>

  <tbody class="project-snippet">
    <%= render @projects %>
  </tbody>
</table>

<br>

<%= paginate @projects %>

<script>

paginator = new ScrollPaginator({
       item: $('.project-snippet'),
       items_source_url: '<%= projects_path %>',
       total_pages: <%= @projects.count/(Project::PAGINATION_SIZE) %>
   }).enable();

</script>


In assets/javascripts/scrollpaginator.js -

ScrollPaginator.VISIBILITIES = ['TOP', 'COMPLETE'];

function ScrollPaginator(options) {
   this.options = {
       object_visibility: 'TOP',
       total_pages: 0
   };
   $.extend(this.options, options);

   this.sample_element = $('<div id="sample_object" class="sample_object"></div>');
   $(this.sample_element)
       .height(this.options.item.height()).width(this.options.item.width())
       .attr('class', $(this.sample_element).attr('class') + this.options.item.attr('class'));

   this.last_loaded_page = 1;
   this.request_next = true;
}

ScrollPaginator.prototype = {
   provision: function () {
       this.sample_element.attr('class', this.options.item.attr('class'));
       this.options.item.parent().append(this.sample_element)
   },

   validVisibility: function () {
       $.inArray(this.options.object_visibility, ScrollPaginator.VISIBILITIES)
   },

   increment_page: function () {
       this.last_loaded_page += 1;
       if (this.last_loaded_page >= this.options.total_pages) {
           this.last_loaded_page = this.options.total_pages;
       }
   },

   decrement_page: function () {
       this.last_loaded_page -= 1;
       if (this.last_loaded_page <= 1) {
           this.last_loaded_page = 1;
       }
   },

   activateOnScrollPagination: function () {
       var _this = this;
       $(window).scroll(function () {
           _this.loadItems();
       });
   },

   isSampleVisible: function () {
       var docViewTop = $(window).scrollTop();
       var docViewBottom = docViewTop + $(window).height();
       var elemTop = $(this.sample_element).offset().top;
       var elemBottom = elemTop + $(this.sample_element).height();
       switch (this.options.object_visibility) {
           case 'TOP':
               return ((elemTop <= docViewBottom) && (elemTop >= docViewTop));
               break;
           case 'COMPLETE':
               return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
               break;
           default:
               console.log('Visibility option is not valid.');
               return false;
       }
   },

   loadItems: function () {
       var _this = this;
       if (this.isSampleVisible()) {
           if (this.request_next) {
               if (this.last_loaded_page < this.options.total_pages) {
                   this.increment_page();

                   /*--- For preventing multiple requests ---*/
                   this.request_next = false;

                   $.ajax({
                       method: 'GET',
                       dataType: 'script',
                       url: this.options.items_source_url + '?page=' + _this.last_loaded_page
                   }).success(function () {
                       _this.request_next = true;
                       _this.loadItems();
                   }).fail(function () {
                       _this.decrement_page();
                   });
               } else {
                   this.request_next = false;
               }
           }
       }
   },

   enable: function () {
       this.provision();
       this.activateOnScrollPagination();
       this.loadItems();
   }
};

Sunday 20 November 2016

Web-interface for Resque's worker with resque-web

In previous blog we have implemented background job with Resque. Now we will implement web interface for these background workers.

For this we will use resque-web gem. Resque’s web interface is a great way to get a high-level understanding of your background workers, and its pluggable design has made it easy for others to contribute a number of really useful plugins.

So, in Gemfile -

gem 'resque-web',


Now, mount it in your config/routes.rb -

require "resque_web"

MyApp::Application.routes.draw do
  mount Resque::Server.new, at: "/resque"
end

In almost cases we certainly want to limit access when using resque-web in production. We can achieve this with routes constraints. Here we are allow access only to admin user.

So, edit your routes.rb - 

authenticate :admin_user do
    mount Resque::Server.new, :at => '/resque'
 end

Now, we have done with resque-web configuration. So,restart your Rails server.
Open up

http://localhost:3000/resque
in a browser to check out the web backend.


On the Overview tab you can see a list of the queues and workers. Each queue shows a count of pending jobs. The list of workers displays what queue(s) each worker is working on, and the job currently being processed (if any).

Sunday 13 November 2016

Check whether stripe's source is already exist or not in Ruby on Rails application

Stripe is a very popular payment solution that integrates really well into a Rails application. In this blog, we’ll guide you through how to check whether stripe's source is already exist or not.

Stripe allows storing of duplicate sources if you are using same card. In this case, if customer have provided same card multiple time. Stripe will create same card details storing multiple times as multiple cards.

For overcome from this situation and if you want make less calls to stripe, it is recommended that you store the fingerprints of all the souces locally and use them for checking uniqueness. Storing fingerprints of sources locally is secure and it uniquely identifies a source.

first we will fetch customer details from stripe -


#fetch the customer
customer = Stripe::Customer.retrieve(stripe_customer_token)

#Retrieve the card fingerprint using the stripe_card_token 
card_fingerprint = Stripe::Token.retrieve(stripe_card_token).try(:card).try(:fingerprint)

# check whether a card with that fingerprint already exists

default_card = customer.sources.all.data.select{|card| card.fingerprint ==  card_fingerprint}.last if card_fingerprint
#create new card if do not already exists

default_card = customer.sources.create({:card => stripe_card_token}) unless default_card

#set the default card of the customer to be this card, as this is the last card provided by User and probably he want this card to be used for further transactions

customer.default_card = default_card.id

# save the customer
customer.save


Sunday 6 November 2016

Background Jobs with Resque gem in Rails

It is very important in website building to keep your response times less. Long-running requests may be degrade server resource. In this situation we can perform some task in background using another resources.

Here we will use Resque for background job. Resque is a Redis-backed library for creating background jobs, placing those jobs on multiple queues, and processing them later.

Setting up Resque -

Hence, Resque is using Redis-backed library so we have to install first redis server -

Please follow the digital ocean link for installing redis https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-redis-on-ubuntu-16-04

Set Up Redis -

config/initializers/resque.rb -


ENV["REDISTOGO_URL"] ||= "redis://username:password@host:1234/"

uri = URI.parse(ENV["REDISTOGO_URL"])
Resque.redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password, :thread_safe => true)


In Gemfile -


gem 'resque'
gem 'resque-web', require: 'resque_web'

Run bundle

Next include it in your application -

require 'resque'

Now we will create a queue for appointment receipt sender. For this create a folder of workers in app directory.

Create a file appointment_receipt_sender.rb -

class AppointmentReceiptSender
  @queue = :appointment_receipt_sender_queue
 
  def self.perform(appt_id)
    appt = Appointment.find appt_id
    client = appt.client

    AppointmentMailer.receipt_email(client).deliver
    appt.receipt_sent_at = Time.now
    appt.save
  end
end

In controller -

def appointment_receipt_sender
  Resque.enqueue(AppointmentReceiptSender, appt_id)
end

To start a worker, create a Rakefile in your app's root (or add this to an existing Rakefile):

require 'your/app'
require 'resque/tasks'

This will load the Resque tasks and load the environment which is required for doing any work.

To start a worker that will pull work off of all queues run the command:

$ rake resque:work QUEUE=*


Monitoring the Resque Queue

Open your config/routes.rb and mount the application like this:

require 'resque/server'

MyApp::Application.routes.draw do
  mount Resque::Server.new, at: "/resque"
end


Then restart your Rails server. Open up
http://localhost:3000/resque
in a browser to check out the web backend.

On the Overview tab you can see a list of the queues and workers. Each queue shows a count of pending jobs. The list of workers displays what queue(s) each worker is working on, and the job currently being processed (if any).