Ruby

Google Glass for Rubyist

Disclaimer: I do not have access to the Google Glass Explorer Program. The code in this blog post is purely speculative, it is not production ready.

Google has recently made the developer documentation for it's Google Glass product publicly available. Named the Mirror API, it allows developers to create applications called Glassware that interacts with user's Glass devices via a RESTful interface. Developers authenticate to the API on the users behalf using OAuth v2. Once authorized, Glassware applications can publish timeline notifications to the user's Glass devices and subscribe to receive notifications of actions preformed on the devices.

Authorization

Since Glassware applications do not get installed directly onto Glass devices the first step to interacting with a user's device is the authorization process. This web based process has the user accessing your site and authenticating with their Google account by way of OAuth v2.

require 'google/api_client'
require 'sinatra'

CLIENT_ID = '<YOUR_CLIENT_ID>'
CLIENT_SECRET = '<YOUR_CLIENT_SECRET>'
SCOPES = [
  'https://www.googleapis.com/auth/glass.timeline',
  'https://www.googleapis.com/auth/userinfo.profile',
  # Add other requested scopes.
]

enable :sessions

def api_client; settings.api_client; end
def mirror_api; settings.mirror_api; end   
def oauth2_api; settings.oauth2_api; end

def store_credentials(user_id, credentials)
  raise NotImplementedError, 'store_credentials not implemented'
end

def get_stored_credentials(user_id)
  raise NotImplementedError, 'get_stored_credentials not implemented'
end

def get_user_info
  result = api_client.execute!(api_method: oauth2_api.userinfo.get,
                               authorization: user_credentials)
  user_info = nil
  if result.status == 200
    user_info = result.data
  else
    puts "An error occurred: #{result.data['error']['message']}"
  end
  user_info
end

def bootstrap_user
  # TODO: Welcome the user to your Glassware
end

def user_credentials
  @authorization ||= (
    auth = api_client.authorization.dup
    auth.redirect_uri = to('/oauth2callback')
    auth.update_token!(
      get_stored_credentials(session[:user_id])
    )
    auth
  )
end

configure do
  client = Google::APIClient.new
  client.authorization.client_id = CLIENT_ID
  client.authorization.client_secret = CLIENT_SECRET
  client.authorization.scope = SCOPES

  mirror = client.discovered_api('mirror', 'v1')
  oauth2 = client.discovered_api('oauth2', 'v2')

  set :api_client, client
  set :mirror_api, mirror
  set :oauth2_api, oauth2
end

before do
  unless user_credentials.access_token || request.path_info =~ /^\/oauth2/
    redirect to('/oauth2authorize')
  end
end

get '/oauth2authorize' do
  redirect user_credentials.authorization_uri.to_s, 303
end

get '/oauth2callback' do
  user_credentials.code = params[:code] if params[:code]
  user_credentials.fetch_access_token!

  user_info = get_user_info
  session[:user_id] = user_info.id
  store_credentials user_info.id, user_credentials
  bootstrap_user

  redirect to('/')
end

get '/' do
  "<h1>Welcome to my Glassware</h1>"
end

The above Sinatra application authenticates the user and stores their authentication for later use. Storing the user's authentication isn't required but it could prove important as most of the time you will be pushing timeline updates to a user's device when they are away from their computer.

Timeline Cards

Timeline cards allow developers to present information to their users. As timeline cards are added through the API they are presented as a queue the that the user can read through as they wish. You may have notice the # TODO comment in the above example, lets use that method to push a timeline card that welcomes the user to our Glassware application.

def insert_timeline_item(timeline_item, opts)
  if opts[:filename]
    media = Google::APIClient::UploadIO.new(opts[:filename], opts[:content_type])
    result = api_client.execute(
      api_method: mirror_api.timeline.insert,
      body_object: timeline_item,
      media: media,
      parameters: {
        'uploadType' => 'multipart',
        'alt' => 'json'}
      authorization: user_credentials)
  else
    result = api_client.execute(
      api_method: mirror_api.timeline.insert,
      body_object: timeline_item,
      authorization: user_credentials)
  end
  if result.success?
    result.data
  else
    puts "An error occurred: #{result.data['error']['message']}"
  end
end

def bootstrap_user
  text = "Welcome to JasonInCode's Mirror API for Rubyist"
  timeline_item = mirror_api.timeline.insert.request_schema.new({ 'text' => text })
  insert_timeline_item(timeline_item)
end

Now when a user authenticates to the application they receive a timeline card welcoming them. You may have noticed that the insert_timeline_item method has a set of optional parameters. This implementation of insert_timeline_item allows the developer to upload images and other media to a user's timeline.

get '/send_image' do
  text = "Sample image upload"
  timeline_item = mirror_api.timeline.insert.request_schema.new({ 'text' => text })
  insert_timeline_item(
    timeline_item,
    {
      filename: 'sample.png',
      content_type: 'image/png'
    }
  )
end

In addition to adding images, audio, and video a developer can also upload a list of actions the user can perform on a timeline card. There are a number of system provided menu items built into Glass devices:

  • REPLY and REPLY_ALL: Initiates a reply to the timeline item using the voice recording UI.
  • DELETE: Deletes the timeline item.
  • SHARE: Allows the Glass user to share the timeline item with their contacts.
  • READ_ALOUD: Tells the device to read the item's speakableText to the user, if that is not set it will read the item's text instead.
  • VOICE_CALL: Calls the phone number in the item's creator.phone_number.
  • NAVIGATE: Starts navigation to the item's location.
  • TOGGLED_PINNED: Toggles the item's isPinned state.

Developers can also provide custom menu items that are specific to the individual Glassware application. The custom menu items can have custom display text and an icon image. Each custom menu item has an id that is returned to the application via the application's registered subscription URL.

get '/item-with-actions' do
  text = "How much wood could a woodchuck chuck?"
  speakableText = "If a woodchuck could chuck wood."
  timeline_item = mirror_api.timeline.insert.request_schema.new({
    'text' => text, 'speakableText' => speakableText
  })

  menuItems = []

  menuItems << { 'action' => 'READ_ALOUD' }

  menuItems << {
    'action' => 'CUSTOM',
    'id' => 'id-for-notification',
    'values' => [{
      'displayName' => 'JasonInCode Action',
      'iconUrl' => 'path/to/icon.png'
    }]
  }

  timeline_item.menuItems = menuItems
  insert_timeline_item(timeline_item)
end

Subscriptions

As mentioned above a Glassware application can register to receive a notifications from a user's Glass device. When creating a subscription the developer specifies a URL for Google to send a request to about the user's actions.

def subscribe_to_notification(collection, callback_url)
  subscription = mirror_api.subscriptions.insert.request_schema.new({
    'collection' => collection,
    'userToken' => session[:user_id],
    'callbackUrl' => callback_url
  })

  result = api_client.execute(
    api_method: mirror_api.subscription.insert,
    body_object: subscription,
    authorization: user_credentials)

  if result.success?
    result.data
  else
    puts "An error occurred: #{result.data['error']['message']}"
  end
end

def bootstrap_user
  # ...

  subscribe_to_notification('timeline', to('/notify'))
end

The code above tells Google to let your application know when a user does an UPDATE, INSERT, or DELETE on their timeline. Google will send a POST request to the specified callbackUrl containing a minimal JSON ping that contains the information to retrieve the full information about. It is important to note that Google requires that the callbackUrl be secured via HTTPS.

post '/notify' do
  request = JSON.parse(request.body.read)

  session[:user_id] = request['userToken']

  text = "Got a notification: #{request}"
  timeline_item = mirror_api.timeline.insert.request_schema.new({ 'text' => text })
  insert_timeline_item(timeline_item)
end

I hope by now you can see the possiblities that Ruby and the Mirror API make available to developers. There are stil topics I didn't touch on such as the Contacts and Location APIs, perhaps in a future post.

Ruby, the Missing LINQ Blues

Having spent a lot of time in the realm of .NET I grew accustomed to LINQ or Language Integrated Queries. LINQ is a way to query collections of objects in a SQL-like DSL. While there is not a direct competitor to the DSL provided by LINQ in Ruby there are several methods that are equivalent to the extension methods provided by LINQ that exist in Ruby.

Where()

One of the most used (just guessing) LINQ methods would be Where(), in .NET allows you to provide a Lambda that is used to filter a collection of objects.

var ints = new[] { 13, 42, 96, 2, 83 };

var evens = ints
    .Where(x => x % 2 == 0);

foreach (int i in evens)
{
  Console.WriteLine(i);
}

And in Ruby this functionality would be achieved with select.

ints = [ 13, 42, 96, 2, 83 ]

evens = ints
    .select { |x| x % 2 == 0 }

puts evens

Both examples output 42, 96, and 2. In the Ruby example find_all would be interchangeable with select.

Select()

In .NET to map a set of items to a different set of items one would use the Select() method. This method is also useful for creating projections of a collection.

var words = new[] { "Hello", "JasonInCode", "Readers" };

var shouting = words
    .Select(x => x.ToUpper();

foreach (String s in shouting)
{
  Console.WriteLine(s);
}

In Ruby you have two options again map and collect. Both are used in the same manner, supply a block that receives a single parameter that is an individual item in the collection and returns the object that it is to be mapped to.

words = %w{ Hello JasonInCode Readers }

shouting = words
    .map { |x| x.upcase }

puts shouting

Both the .NET and Ruby examples will output "HELLO JASONINCODE READERS".

Count()

In .NET you can call the Count() method with no arguments to count the entire collection or provide a Lambda to filter which items get counted. It's usage in .NET looks as follows:

var ints = new[] { 13, 42, 96, 2, 83 };

var all = ints
    .Count();

var some = ints
    .Count(x => x >= 42);

var one = ints
    .Count(x => x == 42);

Console.WriteLine(all);
Console.WriteLine(some);
Console.WriteLine(one);

That would output 5, 3, and 1. To do the similar task in Ruby one would use the count method as so:

ints = [ 13, 42, 96, 2, 83 ]

all = ints
    .count

some = ints
     .count { |x| x >= 42 }

one = ints
    .count 42

puts all, some, one

Any()

Any() is useful when you need to determine if a collection contains one or more items that pass a provided test.

var words = new[] { "C#", "Ruby", "VisualBasic", "Java" };

var yes = words
    .Any(x => x.Contains("y"));

var no = words
    .Any(x => x.Contains("x"));

Console.WriteLine(yes);
Console.WriteLine(no);

Ruby collections have the any? method that serves the very same purpose.

words = %w{ C# Ruby VisualBasic Java }

yes = words
    .any? { |x| x.include? "y" }

no = words
    .any? { |x| x.include? "x" }

puts yes, no

Both example will output true followed by false.

When switching from .NET to Ruby it is nice to find some of my old creature comforts came along for the ride. And once I started learning more about Ruby not only did I find the niceties listed above but a whole list of methods that make Ruby an enjoyable programming experience.

Bonus

If you have spent time in .NET then you likely come across extension methods. A lot of LINQ's methods are actually implemented as extension methods. Extension methods are .NET's way of adding methods to a class that is already implemented.

public statis class StringExtension
{
  public static bool Palindrome(this String me)
  {
    var arr = me.ToLower.ToCharArray();
    Array.Reverse(arr);
    return me.ToLower == new String(arr);
  }
}

Ruby, having an open class implementation, has a much better approach then extension methods. Since a class is never final a developer is free to add and redefine methods.

class String
  def palindrome?
    self.downcase == self.downcase.reverse
  end
end

Creating Custom Cane Metrics

Code quality is a major component of code maintainability. Sloppy or overly complex code is sure to doom a project. If another developer can't tell the purpose of the code at first glance he/she will be less incline to invest time to correct that code's technical debt.

Code complexity, style, and documentation are all factors that developers can control with discipline. Luckily for those of us without discipline (myself included) Square has open sourced Cane, their code quality gem. Cane out of the box can run complexity, style, and documentation checks. Cane also supports the addition of custom checks, this allows you to specifically target the coding habits you want to discourage.

Custom Cane metrics start off easy enough, there are only three requirements:

  • A class level options method that defines the command line options your custom check is expecting. If the check doesn't require and options this method can return an empty Hash
  • A one argument constructor. The command line options defined in your options will be supplied as a Hash to the constructor.
  • A method to return the violations found for the current project named violations

Those requirements can easily be achieved with the following:

class MyCheck < Struct.new(:opts)
  def self.options
    { }
  end

  def violations
    [ ] 
  end
end

Of course this check doesn't actually check anything, so lets make something useful. Something I know I am guilty of is using the puts and print methods to output debugging information to the console while troubleshooting a test or bug. It can annoy others and, honestly, it is very easy to leave behind. So lets implement a PutsCheck metric so these rogue puts and print are caught. We are going to start with a simple class declaration, I'll go ahead and add some of the boiler plate Cane attributes

class PutsCheck < Struct.new(:opts)
  DESCRIPTION =
    "Lines output to console using `puts` or `print`"
  PUTS_REGEX = /^\s*p(uts|rint)?[\s\(]+(.+?)\s*[\)\s]*$/

  def self.key; :puts; end
  def self.name; "puts output checking"; end
  def self.options
    {
      puts_glob:    ['Glob to run puts checks over',
                       default:   '{app,lib}/**/*.rb',
                       variable:  'GLOB',
                       clobber:   :no_puts],
      puts_exclude:  ['Exclude file or glob from puts checking',
                       variable:  'GLOB',
                       type:      Array,
                       default:   [],
                       clobber:   :no_puts],
      no_puts:      ['Disable puts checking', cast: ->(x) { !x }]
    }
  end
end

To walk through what is being declared here is two constants, DESCRIPTION and PUTS_REGEX, along with three class level methods, key, name, and options. The constants will be explained when we implement the violation checking. As for the class methods, key and name are implemented to match Square's own implementation of the metrics shipped with Cane. I have yet to find where they are being used.

The options method requires more explanation, this method defines the command line switches used by the metric. The Hash keys become the command line argument names, so :puts_glob becomes --puts-glob in the command line. The values in the Hash define the defaults for the arguments as well as their documentation. The first element in the Array is the help text for the argument. The key :default defines the arguments default value. The key :variable is used to determine how the value should be interpreted and :type defines the Ruby type the value should be coerced to. Using :clobber and then the name of another argument tells the system that if this other argument is set the ignore this one. Finally :cast allows you to define a lambda to do a custom coercion with.

With the options definition out of the way it is a good time to implement some helper functions to make using the arguments easier.

def file_names
  Dir[opts.fetch(:puts_glob)].reject { |file| excluded?(file) }
end

def exclusions
  @exclusions ||= opts.fetch(:puts_exclude, []).flatten.map do |i|
    Dir[i]
  end.flatten.to_set
end

def excluded?(file)
  exclusions.include?(file)
end

def worker
  Cane.task_runner(opts)
end

The methods exlusions and excluded? help to filter out files specified by the --puts-exclude argument, while file_names returns the files that the user wishes to run the puts check on. The worker method is just a convenient way to use Cane's SimpleTaskRunner when running in a single process or to use Parallel when using multiple. With these methods in place we can implement the violation checking for our metric.

def violations
  return [] if opts[:no_puts]

  worker.map(file_names) do |file_name|
    find_violations(file_name)
  end.flatten
end

def find_violations(file_name)
  Cane::File.iterator(file_name).map.with_index do |line, number|
    puts_match = line.match(PUTS_REGEX)
    result = if !!puts_match
               {
                 file: file_name,
                 line: number + 1,
                 label: "Line outputs '#{puts_match[2]}'",
                 description: DESCRIPTION
               }
             end
  end.compact
end

The violations method returns an Array of violations as long as the user does not use the command line switch --no-puts, in which case the code returns an empty Array. The meat of the metric lies in the find_violations method, using Cane::File.iterator() to ensure the all file encoding is accounted for. We then iterate through the lines of the file to see if they include a call to the methods p, puts, or print using our previously defined constant PUTS_REGEX. If an offending method call is found then it is added to the Array to be returned. Each violations is represented by a Hash object that has the keys :file, which is the file name containing the violation, and :line, where in the file the violation was found. As well as :label, which is specific information about the violation, and :description, which is a general description that the console output groups the violations by.

At this point you can use the custom metric by running it like so, cane -r puts_check.rb --check PutsCheck, or so the check doesn't have to be added every time you can add a .cane file to the project with like so:

> cat .cane
-r puts_check.rb
--check PutsCheck

The PutsCheck metric is now ready to be used as part of a git precommit or as part of a build server process.

Rack::Test and JSON

I recently had the opportunity to make a simple JSON web service using Sinatra, and while testing it I came across something that wasn't apparent from the documentation. I needed to test the web service's response when JSON was POST'ed to an endpoint. The obvious way to do this, or so I thought, would be to use the post() method like so:

post('/endpoint', { 'j': 's', 'o': 'n' })

Turns out that doesn't actually work the way one would expect. Looking at the source code on GitHub, it looks to me that the above code results in the Content Type of application/x-www-form-urlencoded and the Hash being URL encoded. It seems that if you pass a Hash at all Rack::Test will try to URL encoded the values. And as long as you set a specific Content Type Rack::Test won't mess with it. Something like the following will successfully result in a request with application/json for the Content Type and have the JSON as the request body.

post('/endpoint', "{ 'j': 's', 'o': 'n' }", { "CONTENT_TYPE" => "application/json" })

This is a bit more to type but it gets the job done, you can write a helper to wrap this into something shorter like:

def post_json(uri, json)
  post(uri, json, { "CONTENT_TYPE" => "application/json" })
end

I have yet to find another way to achieve this so I am open to any suggestions.

What is in a Type?

Ruby views value types differently then static-typed languages. Traditionally values like Integers, Chars, and Booleans are treated differently then the reference-types. For example in Objective-C in order to pass an NSInteger by reference or store it in a NSArray you have to box the value in a reference-type like NSNumber. Or in C# since value-type variables can't store null if your logic requires a variable to store null or an Integer you have to use the reference-type Nullable<Integer> instead.

This isn't needed in Ruby since the language doesn't have the notion of value-types, instead everything is a reference to an object on the heap. This allows classes like Integer to have methods for developer ease such as .even?, .times, and .upto(). Also having the value-types as objects provides the (albeit minimal) benefit of not requiring that the value be boxed and unboxed as it crosses between the realm of value and reference types.

So while the benefits might be minimal the difference is part of a greater mindset. Ruby is a language written for humans first and machines second. This might seem counter intuitive at first since a human will never execute the code but it makes writing the code more enjoyable.