codedecoder

breaking into the unknown…


Leave a comment

mixin in ruby : include prepend extend

Ruby do not support multiple inheritance directly, but implement it through Mixin . Mixin in ruby is achieved with include, prepend and extend. prepend is introduced with Ruby 2.0. Lets see, how they work and differ from each other.

Lets define few module and classes. We will experiment with them on irb or Rails console and try to understand their behaviour

module Introduction
  def introduce
    puts "Iam Arun"
  end
end

module Welcome
  def greet
    puts "Hi..How are you"
  end
end

module Host
  def serve_food
    puts "chiken biryani"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  prepend Introduction
  include Welcome
  include Host
  extend  SeeOff
end

class Celebrity
  
end

Copy paste above code in irb and keep experimenting as you proceed below :

1 => include and prepend add the module method as instance method, while extend add it as class method.

Calling as class method :

2.2.1 :058 > Person.introduce
NoMethodError: undefined method `introduce’ for Person:Class

2.2.1 :059 > Person.greet
NoMethodError: undefined method `greet’ for Person:Class

2.2.1 :060 > Person.bye
Byee…

You can see that, only bye method of module SeeOff worked as it is extended , so added as class method

Calling as instance method

2.2.1 :061 > Person.new.introduce
Iam Arun

2.2.1 :062 > Person.new.greet
Hi..How are you

2.2.1 :063 > Person.new.bye
NoMethodError: undefined method `bye’ for #<Person:0x8edec04>

Here include and prepend added the module method as instance method to the class, so it worked, but bye method of SeeOff module failed as it is added as a class method so can’t be call on the instance of Person.

Now considering the above behaviour, you may think when to use mixin as class method(using extend) and when to use as instance method(using include or prepened). It depended on the feature a module is providing and how you want to reuse it in your class. Infact in a ordinary class also, you may use the below thumb rule to define a method as a instance method or a class method.

Define a method as class method if you say want to use it within different methods of a class. say prity_print can be a class method
as it can be used in any method to well format the content before retuning it.

Second scenario is that the method behaviour not depend on the instance variable, for example it is not good idea to make full_name as class method as it depend on user’s first_name and last_name which differ from user to user, But say active_users can be a class method as it do not depend on any individual user

2. Adding module method at runtime : use extend to add method only to a specific instantiated object and use include or prepend to add instance methods to all the instance .

Above, the Celebrity class do not have any method, nor it has included or extended any of the module. Now say we want to add bye method at run time to Celebrity.

2.2.1 :065 > Celebrity.bye
NoMethodError: undefined method `bye’ for Celebrity:Class

> Celebrity.extend SeeOff
=> Celebrity
2.2.1 :067 > Celebrity.bye
Byee…

Now say. we have Katrina and Deepika as two celebrity.

2.2.1 :074 > katrina = Celebrity.new
=> #<Celebrity:0xe317eb0>
2.2.1 :075 > deepika = Celebrity.new
=> #<Celebrity:0xe3161f0>

But we want to greet katrina only

2.2.1 :076 > katrina.extend Welcome
=> #<Celebrity:0xe317eb0>

2.2.1 :077 > katrina.greet
Hi..How are you

2.2.1 :078 > deepika.greet
NoMethodError: undefined method `greet’ for #<Celebrity:0xe3161f0>

So you can see that, when extend act on katrina i,e an object already instantiated it add greet method of Welcome as instance method to it(remember it add module method as class method to person). Basically, extend can add a module method as class Method or instance method depending on who called it at the runtime. You can check that , we have extended SeeOff as the class method above, so it will not available to katrina or deepika who are basically a instance of Celebrity class.

katrina.bye
NoMethodError: undefined method `bye’ for #<Celebrity:0xe29b964>

Now say, you want to introduce yourself to any celebrity, thus the introduce method must be available to all its instance. In this case we have to use include.

2.2.1 :116 >Celebrity.include Introduction

2.2.1 :116 > salman = Celebrity.new
=> #<Celebrity:0xe26d848>
2.2.1 :117 > salman.introduce
Iam Arun

2.2.1 :118 >katrina.introduce
Iam Arun

Notice that, Introduction module is mixed to Celebrity class after creation of katrina object, but its introduce method also get available to it, this is because in ruby methods are accessed by reference, any object just contain its own variable and refer for any method in its class, so if some method is added later on then also it will be accessible to a object.

3 => Just like all classes are instances of Ruby’s Class, all modules in Ruby are instances of Module.Module is the superclass of Class, so this means that all classes are also modules, and can be used as such.

You can easily check it by calling ancestors method on Person class

2.2.1 :068 > Person.ancestors
=> [Introduction, Person, Host, Welcome, Object, PP::ObjectMixin, Delayed::MessageSending, ActiveSupport::Dependencies::Loadable, V8::Conversion::Object, JSON::Ext::Generator::GeneratorMethods::Object, Kernel, BasicObject]
2.2.1 :069 >

You can note down below from the above result

  1. extend do not add the module to the ancestors chain
  2. prepend add the module as child of the class in which it is called , see that Person become ancestor of Introduction
  3. include add the ancestors in the reverse order of their call. Host include in last, so it become first ancestor of Person

As you know we can override any method in ancestors class in its child class, and also use super to keep behaviour of ancestor intact and at the same time adding our own behaviour. Let rewrite our initial code to explain this.

module Introduction
  def introduce
    puts "Iam Arun"
    super
  end
end

module Welcome
  def greet
    puts "Hi..How are you"
  end
end

module Host
  def serve_food
    puts "chiken biryani"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  prepend Introduction
  include Welcome
  include Host
  extend  SeeOff
  
  def serve_food
    super
    puts "try lassi"
  end
  
  def introduce
    puts "Happy to meet you"
  end
end

 

Now you can get below result when call different methods of the Person class.

2.2.1 :110 > person = Person.new
=> #<Person:0xb4cd9d58>

2.2.1 :111 > person.serve_food
chiken biryani
try lassi

2.2.1 :112 > person.introduce
Iam Arun
Happy to meet you

And finally, let’s see an alternative way of extending a class. I do not use it myself as I believe in keeping the things simpler. But if you go through, gems and different library, you can see the use of self.included(base) .

Basically included is a callback , which get triggered whenever a module is included into another module or class. The argument base passed to it is basically the target module or class i,e that in which the module is included. Don’t get confused as initially I was. See the example below:

module Welcome
  def self.included(base)
    base.extend SeeOff
  end
  
  def greet
    puts "Hi..How are you"
  end
end

module SeeOff
  def bye
    puts "Byee..."
  end
end

class Person
  include Welcome
end

Note that, In Person class, we have only included the Welcome module, but in the Welcome module we have defined included method within which we have extended the SeeOff class with the line base.extend SeeOff , since Welcome module is included in Person class, when included callback get fired, Person will be become base class for it.So base.extend SeeOff will evaluated as Person.extend SeeOff .

You can check the result in console now:

2.2.1 :037 > Person.bye
Byee…

So you can see that bye method of SeeOff class get added as class method to Person class.

That’s all about mixin in ruby.. 🙂


2 Comments

Rails vs React : porting view in React

React is js framework to manage dynamic user interface . If you are a Rails Developer, you have implemented it numerous time with partial refresh.

Consider a simple scenario where a User comment on a picture or video or slide. You need to show the newly added comment in the comments list without reloading the page.

With rails you do it with partial refresh of the comment list.

It works well and you will be happy till you have tried React. Few of the Problem with Rails partial refresh is as below:

  1. You need to redraw the partial DOM, every time it get refreshed
  2. You need to reinitialize all the needed js on the partial Refresh

If data to render is large, User can easily see the Flicker on page while the partial is getting refreshed.

It give bad user experience. You can easily avoid it by using React. Infact you can completely replace Rails view with React view while using Rails for other great benefit it provides for web development.

Here, I will not going to explain React in detail, but just show you the changes you need to do or adopt to migrate from Rails view to React view. I will first show you the Rails implementation of dynamic listing of comments and then tell you the changes you need to do to make it work in React. If you already know about partial refresh in Rails, you can skip it and go directly to React Implementation

Rails Implementation : Partial Refresh of Dynamic component of View

View : (wordpress not supporting ruby view tag, so I have add the view code as image)
# slide detail page(app/views/slides/show.html.erb)

slide_show

This is slide show page(only relevant part is shown), rendering the partial list comments at top, followed by a input filled for writing comment and ‘Add’ button to submit the comment. At bottom we have a form with some hidden inputs.When user click on add button we populate the comment field of this form and submit it through js.

Below is the partial listing the comments
#list_comments partial(app/views/slides/_list_comments.html.erb)

list_comment_partial

A important thing in this partial is use of highlight_mentioned_user helper for displaying comment. Remember when you mention a user name in FB comment it get highlighted. Same is achieved here with the helper method, which basically highlight the name of user mentioned in the comment. Since this method can be used anywhere, I have placed it in application helper

#app/Helper/application.rb

module ApplicationHelper

 def highlight_mentioned_user(mentioner, content)
     mentioned_users = mentioner.mentionees(User)
     mentioned_users.each do |user|
     first_name = user.first_name
     linked_name = '<a href="javascript:void(0)" class="user_highlight">' + first_name + '</a>'
     content.gsub!(first_name, linked_name)
   end
   content
 end

end

Now let us see the js code which handle submit of comment to controller
#app/assets/javascript/slide.js

function add_comment() {
 if ($("#slide_comment").val() === '') {
   alert("Comment can't be blank");
   return false;
 }
 else {
   var mentioned_user = $('#slide_comment').mentionsInput('getMentions');
   var mentioned_user_ids = [];
   $.each(mentioned_user, function () {
     mentioned_user_ids.push(this.uid);
   });
   $("#slide_comment_mentioned_user").val(mentioned_user_ids.join(","));
   $("#comment").val($("#slide_comment").val());
   //mix panel event for Comment Added
   create_mix_panel_event("comment_added");

   //mix panel event for @mention Used in Comment
   if(mentioned_user_ids.length > 0) {
     create_mix_panel_event("@mention_used_in_comment");
   }
 
   $('#form_slide_add_comment').trigger('submit.rails');
   }
};

So we have add_comment() function in js, which is binded to Add button. When user click this button, it do basic validations , other needed stuff and submit the form.

On submit of form, data passed to controller action which look as below.

# app/controllers/slides_controller.rb

class SlidesController < ApplicationController
  def add_comment
   @slide = Slide.find(params[:slide_id])
   @user_who_commented = current_user
   comment = Comment.build_from(@slide, @user_who_commented.id, params[:comment])
   mentioned_user_ids = params[:slide_comment_mentioned_user].split(',').reject(&:empty?)
   if comment.save
     slide_comment_mention_notification(params[:id], @slide, comment, @user_who_commented, mentioned_user_ids)
   end
  end
end 

Now once control reach the controller action, since it is ajax call i,e request is of type XHR, Rails will render add_comment.js.erb file . In this file we will refresh the partial. It look as below

# app/views/slides/add_comment.js.erb

$('#slide_comments_swap').html('<%= escape_javascript(render partial: "slides/list_comments" ,locals: {slide: @slide} ) %>');
$('#slide_comment').val('');
$("#slide_comment").mentionsInput('clear');

Here we have refreshed the partial and cleared the input field , thus completing the process.

PORTING RAILS VIEW IN REACT :

Let start with React. Use this link to configure Reactt with your Rails App .

We are going to replace rails partial refresh with React, so we do not need the partial  _list_comments.html.erb anymore. Delete the _list_comment.html.erb partial .

The slide show page now look as below :

slide_show_react
You can see that, our view code reduced to one react component. Also it look familiar with the way we use partial in Rail , SlideComments is the name of the React Component(equivalent to _list_comments.html.erb of rails) . We have then passed the required data for the component.

One thing to note here that React accept data as Array of Hash. When you are using rails, you have a no of way to get your data in view itself, But within React component there is no rail code. For example you have passed comments collection to list_comments partial and within that you get the name of user who has commented with comment.user.full_name . But this is not possible with the data passed to React.

In Rails view we are showing each comment with the detail : commenter pic, commenter full_name, comment date and comment text. We need all these in React view also, So you can see that I have created react_slide_comments helper to which I am passing the comments collection, which then return the needed data. The Helper code look as below :

module SlidesHelper
  include ApplicationHelper
  
  # create needed detail to display in react's component
  def react_slide_comments(comments)
    react_comments = []
    comments.each do |comment|
      react_comments << react_slide_comment(comment)
    end
    react_comments
  end
  
  # get data of individual comment
  def react_slide_comment(comment)
    {
      id: comment.id,
      commenter_name: comment.user.full_name,
      commenter_img: comment.user.picture.expiring_url(60, :thumb),
      comment_date: comment.created_at.strftime('%B %d, %Y'),
      comment_text: highlight_mentioned_user(comment, comment.body)
    }
  end
  
end

So at this point, we have all the required data earlier used in the Rails view. In the slide Helper we have included the Application Helper at the top as it contain the method highlight_mentioned_user used for highlighting users mentioned in a comment .

NOTE : you need to create exactly the same DOM structure as you are using in Rails. The only difference is that you will not write as HTML but as React node in JS.

I suggest everyone to use Coffee Script to write the React code, as there is two many lines of code and become cumbersome if writing in pure js.

First we will create the component SlideComments . For this create the coffee file slide_comments.js.coffee in components folder

# app/assets/javascript/components/slide_comments.js.coffee

@SlideComments = React.createClass
  getInitialState: ->
    comments: @props.comments
    project_scoped: @props.project_scoped
    project_id: @props.project_id
    slide_id: @props.slide_id
    
  getDefaultProps: ->
    comments: []
    
  addSlideComment: (response) ->
    if response.comment_saved
      comments = React.addons.update(@state.comments, { $push: [response.comment] })
      @setState comments: comments
    
  render: ->
    React.DOM.div
      className: 'slide_comments_swap'
      React.DOM.ul
        className: 'comments'
        for comment in @state.comments
          React.createElement SlideComment, key: comment.id, comment: comment
      React.createElement SlideCommentForm, project_id: @state.project_id, slide_id: @state.slide_id,  handleNewRecord: @addSlideComment    

In render part you can see that, we are creating same DOM structure as in HTML. For each comment we have created SlideComment component displaying detail of each comment.

# app/assets/javascript/components/slide_comments.js.coffee

@SlideComment = React.createClass
  rawMarkup: ->
    { __html: @props.comment.comment_text, sanitize: true }
    
  render: ->
    React.DOM.li
      className: 'slide_comment'
      React.DOM.img
        className: 'img-circle avatar pull-left'
        width: '30'
        src: @props.comment.commenter_img
      React.DOM.div
        className: 'comment-info'
        React.DOM.h3
          className: 'commenter_name'
          @props.comment.commenter_name
        React.DOM.em
          className: 'comment_date'
          @props.comment.comment_date
        React.DOM.p
          className: 'comment_text'
          dangerouslySetInnerHTML: @rawMarkup()

If you see closely, we have created the same HTML structure as the deleted _slide_comments.html.erb partial.An important thing to note here that we have used dangerouslySetInnerHTML of React in place of raw of rails. Both basically render raw html.

We also have a input box with Add button to add comment in the original Rails view. It get handled in SlideCommentForm component.

# app/assets/javascript/components/slide_comment_form.js.coffee

@SlideCommentForm = React.createClass
  getInitialState: ->
    comment_body: ''
    
  handleKeyDown: (e)->
    if e.key == 'Enter'
      @handleCommentAdd()
      e.preventDefault()
  
  handleCommentAdd: ->
    if $('#slide_comment').val() == ''
      alert 'Comment can\'t be blank'
    else
      mentioned_user = $('#slide_comment').mentionsInput('getMentions')
      mentioned_user_ids = []
      $.each mentioned_user, ->
        mentioned_user_ids.push @uid
      $.ajax
        method: 'post'
        url: "/projects/#{@props.project_id}/slides/#{@props.slide_id}/add_comment"
        dataType: 'JSON'
        data:
          comment: $('#slide_comment').val()
          slide_comment_mentioned_user: mentioned_user_ids.join(',')
        success: (response) =>
          #trigger event for comments count update
          postal.publish
            channel: 'slide'
            topic: 'comment.add'
            data:
              comments_count: response.comments_count
          #mix panel event for Comment Added
          create_mix_panel_event 'comment_added'
          #mix panel event for @mention Used in Comment
          if mentioned_user_ids.length > 0
            create_mix_panel_event '@mention_used_in_comment'
          @props.handleNewRecord response
        complete: ->
          $('#slide_comment').val('');
          $("#slide_comment").mentionsInput('clear');
     
  render: ->
    React.DOM.div
      className: 'input-group'
      React.DOM.input
        id: 'slide_comment'
        className: 'form-control'
        type: 'text'
        placeholder: 'Add a Comment'
        name: 'comment_body'
        onKeyPress: @handleKeyDown
      React.DOM.div
        className: 'input-group-btn'
        React.DOM.button
          id: 'add_slide_comment'
          className: 'btn btn-info'
          onClick: @handleCommentAdd
          'Add'

Here you can see that we have bind onClick event to handleCommentAdd method, and place all the js logic in it like giving alert if no comment is typed and finally making the ajax call to submit the comment to server.

Delete the js code in slide.js as it is ported to React .

When user add a comment it will go to controller through ajax call . The controller code now look as below, we need to add a single line to existing controller code

  def add_comment
    @slide = Slide.find(params[:slide_id])
    @user_who_commented = current_user
    comment = Comment.build_from(@slide, @user_who_commented.id, params[:comment])
    mentioned_user_ids = params[:slide_comment_mentioned_user].split(',').reject(&:empty?)
    if comment.save
      slide_comment_mention_notification(params[:id], @slide, comment, @user_who_commented, mentioned_user_ids)
      comment.create_activity key: 'project.slide_commented', owner: current_user, recipient: comment.commentable, project_id: params[:id]
    end
    render json: {comment_saved: comment.persisted?, comment: react_slide_comment(comment)}
  end

The bolded line is the only line we need to change in controller, as it now need to return the newly added comment which get appended to DOM by React.

Earlier when we are doing it Rails way, we are handling the DOM refresh in add_comment.js.erb file. It is not needed anymore. Delete the add_comment.js.erb file

You are done with porting your dynamic view from Rails to React.You can summarize the steps in below line

  1. Replace Rails partial with React Component
  2. Pass needed data collection as Array of Hash
  3. Move the js code(like handling click of add button ) within React
  4. Delete add_comment.js.erb file handling partial refresh in Rails

That’s all … all your css, js, model, controller etc code will not need any change.

My aim here is to just explain the amount of work needed to move from Rails to React. I will soon explain each line of React code in detail in some other Blog.

Reference:

https://facebook.github.io/react/

https://facebook.github.io/react/docs/component-specs.html

https://facebook.github.io/react/docs/tags-and-attributes.html


Leave a comment

Configure Rails with React

React is js Library created by Facebook to support Dynamic UI. It glue easily with Rails and can easily substitute Rails view.

I have explained porting Rails view to React view in this post .

React gem is available which make its integration with Rails a breeze. lets setup React to work with Rails .

CONFIGURING REACT WITH RAILS :

Add below to your Gemfile

gem 'react-rails'

Run the bundler command from terminal on your project root :

$ bundle install

Initialize react with below command

Below command will provide default setup for React in your rails App

$ rails g react:install
Running via Spring preloader in process 19546
create app/assets/javascripts/components
create app/assets/javascripts/components/.gitkeep
insert app/assets/javascripts/application.js
insert app/assets/javascripts/application.js
insert app/assets/javascripts/application.js
create app/assets/javascripts/components.js

So below three line will be added to the application.js

//= require react
//= require react_ujs
//= require components

These line will be added to the bottom of your application.js file. I suggest to move the first two line toward top just below the jquery files and keep the last line i,e components towards end of the file. The reason is that, you want react js to be called before you do any DOM manipulation. Keeping components js at bottom ensure that you can call a function defined earlier by you in React component.

For Example : my app/assets/javascript/application.js look as below

//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require react
//= require react_ujs

... other js file Iam using ...

// keep react component js at bootom as
// you may need other defined function in it
//= require components
// put other js file above react components

Now configure React Add-ons in your application. The Add-ons have many helper which help you in writing React code.

If you look at component.js file it has below content :

#app/assets/javascript/component.js

//= require_tree ./components

So it basically call require_tree on components folder, thus any js file you create within this folder automatically get loaded.

Add below line to application.rb file

# config/application.rb

  config.react.addons = true

 

This enable Reacts addons which provide a number of useful helper method listed here .

With this you are ready to start with react. While working with React you need to write a lot of JS code, So I suggest to start using Coffee Script.

Even you can try it online here and use this editor to convert your existing js code to Coffee script .


Leave a comment

image cropping with paperclip

We are using paperclip for managing files and images in our project . I assume you all know how to use paperclip with its default behaviour .

Our user model managing user profile picture with paperclip look as below :

class User < ActiveRecord::Base
	has_attached_file :picture, 
                    styles: { medium: "300x300#", thumb: "80x80#" }, 
                    content_type: ['image/jpeg','image/jpg', 'image/png'],
                    default_url: "/images/:style/missing.png", 
                    whiny: false, 
                    use_timestamp: false
end

I have used few of the option available with has_attached_file method of paperclip . The complete list is available here .

So what it do, when ever user select his picture from UI, paperclip creates different size of it, here medium and thumb size. you can add more style as per your need.

Now we need to enhance user experience and let him to crop the picture also, which is common now a days. So know user have to go through two steps :

STEP 1 : Crop the picture

This step just allow user to select part of the picture he want to upload rather then the whole pic. You can let the user do that by providing 4 input fields crop_x, crop_y, crop_w, crop_h which is basically x cordinate, y cordinate setting the origin from where image is to cut , with the specified width and height.

But this is not user friendly. A lot of libraries are available which can serve the above purpose in better way.

I used croppic which worked well for me . I will write on its usage on some other day. Meanwhile you can try it yourself.

Our end goal in this step is to get user selected portion of image i,e crop_x, crop_y, crop_w, crop_h

STEP 3 : Make Paperclip to work on cropped part.

So, now paperclip should create different size of cropped part rather then the whole image. To make it work we first need to understand its default behaviour of paper clip and its is in this file . Below are the important lines of this file(I have taken only the line need to understood from each method)

module Paperclip
  # Handles thumbnailing images that are uploaded.
  class Thumbnail < Processor
    
    attr_accessor :current_geometry, :target_geometry, :format, :whiny, :convert_options,
      :source_file_options, :animated, :auto_orient
    
    def initialize(file, options = {}, attachment = nil)
      super
      geometry             = options[:geometry].to_s
      @crop                = geometry[-1,1] == '#'
      @target_geometry     = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
      @current_geometry    = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
    end
    
    def crop?
      @crop
    end
    
    def transformation_command
      scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
      trans = []
      trans << "-coalesce" if animated?
      trans << "-auto-orient" if auto_orient
      trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
      trans << "-crop" << %["#{crop}"] << "+repage" if crop
      trans << '-layers "optimize"' if animated?
      trans
    end
  end
end

So you can see that thumbnail class is wrapped up in paperclip module and inherit from processor class . In the initialize method it set @target_geometry and @current_geometry . The main work is handle by transformation_command method . The first line  basically give back scale of the uploaded image and its cropped part . transformation_to method is defined here .

Now we will override these methods of Thumbnail class to suit our need . Read about method overriding in this post .

We are going to create our own custom processor. Lets create a paperclip_processors folder within lib folder and add custom_processor.rb file to it. It will look as below :

module Paperclip
  class CustomCropper < Thumbnail
    def initialize(file, options = {}, attachment = nil)
      super
      if target.crop_w && target.crop_x
        @current_geometry.width  = target.crop_w
        @current_geometry.height = target.crop_h
      end
    end
    
    def target
      @attachment.instance
    end
    
    def transformation_command
      # call crop processing only if user inputs are there
      if target.crop_w && target.crop_x
        crop_command = [
          "-crop",
          "#{target.crop_w}x" \
            "#{target.crop_h}+" \
            "#{target.crop_x}+" \
            "#{target.crop_y}",
          "+repage"
        ]
        crop_command + super
      else
        super
      end
    end
    
  end
end

Our custom_cropper inherit from thumbnail class and override its initialize and transform method. In these method if user inputs for crop is not given, super is called which exhibit the behaviour of Thumbnail without any modification . This condition is put in place as sometime user sync his data from linkedin and at that time crop inputs is not available or say we have created some user in from seed.rb file which face the same issue.

Now our custom processor is defined, we just need to modify the user model to call our custom processor explicitly and so the default thumbnail processor will be not called . The user model will now look as below :

class User < ActiveRecord::Base
	has_attached_file :picture, 
                    styles: { medium: "300x300#", thumb: "80x80#" }, 
                    content_type: ['image/jpeg','image/jpg', 'image/png'],
                    default_url: "/images/:style/missing.png", 
                    whiny: false, 
                    use_timestamp: false,
                    processors: [:custom_cropper]

        attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
end

We have defined attr_accessor for the cordinates and width height.

The controller code will look something like as below

  def upload_profile_pic
    @user.crop_x = params[:imgX1].to_i
    @user.crop_y = params[:imgY1].to_i
    @user.crop_w = params[:imgW].to_i
    @user.crop_h = params[:imgH].to_i
    @user.picture = params[:user][:picture]
    @user.save
  end

That’ s all you need to make your Paperclip custom processor .

 

Resources :

https://github.com/thoughtbot/paperclip

http://www.rubydoc.info/gems/paperclip/Paperclip/ClassMethods

https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/thumbnail.rb

http://www.rubydoc.info/gems/paperclip/Paperclip/Processor


1 Comment

jQuery Slider drag drop with Capybara

We have used jQuery Slider in one of our application, which resize the images within our library when user move it left and right. Since the no of images we can show per page, varies as user moves the slider, we have hooked a ajax call to stop event of slider which reset the per_page value in controller.

The code look as below:

JS code:

    var setupSlider = function () {

        var sl_value = $.cookie("sl_value");
        if (sl_value === undefined) {
            savedSliderValue = 0;
        } else {
            savedSliderValue = parseInt(sl_value);
        }

        $(".size-slider .slider").slider({
            range: "min",
            value: savedSliderValue,
            min: -50,
            max: 0,
            slide: function (event, ui) {
                var fraction = (1 + ui.value / 100),
                        newWidth = originalWidth * fraction;

                $(".slide-library .slide").width(newWidth);
            },
            stop: function (event, ui) {
                var sl_value = $(".size-slider .slider").slider("option", "value");
                $.cookie("sl_value", sl_value);
                var expansion_factor = (0 - ui.value);
                $.ajax({
                    type: "GET",
                    url: 'desktop.js',
                    data: {
                        expansion_factor: expansion_factor
                    },
                    contentType: 'script'
                });
           }
     });

Controller Logic look as Below:

    #the slide_per_page will change on slide resizing. default is 12
    cookies[:slide_per_page] ||= 12
    cookies[:slide_per_page] = params[:expansion_factor].present? ? (12 + (12 * params[:expansion_factor].to_i/50)) : cookies[:slide_per_page]
    #reset page number to 1 as library resizing reinit the pagination
    page_number = params[:expansion_factor].present? ? 1 : current_page
    @library_slides =  Slide.all.paginate(:page => page_number, :per_page => cookies[:slide_per_page])

 

Now the test case, need to simulate the drag drop behaviour of slider.

Problem :

We are using Capybara which do give drag_to method to simulate drag and drop, but it take argument as source and target i,e you can drop one element on other element, But here there is no such distinction for the Slider as User can drag it anywhere on slider length, left or right.

Solution:

Capybara internally use Selenium and it have drag_and_drop_by method which simulate drag and drop as offset i,e as X and Y coordinate . So we will extend Capybara to have our custom method.

Well we know the solution, so lets move ahead. Now the question is where to put our code, so that Capybara pick it up and is then available in our Rspec test cases.

All Rspec configuration is loaded from spec_helper.rb file whenever, you run your Rspec tests. My spec_helper.rb file look as below

require 'database_cleaner'
require 'factory_girl_rails'
require 'support/capybara'
require 'warden'
require 'devise'

RSpec.configure do |config|
  config.include Capybara::DSL
  #many more line of configuration...
end

So you can see that in the third line we have required ‘support/capybara’ (complete path – spec/csupport/capybara) , this file contain capybara specific configuration and initialize Capybara whenever Rspec is run. We will add our extension code at bottom of this file

support/capybara

module CapybaraExtension
  def drag_by(right_by, down_by)
    base.drag_by(right_by, down_by)
  end
end

module CapybaraSeleniumExtension
  def drag_by(right_by, down_by)
    driver.browser.action.drag_and_drop_by(native, right_by, down_by).perform
  end
end

::Capybara::Selenium::Node.send :include, CapybaraSeleniumExtension
::Capybara::Node::Element.send :include, CapybaraExtension

So now we have a drag_by method which perfom drag and drop on element with offset.Using it we can now simulate the jQuery Slider behaviour. Finally The Rspec test case look as below

  it "it should reinit the pagination on slide resizing" do
    visit slides_desktop_path(@project)
    #pagination set to 12 and we have around 33 records so including 2,3 and next there should be 3 link. 
    #Note that in pagination previous and 1 is default and so disabled when page loads
    expect(find("div.pagination")).to have_selector(".next_page", :count => 1)
    expect(find("div.pagination")).to have_selector(".previous_page", :count => 1)
    expect(find("div.pagination")).to have_selector("em", :count => 1)
    expect(find("div.pagination em.current").text).to eq("1")
    expect(find("div.pagination")).to have_selector("a", :count => 3)
    first(".ui-slider-handle").drag_by(-50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 2)
    first(".ui-slider-handle").drag_by(50, 0)
    sleep 2
    expect(find("div.pagination")).to have_selector("a", :count => 3)
  end

That’s all. Run your test and see it passing 🙂


Leave a comment

simple file upload in rails

I have been doing file upload in rails using attachment_fu in initial days and then paperclip since its arrival. But have never used simple file upload i,e without using any gems. Recently I have to redesign the paperclip upload functionality of one of my application(will discuss in some other post), So thought of starting from beginning i,e how simple uploader works, what benefits paperclip or other available gem adds to it etc .

I am assuming you already have some rails 4 application running. We will add photo upload feature to it. This will be our flow.

->There will be a upload button on new photo page.
->User upload a pic using the simple uploaded.
-> On successful upload he will be redirected to show page, displaying the uploaded image.

generate the model and controller with below command on your project root from terminal

$ rails g model Photo pic:attachement
invoke active_record
create db/migrate/20150626122526_create_photos.rb
create app/models/photo.rb

If you see the generated migration file, it has below content

class CreatePhotos < ActiveRecord::Migration
  def change
    create_table :photos do |t|
      t.attachment :pic
      t.timestamps null: false
    end
  end
end

Run migration to add the photos table

$rake db:migrate

Once the migration is complete and you chek the schema.rb file, you will see below detail of the photos table.

  create_table "photos", force: :cascade do |t|
    t.string   "pic_file_name"
    t.string   "pic_content_type"
    t.integer  "pic_file_size"
    t.datetime "pic_updated_at"
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
  end

So you can see that t.attachement in the migration file get converted into below 4 fields.

t.string “pic_file_name”
t.string “pic_content_type”
t.integer “pic_file_size”
t.datetime “pic_updated_at”

They represent internal property of any attachement. When user upload any file, you need to read these values from params and need to update these fields. Let generate the controller now

$ rails g controller photos new show create.

It will generate new, show and create action in photos controller and the corresponding views.
It will also generate get routes for all these actions.

  get 'photos/new'
  get 'photos/create'
  get 'photos/show'

remove them and replace with resourceful routes.

resources :photos

write the file upload code to the photos/new.html.erb

<%= form_for @photo, :url => photos_path, :html => { :multipart => true } do |form| %>
  <%= form.file_field :pic %>
  <%=form.submit 'upload'%>
<% end %>

This simply provide a file upload field with a upload button, which submit data to create action.

The whole controller code look as below:

class PhotosController < ApplicationController
  before_action :set_photo, only: [:show]

  def show
  end

  def new
    @photo = Photo.new
  end

  def create
    @photo = Photo.new
    @photo_object = params[:photo][:pic]
    @photo.pic_file_name = @photo_object.original_filename
    @photo.pic_content_type = @photo_object.content_type
    respond_to do |format|
      if @photo.save
        file_name_filesystem = @photo.id.to_s + @photo_object.original_filename
        photo_path_on_filesystem = Rails.root.join('public','uploads', file_name_filesystem)
        File.open(photo_path_on_filesystem,'wb') do |file| 
          file.write(@photo_object.read)
        end
        format.html { redirect_to @photo, notice: 'Photo was successfully created.' }
        format.json { render :show, status: :created, location: @photo }
      else
        format.html { render :new }
        format.json { render json: @photo.errors, status: :unprocessable_entity }
      end
    end
  end

  private
  def set_photo
    @photo = Photo.find(params[:id])
  end
end

The points to notice here is the create action. If you put debugger(I use byebug) inside the create action and try to see the params, it look as below

(byebug) pp params[:photo][:pic]
#<ActionDispatch::Http::UploadedFile:0xb3b9be60
@content_type=”image/jpeg”,
@headers=
“Content-Disposition: form-data; name=\”photo[pic]\”; filename=\”user1.jpeg\”\r\nContent-Type: image/jpeg\r\n”,
@original_filename=”user1.jpeg”,
@tempfile=#<File:/tmp/RackMultipart20150626-7518-liirs5.jpeg>>

(byebug) pp params[:photo][:pic].content_type
“image/jpeg”

(byebug) pp params[:photo][:pic].original_filename
“user1.jpeg”

(byebug) pp params[:photo][:pic].tempfile
#<File:/tmp/RackMultipart20150626-7518-liirs5.jpeg>

(byebug) pp params[:photo][:pic].tempfile.path
“/tmp/RackMultipart20150626-7518-liirs5.jpeg”

So you can see that params[:photo][:pic] is an object in itself from which we can get the name of the file, its content etc.

We have first saved file_name and content_type in db, and then write the files content in public/uploads directory of our project. So that when user visit show page next time we will show him the uploaded image.

NOTE: Instead of writing the content to filesystem, you can send it to any API which handle its storage or do whatever with the data you want to do.

Now in show.html.erb we have below code to render the uploaded photo.

 
<% pic_path = @photo.id.to_s + @photo.pic_file_name%> 
<%img src="/uploads/<%=pic_path%>" alt="" />

So from above implementation, now we can see what paperclip or other gem add to it:

-> You do not need to handle writing the uplaoded file to filesystem yourself. Gem will take care of it
-> Gem can handle storing the uploaded file on ThirdParty storage like amazone
-> They can provide further processing on image like creating tumbnail, applying affects etc.


Leave a comment

heroku timeout code H13

Recently while going live with one of our financial domain project on Heroku we get with this dreaded error.

at=error code=H13 desc=”Connection closed without response”

The error occurring whenever a user try to submit his loan application. Basically a user never able to submit his application due to the above error as the process getting killed due to response taking too long to comeback.

some of the culprit line in logs look like as below:

2015-06-08T19:10:34.297539+00:00 heroku[router]: at=error code=H13 desc=”Connection closed without response” method=POST path=”/loans/credit_request” host=getfinance.com request_id=0f4a7e2f-28a6-4542-a308-db3a9a72c0e6 fwd=”90.161.130.26″ dyno=web.2 connect=0ms service=26007ms status=503 bytes=0
2015-06-08T19:10:34Z app[postgres.32439]: [MAUVE] could not receive data from client: Connection reset by peer
2015-06-08T19:10:34.277236+00:00 app[web.2]: E, [2015-06-08T19:10:34.277078 #3] ERROR — : worker=0 PID:9 timeout (26s > 25s), killing

So basically Heroku is not to blamed. It is right in killing a process if it see no response coming in 25 second.

We know that oue API is the culprit. This is how our whole application work

-> User come to our application build in rails
-> Fill the details related to loan he want to apply and submit it.user see progress bar rotating
-> On submit we capture the data and make a API call to another application written in java.
-> The API do a lot of processing on the data, create citadel certificate, register the user on OpenAm,trigger few emails etc and then send back response.
-> when response is success we reload the page with success message or render the error

It is the second last step in which Heroku seeing a problem. Heroku keep waiting for the response, and when it see that it is exceeding 25 second, it kill the existing process, so the user page never get refreshed and hanged for ever with the progress bar rotating.

So we know the problem…but what will be the solution.

Idle solution is to improve on our API code and make it to return a response in say 15 second(our taking between 25 to 30 seconds). but it is long term plan. we are already live and it is not that we have not worked on reducing the response time(well it is crime if you make user to wait 30 second for response) but since a lot of processing undergo before sending the response, we can’t do much.

So we decided to live with the current response time and fool Heroku to believe that request response cycle is working.

Here is the Plan:
-> push the API call to background job
-> submit the form as ajax
-> In the action which handle submit, trigger the background job and render the job_id , the path to which need to be redirected and other parameter you want to reuse
-> On success of ajax request, trigger another ajax call to a separate method which keep checking the status of the job after a fix interval say 3 seconds.
-> when the second ajax call see that status is complete, it will reload the page

So now, although user see some increase in time due to overhead introduced in checking status after every 3 seconds, Heroku see the request response cycle working as in every 3 second it see a request coming asking for the job status and a response going on with the current status : pending , queued, running etc .

So here is the code:

Add sidekiq to Gemfile and run bundle install

gem 'sidekiq'
gem 'sidekiq-status'

routes

  resources :loans
  post 'credit_request' => 'loans#check_credit_request', :as => :credit_request
  get 'check_job_status' => 'loans#check_job_status', :as => :loan_status

write the background code in lib folder say – lib/application.rb

require 'rest_client'
require 'base64'

module LoanPath
  class Application
    
    include Sidekiq::Worker
    include Sidekiq::Status::Worker
    sidekiq_options :retry => false
    
    def perform(*args)
      apply_credit(args[1])
      lp_status_code = background_task_output[:lpcode].present? ? background_task_output[:lpcode] : ""
      lp_status_message = background_task_output[:lp_message].present? ? background_task_output[:lp_message] : ""
      lp_data = background_task_output[:lp_data].present? ? background_task_output[:lp_data] : ""
      store :lp_status_code => lp_status_code
      store :lp_status_message => lp_status_message
      store :lp_data => lp_data
      at 100,100, background_task_output[:lp_message] 
                   if background_task_output[:lp_message].present?
    end
  end
  
  def apply_credit(detail)
    begin
      uri = APP_CONSTANTS["credit_request_endpoint"]
      payload = "whatever xml or other data you want to send"
       rest_resource = RestClient::Resource.new(uri, {:user => "your username", :password => "xyz", :timeout => 60, :open_timeout =>60})
      credit_submit = rest_resource.post payload, :content_type => "application/xml"
      {:lpcode => "LpValid", :lp_message => "Credit Request Submitted Successfully", :lp_data => credit_submit}
    rescue Exception => e
      error_message = "System encountered error, please try later"
      {:lpcode => "LpError", :lp_message => error_message, :lp_data => nil}
    end
  end
    
  def running_background_job(job_id)
    status = Sidekiq::Status::status(job_id)
    status_message = Sidekiq::Status::message(job_id)
    lp_status_code = Sidekiq::Status::get(job_id, :lp_status_code)
    lp_status_message = Sidekiq::Status::get(job_id, :lp_status_message)
    lp_data = Sidekiq::Status::get(job_id, :lp_data)
    {:status => status.to_s, :status_message => status_message, 
     :lp_status_code => lp_status_code, :lp_status_message => 
     lp_status_message,:lp_data => lp_data}
  end
  
end

The loan view to fill the details is as below:

<%= form_tag credit_request_path, :method => :post, :id => "credit-request" do %>
   your form fields
   <%= submit_tag 'Submit Credit Request', :id => 'apply-credit'%>
<% end %>

controller code which handle the loan submit is as below:

def credit_request
  job_id = LoanPath::Application.perform_async("apply_credit", params)
  render :json => {:job_id => job_id , :current_url => loan_url(params[:id])}
end

def check_job_status
  job_status = BackgroundJobs.new.running_background_job(params[:job_id])
  @application_saved = "Error"
  if (job_status[:lp_status_code].to_s == "LpValid")
    @application_saved = "Success"
    @message = "Credit Application Submitted Successfully."
  elsif (job_status[:lp_status_code].to_s == "LpError")
    @message = job_status[:lp_status_message].present? ? 
               job_status[:lp_status_message] : 
               "System encountered error, please try later"
  end
  render :json => {:status => job_status[:status], :redirect_to => params[:current_path], :message => @message, :application_status => @application_saved}
end

Now the most important, the  js code which submit the form through ajax and keep checking status is as below

$('#apply-credit').click(function (e) {
    e.preventDefault();
    if ($('#accept_tc').is(':checked')) {
        var intervalId = '';
        
        $.ajax({
            url: "/loans/credit_request",
            data: $("#credit-request").serialize(),
            dataType: "json",
            type: "POST",
            success: function (job) {
              intervalId = setInterval(function () {
                  checkStatus(job,intervalId);
                }, 3000);
            }
          });
        
        $('#first_name').focus();
        $('#credit-request').block({
            message: null
          });
        return false;
      } else {
        alert('Please read and accept the terms, above, before submitting credit request.');
        $('#accept_tc').focus();
        return false;
      }
      
    })

function checkStatus(job, intervalId) {
    $.ajax({
        url: "/check_job_status",
        data: {
          job_id: job.job_id,
          current_path: job.current_url,
        },
        type: "get",
        dataType: "json",
        success: function (jobStatus) {
          console.log(jobStatus);
          if (jobStatus.status === 'complete' || jobStatus.status === "failed") {
              if (jobStatus.application_status === "Success") {
                  window.location = jobStatus.redirect_to;
                }
              else {
                  $('#ajax_message_success').hide();
                  $("#ajax_message_error").hide();
                  $("#ajax_message_error").text(jobStatus.message);
                  $('#ajax_message_error').show();
                  $("#application_tab").unblock();
                  $(window).scrollTop(0);
                }
                window.clearInterval(intervalId);
              }
            }
          });
      }

That’s all now Heroku will not complain about Timeout error


Leave a comment

what are the components of rails ?

rails is responsible for popularity of ruby to a great extent. Rails 4.2.1 is out and it summarizes itself with below line, each words are true to itself.

Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.

As rails is maturing , it is refactoring and restructuring itself. some of the features are removed from core rails library and build into separate gem, which you can include or exclude as per your needs. For example, observers are part of ActiveRecord before 4.0 but after that it is moved to separate gem rails-observer .

Note that rails is gem in itself . It has structured itself in different modules or component , each providing a specific functionality. When you create a new rails application with command below:

$ rails new demoapp

By default all the components get loaded due to below line of code in config/application.rb file

require File.expand_path('../boot', __FILE__)

require 'rails/all'

Bundler.require(*Rails.groups)

module Demoapp
  class Application < Rails::Application
   ----more code ...
  end
end

You are using require ‘rails/all’ , which ask rails to load all the components. Read here about rails booting and loading. But it is unnecessary to load all the components if you do not need it. say your application do not need to send any email, so it is better to not load ActionMailer component or module. Remember that, heavy your application is , more time it will take to load, so keep unwanted thing out.

Well for this you must know what are the components and what it do for you. Below goes the list :

=> ActionMailer :

It manage email support for rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.

=> ActionPack :

It is responsible for mapping the incoming request i,e URL to a controller action and then rendering the corresponding  view. Internally, it is using rack to communicate with the server. It also manage view caching

=>ActionView :

It is responsible for rendering the view . You may not need it if you expose service as an API rather then a interface .The view rendering is done through external gems like erubis for .erb which is the default(you do not install it but get installed as rails dependency. you will see it if you do gem list on your console) or say haml for .haml templates.

=>ActiveJob :

Active Job is a framework for declaring jobs and making them run on a variety of queueing backends. The basic idea is to provide support for creating job to be run later, which can be then handled with other gem build over it. So to say ActiveJob will provide to infrastructure to create the job, which can be then handled by any of the gem sidekiq or resque or delayed job etc .

=>ActiveModel :

They are responsible for model name introspections, conversions, translations and validations . Basically ActiveModel implementations are ORMs (see ActiveRecord, below), but they can also use non-relational storage like MongoDB, Redis, Memcached or even just local machine memory.

 =>ActiveRecord :

They are responsible to map your model with your ORM database. Remember when you do @user.save, the user detail directly get saved in users table.this mapping is getting handled with ActiveRecord. You do not need it if your model do not interact with database . all the callbacks like before_save etc and associations between model are covered under it.

=>ActiveSupport :

ActiveSupport is a compatibility library including methods that aren’t necessarily specific to Rails. You’ll see ActiveSupport used by non-Rails libraries because it contains such a lot of useful baseline functionality. ActiveSupport includes methods like how Rails changes words from single to plural, or CamelCase to snake_case. It also includes significantly better time and date support than the Ruby standard library.

=>Bundler :

Bundler manages an application’s dependencies through its entire life, across many machines, systematically and repeatably. It is responsible to manage all your gem dependency .

=>Railities :

Railties is responsible for gluing all frameworks together. Overall, it:

->handles the bootstrapping process for a Rails application
->manages the rails command line interface;
->and provides the Rails generators core.

=>sprockets-rails :

 It use to manage your assets pipeline introduced since rails 3.1. Initially it was a core feature of rails but has moved into separate gem sprockets-rails since rails 4.0, so that it can be refactored and develop independently . If you have used it with before rails 4, you have observed the log containing details of all the assets requested which is annoying. but after rails 4.0 when it is moved as separate gem, The configuration option config.assets.logger has been removed. Sprockets will no longer pollute your logs when assets are compiled. Nevertheless, Action Pack will continue to log when assets are requested from your server if they are not precompiled. Also there is increase in performance, now assetes get compiled in about 2 second rather then 8 to 10 second

=> Gems:

They are independent library, which you keep adding as per your need.


2 Comments

how rails boot : order of config files loading

Thinking on it for long, but only today get time to delve into it. Have you ever get curious of what going under the hood when you start the rails server, how your rails application get booted, when your gem gets loaded and config files get executed. Well there goes a lot of thing between you type rails s on your project folder from terminal and go to localhost:3000 in your browser.

I tried to see it myself. To get the basic idea I gone through rails doc on initialization . It is quite elaborative make me lost in the details. So I figure out my own way to get the sense of things : “I use puts statement in all the important files, relevant as per the rails doc”

Below is the output on console when I started the server (I am using rails 4.1.9):

$ rails server
	Iam in Gemfile
	Iam in config/boot.rb
	Iam in config/application.rb
	=> Booting Puma
	=> Rails 4.1.5 application starting in development on http://0.0.0.0:3000
	=> Run `rails server -h` for more startup options
	=> Notice: server is listening on all interfaces (0.0.0.0).
	=> Ctrl-C to shutdown server
	Iam in config/environments/development.rb
	Iam in config/initializer folder
        Iam in config/routes.rb
	Iam in config/environment.rb
	Iam in config.ru
	Puma 2.8.2 starting...
	* Min threads: 0, max threads: 16
	* Environment: development
	* Listening on tcp://0.0.0.0:3000

 

So, now you can see the order in which the files is getting loaded are as below:

Gemfile   -> boot.rb    ->  config/application.rb   -> basic server message get printed  -> config/environments/development.rb  ->  files in initializer foler  ->  config/routes.rb  ->  config/environment.rb  ->  config.ru

Now lets see, how and from where they are getting loaded at the code level .

Step 1: Starting the Server

The story begin when you start the server with the below command

$rails server

any command which run from terminal is a executable, residing in one of the folder in load path of your system. You can see it with below command:

$PATH
-bash:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin

all the location separated by : is searched by your system to run any command you type on terminal, So it means rails executable (rails executable get installed when you install a rails gem) must be present in any one of these position.

But wait, what the second argument server is for ?  well it is argument to command rails . You can run without it

$ rails
Iam in Gemfile
Iam in Gemfile
Iam in boot.rb
Usage: rails COMMAND [ARGS]

The most common rails commands are:
 generate    Generate new code (short-cut alias: "g")
 console     Start the Rails console (short-cut alias: "c")
 server      Start the Rails server (short-cut alias: "s")
 dbconsole   Start a console for the database specified in config/database.yml
             (short-cut alias: "db")
 new         Create a new Rails application. "rails new my_app" creates a
             new application called MyApp in "./my_app"

So you can see that rails command still run without the server option, but it give you message how to use it properly.

O.K, lets find what code the rails executable have. It may be present in any of folder within the path above. search each of themfor rails . You will get it in bin. Open with nano command to see its content

$ nano bin/rails

#!/usr/bin/env ruby
begin
  load File.expand_path("../spring", __FILE__)
rescue LoadError
end
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require_relative '../config/boot'
require 'rails/commands'

So the code here first try to load a file spring, which basically have the code to load the gems as below:

$ nano bin/spring

#!/usr/bin/env ruby

unless defined?(Spring)
  require "rubygems"
  require "bundler"

  if match = Bundler.default_lockfile.read.match(/^GEM$.*?^    spring \((.*?)\)$.*?^$/m)
    ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
    ENV["GEM_HOME"] = ""
    Gem.paths = ENV

    gem "spring", match[1]
    require "spring/binstub"
  end
end

The remaining line of rails executable will craete a constant variable holding path of application.rb, load the boot file and the last line will load the commands file(located in installed rails gem at rails/railties/lib/rails/commands.rb )

So till now, you get to know, how the rails server start, why Gemfile are loaded first, followed by boot.rb file.

So we have understand upto :  Gemfile  -> boot.rb

 

Step 2:  Insight into boot.rb file:

We will look into boot.rb file and see how it influence the remaining order of things. It has below line of code

# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)

require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])

So boot.rb file do not have any other influence on the remaining sequence of things. It just install all your gems and done with its role

 

Step 3: Insight into way application.rb is loaded

we have noticed that the last line in rake executable is require ‘rails/commands’ , it basically load the file  rails/railties/lib/rails/commands.rb . It has below content

#https://github.com/rails/rails/blob/master/railties/lib/rails/commands.rb
ARGV << '--help' if ARGV.empty? 
aliases = {   "g"  => "generate",
  "d"  => "destroy",
  "c"  => "console",
  "s"  => "server",
  "db" => "dbconsole",
  "r"  => "runner",
  "t"  => "test",
}

command = ARGV.shift
command = aliases[command] || command

require 'rails/commands/commands_tasks'

Rails::CommandsTasks.new(ARGV).run_command!(command)

So you can see that, the above code creating aliase for different commands. now you know how you can use rails s also in place of rails server .

The last two line is important. It load command_task.rb file at(rails/railties/lib/rails/commands/commands_tasks.rb) and the last line run the command you passed i,e for server command , it becomes

Rails::CommandsTasks.new("server").run_command!(command)

basically the above line execute below line of code from the file rails/railties/lib/rails/commands/commands_tasks.rb

    
 #https://github.com/rails/rails/blob/master/railties/lib/rails/commands/commands_tasks.rb
  def server
      set_application_directory!
      require_command!("server")

      Rails::Server.new.tap do |server|
        # We need to require application after the server sets environment,
        # otherwise the --environment option given to the server won't propagate.
        require APP_PATH
        Dir.chdir(Rails.application.root)
        server.start
      end
    end

from here you can see that APP_PATH which store the application.rb file get loaded and the the server get started.

So upto now, you should have clarity on below sequence of flow

rails server -> Gemfile loaded -> boot.rb loaded -> application.rb loaded

 

Step 4: insight into server.start

in the above code after config/application is loaded(require APP_PATH), directroy changed to rails root folder and  server.start is called. The code is as below:

#https://github.com/rails/rails/blob/master/railties/lib/rails/commands/server.rb
module Rails
 class Server < ::Rack::Server
   def start
    print_boot_information
    trap(:INT) { exit }
    create_tmp_directories
    log_to_stdout if options[:log_stdout]

    super
   ensure
    puts 'Exiting' unless @options && options[:daemonize]
  end

  def print_boot_information
   url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
   puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
   puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
   puts "=> Run `rails server -h` for more startup options"

   puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
  end
 end
end

So you can see that the start method doing basic stuff  like printing the boot message(the message you see after application.rb file loads), making temp directory etc and calling super, which means calling the method of same name from the parent class i,e from the Rack::Server class. Rack::Server is defined in another Gem rack .

 

Step 5: look into ::Rack::Server class

https://github.com/rack/rack/blob/master/lib/rack/server.rb
module Rack
  class Server

    attr_writer :options

    def initialize(options = nil)
      @options = options
      @app = options[:app] if options && options[:app]
    end

    def app
      @app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
    end

    def start &blk
      # ...

      server.run wrapped_app, options, &blk
    end

    def server
      @_server ||= Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
    end

    private
      def build_app_and_options_from_config
        if !::File.exist? options[:config]
          abort "configuration #{options[:config]} not found"
        end

        app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
        self.options.merge! options
        app
      end

      def build_app_from_string
        Rack::Builder.new_from_string(self.options[:builder])
      end

      def wrapped_app
        @wrapped_app ||= build_app app
      end
  end
end

So here you can see start calls wrapped_app function which call build_app function to which app function is passed which in turn call  build_app_and_options_from_config function, which have the code to load all the files in config folder.

Now you can understand, how all the files in config folder in rails are loaded automatically. The files are loaded from config folder in alphabetical order, the folder getting the first priority i,e config/environment folder/*.rb files  ->  config/initializer/*rb files -> other files in config folder

So we are done with the whole loading process below:

rails server -> Gemfile loaded -> boot.rb loaded -> application.rb loaded ->config/environments/development.rb   loaded -> config/initializers/* loaded    ->  config/environmet.rb loaded -> config.ru loaded -> server started

 


2 Comments

method overriding in ruby

method overriding refers to replacing a method of parent class in its subclass. The method definition in the subclass should have same signature as the method in the parent class. So, when a method is called on the subclass object, the overridden method in the subclass will be called.

lets create overriding_demo.rb file and add below line to it

class Person

  def welcome(name,msg,food)
	puts "Hi #{name}"
	puts "Hi #{msg}"
	puts "I will cook #{food} for you"
  end

end

class Indian < Person

  def welcome(name,msg,food)
	puts "sorry..I do not know you"
  end

end

puts "************welcome in parent class*************"
Person.new.welcome("arun", "how are you", "pizaa")

puts "\n\n************welcome in subclass*************"
Indian.new.welcome("arun", "namaste..kaise hain app", "rice")

Now when you run the above file you will get the below output:

$ ruby overriding_demo.rb
************welcome in parent class*************
Hi arun
how are you
I will cook pizaaa for you

************welcome in subclass*************
sorry..I do not know you

 

So you can see that, welcome method is overrided in the subclass .Here an important aspect is use of super keyword , which allow the overriding method in subclass to execute the parent method and modify or add on its output.

So basically, super call the parent method implicitly. Obviously , you need to pass the arguments to the parent method. but super is not invoked as a normal method. super itself decide what argument to passed to the parent method when it is invoked in the subclass method, depending on the way , it is called.

Below are the convention used by super to pass argument to the parent method :

-> super called without any argument i,e simply as super then the argument passed to subclass method will be passed as argument to parent class .

-> super called with empty argument i,e as super() then no argument is passed to the parent method. It is useful if the parent method set some defaults and you want to stick to that . But, it will throw error, if the parent method expecting some arguments.

-> super called with arguments i,e as super(a,b,c) then only the argument a,b,c will be passed to the parent method.

To demo it, replace overriding_demo.rb file with below line of code:

class Person

  def welcome(name,msg,food)
	puts "Hi #{name}"
	puts "#{msg}"
	puts "I will cook #{food} for you"
  end

end

class Indian < Person

  def welcome(name,msg,food)
    super
    puts "I will show you bollywood movie"
  end

end


puts "************welcome in subclass*************"
Indian.new.welcome("arun", "namaste..kaise hain app", "rice")

 

run it, you will see below output

$ ruby overriding_demo.rb
************welcome in subclass*************
Hi arun
namaste..kaise hain app
I will cook rice for you
I will show you bollywood movie

So in the above code, the subclass call super to maintain the behavior of parent class and at the same time add some feature itself. Also note that the argument passed to the welcome method in the subclass get passed to the parent method implicitly.

now try to call the code again after replacing super with super() i,e calling with empty argument

$ ruby overriding_demo.rb
************welcome in subclass*************
welcome.rb:3:in `welcome’: wrong number of arguments (0 for 3) (ArgumentError)

So you get error which is expected as the welcome method need 3 arguments but you are not passing any

Again, modify the super in above code with super(“ajay”, “how are you”, “chinese”) and see the ouput again

$ ruby overriding_demo.rb
************welcome in subclass*************
Hi ajay
how are you
I will cook chinese for you
I will show you bollywood movie

So you can see that, this time welcome method of parent is invoked with argument passed to super.

That’s all about method overriding. just want to add that, you can override any thing in ruby, but avoid overriding inbuilt library methods as it may break other things in your class if that depend on the original method.