Dynamically Publishing Apps

What do we mean by dynamic?

This tutorial is meant to address the use case where you already have an application that tracks content you want to publish, and you’re looking for an easy strategy to publish it out. In this tutorial, we will walk through how two Rails implementations (EduAppCenter and CASA on Rails) accomplish this.

Mapping attributes to CASA

In EduAppCenter, Instructure stores a good deal of metadata about applications; to share with CASA, EduAppCenter needs to map its conception of metadata to CASA’s attribute structure.

The identity structure is composed of two attributes that EduAppCenter tracks:

short_name is a unique string key that a publisher assigns to their app. This is perfect for the id value of the CASA payload, as its unique for the publisher.

EduAppCenter is an aggregator for apps published by numerous peers. Instead of putting the same originator_id on all of its apps, instead EduAppCenter assigns originator_id at the granularity of each publisher on the system. This is accessible on EduAppCenter’s app model as casa_uuid.

CASA has not defined any require attributes yet, so that section is empty.

The only tricky part is use because of the fact that we need to map numerous keys to their values. The logic behind some of these…

The title attribute 1f2625c2-615f-11e3-bf13-d231feb1dc81 is a direct mapping from the name on EduAppCenter.

The tags attribute c6e33506-b170-475b-83e9-4ecd6b6dd42a directly maps from a tags relation that EduAppCenter tracks for its own use.

The author attribute d59e3a1f-c034-4309-a282-60228089194e is an array of objects, each one containing several keys. Because EduAppCenter only tracks one author, it just maps author_name, contact_email and support_url to a single object in the array.

A twist in the attribute structure is that an app may or may not have an organization. To bypass this issue, EduAppCenter sets that key-value pair after defining the rest of the structure, and only if present. This bit, plus the entire payload definition, here:

Finally, they define a new controller method that returns the CASA payload for all CASA-enabled apps in JSON format:

defcasarenderjson:LtiApp.casas.map(&:casa),root:falseend

Abstracting attribute mappings

Similar to EduAppCenter, CASA on Rails maps the basic attributes right onto its representation of a payload that it will share with peers:

defto_transit_payloadiforiginated?payload={'identity'=>{'id'=>payload_id,'originator_id'=>payload_originator_id},'original'=>{'uri'=>self.uri,'share'=>self.share,'propagate'=>self.propagate,'timestamp'=>self.updated_at.to_datetime.rfc3339,'use'=>{},'require'=>{}}}# .. map use and require attributes .. #payloadelse# .. ignore as this is for relaying apps it discovered from others .. #endend

The difference is how it maps the use and require sections. Because of the extensible nature of attributes, CASA on Rails implements an architecture where attribute mappings can simply be attached to a handler, and it iterates over these:

defto_transit_payloadiforiginated?payload={'identity'=>{'id'=>payload_id,'originator_id'=>payload_originator_id},'original'=>{'uri'=>self.uri,'share'=>self.share,'propagate'=>self.propagate,'timestamp'=>self.updated_at.to_datetime.rfc3339,'use'=>{},'require'=>{}}}Casa::Payload.attributes_map.eachdo|type,mappings|mappings.eachdo|field,klass|key=klass.send:uuidifklass.respond_to?(:make_for)# use handler's mapping function to compute payload value from app valuevalue=klass.make_for(self)else# directly copy value from app to payloadvalue=self.send(field)endpayload['original'][type][key]=valueunlessvalue.nil?endendpayloadelse# .. ignore as this is for relaying apps it discovered from others .. #endend

In some cases, composition is more challenging than just copying from one to the other. That’s why a make_for method is called on the handler for the app if it’s defined, making it easy for the handler to do something more sophisticated. For example, consider the definition for author: