typekit-client 0.0.2 → 0.0.3
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +9 -5
- data/Guardfile +7 -0
- data/README.md +160 -131
- data/bin/typekit +21 -8
- data/lib/typekit/client.rb +5 -5
- data/lib/typekit/configuration/base.rb +1 -1
- data/lib/typekit/configuration/default.rb +9 -5
- data/lib/typekit/connection/dispatcher.rb +2 -2
- data/lib/typekit/connection/request.rb +5 -2
- data/lib/typekit/connection/response.rb +4 -8
- data/lib/typekit/helper.rb +22 -0
- data/lib/typekit/processing/converter/boolean.rb +11 -0
- data/lib/typekit/processing/converter/datetime.rb +11 -0
- data/lib/typekit/processing/converter/errors.rb +22 -0
- data/lib/typekit/processing/converter/record.rb +15 -0
- data/lib/typekit/processing/converter/records.rb +18 -0
- data/lib/typekit/processing/converter/unknown.rb +14 -0
- data/lib/typekit/processing/converter.rb +32 -0
- data/lib/typekit/processing/parser/json.rb +15 -0
- data/lib/typekit/processing/parser/yaml.rb +15 -0
- data/lib/typekit/processing/parser.rb +14 -0
- data/lib/typekit/processing/translator.rb +16 -0
- data/lib/typekit/processing.rb +9 -0
- data/lib/typekit/record/base.rb +30 -0
- data/lib/typekit/record/family.rb +7 -0
- data/lib/typekit/record/kit.rb +7 -0
- data/lib/typekit/record/library.rb +7 -0
- data/lib/typekit/record/variation.rb +8 -0
- data/lib/typekit/record.rb +35 -0
- data/lib/typekit/routing/{map.rb → mapper.rb} +1 -1
- data/lib/typekit/routing/node/base.rb +1 -0
- data/lib/typekit/routing.rb +1 -1
- data/lib/typekit/version.rb +1 -1
- data/lib/typekit.rb +4 -4
- data/spec/cassettes/delete_kits_xxx_ok.yml +16 -0
- data/spec/typekit/client_spec.rb +11 -3
- data/spec/typekit/configuration_spec.rb +20 -4
- data/spec/typekit/connection/dispatcher_spec.rb +4 -4
- data/spec/typekit/connection/response_spec.rb +18 -0
- data/spec/typekit/helper_spec.rb +34 -0
- data/spec/typekit/processing/converter_spec.rb +54 -0
- data/spec/typekit/processing/parser_spec.rb +23 -0
- data/spec/typekit/record/base_spec.rb +33 -0
- data/spec/typekit/record_spec.rb +48 -0
- data/spec/typekit/routing/mapper_spec.rb +177 -0
- data/spec/typekit/routing/node_spec.rb +31 -10
- data/typekit-client.gemspec +2 -1
- metadata +53 -13
- data/lib/typekit/parser/json.rb +0 -13
- data/lib/typekit/parser/yaml.rb +0 -13
- data/lib/typekit/parser.rb +0 -14
- data/lib/typekit/processor.rb +0 -38
- data/spec/typekit/processor_spec.rb +0 -34
- data/spec/typekit/routing/map_spec.rb +0 -106
data/lib/typekit/helper.rb
CHANGED
@@ -22,6 +22,28 @@ module Typekit
|
|
22
22
|
Rack::Utils.build_nested_query(prepare_parameters(parameters))
|
23
23
|
end
|
24
24
|
|
25
|
+
def self.pluralize(name)
|
26
|
+
case name
|
27
|
+
when /^.*s$/
|
28
|
+
name
|
29
|
+
when /^(?<root>.*)y$/
|
30
|
+
"#{ Regexp.last_match(:root) }ies"
|
31
|
+
else
|
32
|
+
"#{ name }s"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.singularize(name)
|
37
|
+
case name
|
38
|
+
when /^(?<root>.*)ies$/
|
39
|
+
"#{ Regexp.last_match(:root) }y"
|
40
|
+
when /^(?<root>.*)s$/
|
41
|
+
Regexp.last_match(:root)
|
42
|
+
else
|
43
|
+
name
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
25
47
|
private
|
26
48
|
|
27
49
|
def self.prepare_parameters(parameters)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Processing
|
3
|
+
module Converter
|
4
|
+
class Errors
|
5
|
+
ERRORS = {
|
6
|
+
400 => 'There are errors in the data provided by your application',
|
7
|
+
401 => 'Authentication is needed to access the requested endpoint',
|
8
|
+
403 => 'Your application has been rate limited',
|
9
|
+
404 => 'You are requesting a resource that does not exist',
|
10
|
+
500 => 'The servers of Typekit are unable to process the request',
|
11
|
+
503 => 'The Typekit API is offline for maintenance'
|
12
|
+
}
|
13
|
+
ERRORS.default = 'Unknown server error'
|
14
|
+
ERRORS.freeze
|
15
|
+
|
16
|
+
def process(response, errors)
|
17
|
+
raise Error, Array(errors || ERRORS[response.code]).join(', ')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Processing
|
3
|
+
module Converter
|
4
|
+
class Record
|
5
|
+
def initialize(name)
|
6
|
+
@klass = Typekit::Record.const_get(name.to_s.capitalize)
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(response, attributes)
|
10
|
+
@klass.new(attributes)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Processing
|
3
|
+
module Converter
|
4
|
+
class Records
|
5
|
+
def initialize(name)
|
6
|
+
name = Helper.singularize(name.to_s).capitalize
|
7
|
+
@klass = Typekit::Record.const_get(name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def process(response, attribute_collection)
|
11
|
+
attribute_collection.map do |attributes|
|
12
|
+
@klass.new(attributes)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'converter/record'
|
2
|
+
require_relative 'converter/records'
|
3
|
+
require_relative 'converter/boolean'
|
4
|
+
require_relative 'converter/datetime'
|
5
|
+
require_relative 'converter/errors'
|
6
|
+
require_relative 'converter/unknown'
|
7
|
+
|
8
|
+
module Typekit
|
9
|
+
module Processing
|
10
|
+
module Converter
|
11
|
+
MAPPING = {
|
12
|
+
'ok' => Boolean,
|
13
|
+
'errors' => Errors,
|
14
|
+
'published' => DateTime
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
def self.build(name)
|
18
|
+
if MAPPING.key?(name)
|
19
|
+
MAPPING[name].new
|
20
|
+
elsif Typekit::Record.collection?(name)
|
21
|
+
Records.new(name)
|
22
|
+
elsif Typekit::Record.member?(name)
|
23
|
+
Record.new(name)
|
24
|
+
else
|
25
|
+
Unknown.new(name)
|
26
|
+
end
|
27
|
+
rescue NameError
|
28
|
+
raise Error, 'Unknown converter'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'parser/json'
|
2
|
+
require_relative 'parser/yaml'
|
3
|
+
|
4
|
+
module Typekit
|
5
|
+
module Processing
|
6
|
+
module Parser
|
7
|
+
def self.build(format)
|
8
|
+
self.const_get(format.to_s.upcase).new
|
9
|
+
rescue NameError
|
10
|
+
raise Error, 'Unknown format'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Typekit
|
2
|
+
module Processing
|
3
|
+
class Translator
|
4
|
+
def initialize(format:)
|
5
|
+
@parser = Parser.build(format)
|
6
|
+
end
|
7
|
+
|
8
|
+
def process(response)
|
9
|
+
data = @parser.process(response.body) rescue nil
|
10
|
+
data = { nil => nil } unless data.is_a?(Hash) && data.length == 1
|
11
|
+
name, object = *data.first
|
12
|
+
Converter.build(name).process(response, object)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Typekit
|
4
|
+
module Record
|
5
|
+
class Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :attributes
|
9
|
+
def_delegator :attributes, :to_json
|
10
|
+
|
11
|
+
def initialize(attributes = {})
|
12
|
+
@attributes = Hash[attributes.map { |k, v| [ k.to_sym, v ] }]
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *arguments)
|
16
|
+
if name.to_s =~ /^(?<name>.*)=$/
|
17
|
+
name = Regexp.last_match(:name).to_sym
|
18
|
+
return super unless arguments.length == 1
|
19
|
+
return super unless @attributes.key?(name)
|
20
|
+
@attributes[name] = arguments.first
|
21
|
+
else
|
22
|
+
return super unless arguments.length.zero?
|
23
|
+
return super unless @attributes.key?(name)
|
24
|
+
@attributes[name]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'record/base'
|
2
|
+
require_relative 'record/family'
|
3
|
+
require_relative 'record/variation'
|
4
|
+
require_relative 'record/kit'
|
5
|
+
require_relative 'record/library'
|
6
|
+
|
7
|
+
module Typekit
|
8
|
+
module Record
|
9
|
+
def self.classes
|
10
|
+
@classes ||= ObjectSpace.each_object(Class).select do |klass|
|
11
|
+
klass < Base
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.collections
|
16
|
+
@collections ||= members.map(&:to_s).map do |name|
|
17
|
+
Helper.pluralize(name.to_s)
|
18
|
+
end.map(&:to_sym)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.members
|
22
|
+
@members ||= classes.map(&:to_s).map(&:downcase).map do |name|
|
23
|
+
name.sub(/^.*::/, '')
|
24
|
+
end.map(&:to_sym)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.collection?(name)
|
28
|
+
collections.include?(name.to_sym)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.member?(name)
|
32
|
+
members.include?(name.to_sym)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/typekit/routing.rb
CHANGED
data/lib/typekit/version.rb
CHANGED
data/lib/typekit.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require_relative 'typekit/version'
|
2
|
-
require_relative 'typekit/core'
|
3
2
|
|
3
|
+
require_relative 'typekit/core'
|
4
4
|
require_relative 'typekit/helper'
|
5
5
|
require_relative 'typekit/configuration'
|
6
6
|
|
7
|
-
require_relative 'typekit/connection'
|
8
7
|
require_relative 'typekit/routing'
|
8
|
+
require_relative 'typekit/connection'
|
9
|
+
require_relative 'typekit/processing'
|
9
10
|
|
10
|
-
require_relative 'typekit/
|
11
|
-
require_relative 'typekit/processor'
|
11
|
+
require_relative 'typekit/record'
|
12
12
|
|
13
13
|
require_relative 'typekit/client'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: delete
|
5
|
+
uri: https://typekit.com/api/v1/json/kits/xxx
|
6
|
+
response:
|
7
|
+
status:
|
8
|
+
code: 200
|
9
|
+
message: OK
|
10
|
+
headers:
|
11
|
+
Status:
|
12
|
+
- 200 OK
|
13
|
+
body:
|
14
|
+
encoding: UTF-8
|
15
|
+
string: '{"ok":true}'
|
16
|
+
recorded_at: Fri, 30 May 2014 00:00:00 GMT
|
data/spec/typekit/client_spec.rb
CHANGED
@@ -9,9 +9,9 @@ describe Typekit::Client do
|
|
9
9
|
context 'when successful' do
|
10
10
|
options = { vcr: { cassette_name: 'index_kits_ok' } }
|
11
11
|
|
12
|
-
it 'returns
|
12
|
+
it 'returns Records', options do
|
13
13
|
result = subject.index(:kits)
|
14
|
-
expect(result).to
|
14
|
+
expect(result.map(&:class).uniq).to eq([ Typekit::Record::Kit ])
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -20,7 +20,7 @@ describe Typekit::Client do
|
|
20
20
|
|
21
21
|
it 'raises exceptions', options do
|
22
22
|
expect { subject.index(:kits) }.to \
|
23
|
-
raise_error(Typekit::Error, /
|
23
|
+
raise_error(Typekit::Processing::Error, /Not authorized/i)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
@@ -33,4 +33,12 @@ describe Typekit::Client do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
|
+
|
37
|
+
describe '#delete' do
|
38
|
+
options = { vcr: { cassette_name: 'delete_kits_xxx_ok' } }
|
39
|
+
|
40
|
+
it 'returns true', options do
|
41
|
+
expect(subject.delete(:kits, 'xxx')).to be(true)
|
42
|
+
end
|
43
|
+
end
|
36
44
|
end
|
@@ -25,10 +25,26 @@ describe Typekit::Configuration do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
describe 'Base
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
describe 'Base' do
|
29
|
+
describe '#mapper' do
|
30
|
+
it 'returns a Mapper' do
|
31
|
+
expect(build(:default, token: 'nekot').mapper).to \
|
32
|
+
be_kind_of(Typekit::Routing::Mapper)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#dispatcher' do
|
37
|
+
it 'returns a Dispatcher' do
|
38
|
+
expect(build(:default, token: 'nekot').dispatcher).to \
|
39
|
+
be_kind_of(Typekit::Connection::Dispatcher)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#translator' do
|
44
|
+
it 'returns a Translator' do
|
45
|
+
expect(build(:default, token: 'nekot').translator).to \
|
46
|
+
be_kind_of(Typekit::Processing::Translator)
|
47
|
+
end
|
32
48
|
end
|
33
49
|
end
|
34
50
|
end
|
@@ -12,14 +12,14 @@ describe Typekit::Connection::Dispatcher do
|
|
12
12
|
double('Request', action: action, address: address, parameters: {})
|
13
13
|
end
|
14
14
|
|
15
|
-
describe '#
|
15
|
+
describe '#process' do
|
16
16
|
restful_actions.each do |action|
|
17
17
|
method = rest_http_dictionary[action]
|
18
18
|
|
19
19
|
context "when sending #{ action } Requests" do
|
20
20
|
it 'sets the token header' do
|
21
21
|
stub = stub_http_request(method, address)
|
22
|
-
response = subject.
|
22
|
+
response = subject.process(create_request(action))
|
23
23
|
expect(stub).to have_requested(method, address).
|
24
24
|
with(:headers => { 'X-Typekit-Token' => token })
|
25
25
|
end
|
@@ -27,8 +27,8 @@ describe Typekit::Connection::Dispatcher do
|
|
27
27
|
it 'returns Responses' do
|
28
28
|
stub_http_request(method, address).
|
29
29
|
to_return(code: '200', body: 'Hej!')
|
30
|
-
response = subject.
|
31
|
-
expect([ response.code, response.
|
30
|
+
response = subject.process(create_request(action))
|
31
|
+
expect([ response.code, response.body ]).to eq([ 200, 'Hej!' ])
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'typekit'
|
3
|
+
|
4
|
+
describe Typekit::Connection::Response do
|
5
|
+
let(:subject_class) { Typekit::Connection::Response }
|
6
|
+
|
7
|
+
def create(code: 200, body: '')
|
8
|
+
subject_class.new(code: code, body: body)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is considered to be successful for HTTP OK' do
|
12
|
+
expect(create(code: 200)).to be_success
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'is considered to be successful for HTTP Found' do
|
16
|
+
expect(create(code: 302)).to be_success
|
17
|
+
end
|
18
|
+
end
|
data/spec/typekit/helper_spec.rb
CHANGED
@@ -38,6 +38,40 @@ describe Typekit::Helper do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
describe '.pluralize' do
|
42
|
+
{
|
43
|
+
'kit' => 'kits',
|
44
|
+
'kits' => 'kits',
|
45
|
+
'family' => 'families',
|
46
|
+
'families' => 'families',
|
47
|
+
'library' => 'libraries',
|
48
|
+
'libraries' => 'libraries',
|
49
|
+
'variant' => 'variants',
|
50
|
+
'variants' => 'variants'
|
51
|
+
}.each do |k, v|
|
52
|
+
it "returns #{ v } for #{ k }" do
|
53
|
+
expect(subject_module.pluralize(k)).to eq(v)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '.singularize' do
|
59
|
+
{
|
60
|
+
'kit' => 'kit',
|
61
|
+
'kits' => 'kit',
|
62
|
+
'family' => 'family',
|
63
|
+
'families' => 'family',
|
64
|
+
'library' => 'library',
|
65
|
+
'libraries' => 'library',
|
66
|
+
'variant' => 'variant',
|
67
|
+
'variants' => 'variant'
|
68
|
+
}.each do |k, v|
|
69
|
+
it "returns #{ v } for #{ k }" do
|
70
|
+
expect(subject_module.singularize(k)).to eq(v)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
41
75
|
describe '.build_query' do
|
42
76
|
it 'handels ordinary parameters' do
|
43
77
|
queries = [
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'typekit'
|
3
|
+
|
4
|
+
describe Typekit::Processing::Converter do
|
5
|
+
def create(name)
|
6
|
+
Typekit::Processing::Converter.build(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:request) { double(code: 200) }
|
10
|
+
|
11
|
+
describe '.build#process' do
|
12
|
+
%w{family kit library}.each do |name|
|
13
|
+
it "maps '#{ name }' to a Record" do
|
14
|
+
result = create(name).process(request, {})
|
15
|
+
expect(result).to be_kind_of(Typekit::Record::Base)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
%w{families kits libraries}.each do |name|
|
20
|
+
it "maps '#{ name }' to an array of Records" do
|
21
|
+
result = create(name).process(request, [ {} ])
|
22
|
+
result.each { |r| expect(r).to be_kind_of(Typekit::Record::Base) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
%w{ok}.each do |name|
|
27
|
+
it "maps '#{ name }' to a boolean" do
|
28
|
+
result = create(name).process(request, true)
|
29
|
+
expect(result).to be_kind_of(::TrueClass)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
%w{published}.each do |name|
|
34
|
+
it "maps '#{ name }' to a datetime" do
|
35
|
+
result = create(name).process(request, '2010-05-20T21:15:31Z')
|
36
|
+
expect(result).to be_kind_of(::DateTime)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
[ nil, 'errors' ].each do |name|
|
41
|
+
it "raises an exception for '#{ name }'" do
|
42
|
+
expect { create(name).process(request, nil) }.to \
|
43
|
+
raise_error(Typekit::Processing::Error)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
%w{kittens puppies}.each do |name|
|
48
|
+
it "returns unknowns like '#{ name }' as they are" do
|
49
|
+
result = create(name).process(request, 42)
|
50
|
+
expect(result).to be(42)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'typekit'
|
3
|
+
|
4
|
+
describe Typekit::Processing::Parser do
|
5
|
+
let(:subject_class) { Typekit::Processing::Parser }
|
6
|
+
|
7
|
+
it 'supports JSON' do
|
8
|
+
subject = subject_class.build(:json)
|
9
|
+
result = subject.process('{ "kits": [] }')
|
10
|
+
expect(result).to eq("kits" => [])
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'supports YAML' do
|
14
|
+
subject = subject_class.build(:yaml)
|
15
|
+
result = subject.process("---\nkits: []")
|
16
|
+
expect(result).to eq("kits" => [])
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not support XML' do
|
20
|
+
expect { subject_class.build(:xml) }.to \
|
21
|
+
raise_error(Typekit::Processing::Error, /Unknown format/i)
|
22
|
+
end
|
23
|
+
end
|