Slide 1

Slide 1 text

Rapidly Mapping APIs

Slide 2

Slide 2 text

github.com/CodingZeal/RubyConf2014

Slide 3

Slide 3 text

Consume Coerce Consolidate

Slide 4

Slide 4 text

Consume Coerce Consolidate

Slide 5

Slide 5 text

Connection: Relationship Manager

Slide 6

Slide 6 text

Connection Resource HTTP GET/POST

Slide 7

Slide 7 text

https://www.googleapis.com/civicinfo/v1/elections?key=

Slide 8

Slide 8 text

https://www.googleapis.com/civicinfo/v1/elections?key=

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

class Connection # ... def initialize(options={}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) end # ... endd

Slide 11

Slide 11 text

https://www.googleapis.com/civicinfo/v1/elections?key=

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

connection = Connection.new( api_version: “2” ) connection.query( key: “qwerty12345” )

Slide 14

Slide 14 text

Client: Routing and Collection

Slide 15

Slide 15 text

Connection Application Client

Slide 16

Slide 16 text

class Client def initialize(options={}) @connection = options.fetch(:connection) @routes = options.fetch(:routes) end end

Slide 17

Slide 17 text

https://www.googleapis.com/civicinfo/v1/elections?key=

Slide 18

Slide 18 text

# GET /elections { elections: { method: "get", path: "/elections" } }

Slide 19

Slide 19 text

connection = Connection.new connection.query( key: “qwerty12345” ) routes = { elections: { method: "get", path: "/elections" } } client = Client.new(connection: connection, routes: routes)

Slide 20

Slide 20 text

client.elections # ???

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

# client.elections { "kind" => "civicinfo#electionsQueryResponse", "elections" => [{ "id" => "2000", "name" => "VIP Test Election", "electionDay" => "2015-06-06" }] }

Slide 23

Slide 23 text

Consume Coerce Consolidate

Slide 24

Slide 24 text

Representation: Entity mapping

Slide 25

Slide 25 text

Application Client Representation Class Instance Hash Instance

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

require "ostruct" class Representation < OpenStruct end

Slide 28

Slide 28 text

Consume Coerce Consolidate

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

class Election < Representation # ... end

Slide 32

Slide 32 text

representation = Representation.new(client.elections)

Slide 33

Slide 33 text

{ "kind" => "civicinfo#electionsQueryResponse", "elections" => [{ "id" => "2000", "name" => "VIP Test Election", "electionDay" => "2015-06-06" }] }

Slide 34

Slide 34 text

#

Slide 35

Slide 35 text

Mutability

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

class Election < Representation def address [self.street, self.city, self.state, self.zipcode].join(' ') end end

Slide 38

Slide 38 text

class Election < Representation include ActiveModel::Validations end

Slide 39

Slide 39 text

Flexibility

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

@AdamCuppy

Slide 42

Slide 42 text

Thank You!

Slide 43

Slide 43 text

Catch Changes Release Patches

Slide 44

Slide 44 text

{ "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" }] }

Slide 45

Slide 45 text

elections[0][“locations”][“address”] elections[1][“locations”][“address”]

Slide 46

Slide 46 text

elections.rb:12:in `': undefined method `[]' for nil:NilClass (NoMethodError)

Slide 47

Slide 47 text

elections[1][“locations”]

Slide 48

Slide 48 text

elections[1].fetch(“locations”)

Slide 49

Slide 49 text

elections.rb:12:in `fetch': key not found: "location" (KeyError)

Slide 50

Slide 50 text

.fetch(“locations”, { address: “Not determined” })