Skip to main content

Ruby SDK

The Unizo Ruby SDK provides an elegant and idiomatic way to interact with the Unizo API from Ruby applications. It supports Ruby 2.7+ and follows Ruby best practices and conventions.

Installation

Add to your Gemfile:

gem 'unizo-sdk'

Then run:

bundle install

Gem Install

gem install unizo-sdk

Rails Application

In your Gemfile:

gem 'unizo-sdk', '~> 1.0'

Basic Setup

require 'unizo'

# Initialize the client
client = Unizo::Client.new(
api_key: 'your-api-key',
environment: :production # or :sandbox
)

Configuration Options

client = Unizo::Client.new(
api_key: 'your-api-key',
environment: :production,
timeout: 30, # Request timeout in seconds
base_url: 'https://api.unizo.com',
user_agent: 'MyApp/1.0.0',
retry_config: {
attempts: 3,
delay: 1.0,
backoff_factor: 2.0
},
debug: false, # Enable debug logging
logger: Logger.new(STDOUT)
)

Environment Variables

# Set environment variables
ENV['UNIZO_API_KEY'] = 'your-api-key'
ENV['UNIZO_ENVIRONMENT'] = 'production'

# Initialize client with environment variables
client = Unizo::Client.new

Authentication

API Key Authentication

# Set during initialization
client = Unizo::Client.new(api_key: 'your-api-key')

# Or set later
client.api_key = 'your-api-key'

# Using a block for temporary authentication
client.with_api_key('temp-api-key') do |temp_client|
identity = temp_client.identity.get
end

Bearer Token Authentication

# For user-specific operations
client = Unizo::Client.new(bearer_token: 'user-access-token')

# Or set dynamically
client.bearer_token = 'user-access-token'

Usage Examples

Identity Management

begin
# Get user identity
identity = client.identity.get
puts "User ID: #{identity.id}"
puts "Email: #{identity.email}"

# Update user profile
updated_profile = client.identity.update(
name: 'John Doe',
email: 'john@example.com',
metadata: {
department: 'Engineering',
role: 'Senior Developer'
}
)

puts "Profile updated: #{updated_profile.name}"

rescue Unizo::Error => e
puts "Error: #{e.message}"
end

Source Code Management

# List repositories
repositories = client.source_code.repositories.list(
page: 1,
limit: 50,
provider: 'github'
)

repositories.data.each do |repo|
puts "Repository: #{repo.name} (#{repo.url})"
end

# Get repository details
repo = client.source_code.repositories.get('repo-id')
puts "Repository stats: #{repo.stats}"

# Create webhook
webhook = client.source_code.webhooks.create(
repository_id: 'repo-id',
url: 'https://your-app.com/webhooks/source-code',
events: [:push, :pull_request],
secret: 'webhook-secret'
)

puts "Webhook created: #{webhook.id}"

Ticketing System

# Create a ticket
ticket = client.ticketing.tickets.create(
title: 'Bug Report: Login Issues',
description: 'Users are experiencing login failures after the latest update',
priority: :high,
assignee: 'user-id',
tags: %w[bug login urgent],
due_date: 3.days.from_now
)

puts "Created ticket: #{ticket.id}"

# List tickets with filtering
tickets = client.ticketing.tickets.list(
status: :open,
priority: :high,
assignee: 'current-user',
page: 1,
limit: 25,
created_after: 1.week.ago
)

puts "Found #{tickets.total} tickets"

# Update ticket
updated_ticket = client.ticketing.tickets.update(
ticket.id,
status: :in_progress,
comments: [{
text: 'Started investigating the issue',
author: 'developer-id',
timestamp: Time.current
}]
)

Communications

# Send email with template
email_result = client.communications.email.send(
to: ['user@example.com'],
subject: 'Welcome to Unizo',
template: 'welcome-template',
variables: {
user_name: 'John Doe',
activation_link: 'https://app.unizo.com/activate/token'
},
attachments: [{
filename: 'welcome_guide.pdf',
content: File.read('path/to/welcome_guide.pdf'),
content_type: 'application/pdf'
}]
)

# Send plain text email
plain_email = client.communications.email.send(
to: ['user@example.com'],
subject: 'System Notification',
text: 'Your account has been activated successfully.'
)

# Send SMS
sms_result = client.communications.sms.send(
to: '+1234567890',
message: 'Your verification code is: 123456',
sender_id: 'UNIZO'
)

puts "SMS sent: #{sms_result.message_id}"

Incident Management

# Create incident
incident = client.incidents.create(
title: 'API Service Degradation',
description: 'Response times increased by 200% across all endpoints',
severity: :major,
status: :investigating,
services: %w[api-gateway database cache],
affected_users: 1500
)

puts "Created incident: #{incident.id}"

# List active incidents
active_incidents = client.incidents.list(
status: [:open, :investigating],
severity: [:critical, :major]
)

# Update incident with timeline
client.incidents.update(
incident.id,
status: :resolved,
resolution: 'Scaled up database instances and optimized queries',
timeline: [{
timestamp: Time.current,
event: 'Issue resolved',
description: 'Database performance restored to normal levels'
}]
)

Error Handling

Exception Types

begin
identity = client.identity.get
rescue Unizo::ValidationError => e
puts "Validation error: #{e.message}"
puts "Field errors: #{e.field_errors}"
rescue Unizo::AuthenticationError => e
puts "Authentication failed: #{e.message}"
# Handle re-authentication
rescue Unizo::RateLimitError => e
puts "Rate limit exceeded. Retry after: #{e.retry_after} seconds"
# Implement backoff strategy
rescue Unizo::NotFoundError => e
puts "Resource not found: #{e.message}"
rescue Unizo::ServerError => e
puts "Server error: #{e.message}"
# Log for debugging
rescue Unizo::Error => e
puts "Unizo API error: #{e.message}"
puts "Status code: #{e.status_code}"
puts "Error code: #{e.code}"
end

Retry Logic

module RetryHelper
def self.with_retry(max_attempts: 3, base_delay: 1.0, &block)
attempt = 1

begin
yield
rescue Unizo::RateLimitError => e
if attempt < max_attempts
delay = base_delay * (2 ** (attempt - 1))
sleep([delay, e.retry_after].max)
attempt += 1
retry
else
raise
end
end
end
end

# Usage
identity = RetryHelper.with_retry(max_attempts: 3) do
client.identity.get
end

Circuit Breaker Pattern

class CircuitBreaker
def initialize(failure_threshold: 5, recovery_timeout: 60)
@failure_threshold = failure_threshold
@recovery_timeout = recovery_timeout
@failure_count = 0
@last_failure_time = nil
@state = :closed
end

def call(&block)
case @state
when :open
if Time.current - @last_failure_time > @recovery_timeout
@state = :half_open
attempt_call(&block)
else
raise Unizo::CircuitBreakerError, 'Circuit breaker is open'
end
when :half_open, :closed
attempt_call(&block)
end
end

private

def attempt_call(&block)
begin
result = yield
reset_failure_count
result
rescue Unizo::Error => e
record_failure
raise
end
end

def record_failure
@failure_count += 1
@last_failure_time = Time.current

if @failure_count >= @failure_threshold
@state = :open
end
end

def reset_failure_count
@failure_count = 0
@state = :closed
end
end

# Usage
circuit_breaker = CircuitBreaker.new(failure_threshold: 3, recovery_timeout: 30)

begin
identity = circuit_breaker.call do
client.identity.get
end
rescue Unizo::CircuitBreakerError => e
puts "Service temporarily unavailable: #{e.message}"
end

Rails Integration

Configuration

# config/initializers/unizo.rb
Unizo.configure do |config|
config.api_key = Rails.application.credentials.unizo_api_key
config.environment = Rails.env.production? ? :production : :sandbox
config.timeout = 30
config.logger = Rails.logger
end

Service Objects

# app/services/unizo_service.rb
class UnizoService
def self.client
@client ||= Unizo::Client.new
end

def self.create_support_ticket(user, subject, message)
client.ticketing.tickets.create(
title: subject,
description: message,
priority: :medium,
metadata: {
user_id: user.id,
user_email: user.email
}
)
end

def self.get_user_tickets(user)
client.ticketing.tickets.list(
metadata: { user_email: user.email },
status: :open
).data
end
end

Controller Usage

# app/controllers/support_tickets_controller.rb
class SupportTicketsController < ApplicationController
def create
@ticket = UnizoService.create_support_ticket(
current_user,
params[:subject],
params[:message]
)

if @ticket
redirect_to support_tickets_path, notice: 'Ticket created successfully'
else
render :new, alert: 'Failed to create ticket'
end
rescue Unizo::Error => e
Rails.logger.error "Unizo API error: #{e.message}"
render :new, alert: 'Service temporarily unavailable'
end

def index
@tickets = UnizoService.get_user_tickets(current_user)
rescue Unizo::Error => e
@tickets = []
flash.now[:alert] = 'Unable to load tickets at this time'
end
end

Background Jobs

# app/jobs/create_incident_job.rb
class CreateIncidentJob < ApplicationJob
queue_as :urgent

retry_on Unizo::RateLimitError, wait: ->(executions) { executions ** 2 }
retry_on Unizo::ServerError, attempts: 3, wait: 5.seconds

def perform(title, description, severity = :major)
client = Unizo::Client.new

incident = client.incidents.create(
title: title,
description: description,
severity: severity.to_sym,
status: :open
)

# Notify relevant teams
NotificationService.notify_incident_created(incident)
end
end

Webhook Handling

Sinatra Example

require 'sinatra'
require 'unizo'

post '/webhooks/unizo' do
signature = request.env['HTTP_X_UNIZO_SIGNATURE']
payload = request.body.read

unless Unizo::Webhook.validate_signature(payload, signature, ENV['WEBHOOK_SECRET'])
halt 401, 'Invalid signature'
end

event = JSON.parse(payload)

case event['type']
when 'ticket.created'
handle_ticket_created(event['data'])
when 'incident.updated'
handle_incident_updated(event['data'])
else
puts "Unknown event type: #{event['type']}"
end

'OK'
end

def handle_ticket_created(ticket_data)
puts "New ticket created: #{ticket_data['id']}"
# Process ticket creation
end

def handle_incident_updated(incident_data)
puts "Incident updated: #{incident_data['id']}"
# Process incident update
end

Rails Webhook Controller

# app/controllers/webhooks_controller.rb
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token

def unizo
signature = request.headers['X-Unizo-Signature']
payload = request.raw_post

unless Unizo::Webhook.validate_signature(payload, signature, webhook_secret)
head :unauthorized
return
end

event = JSON.parse(payload)

case event['type']
when 'ticket.created'
TicketCreatedJob.perform_later(event['data'])
when 'incident.updated'
IncidentUpdatedJob.perform_later(event['data'])
end

head :ok
end

private

def webhook_secret
Rails.application.credentials.unizo_webhook_secret
end
end

Testing

RSpec Examples

# spec/support/unizo_helper.rb
module UnizoHelper
def stub_unizo_client
client = instance_double(Unizo::Client)
allow(Unizo::Client).to receive(:new).and_return(client)
client
end

def stub_unizo_identity(client, identity_data = {})
identity_service = instance_double(Unizo::IdentityService)
allow(client).to receive(:identity).and_return(identity_service)

identity = OpenStruct.new({
id: 'user-123',
email: 'test@example.com',
name: 'Test User'
}.merge(identity_data))

allow(identity_service).to receive(:get).and_return(identity)
identity_service
end
end

RSpec.configure do |config|
config.include UnizoHelper
end
# spec/services/unizo_service_spec.rb
require 'rails_helper'

RSpec.describe UnizoService do
let(:client) { stub_unizo_client }
let(:user) { create(:user) }

before do
allow(described_class).to receive(:client).and_return(client)
end

describe '.create_support_ticket' do
let(:ticket_service) { instance_double(Unizo::TicketService) }
let(:tickets_service) { instance_double(Unizo::TicketsService) }

before do
allow(client).to receive(:ticketing).and_return(ticket_service)
allow(ticket_service).to receive(:tickets).and_return(tickets_service)
end

it 'creates a ticket with correct parameters' do
expected_params = {
title: 'Help needed',
description: 'I need assistance',
priority: :medium,
metadata: {
user_id: user.id,
user_email: user.email
}
}

ticket = OpenStruct.new(id: 'ticket-123', title: 'Help needed')
allow(tickets_service).to receive(:create).with(expected_params).and_return(ticket)

result = described_class.create_support_ticket(user, 'Help needed', 'I need assistance')

expect(result.id).to eq('ticket-123')
expect(tickets_service).to have_received(:create).with(expected_params)
end

it 'handles validation errors' do
allow(tickets_service).to receive(:create).and_raise(Unizo::ValidationError.new('Title is required'))

expect {
described_class.create_support_ticket(user, '', 'Description')
}.to raise_error(Unizo::ValidationError)
end
end
end

VCR for Integration Tests

# spec/support/vcr.rb
require 'vcr'

VCR.configure do |config|
config.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
config.hook_into :webmock
config.configure_rspec_metadata!

config.filter_sensitive_data('<API_KEY>') { ENV['UNIZO_API_KEY'] }
config.filter_sensitive_data('<WEBHOOK_SECRET>') { ENV['WEBHOOK_SECRET'] }
end
# spec/integration/unizo_client_spec.rb
require 'rails_helper'

RSpec.describe 'Unizo Client Integration', :vcr do
let(:client) { Unizo::Client.new(api_key: ENV['UNIZO_API_KEY']) }

it 'can fetch user identity' do
VCR.use_cassette('unizo/identity/get') do
identity = client.identity.get
expect(identity.id).to be_present
expect(identity.email).to be_present
end
end

it 'can create and update tickets' do
VCR.use_cassette('unizo/tickets/create_and_update') do
ticket = client.ticketing.tickets.create(
title: 'Test Ticket',
description: 'This is a test ticket'
)

expect(ticket.id).to be_present
expect(ticket.status).to eq('open')

updated_ticket = client.ticketing.tickets.update(
ticket.id,
status: :in_progress
)

expect(updated_ticket.status).to eq('in_progress')
end
end
end

Performance Optimization

Connection Pooling

require 'net/http/persistent'

# Configure persistent connections
http = Net::HTTP::Persistent.new(name: 'unizo_client')
http.idle_timeout = 10
http.pool_size = 20

client = Unizo::Client.new(
api_key: 'your-api-key',
http_client: http
)

Caching

# Simple in-memory cache
class SimpleCache
def initialize(ttl: 300)
@cache = {}
@ttl = ttl
end

def get(key)
entry = @cache[key]
return nil unless entry

if Time.current - entry[:timestamp] > @ttl
@cache.delete(key)
return nil
end

entry[:value]
end

def set(key, value)
@cache[key] = {
value: value,
timestamp: Time.current
}
end
end

# Cached client wrapper
class CachedUnizoClient
def initialize(client, cache = SimpleCache.new)
@client = client
@cache = cache
end

def get_identity
@cache.get('identity') || begin
identity = @client.identity.get
@cache.set('identity', identity)
identity
end
end
end

Batch Operations

# Batch ticket creation
tickets_data = [
{ title: 'Issue 1', description: 'First issue' },
{ title: 'Issue 2', description: 'Second issue' },
{ title: 'Issue 3', description: 'Third issue' }
]

# Process in batches
tickets_data.each_slice(10) do |batch|
results = client.ticketing.tickets.create_batch(batch)
puts "Created #{results.size} tickets"
end

Logging and Debugging

require 'logger'

# Custom logger
logger = Logger.new(STDOUT)
logger.level = Logger::DEBUG

client = Unizo::Client.new(
api_key: 'your-api-key',
logger: logger,
debug: true
)

# Custom log formatter
logger.formatter = proc do |severity, datetime, progname, msg|
"[#{datetime}] #{severity}: #{msg}\n"
end

# Request/response logging
class LoggingMiddleware
def initialize(app, logger)
@app = app
@logger = logger
end

def call(request)
@logger.info "Request: #{request.method} #{request.url}"
@logger.debug "Headers: #{request.headers}"
@logger.debug "Body: #{request.body}" if request.body

response = @app.call(request)

@logger.info "Response: #{response.status}"
@logger.debug "Response body: #{response.body}"

response
end
end

client.add_middleware(LoggingMiddleware.new(client, logger))

Async Operations with Concurrent-Ruby

require 'concurrent-ruby'

# Async operations
class AsyncUnizoClient
def initialize(client)
@client = client
@executor = Concurrent::ThreadPoolExecutor.new(min_threads: 2, max_threads: 10)
end

def get_identity_async
Concurrent::Future.execute(executor: @executor) do
@client.identity.get
end
end

def get_tickets_async(params = {})
Concurrent::Future.execute(executor: @executor) do
@client.ticketing.tickets.list(params)
end
end

def process_user_data_async
identity_future = get_identity_async
tickets_future = get_tickets_async(status: :open)

Concurrent::Future.zip(identity_future, tickets_future).then do |identity, tickets|
{
user: identity.value,
tickets: tickets.value.data
}
end
end

def shutdown
@executor.shutdown
@executor.wait_for_termination(10)
end
end

# Usage
async_client = AsyncUnizoClient.new(client)

future = async_client.process_user_data_async
result = future.value

puts "User: #{result[:user].name}"
puts "Open tickets: #{result[:tickets].size}"

async_client.shutdown

Resources

Support

Requirements

  • Ruby 2.7+
  • Bundler 2.0+
  • faraday >= 1.8.0 (HTTP client)
  • json >= 2.0 (JSON parsing)
  • concurrent-ruby >= 1.1.0 (async operations, optional)