Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RubyConf 2014: Rapidly Mapping JSON/XML APIs in Ruby

RubyConf 2014: Rapidly Mapping JSON/XML APIs in Ruby

Whether you are using a 3rd party or custom JSON API service, integration into an existing application can be time consuming: Where do you start? How can you build an object relational model that can adapt to changes in the schema?

In this talk we'll start with an existing JSON schema and build a Ruby adapter to provide a pure Ruby interface to: consume, modify and re-export that schema into JSON and XML.

Adam Cuppy (he/him)

November 17, 2014
Tweet

More Decks by Adam Cuppy (he/him)

Other Decks in Technology

Transcript

  1. class Connection include HTTParty DEFAULT_API_VERSION = "2" DEFAULT_BASE_URI = "https://www.googleapis.com/civicinfo"

    DEFAULT_QUERY = {} base_uri DEFAULT_BASE_URI def initialize(options={}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @query = options.fetch(:query, DEFAULT_QUERY) end def query(params={}) @query.update(params) end # ... end
  2. connection = Connection.new connection.query( key: “qwerty12345” ) routes = {

    elections: { method: "get", path: "/elections" } } client = Client.new(connection: connection, routes: routes)
  3. class Client # ... def method_missing(method) # => :elections route_map

    = routes.fetch(method) http_method = route_map.fetch(:method) # => "get" relative_path = route_map.fetch(:path) # => "/elections" # call the connection for records @connection.send(http_method, relative_path) end # ... end
  4. # client.elections { "kind" => "civicinfo#electionsQueryResponse", "elections" => [{ "id"

    => "2000", "name" => "VIP Test Election", "electionDay" => "2015-06-06" }] }
  5. Representation { “id" => "2000", “name" => "VIP Test Election",

    “electionDay" => "2015-06-06" } #<User:0x0000 @id="2000", @name="VIP Test Election”, @electionDay=“2015-06-06”>
  6. require "ostruct" class Representation < OpenStruct attr_reader :parent, :children def

    initialize(representation={}, parent=nil) super(representation) @parent = parent @children = [] bind_parent(parent) represent_children(representation) end # ... end
  7. class Representation < OpenStruct # ... def represent(key, collection, parent=nil)

    collection = Array(collection) collection.map do |item| definition_class = definition_class_for_key(key) definition_class.new(item, parent) end end def definition_class_for_key(key) definition_class = convert_key_to_class_string(key) Object.const_get(definition_class) rescue NameError Object.const_set(definition_class, Representation) end # ... end
  8. { "kind" => "civicinfo#electionsQueryResponse", "elections" => [{ "id" => "2000",

    "name" => "VIP Test Election", "electionDay" => "2015-06-06" }] }
  9. { "id" => "2000", "name" => "VIP Test Election", "electionDay"

    => "2015-06-06", "locations" => { "street"=> "111 Main St.", "city" => "Medford", "state" => "Oregon", "zipcode" => "97501" } }
  10. Representable Take an object and decorate it with a representer

    module, render a JSON, XML or YAML document from that object. You can also use representers to parse a document and create or populate an object. https://github.com/apotonick/representable#representable
  11. { "kind" => "civicinfo#electionsQueryResponse", "elections" => [{ "id" => "2000",

    "name" => "VIP Test Election", "electionDay" => "2015-06-06", "locations" => { "address" => "111 Main St. Medford, Oregon 97501" } },{ "id" => "2000", "name" => "VIP Test Election", "electionDay" => "2015-06-06" }] }