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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.md +9 -5
  5. data/Guardfile +7 -0
  6. data/README.md +160 -131
  7. data/bin/typekit +21 -8
  8. data/lib/typekit/client.rb +5 -5
  9. data/lib/typekit/configuration/base.rb +1 -1
  10. data/lib/typekit/configuration/default.rb +9 -5
  11. data/lib/typekit/connection/dispatcher.rb +2 -2
  12. data/lib/typekit/connection/request.rb +5 -2
  13. data/lib/typekit/connection/response.rb +4 -8
  14. data/lib/typekit/helper.rb +22 -0
  15. data/lib/typekit/processing/converter/boolean.rb +11 -0
  16. data/lib/typekit/processing/converter/datetime.rb +11 -0
  17. data/lib/typekit/processing/converter/errors.rb +22 -0
  18. data/lib/typekit/processing/converter/record.rb +15 -0
  19. data/lib/typekit/processing/converter/records.rb +18 -0
  20. data/lib/typekit/processing/converter/unknown.rb +14 -0
  21. data/lib/typekit/processing/converter.rb +32 -0
  22. data/lib/typekit/processing/parser/json.rb +15 -0
  23. data/lib/typekit/processing/parser/yaml.rb +15 -0
  24. data/lib/typekit/processing/parser.rb +14 -0
  25. data/lib/typekit/processing/translator.rb +16 -0
  26. data/lib/typekit/processing.rb +9 -0
  27. data/lib/typekit/record/base.rb +30 -0
  28. data/lib/typekit/record/family.rb +7 -0
  29. data/lib/typekit/record/kit.rb +7 -0
  30. data/lib/typekit/record/library.rb +7 -0
  31. data/lib/typekit/record/variation.rb +8 -0
  32. data/lib/typekit/record.rb +35 -0
  33. data/lib/typekit/routing/{map.rb → mapper.rb} +1 -1
  34. data/lib/typekit/routing/node/base.rb +1 -0
  35. data/lib/typekit/routing.rb +1 -1
  36. data/lib/typekit/version.rb +1 -1
  37. data/lib/typekit.rb +4 -4
  38. data/spec/cassettes/delete_kits_xxx_ok.yml +16 -0
  39. data/spec/typekit/client_spec.rb +11 -3
  40. data/spec/typekit/configuration_spec.rb +20 -4
  41. data/spec/typekit/connection/dispatcher_spec.rb +4 -4
  42. data/spec/typekit/connection/response_spec.rb +18 -0
  43. data/spec/typekit/helper_spec.rb +34 -0
  44. data/spec/typekit/processing/converter_spec.rb +54 -0
  45. data/spec/typekit/processing/parser_spec.rb +23 -0
  46. data/spec/typekit/record/base_spec.rb +33 -0
  47. data/spec/typekit/record_spec.rb +48 -0
  48. data/spec/typekit/routing/mapper_spec.rb +177 -0
  49. data/spec/typekit/routing/node_spec.rb +31 -10
  50. data/typekit-client.gemspec +2 -1
  51. metadata +53 -13
  52. data/lib/typekit/parser/json.rb +0 -13
  53. data/lib/typekit/parser/yaml.rb +0 -13
  54. data/lib/typekit/parser.rb +0 -14
  55. data/lib/typekit/processor.rb +0 -38
  56. data/spec/typekit/processor_spec.rb +0 -34
  57. data/spec/typekit/routing/map_spec.rb +0 -106
@@ -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,11 @@
1
+ module Typekit
2
+ module Processing
3
+ module Converter
4
+ class Boolean
5
+ def process(response, object)
6
+ object # already boolean
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Typekit
2
+ module Processing
3
+ module Converter
4
+ class DateTime
5
+ def process(response, object)
6
+ ::DateTime.parse(object)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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,14 @@
1
+ module Typekit
2
+ module Processing
3
+ module Converter
4
+ class Unknown
5
+ def initialize(name)
6
+ end
7
+
8
+ def process(response, object)
9
+ object
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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,15 @@
1
+ require 'json'
2
+
3
+ module Typekit
4
+ module Processing
5
+ module Parser
6
+ class JSON
7
+ def process(data)
8
+ ::JSON.parse(data)
9
+ rescue
10
+ raise Error, 'Unable to parse'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'yaml'
2
+
3
+ module Typekit
4
+ module Processing
5
+ module Parser
6
+ class YAML
7
+ def process(data)
8
+ ::YAML.load(data)
9
+ rescue
10
+ raise Error, 'Unable to parse'
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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,9 @@
1
+ require_relative 'processing/parser'
2
+ require_relative 'processing/converter'
3
+ require_relative 'processing/translator'
4
+
5
+ module Typekit
6
+ module Processing
7
+ Error = Class.new(Typekit::Error)
8
+ end
9
+ 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,7 @@
1
+ module Typekit
2
+ module Record
3
+ class Family < Base
4
+ # has_many :libraries, :variations
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Typekit
2
+ module Record
3
+ class Kit < Base
4
+ # has_many :families
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Typekit
2
+ module Record
3
+ class Library < Base
4
+ # has_many :families
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Typekit
2
+ module Record
3
+ class Variation < Base
4
+ # belongs_to :family
5
+ # has_many :libraries
6
+ end
7
+ end
8
+ end
@@ -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
@@ -2,7 +2,7 @@ require 'forwardable'
2
2
 
3
3
  module Typekit
4
4
  module Routing
5
- class Map
5
+ class Mapper
6
6
  extend Forwardable
7
7
 
8
8
  def_delegator :@root, :assemble, :trace
@@ -37,6 +37,7 @@ module Typekit
37
37
 
38
38
  def authorize(request)
39
39
  raise Error, 'Not permitted' unless permitted?(request)
40
+ request.sign(self)
40
41
  request
41
42
  end
42
43
  end
@@ -1,6 +1,6 @@
1
1
  require_relative 'routing/node'
2
2
  require_relative 'routing/proxy'
3
- require_relative 'routing/map'
3
+ require_relative 'routing/mapper'
4
4
 
5
5
  module Typekit
6
6
  module Routing
@@ -1,3 +1,3 @@
1
1
  module Typekit
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
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/parser'
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
@@ -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 hashes', options do
12
+ it 'returns Records', options do
13
13
  result = subject.index(:kits)
14
- expect(result).to be_kind_of(Hash)
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, /(authentication|authorized)/i)
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#map' do
29
- it 'returns a Map' do
30
- expect(build(:default, token: 'nekot').map).to \
31
- be_kind_of(Typekit::Routing::Map)
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 '#deliver' do
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.deliver(create_request(action))
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.deliver(create_request(action))
31
- expect([ response.code, response.content ]).to eq([ 200, 'Hej!' ])
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
@@ -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