Step-by-Step Guide: Consuming CubeMaster API with Ruby on Rails (RestClient)

To use the CubeMaster API, you need an API key (TokenID) for authentication. Here's how to get started:

  1. Visit the CubeMaster website: https://cubemaster.net.
  2. Locate the "Sign In" option (typically found in the top-right corner).
  3. Fill out the registration form with your details (e.g., name, email, password, company information).
  4. After signing up, log in to your account dashboard.
  5. Navigate to the "Settings" - "Integration" section to generate your API key (TokenID).
  6. Generate an API key. Once generated, you’ll receive a unique TokenID (e.g., abc123xyz789). Copy this key and store it securely, as it will be used in the HTTP headers of your API requests.
  7. Copy the TokenID and store it securely.

Note: The TokenID will be used in the HTTP headers of your POST request for authentication.

Tip: In Ruby on Rails, use the dotenv-rails gem to manage environment variables. Add your TokenID to a .env file:

CUBEMASTER_API_KEY=abc123xyz789

Then access it in your code with ENV['CUBEMASTER_API_KEY'].

Ensure your Rails app is ready to make API calls using the RestClient gem.

  1. Open your Rails project’s Gemfile and add:
  2. gem 'rest-client'
  3. Run bundle install in your terminal to install the gem.
  4. Create a new controller (e.g., LoadsController) to handle API interactions:
    rails g controller Loads create
  5. In config/routes.rb, add a route for the create action:
    post '/loads/create', to: 'loads#create'
  6. Verify your Rails app is running locally by executing rails server and visiting http://localhost:3000.

UI Element: In your view (e.g., app/views/loads/new.html.erb), add a simple form to trigger the API call:

<%= form_with url: "/loads/create", method: :post do |form| %>
  <%= form.submit "Create Load", class: "btn btn-primary" %>
<% end %>

A RESTful API (Representational State Transfer) is a way for applications to communicate over the internet using standard HTTP methods like GET, POST, PUT, and DELETE. Here’s a beginner-friendly breakdown:

  • Endpoint: A URL (e.g., https://api.cubemaster.net/loads) where the API listens for requests.
  • HTTP Methods:
    • GET: Retrieve data.
    • POST: Send data to create something (like our load).
  • Request: You send data (e.g., JSON) to the API.
  • Response: The API sends back data (e.g., success message or error).
  • Headers: Extra info sent with the request, like authentication tokens.

In this guide, we’ll use a POST request to the /loads endpoint to build a load, sending JSON data and receiving a JSON response.

Assume your customer’s legacy database stores order/shipment data (e.g., items, quantities, dimensions). Integrate this into your API request.

  1. Create a model for your legacy data (e.g., Order):
    rails g model Order name:string length:decimal width:decimal height:decimal weight:decimal qty:integer
  2. Migrate the database: rails db:migrate.
  3. Seed sample data in db/seeds.rb:
    Order.create(name: "ITEM001", length: 72, width: 30, height: 75, weight: 1002.45, qty: 16)
    Order.create(name: "ITEM002", length: 27.31, width: 37.5, height: 76.67, weight: 521.45, qty: 28)
    Run rails db:seed.
  4. In your controller, fetch this data to build the Cargoes array:
    orders = Order.all
    cargoes = orders.map do |order|
      {
        "Name": order.name,
        "Length": order.length,
        "Width": order.width,
        "Height": order.height,
        "Weight": order.weight,
        "Qty": order.qty,
        "OrientationsAllowed": "OrientationsAll",
        "TurnAllowedOnFloor": false,
        "ColorKnownName": "Brown" # Example default
      }
    end

This data will be merged into the API request payload in the next step.

Now, build the request payload and send it to the CubeMaster API using RestClient.

In app/controllers/loads_controller.rb:

require 'rest-client'
require 'json'

class LoadsController < ApplicationController
  def create
    # Fetch legacy data
    orders = Order.all
    cargoes = orders.map do |order|
      {
        "Name": order.name,
        "Length": order.length,
        "Width": order.width,
        "Height": order.height,
        "Weight": order.weight,
        "Qty": order.qty,
        "OrientationsAllowed": "OrientationsAll",
        "TurnAllowedOnFloor": false,
        "ColorKnownName": "Brown"
      }
    end

    # Full payload
    payload = {
      "Title": "New Mixed Truck Load",
      "Description": "Hello Web API",
      "Cargoes": cargoes + [
        {
          "Name": "SKU0005",
          "Length": 27.31,
          "Width": 9.5,
          "Height": 75.67,
          "Weight": 501.45,
          "OrientationsAllowed": "OrientationsAll",
          "TurnAllowedOnFloor": true,
          "Qty": 24,
          "ColorKnownName": "Beige"
        },
        { "Name": "SKU0005", "Qty": 23 },
        { "Name": "SKU0008", "Qty": 34 }
      ],
      "Containers": [
        {
          "VehicleType": "Dry",
          "Name": "53FT-Intermodal",
          "Length": 630,
          "Width": 98,
          "Height": 106,
          "ColorKnownName": "Blue"
        }
      ],
      "Rules": {
        "IsWeightLimited": true,
        "IsSequenceUsed": false,
        "FillDirection": "FrontToRear",
        "CalculationType": "MixLoad"
      }
    }.to_json

    # Send POST request
    response = RestClient.post(
      'https://api.cubemaster.net/loads',
      payload,
      {
        content_type: :json,
        'TokenID': ENV['CUBEMASTER_API_KEY']
      }
    )

    # Handle response (next step)
    @result = JSON.parse(response.body)
    render 'result'
  rescue RestClient::ExceptionWithResponse => e
    @error = e.response
    render 'error'
  end
end

Request JSON:

{
    "Title": "New Mixed Truck Load",
    "Description": "Hello Web API",
    "Cargoes": [
        {
            "Name": "ITEM001",
            "Length": 72,
            "Width": 30,
            "Height": 75,
            "Weight": 1002.45,
            "OrientationsAllowed": "OrientationsAll",
            "TurnAllowedOnFloor": false,
            "Qty": 16,
            "ColorKnownName": "Brown"
        },
        {
            "Name": "ITEM002",
            "Length": 27.31,
            "Width": 37.5,
            "Height": 76.67,
            "Weight": 521.45,
            "OrientationsAllowed": "OrientationsAll",
            "TurnAllowedOnFloor": false,
            "Qty": 28,
            "ColorKnownName": "Brown"
        },
        {
            "Name": "SKU0005",
            "Length": 27.31,
            "Width": 9.5,
            "Height": 75.67,
            "Weight": 501.45,
            "OrientationsAllowed": "OrientationsAll",
            "TurnAllowedOnFloor": true,
            "Qty": 24,
            "ColorKnownName": "Beige"
        },
        { "Name": "SKU0005", "Qty": 23 },
        { "Name": "SKU0008", "Qty": 34 }
    ],
    "Containers": [
        {
            "VehicleType": "Dry",
            "Name": "53FT-Intermodal",
            "Length": 630,
            "Width": 98,
            "Height": 106,
            "ColorKnownName": "Blue"
        }
    ],
    "Rules": {
        "IsWeightLimited": true,
        "IsSequenceUsed": false,
        "FillDirection": "FrontToRear",
        "CalculationType": "MixLoad"
    }
}

UI Element: The form submission triggers this action. The TokenID is passed in the headers for authentication.

After sending the request, process the response to display results or handle errors.

Create app/views/loads/result.html.erb:

<h2>Load Creation Result</h2>
<p>Status: <%= @result["status"] %></p>
<p>Message: <%= @result["message"] %></p>
<% if @result["calculationError"].present? %>
  <p class="text-danger">Error: <%= @result["calculationError"] %></p>
<% end %>
<h3>Load Summary</h3>
<ul>
  <li>Cargoes Loaded: <%= @result["loadSummary"]["cargoesLoaded"] %></li>
  <li>Volume Loaded: <%= @result["loadSummary"]["volumeLoaded"] %></li>
  <li>Weight Loaded: <%= @result["loadSummary"]["weightLoaded"] %></li>
</ul>
<%= link_to "Back", new_load_path, class: "btn btn-secondary" %>

Create app/views/loads/error.html.erb for errors:

<h2>Error Occurred</h2>
<p class="text-danger"><%= @error %></p>
<%= link_to "Try Again", new_load_path, class: "btn btn-secondary" %>

Response JSON:

{
    "status": "succeed",
    "message": "Engine created. 5 cargoes. 1 empty containers. Calculation started. Calculation ended. The load built successfully. The load saved to the cloud database.",
    "calculationError": "InvalidCargoSize",
    "document": {
        "title": "New Mixed Truck Load",
        "description": "Hello Web API",
        "isShared": true,
        "isAutoSaved": true,
        "isPending": false,
        "calculationTimeInSeconds": 0.6152743,
        "processId": "",
        "batchId": "",
        "createdBy": "CHANG@LOGEN.CO.KR",
        "createdAt": "2023-02-11T01:17:01.7392204+09:00",
        "updatedAt": "0001-01-01T00:00:00"
    },
    "loadSummary": {
        "cargoesLoaded": 68,
        "piecesLoaded": 68,
        "cargoesLeft": 0,
        "piecesLeft": 57,
        "unitloadsLoaded": 0,
        "volumeLoaded": 5261723.4606,
        "weightLoaded": 42674.59999999999,
        "priceLoaded": 0,
        "containersLoaded": 1
    },
    "filledContainers": [ /* Truncated for brevity */ ],
    "manifest": [ /* Truncated for brevity */ ],
    "reportLinks": {
        "overview": "https://cubemaster.net/source/report/openlink.asp?id=cc3717aa-3460-4af0-ae59-6908bc0a496e"
    },
    "uom": "UnitEnglish"
}

Handling Details: Check status for success, display loadSummary metrics, and show calculationError if present.

Ensure your API integration works smoothly by monitoring and debugging.

  1. Logging: Add logging to your controller:
    Rails.logger.info "Request sent: #{payload}"
    Rails.logger.info "Response received: #{response.body}"
    Check logs in log/development.log.
  2. Pry Debugging: Add gem 'pry' to your Gemfile, then insert binding.pry before the RestClient.post call to inspect variables interactively.
  3. RestClient Debugging: Enable verbose output:
    RestClient.log = 'stdout'
    This prints request/response details to the console.
  4. Test with Postman: Before coding, test the API in Postman with the same JSON and headers to verify behavior.
  5. Error Handling: Review the rescue block in Step 5 to catch and log API errors.

Tip: If you encounter issues like InvalidCargoSize, double-check your Cargoes data against API requirements.