Optimize JavaScript code using BabelJS

Though Babel is a transpiler to convert ESNext code to ES5, it can be used to optimize the code as well.

Let’s say we want to convert following ES6 code using Babel.

let a = 10;
let b = 42;

if (a < b) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

It will get translated to:

"use strict";

var a = 10;
var b = 42;

if (a < b) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

All good so far. Let’s try Babel’s constant folding plugin. I am compiling the ES6 code from command line this time.

babel --plugins constant-folding index.js --out-file bundle.js -w

This gives us following output:

"use strict";

var a = 10;
var b = 42;

if (true) {
  console.log("a is less than b");
} else {
  console.log("b is less than a");
}

If condition changed from (a < b) to (true). The constant-folding plugin has smartly evaluated the conditional expression a < b and replaced the code with the result of that expression.

This plugin can also optimize other expressions as shown below.

// Unary operators
console.log(!true);

// Binary expression
console.log(20 + 22);

// Function calls
console.log(Math.min(91, 2 + 20));

Which gets optimized to:

// Unary operators
console.log(false);

// Binary expression
console.log(42);

// Function calls
console.log(22);

How does this actually work?

Though we are using a “constant folding plugin” to achieve this optimization, the real work happens in Babel itself.

For every expression in the code, the constant folding plugin calls evaluate function from Babel source code. This function checks whether it can confidently find end value for a given expression.

The evaluate function returns confidence level and end value for any given expression. Based on this “confidence level”, constant folding plugin replaces the expression with their end values altering our original code as follows.

How evaluate handles different cases

For code Math.min(10, 20), evaluate will return

{ confident: true, value: 10 }

For code a < b, evaluate will return

{ confident: true, value: true }.

But for user defined function like foo('bar') or browser defined console.log('hello'), evaluate will return

{ confident: false, value: undefined }.

In the above case “confident” value will be “false” even if function returns a constant value. For example for code foo(100), evaluate will return

{ confident: false, value: undefined }.

In the above case function foo will always return 100. Still Babel has low confidence level. Why? That’s because Babel sees that it is a function and it bails out. It does not even look inside to try to figure things out.

Here is evaluate code in Babel. You should check it out.

How much optimization is possible?

How much help we will get from Babel for optimizing our code? Will it optimize everything?

The answer is unfortunately no.

As of now, Babel optimizes logical, binary, conditional expressions. It can also evaluate function calls on literals like "babel".length confidently if the literal is string or number.

For function calls, it supports only certain callees like String, Number and Math. So call to a user defined function, even if it’s returning a fixed value, will not be optimized.

Experimental feature

This feature looks great. But it’s available as experimental feature. If you use the plugin you will get following warning. unless you enable experimental flag.

$ babel --plugins constant-folding index.js --out-file bundle.js -w

[BABEL] index.js: THE TRANSFORMER constant-folding HAS BEEN MARKED AS EXPERIMENTAL AND IS WIP. USE AT YOUR OWN RISK. THIS WILL HIGHLY LIKELY BREAK YOUR CODE SO USE WITH **EXTREME** CAUTION. ENABLE THE `experimental` OPTION TO IGNORE THIS WARNING.

In order to get rid of warning you need to pass --experimental flag like this.

$ babel --plugins constant-folding index.js --out-file bundle.js -w
--experimental

Eliminating dead code

In above code example, we know that the result of if (a < b) is true based on values of a and b. Since the result is not going to change no matter what there is no need to have the if and else clauses.

That’s dead code.

Can Babel help us eliminate dead code?

Yes with the help of minification.deadCodeElimination option.

babel --optional minification.deadCodeElimination index.js --out-file bundle.js -w

Which converts earlier code to:

"use strict";

console.log("a is less than b");

I will talk about how Babel can eliminate dead code in a later post.

How to test React Native App on real iPhone

I have been developing a new iOS app using React Native. I have been testing it using simulator provided by Xcode. Now it’s time to test the app using a real device. One could ask why test on a real device if it works perfectly on a simulator. Here are some of the reasons.

  • There are a number of device specific features which are not testable on a simulator. Phone calls, camera, gps data, compass and vibration are a few of them.
  • Testing unexpected cases that can only be tested on a real device. Like how your application handles incoming calls, low memory situations, low disk space, limited data connectivity etc.
  • Hardware-wise, your device is different than your Mac. If your app is graphics intensive and requires a lot of CPU usage, it might work seamlessly on your simulator depending on your Mac’s specifications. But it might be laggy and glitchy on the real device.

Now let’s look into how you can start testing your React Native app on an real iPhone.

Step 1 - Getting your app installed on a real iPhone

Plug in your device to your Mac and open Xcode. You will be able to select your device to the right of the Play and Stop buttons.

select device

You might run into one of the following two errors if you have not enrolled into Apple’s Developer Program.

xcode error 1

xcode error 2

To fix above issues please enroll in Apple Developer program.

Given below is another type of error you might encounter.

The Developer Disk Image could not be mounted :
You may be running a version of iOS that is not supported by this version of XCode

could not mount

This can happen if your iOS version is not supported by your current version of Xcode. To fix this, just update your Xcode to the latest version from the App Store.

Also make sure, in your Xcode project setup, the Deployment Target is set to an iOS version less than or equal to the iOS version installed on the device you are deploying to. For instance, an app targeted for iOS version 7.0 will work on a device with version 8.0 but not vice versa.

deployment target

Step 2 - Fixing could not connect to development server error after installation

So the app is installed and you can see the launch screen, great. But soon you might get the following error screen on your device.

Could not connect to development server.
Ensure node server is running and available on the same
network – run ‘npm start’ from react-native root.

connection error

To fix this, check your AppDelegate.m file in your project’s iOS folder.

Edit the following line:

jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];

Just replace localhost with your Mac IP address e.g. 192.168.x.x. Save and click run again. This will fix the error.

Step 3 - Connecting to an API hosted on a local development server

So now the app is installed and you can happily navigate through the app screens. But what if you try to make an API call to a local development server then you might run into issue.

In my case, I had a Ruby on Rails app running on my local machine and I wanted the iOS app to access the same. To do this, the app should be able to access the rails server using the private ip of the server e.g. 192.168.x.x:3000. Turned out that I had started my rails server using just the rails s command.

The default host for Rails servers has changed in 4.2 . It now runs on localhost, which means it’ll only accept connections from the IP on which the server is hosted and it’ll not accept connections from outside e.g. my iPhone. To listen on all addresses, which will allow you to connect from other machines on the local network, you must explicitly bind to a more permissive address. You’ll need to run rails server -b 0.0.0.0 to start your server. Or you can use the more verbose rails server --binding=0.0.0.0

Doing so, you can connect to your Rails app from elsewhere on your local network, by browsing to e.g. http://192.168.x.x:3000

Now the ios app can now make API calls to your local rails API server.

Summary

These were some of the issues I came across while testing my iOS app on an actual device and how I fixed them. Hopefully, this blog post helps you fix these error cases get you started on testing quickly.

Using ReactJS with Rails Action Cable

Recent DHH, announced availability of alpha version of Action Cable.

Action Cable is still under heavy development but it comes with examples to demonstrate its usage.

Action Cable integrates websocket based real-time communication in Ruby on Rails applications. It allows building realtime applications like Chats, Status updates, etc.

Action Cable + React

Action Cable provides real time communication. ReactJS is a good tool to manage view complexity on the client side. Together they make it easy to develop snappy web applications which requires state management on the client side without too much work.

Anytime data changes the new data is instantly provided by Action Cable and the new data is shown on the view without user doing anything on the application by ReactJS.

Integrating React

The official Action Cable Example is a chat application. We will be building the same application using ReactJS.

First follow the instructions mentioned to get a working chat application using Action Cable.

Now that the chat application is working let’s get started with adding ReactJS to the application.

Please note that we have also posted a number of videos on learning ReactJS. Check them out if you are intereted.

Step 1 - Add required gems to Gemfile

# react-rails isn't compatible yet with latest Sprockets.
# https://github.com/reactjs/react-rails/pull/322
gem 'react-rails', github: 'vipulnsward/react-rails', branch: 'sprockets-3-compat'

# Add support to use es6 based on top of babel, instead of using coffeescript
gem 'sprockets-es6'

Step 2 - Add required JavaScript files

Follow react-rails installation and run rails g react:install.

This will

  • create a components.js file.
  • create app/assets/javascripts/components/ directory.

Now put following lines in your application.js:

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

Make sure your app/assets/javascripts/application.js looks like this

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require react
//= require react_ujs
//= require components
//= require cable

//= require channels
//= require_tree .

Step 3 - Setup Action Cable to start listening to events

We will be using es6, so lets replace the file app/assets/javascripts/channels/index.coffee, with app/assets/javascripts/channels/index.es6 and add following code.

var App = {};
App.cable = Cable.createConsumer('ws://localhost:28080');

Also remove file app/assets/javascripts/channels/comments.coffee, which is used to setup subscription. We will be doing this setup from our React Component.

Step 4 - Create CommentList React Component

Add following code to app/assets/javascripts/components/comments_list.js.jsx.

var CommentList = React.createClass({
  getInitialState(){
    let message = JSON.parse(this.props.message);
    return {message: message};
  },

  render() {
    let comments = this.state.message.comments.map((comment) => {
      return this.renderComment(comment);
    });

    return (
      <div>{comments}</div>
    );
  },

  renderComment(comment) {
    return (
      <article key={comment.id}>
        <h3>Comment by { comment.user.name }</h3>
        <p>{ comment.content }</p>
      </article>
    );
  }
});

Here we have defined a simple component to display a list of comments associated with a message. Message and associated comments are passed as props.

Step 5 - Create message JSON builder

Next we need to setup data needed to be passed to the component.

Add following code to app/views/messages/_message.json.jbuilder.

json.(message, :created_at, :updated_at, :title, :content, :id)
json.comments(message.comments) do |comment|
  json.extract! comment, :id, :content
  json.user do
    json.extract! comment.user, :id, :name
  end
end

This would push JSON data to our CommentList component.

Step 6 - Create Rails Views to display component

We now need to setup our views for Message and display of Comments.

We need form to create new Comments on messages. This already exists in app/views/comments/_new.html.erb and we will euse it as is.

<%= form_for [ message, Comment.new ], remote: true do |form| %>
  <%= form.text_area :content, size: '100x20' %><br>
  <%= form.submit 'Post comment' %>
<% end %>

After creating comment we need to replace current form with new form, following view takes care of that.

From the file app/views/comments/create.js.erb delete the line containing following code. Please note that below line needs to be deleted.

$('#comments').append('<%=j render @comment %>');

We need to display the message details and render our component to display comments. Insert following code in app/views/messages/show.html.erb just before <%= render 'comments/comments', message: @message %>

<%= react_component 'CommentList', message: render(partial: 'messages/message.json', locals: {message: @message}) %>

After inserting the code would look like this.

<h1><%= @message.title %></h1>
<p><%= @message.content %></p>
<%= react_component 'CommentList', message: render(partial: 'messages/message.json', locals: {message: @message}) %>
<%= render 'comments/new', message: @message %>

Notice how we are rendering CommentList, based on Message json content from jbuilder view we created.

Step 7 - Setup Subscription to listen to Action Cable from React Component

To listen to new updates to comments, we need to setup subscription from Action Cable.

Add following code to CommentList component.

setupSubscription(){

  App.comments = App.cable.subscriptions.create("CommentsChannel", {
    message_id: this.state.message.id,

    connected: function () {
      setTimeout(() => this.perform('follow',
                                    { message_id: this.message_id}), 1000 );
    },

    received: function (data) {
      this.updateCommentList(data.comment);
    },

    updateCommentList: this.updateCommentList

    });
}

We need to also setup related AC Channel code on Rails end.

Make following code exists in app/channels/comments_channel.rb

class CommentsChannel < ApplicationCable::Channel
  def follow(data)
    stop_all_streams
    stream_from "messages:#{data['message_id'].to_i}:comments"
  end

  def unfollow
    stop_all_streams
  end
end

In our React Component, we use App.cable.subscriptions.create to create a new subscription for updates, and pass the channel we want to listen to. It accepts following methods for callback hooks.

  • connected: Subscription was connected successfully. Here we use perform method to call related action, and pass data to the method. perform('follow', {message_id: this.message_id}), 1000), calls CommentsChannel#follow(data).

  • received: We received new data notification from Rails. Here we take action to update our Component. We have passed updateCommentList: this.updateCommentList, which is a Component method that is called with data received from Rails.

Complete React Component

Here’s how our complete Component looks like.

var CommentList = React.createClass({
  getInitialState(){
    let message = JSON.parse(this.props.message);
    return {message: message};
  },

  render() {
    let comments = this.state.message.comments.map((comment) => {
      return this.renderComment(comment);
    });

    return (
        <div>{comments}</div>
    );
  },

  renderComment(comment) {
    return (
        <article key={comment.id}>
          <h3>Comment by { comment.user.name } </h3>
          <p>{ comment.content }</p>
        </article>
    );
  },

  componentDidMount() {
    this.setupSubscription();
  },

  updateCommentList(comment) {
    let message = JSON.parse(comment);
    this.setState({message: message});
  },

  setupSubscription() {

    App.comments = App.cable.subscriptions.create("CommentsChannel", {
      message_id: this.state.message.id,

      connected: function () {
        // Timeout here is needed to make sure Subscription
        // is setup properly, before we do any actions.
        setTimeout(() => this.perform('follow',
                                      {message_id: this.message_id}),
                                      1000);
      },

      received: function(data) {
        this.updateCommentList(data.comment);
      },

      updateCommentList: this.updateCommentList
    });
  }
});

Step 7 - Broadcast message when a new comment is created.

Our final piece is to broadcast new updates to message to the listeners, that have subscribed to the channel.

Add following code to app/jobs/message_relay_job.rb

class MessageRelayJob < ApplicationJob
  def perform(message)
    comment =  MessagesController.render(partial: 'messages/message',
                                         locals: {message: message})
    ActionCable.server.broadcast "messages:#{message.id}:comments",
                                 comment: comment
  end
end

which is then called from Comment model, like so-

Add this line to Comment model file app/model/comment.rb

after_commit { MessageRelayJob.perform_later(self.message) }

We are using message relay here, and will be getting rid of existing comment relay file - app/jobs/comment_relay_job.rb. We will also remove reference to CommentRelayJob from Comment model, since after_commit it now calls the MessageRelayJob.

Summary

Hopefully we have shown that Action Cable is going to be a good friend of ReactJS in future. Only time will tell.

Complete working example for Action Cable + ReactJS can be found here.

Verifying PubSub Services from Rails using Redis

Let’s say that we have a Service that reads and writes to Redis. We have BaseRedisService for managing connection, RedisWriterService to write to Redis and RedisReaderService to read from Redis.

require 'redis'

 # Base class to manage connection
 class BaseRedisService
   REDIS_HOST = '127.0.0.1'

   def connection
     if !(defined?(@@connection) && @@connection)
       @@connection =  Redis.new({ host: REDIS_HOST })
     end
     @@connection
   end

 end
# Class to write to Redis
 class RedisWriterService <  BaseRedisService
   attr_reader :key, :value

   def initialize key, value
     @key, @value = key, value
   end

   def process
     connection.set key, value
   end

 end
# Class to read from Redis
class RedisReaderService < BaseRedisService
  attr_reader :key

  def initialize key
    @key = key
  end

  def process
    connection.get key
  end

end

A test for the above mentioned case might look like this.

require 'test_helper'

class RedisPubSubServiceTest < ActiveSupport::TestCase

  def test_writing_and_reading_value_using_redis
    value = 'Vipul A M'

    RedisWriterService.new('name', value).process
    assert_equal value, RedisReaderService.new('name').process
  end

end

But now, we need to add PubSub to the service and verify that the service sends proper messages to Redis. For verifying from such a service, the service would need to listen to messages sent to Redis. Problem is that Redis listens in a loop. We would need to explicitly release control from listening and allow our tests to go ahead and verify some scenario.

Lets look at how these services would look.

class RedisPublisherService < BaseRedisService
  attr_reader :options, :channel

  def initialize channel, options
    @channel     = channel
    @options = options
  end

  def process
    connection.publish(options, channel)
  end

end

and our Subscriber looks like this.

class RedisSubscriberService < BaseRedisService
  attr_reader :channel

  def initialize channel
    @channel = channel
  end

  def process
    connection.subscribe(channel) do |on|
      on.message do |channel, message|
        puts message
      end
    end
  end

end

Notice that we don’t have control over returning value from the loop easily. Right now we just print on receiving a new message.

Now, lets start persisting our messages to some array in our Service. Also we will start exposing this from a thread variable so that it could be accessed from outside of execution of this listen loop.

class RedisSubscriberService < BaseRedisService
  attr_reader :channel, :messages, :messages_count

  def initialize channel, messages_count = 5
    @channel        = channel
    @messages       = []
    @messages_count = messages_count
  end

  def process
    connection.subscribe(channel) do |on|
      on.message do |channel, message|
        messages.unshift message
        Thread.current[:messages] = messages
      end
    end
  end

end

We now have a way to access message state from the service to read any messages received by it. Lets say we define a new SubscriberService from a Thread, we could read messages like this.

subscriber = Thread.new { RedisSubscriberService.new('payment_alerts').process }
# Print first message from messages received
puts subscriber[:messages].first

Armed with this, we can now define a set of Rails helpers to use in our Rails tests.

module SubscriberHelpers
  THREAD_PROCESS_TIMEOUT = 0.5

  def setup_subscriber channel = 'test_channel'
    @subscriber = Thread.new { RedisSubscriberService.new(channel).process }
  end

  def teardown_subscriber
    @subscriber.kill
  end

  def with_subscriber
    yield
    @subscriber.join THREAD_PROCESS_TIMEOUT
  end

  def message_from_subscription
    @subscriber[:messages].first
  end
end

Notice the with_subscriber method. It executes some code passed to it, then passes method execution to the subscriber process to read any messages sent and store onto messages store.

The count of the variable THREAD_PROCESS_TIMEOUT, can be experimented to set to a value that suites the system that’s being verified.

In our tests, we can now verify PubSub as-

require 'test_helper'

class RedisPubSubServiceTest < ActiveSupport::TestCase
  include SubscriberHelpers

  def setup
    setup_subscriber
  end

  def teardown
    teardown_subscriber
  end

  def test_writing_and_reading_back_values_from_pub_sub
    value = 'Vipul A M'

    with_subscriber do
      RedisPublisherService.new('test_channel', value).process
    end

    assert_equal value, message_from_subscription
  end
end

Summary

We took a look at how PubSub based services can be verified by using threads and exposing messages from them, for verification. These can be tailored to support any similar PubSub service like Redis, and can be used to easily verify values being published from our services from Rails tests.

Voice based phone verification using twilio

In my previous blog post, I talked about how to do phone verification using SMS and now in this blog post I’m going to talk about how to do voice based phone verification using Twilio.

Requirement

Let’s change the requirement a bit this time.

  • After the user signs up, send an SMS verification. If SMS verification goes well then use that phone number for future use.
  • But if the user’s phone number doesn’t support the SMS feature, then call on user’s phone number for the verification.
  • In the call, ask the user to press 1 from keypad to complete the verification. If that user presses the 1 key, then mark the phone number as verified.

Step 1: Make a call for phone verification

We already handled SMS verification, so let’s add call related changes in PhoneVerificationService class.

class PhoneVerificationService
  attr_reader :user, :verification_through

  VERIFICATION_THROUGH_SMS  = :sms
  VERIFICATION_THROUGH_CALL = :call

  def initialize options
    @user                 = User.find(options[:user_id])
    @verification_through = options[:verification_through] || VERIFICATION_THROUGH_SMS
  end

  def process
    if verification_through == VERIFICATION_THROUGH_SMS
      perform_sms_verification
    else
      make_call
    end
  end

  private

  def from
    #phone number given by twilio
    Settings.twilio_number_for_app
  end

  def to
    "+1#{user.phone_number}"
  end

  def body
    "Please reply with this code '#{user.phone_verification_code}'" <<
    "to verify your phone number"
  end

  def send_sms
    Rails.logger.info "SMS: From: #{from} To: #{to} Body: \"#{body}\""

    twilio_client.account.messages.create ( from: from, to: to, body: body)
  end

  def make_call
    Rails.logger.info "Call: From: #{from} To: #{to}"

    twilio_client.account.calls.create( from: from, to: to, url: callback_url)
  end

  def perform_sms_verification
    begin
      send_sms
    rescue Twilio::REST::RequestError => e
      return make_call if e.message.include?('is not a mobile number')
      raise e.message
    end
  end

  def callback_url
    Rails.application.routes.url_helpers
      .phone_verifications_voice_url(host: Settings.app_host,
                                     verification_code: user.phone_verification_code)
  end

  def twilio_client
    @twilio ||= Twilio::REST::Client.new(Settings.twilio_account_sid,
                                         Settings.twilio_auth_token)
  end
end

In PhoneVerificationService class we have added some major changes:

  1. We have defined one more attribute reader verification_through and in initialize method we have set it.
  2. We have created two new constants VERIFICATION_THROUGH_SMS & VERIFICATION_THROUGH_CALL.
  3. We have changed process method and now we check which verification process should be taken place based on verification_through attribute.
  4. We also have added new methods perform_sms_verification, make_call and callback_url.

Let’s go through these newly added methods.

perform_sms_verification:

In this method, first we try to send an SMS verification. If SMS verification fails with error message like ‘is not a mobile number’ then in the rescue block, we make a phone verification call or we raise the error.

make_call:

In this method, we create an actual phone call and pass the all required info to twilio client object.

callback_url:

In this method, we set the callback_url which is required for Twilio for making a call. When we call the user for verification, our app needs to behave like a human and should ask to the user to press 1 to complete the verification (i.e. In the form of TwiML). This callback_url needs be to set in our app.

Step 2: Add voice action in phone verification controller

For callback_url, add route for voice action in config/routes.rb

post 'phone_verifications/voice' => 'phone_verifications#voice'

Add voice action and required code for it in PhoneVerificationsController.

class PhoneVerificationsController < ApplicationController
  skip_before_filter :verify_authenticity_token
  after_filter :set_header

  HUMAN_VOICE = 'alice'

  def voice
    verification_code = params[:verification_code]

    response = Twilio::TwiML::Response.new do |r|
      r.Gather numDigits: '1',
               action: "/phone_verifications/verify_from_voice?verification_code=#{verification_code}",
               method: 'post' do |g|

        g.Say 'Press 1 to verify your phone number.', voice: HUMAN_VOICE
      end
    end

    render_twiml response
  end

  def verify_from_message
    user = get_user_for_phone_verification
    user.mark_phone_as_verified! if user

    render nothing: true
  end

  private

  def get_user_for_phone_verification
    phone_verification_code = params['Body'].try(:strip)
    phone_number            = params['From'].gsub('+1', '')

    condition = { phone_verification_code: phone_verification_code,
                  phone_number: phone_number }

    User.unverified_phones.where(condition).first
  end

  def set_header
    response.headers["Content-Type"] = "text/xml"
  end

  def render_twiml(response)
    render text: response.text
  end
end

In this voice method, we have set up the Twilio response. When a call is made to the user, this response will get converted into robotic human voice. render_twiml method which sets twilio response in text form is required for Twilio APIs. Set up the response header in the set_header method which gets called in after_filter method.

Step 3: Set request URL for voice phone verification in Twilio

In voice action method, we have setup the request url in the action key of Twilio response object, that also needs to be set in your Twilio account. So when, user replies back to the call query, Twilio will make a request to our app and adds its own some values as parameters to the request. Using those parameters we handle the actual phone verification in the app.

Open twilio account and under NUMBERS section/tab, click on your Twilio number. Then in Voice section, add request URL with HTTP POST method. Add URL like this.

http://your.ngrok.com/phone_verifications/verify_from_voice/

We need Ngrok to expose local url to external world. Read more about it in my privous blog post.

Step 4: Add verify_from_voice action in phone verification controller

First add route for this action in config/routes.rb

post "phone_verifications/verify_from_voice" => "phone_verifications#verify_from_voice

Add this method, in your PhoneVerificationsController.

def verify_from_voice
    response = Twilio::TwiML::Response.new do |r|
      if params['Digits'] == "1"
        user = get_user_for_phone_verification
        user.mark_phone_as_verified!

        r.Say 'Thank you. Your phone number has been verified successfully.',
               voice: HUMAN_VOICE
      else
        r.Say 'Sorry. Your phone number has not verified.',
               voice: HUMAN_VOICE
      end
    end

    render_twiml response
  end

Modify private method get_user_for_phone_verification to support voice verification in PhoneVerificationsController.

def get_user_for_phone_verification
    if params['Called'].present?
      phone_verification_code = params['verification_code']
      phone_number            = params['To']
    else
      phone_verification_code = params['Body'].try(:strip)
      phone_number            = params['From']
    end
    condition = { phone_verification_code: phone_verification_code,
                  phone_number: phone_number.gsub('+1', '') }

    User.unverified_phones.where(condition).first
  end

In the verify_from_voice method, we get parameters Digits, To & verification_code from Twilio request. Using these parameters, we search for user in the database. If we get the proper user then we mark user’s phone number as verified phone number.

Gotcha with after_commit callback in Rails

after_commit callback in Rails is triggered after the end of transaction.

For eg. I have a Post model for which the number of lines of the content are calculated in the after_commit callback:

class Post < ActiveRecord::Base
  after_commit :calculate_total_lines, if: -> (post) { post.previous_changes.include?(:content) }

  def calculate_total_lines
    update! total_lines: content.split("\n").length
  end
end
post = Post.create! content: "Lets discuss Rails 5.\n", author: 'Prathamesh'
assert_equal 1, post.total_lines

Now lets wrap the creation of post inside a transaction block:

Post.transaction do
  post = Post.create! content: "Lets discuss Rails 5.\n", author: 'Prathamesh'
  assert_equal 1, post.total_lines
end

The test will fail now:

#   1) Failure:
# BugTest#test_within_transaction [after_commit_test.rb:45]:
# Expected: 1
#   Actual: nil

Why? Lets recall. after_commit callback will get executed after the end of transaction.

So until all the code inside transaction is completed, the callback is not going to get executed.

Here is a gist with complete test.

Next time you are using an after_commit callback and a transaction, make sure that code inside the transaction is not dependent on the result of the callback.

Blue border around JWPLAYER video

Latest versions of JWPlayer(6.9 onwards) add blue border around the video when it is in focus.

JWPlayer blue border

This is because of the CSS class jwplayer-tab-focus.

The blue border around currently selected video allows to identify which instance of JWPlayer is in focus.

But with a single JWPlayer instance, it can be annoying.

To remove this blue border, we can override the default JWPlayer CSS as follows.

.jw-tab-focus:focus {
    outline: none;
}

To keep all the overridden CSS in once place, we can add this change in a separate file such as jwplayer_overrides.css.