typekit-client 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|