typekit-client 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +350 -0
- data/Rakefile +7 -0
- data/bin/typekit +146 -0
- data/lib/typekit.rb +13 -0
- data/lib/typekit/client.rb +38 -0
- data/lib/typekit/configuration.rb +16 -0
- data/lib/typekit/configuration/base.rb +21 -0
- data/lib/typekit/configuration/default.rb +34 -0
- data/lib/typekit/connection.rb +10 -0
- data/lib/typekit/connection/adaptor.rb +13 -0
- data/lib/typekit/connection/adaptor/standard.rb +32 -0
- data/lib/typekit/connection/dispatcher.rb +17 -0
- data/lib/typekit/connection/request.rb +23 -0
- data/lib/typekit/connection/response.rb +20 -0
- data/lib/typekit/core.rb +16 -0
- data/lib/typekit/helper.rb +43 -0
- data/lib/typekit/parser.rb +14 -0
- data/lib/typekit/parser/json.rb +13 -0
- data/lib/typekit/parser/yaml.rb +13 -0
- data/lib/typekit/processor.rb +38 -0
- data/lib/typekit/routing.rb +9 -0
- data/lib/typekit/routing/map.rb +43 -0
- data/lib/typekit/routing/node.rb +5 -0
- data/lib/typekit/routing/node/base.rb +45 -0
- data/lib/typekit/routing/node/collection.rb +34 -0
- data/lib/typekit/routing/node/operation.rb +32 -0
- data/lib/typekit/routing/node/root.rb +8 -0
- data/lib/typekit/routing/node/scope.rb +19 -0
- data/lib/typekit/routing/proxy.rb +17 -0
- data/lib/typekit/version.rb +3 -0
- data/spec/cassettes/index_kits_ok.yml +16 -0
- data/spec/cassettes/index_kits_unauthorized.yml +16 -0
- data/spec/cassettes/show_families_calluna_found.yml +16 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/rest_helper.rb +22 -0
- data/spec/typekit/client_spec.rb +36 -0
- data/spec/typekit/configuration_spec.rb +34 -0
- data/spec/typekit/connection/adaptor_spec.rb +24 -0
- data/spec/typekit/connection/dispatcher_spec.rb +36 -0
- data/spec/typekit/connection/request_spec.rb +13 -0
- data/spec/typekit/helper_spec.rb +93 -0
- data/spec/typekit/processor_spec.rb +34 -0
- data/spec/typekit/routing/map_spec.rb +106 -0
- data/spec/typekit/routing/node_spec.rb +40 -0
- data/typekit-client.gemspec +33 -0
- metadata +208 -0
data/lib/typekit.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative 'typekit/version'
|
2
|
+
require_relative 'typekit/core'
|
3
|
+
|
4
|
+
require_relative 'typekit/helper'
|
5
|
+
require_relative 'typekit/configuration'
|
6
|
+
|
7
|
+
require_relative 'typekit/connection'
|
8
|
+
require_relative 'typekit/routing'
|
9
|
+
|
10
|
+
require_relative 'typekit/parser'
|
11
|
+
require_relative 'typekit/processor'
|
12
|
+
|
13
|
+
require_relative 'typekit/client'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Typekit
|
4
|
+
class Client
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@config, :map, :dispatcher, :processor
|
8
|
+
private def_delegator :dispatcher, :deliver
|
9
|
+
private def_delegator :processor, :process
|
10
|
+
|
11
|
+
def initialize(config: :default, **options)
|
12
|
+
@config = Configuration.build(config, **options)
|
13
|
+
end
|
14
|
+
|
15
|
+
def perform(*arguments)
|
16
|
+
process(deliver(trace(*arguments)))
|
17
|
+
end
|
18
|
+
|
19
|
+
Typekit.actions.each do |action|
|
20
|
+
define_method(action) do |*arguments|
|
21
|
+
perform(action, *arguments)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def prepare(action, *path)
|
28
|
+
parameters = path.last.is_a?(Hash) ? path.pop : {}
|
29
|
+
[ action.to_sym, path.flatten.map(&:to_sym), parameters ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def trace(*arguments)
|
33
|
+
action, path, parameters = prepare(*arguments)
|
34
|
+
request = Connection::Request.new(action: action, parameters: parameters)
|
35
|
+
map.trace(request, path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'configuration/base'
|
2
|
+
require_relative 'configuration/default'
|
3
|
+
|
4
|
+
module Typekit
|
5
|
+
module Configuration
|
6
|
+
Error = Class.new(Typekit::Error)
|
7
|
+
|
8
|
+
def self.build(name, **options)
|
9
|
+
self.const_get(name.to_s.capitalize).new(**options)
|
10
|
+
rescue NameError
|
11
|
+
raise Error, 'Unknown configuration'
|
12
|
+
rescue ArgumentError => e
|
13
|
+
raise Error, 'Not enough arguments'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Configuration
|
3
|
+
class Base
|
4
|
+
attr_reader :version, :format, :token
|
5
|
+
|
6
|
+
def initialize(version: 1, format: :json, token:)
|
7
|
+
@version = version
|
8
|
+
@format = format
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
[ :map, :dispatcher, :processor ].each do |component|
|
13
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
14
|
+
def #{ component }
|
15
|
+
@#{ component } ||= build_#{ component }
|
16
|
+
end
|
17
|
+
METHOD
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Configuration
|
3
|
+
class Default < Base
|
4
|
+
private
|
5
|
+
|
6
|
+
def build_map
|
7
|
+
context = [ Typekit.address, "v#{ version }", format ]
|
8
|
+
Routing::Map.new do
|
9
|
+
scope context do
|
10
|
+
resources :families, only: :show do
|
11
|
+
show ':variant', on: :member
|
12
|
+
end
|
13
|
+
|
14
|
+
resources :kits do
|
15
|
+
resources :families, only: [ :show, :update, :delete ]
|
16
|
+
show :published, on: :member
|
17
|
+
update :publish, on: :member
|
18
|
+
end
|
19
|
+
|
20
|
+
resources :libraries, only: [ :index, :show ]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_dispatcher
|
26
|
+
Connection::Dispatcher.new(adaptor: :standard, token: token)
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_processor
|
30
|
+
Processor.new(format: format)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Typekit
|
5
|
+
module Connection
|
6
|
+
module Adaptor
|
7
|
+
class Standard
|
8
|
+
def process(method, address, parameters = {}, headers = {})
|
9
|
+
klass = Net::HTTP.const_get(method.to_s.capitalize)
|
10
|
+
request = klass.new(build_uri(address, parameters))
|
11
|
+
headers.each { |k, v| request[k] = v }
|
12
|
+
http = Net::HTTP.new(request.uri.host, request.uri.port)
|
13
|
+
http.use_ssl = true if address =~ /^https:/
|
14
|
+
response = http.request(request)
|
15
|
+
[ response.code, response.to_hash, response.body ]
|
16
|
+
rescue NameError
|
17
|
+
raise Error, 'Invalid method'
|
18
|
+
rescue SocketError
|
19
|
+
raise Error, 'Connection failed'
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def build_uri(address, parameters)
|
25
|
+
chunks = [ address ]
|
26
|
+
chunks << Helper.build_query(parameters) unless parameters.empty?
|
27
|
+
URI(chunks.join('?'))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Connection
|
3
|
+
class Dispatcher
|
4
|
+
def initialize(adaptor: :standard, token:)
|
5
|
+
@token = token
|
6
|
+
@adaptor = Adaptor.build(adaptor)
|
7
|
+
end
|
8
|
+
|
9
|
+
def deliver(request)
|
10
|
+
method = Helper.translate_action(request.action)
|
11
|
+
code, _, body = @adaptor.process(method, request.address,
|
12
|
+
request.parameters, 'X-Typekit-Token' => @token)
|
13
|
+
Response.new(code: code.to_i, content: body)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Typekit
|
4
|
+
module Connection
|
5
|
+
class Request
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :action, :parameters, :path
|
9
|
+
def_delegators :@path, :<<
|
10
|
+
|
11
|
+
def initialize(action:, parameters: {})
|
12
|
+
@action = action
|
13
|
+
@parameters = parameters
|
14
|
+
@path = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def address
|
18
|
+
# TODO: cache?
|
19
|
+
@path.map(&:to_s).join('/')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Connection
|
3
|
+
class Response
|
4
|
+
attr_reader :code, :content
|
5
|
+
|
6
|
+
def initialize(code:, content:)
|
7
|
+
@code = code
|
8
|
+
@content = content
|
9
|
+
end
|
10
|
+
|
11
|
+
def success?
|
12
|
+
@code == 200
|
13
|
+
end
|
14
|
+
|
15
|
+
def redirect?
|
16
|
+
@code == 302
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/typekit/core.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Typekit
|
2
|
+
Error = Class.new(StandardError)
|
3
|
+
|
4
|
+
@address = 'https://typekit.com/api'.freeze
|
5
|
+
|
6
|
+
@actions = [ :index, :show, :create, :update, :delete ].freeze
|
7
|
+
@collection_actions = [ :index, :create ].freeze
|
8
|
+
@member_actions = [ :show, :update, :delete ].freeze
|
9
|
+
@action_dictionary = { :index => :get, :show => :get,
|
10
|
+
:create => :post, :update => :post, :delete => :delete }.freeze
|
11
|
+
|
12
|
+
singleton_class.class_eval do
|
13
|
+
attr_reader :address, :actions, :collection_actions,
|
14
|
+
:member_actions, :action_dictionary
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Typekit
|
4
|
+
module Helper
|
5
|
+
Error = Class.new(Typekit::Error)
|
6
|
+
|
7
|
+
def self.member_action?(action)
|
8
|
+
if Typekit.member_actions.include?(action)
|
9
|
+
true
|
10
|
+
elsif Typekit.collection_actions.include?(action)
|
11
|
+
false
|
12
|
+
else
|
13
|
+
raise Error, 'Unknown action'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.translate_action(action)
|
18
|
+
Typekit.action_dictionary[action] or raise Error, 'Unknown action'
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build_query(parameters)
|
22
|
+
Rack::Utils.build_nested_query(prepare_parameters(parameters))
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def self.prepare_parameters(parameters)
|
28
|
+
# PATCH: https://github.com/rack/rack/issues/557
|
29
|
+
Hash[
|
30
|
+
parameters.map do |key, value|
|
31
|
+
case value
|
32
|
+
when Integer, TrueClass, FalseClass
|
33
|
+
[ key, value.to_s ]
|
34
|
+
when Hash
|
35
|
+
[ key, prepare_parameters(value) ]
|
36
|
+
else
|
37
|
+
[ key, value ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'parser/json'
|
2
|
+
require_relative 'parser/yaml'
|
3
|
+
|
4
|
+
module Typekit
|
5
|
+
module Parser
|
6
|
+
Error = Class.new(Typekit::Error)
|
7
|
+
|
8
|
+
def self.build(format)
|
9
|
+
self.const_get(format.to_s.upcase).new
|
10
|
+
rescue NameError
|
11
|
+
raise Error, 'Unknown format'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Typekit
|
2
|
+
class Processor
|
3
|
+
ERRORS = {
|
4
|
+
400 => 'There are errors in the data provided by your application',
|
5
|
+
401 => 'Authentication is needed to access the requested endpoint',
|
6
|
+
403 => 'Your application has been rate limited',
|
7
|
+
404 => 'You are requesting a resource that does not exist',
|
8
|
+
500 => 'Typekit’s servers are unable to process the request',
|
9
|
+
503 => 'Typekit’s API is offline for maintenance'
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
def initialize(format:)
|
13
|
+
@parser = Parser.build(format)
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(response)
|
17
|
+
if response.success?
|
18
|
+
data = @parser.process(response.content)
|
19
|
+
process_success(response, data)
|
20
|
+
else
|
21
|
+
data = @parser.process(response.content) rescue {}
|
22
|
+
process_failure(response, data)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def process_success(request, data)
|
29
|
+
data
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_failure(request, data)
|
33
|
+
return data if request.redirect?
|
34
|
+
message = data['errors'] || ERRORS[request.code] || 'Unknown server error'
|
35
|
+
raise Error, Array(message).join(', ')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Typekit
|
4
|
+
module Routing
|
5
|
+
class Map
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegator :@root, :assemble, :trace
|
9
|
+
|
10
|
+
def initialize(&block)
|
11
|
+
@root = Node::Root.new
|
12
|
+
define(&block) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
def define(&block)
|
16
|
+
proxy = Proxy.new(self)
|
17
|
+
proxy.instance_eval(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def define_scope(path, parent: @root, &block)
|
21
|
+
child = Node::Scope.new(path)
|
22
|
+
parent.append(child)
|
23
|
+
proxy = Proxy.new(self, parent: child)
|
24
|
+
proxy.instance_eval(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def define_resources(name, parent: @root, **options, &block)
|
28
|
+
child = Node::Collection.new(name, **options)
|
29
|
+
parent.append(child)
|
30
|
+
return unless block_given?
|
31
|
+
proxy = Proxy.new(self, parent: child)
|
32
|
+
proxy.instance_eval(&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
Typekit.actions.each do |action|
|
36
|
+
define_method "define_#{ action }" do |name, parent:, **options|
|
37
|
+
child = Node::Operation.new(name, action: action, **options)
|
38
|
+
parent.append(child)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|