Decoding and Interacting with Barcodes

Episode #67 by David Kimura

Summary

Using the QuaggaJS Library, learn how to scan and decode barcodes with your Ruby on Rails application. Using provided callbacks, interact with controller actions to provide a seamless experience.
rails javascript view ajax 11:40

Resources

Summary

# application.css
#barcode-scanner canvas.drawingBuffer, #barcode-scanner video.drawingBuffer {
  display: none;
}

#barcode-scanner canvas, #barcode-scanner video {
  width: 100%;
  height: auto;
}

# application.js
//= require quagga
//= require_tree .

function order_by_occurrence(arr) {
  var counts = {};
  arr.forEach(function(value){
      if(!counts[value]) {
          counts[value] = 0;
      }
      counts[value]++;
  });

  return Object.keys(counts).sort(function(curKey,nextKey) {
      return counts[curKey] < counts[nextKey];
  });
}

function load_quagga(){
  if ($('#barcode-scanner').length > 0 && navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {

    var last_result = [];
    if (Quagga.initialized == undefined) {
      Quagga.onDetected(function(result) {
        var last_code = result.codeResult.code;
        last_result.push(last_code);
        if (last_result.length > 20) {
          code = order_by_occurrence(last_result)[0];
          last_result = [];
          Quagga.stop();
          $.ajax({
            type: "POST",
            url: '/products/get_barcode',
            data: { upc: code }
          });
        }
      });
    }

    Quagga.init({
      inputStream : {
        name : "Live",
        type : "LiveStream",
        numOfWorkers: navigator.hardwareConcurrency,
        target: document.querySelector('#barcode-scanner')  
      },
      decoder: {
          readers : ['ean_reader','ean_8_reader','code_39_reader','code_39_vin_reader','codabar_reader','upc_reader','upc_e_reader']
      }
    },function(err) {
        if (err) { console.log(err); return }
        Quagga.initialized = true;
        Quagga.start();
    });

  }
};
$(document).on('turbolinks:load', load_quagga);

# visitors/index.html.erb
<div id='barcode-scanner'></div>

# routes.rb
Rails.application.routes.draw do
  resources :products do
    post :get_barcode, on: :collection
  end
  root 'visitors#index'

# rake routes
#               Prefix Verb   URI Pattern                     Controller#Action
# get_barcode_products POST   /products/get_barcode(.:format) products#get_barcode
#             products GET    /products(.:format)             products#index
#                      POST   /products(.:format)             products#create
#          new_product GET    /products/new(.:format)         products#new
#         edit_product GET    /products/:id/edit(.:format)    products#edit
#              product GET    /products/:id(.:format)         products#show
#                      PATCH  /products/:id(.:format)         products#update
#                      PUT    /products/:id(.:format)         products#update
#                      DELETE /products/:id(.:format)         products#destroy
#                 root GET    /                               visitors#index
end

# products_controller.rb
  # GET /products/new
  def new
    @product = Product.new
    @product.upc = params[:upc]
  end

  ...

  # POST /products/get_barcode
  def get_barcode
    @product = Product.find_or_initialize_by(upc: params[:upc])
    unless @product.new_record?
      redirect_to @product
    else
      redirect_to new_product_path(upc: params[:upc])
    end
  end