RightSupport

This is a library of reusable, unit- and functional-tested Ruby code that RightScale has found broadly useful.

Build Status

Coverage Status

Maintained by the RightScale Technicolor Team

What Does It Do?

Logging

SystemLogger is a rewrite of the seattle.rb SyslogLogger class that features the following improvements:

  • Contains several bugfixes vs. SyslogLogger 1.4.0
  • Inherits from standard Ruby Logger class for guaranteed compatibility
  • Can be configured with options about how to handle newlines, ANSI escape codes, etc

We also provide Log::Mixin, a powerful tool that lets you sprinkle a logger into any object or class, and supports per-class or per-instance custom loggers!

class Jet < Aircraft include RightSupport::Log.Mixin end

#most jets log to the syslog Jet.logger = RightSupport::Log::SystemLogger.new('black box', :facility=>'local0')

#but MY jet @my_jet.logger = Logger.new(StringIO.new)

Logging for Rack Applications

RightSupport also provides Rack middleware that allows a Rack app to use any log sink it wishes. The stock Rack logger middleware is inflexible and gives the end user no control over which logger is used or where the log entries go. Example of usage on Sinatra based application:

class MyApp < Sinatra::Base ... # Specify which logger will be used as default logger LOGGER =\ if ENV['RACK_ENV'].downcase == 'development' Logger.new(STDOUT) else RightSupport::Log::SystemLogger.new("MyApp", => "local0") end

# use the RequestLogger via LogSetter to log requests for application
use RightSupport::Rack::LogSetter, {:logger => LOGGER}
use RightSupport::Rack::RequestLogger    

# set logger and mix Log::Mixin
RightSupport::Log::Mixin.default_logger = LOGGER
include RightSupport::Log::Mixin
...

end

After that all rack requests to MyApp will be logged, and since we mixed Log::Mixin we can use:

logger.info "Hello world\nThis will appear on separate lines\nand without \e[33;0mbeautiful colors"

Networking Stuff

HTTP Client

We provide a very thin wrapper around the rest-client gem that enables simple but robust rest requests with a timeout, headers, etc.

HTTPClient is interface-compatible with the RestClient module, but allows an optional timeout to be specified as an extra parameter.

# Create a wrapper object @client = RightSupport::Net::HTTPClient.new

# Default timeout is 5 seconds @client.get('http://localhost')

# Make sure the HTTPClient request fails after 1 second so we can report an error # and move on! @client.get('http://localhost', => {'X-Hello'=>'hi!', :timeout => 1)}

# HTTPClient transforms String or Hash :query into query string, for example; @client.get('http://localhost/moo', :query=>{:a=>{:b=>:c}} ) # the url that would be requested is http://localhost/moo?a[b]=c

Client-Side Load Balancer

RequestBalancer randomly chooses endpoints for a network request, which lets you perform easy client-side load balancing:

include RightSupport::Net

urls = ['http://localhost', 'http://otherhost'] RequestBalancer.request(urls, :fatal=>RestClient::ResourceNotFound) do |url| REST.get(url) end

The balancer will keep trying requests until one of them succeeds without throwing any exceptions. (NB: a nil return value counts as success!!)

HTTP Request Tracking for Rack

Correlate data flows across your entire architecture with the RequestTracker middleware, which uses custom HTTP headers to "tag" every Web request with a UUID, and works with RequestLogger to log "begin" and "end" lines containing the UUID.

If your app consumes the UUID and passes it on to HTTP services that it invokes, the same request UUID will appear in all logs and you can grep for the "big picture" instead of wasting time paging between logs.

To use this functionality you need:

use RightSupport::Rack::RequestTracker

Statistics Gathering

Profile your compute-heavy and network activities using a Stats::Activity counter.

stats = RightSupport::Stats::Activity.new

puts 'enter 5 lines:' 5.times do stats.update(:read_input) line = STDIN.readline stats.finish end

puts "Only %.1f lines/sec? You are a slow typist!" % [stats.avg_rate]

Input Validation

Validation contains several format-checkers that can be used to validate your web app's models before saving, check for preconditions in your controllers, and so forth.

You can use it as a mixin by including the appropriate child module of RightSupport::Validation.

class AwesomenessGenerator < ActiveRecord::Base include RightSupport::Validation::OpenSSL

before_save do |record|
  errors[:foo] = 'Hey, that's not a key!' unless pem_public_key?(record.foo)
end

end

But you really don't want to do that, do you? Instead, you want to call the module methods of RightSupport::Validation, which contains all of the same mixin methods, but does not pollute the dispatch table of your application classes.

the_key = STDIN.read raise ArgumentError unless RightSupport::Validation.ssh_public_key?(the_key)

String Manipulation

StringExtensions contains String#camelize, which is only defined if the ActiveSupport gem cannot be loaded. It also has some RightScale-specific methods:

  • String#snake_case
  • String#to_const
  • String#to_const_path

Net::StringEncoder applies and removes URL-escape, base64 and other encodings.

Configuration

RightSupport::Config contains functionality, which provides possibility to use human-readable yaml configuration files. For example, you have following yaml file '/tmp/features_config.yml', with content:

eat: khlav kalash: YES! speak: klingonese: false belarusian: true

Then, if you would add configuration in you class, like:

class SweetestClass
CONFIG = RightSupport::Config.features('/tmp/features_config.yml') end

  • RightSupport::Config.features receives file` path, io or string.

Now you can call CONFIG['feature_group']['feature'], like:

  • SweetestClass::CONFIG['eat']['khlav kalash'] would return 'YES!'
  • SweetestClass::CONFIG['speak']['belarusian'] would return true