typekit-client 0.0.5 → 0.0.6

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -3
  3. data/.travis.yml +4 -0
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +12 -0
  6. data/Guardfile +21 -3
  7. data/README.md +253 -248
  8. data/bin/typekit-client +16 -16
  9. data/bin/typekit-publisher +6 -5
  10. data/lib/typekit.rb +6 -2
  11. data/lib/typekit/client.rb +66 -24
  12. data/lib/typekit/collection.rb +6 -11
  13. data/lib/typekit/collection/base.rb +40 -0
  14. data/lib/typekit/collection/serialization.rb +17 -0
  15. data/lib/typekit/converter.rb +26 -0
  16. data/lib/typekit/converter/boolean.rb +12 -0
  17. data/lib/typekit/converter/collection.rb +13 -0
  18. data/lib/typekit/converter/datetime.rb +12 -0
  19. data/lib/typekit/converter/element.rb +13 -0
  20. data/lib/typekit/converter/errors.rb +12 -0
  21. data/lib/typekit/converter/unknown.rb +13 -0
  22. data/lib/typekit/core.rb +8 -10
  23. data/lib/typekit/element.rb +27 -0
  24. data/lib/typekit/element/association.rb +63 -0
  25. data/lib/typekit/element/base.rb +61 -0
  26. data/lib/typekit/element/persistence.rb +61 -0
  27. data/lib/typekit/element/query.rb +25 -0
  28. data/lib/typekit/element/serialization.rb +17 -0
  29. data/lib/typekit/error.rb +23 -0
  30. data/lib/typekit/helper.rb +59 -10
  31. data/lib/typekit/record.rb +18 -22
  32. data/lib/typekit/record/family.rb +24 -1
  33. data/lib/typekit/record/kit.rb +29 -1
  34. data/lib/typekit/record/library.rb +4 -1
  35. data/lib/typekit/record/variation.rb +13 -1
  36. data/lib/typekit/version.rb +1 -1
  37. data/spec/feature/create_kit_spec.rb +52 -0
  38. data/spec/feature/delete_kit_spec.rb +33 -0
  39. data/spec/feature/index_kits_spec.rb +55 -0
  40. data/spec/feature/publish_kit_spec.rb +15 -0
  41. data/spec/feature/show_family_spec.rb +75 -0
  42. data/spec/feature/show_kit_spec.rb +56 -0
  43. data/spec/feature/show_library_spec.rb +29 -0
  44. data/spec/feature/show_variation_spec.rb +18 -0
  45. data/spec/feature/update_kit_spec.rb +88 -0
  46. data/spec/fixture/cassette/create_kits_bad.yml +16 -0
  47. data/spec/fixture/cassette/create_kits_ok.yml +16 -0
  48. data/spec/fixture/cassette/delete_kits_xxx_not_found.yml +13 -0
  49. data/spec/{cassettes → fixture/cassette}/delete_kits_xxx_ok.yml +0 -3
  50. data/spec/fixture/cassette/index_kits_show_kit_xxx_ok.yml +25 -0
  51. data/spec/{cassettes → fixture/cassette}/index_kits_unauthorized.yml +0 -3
  52. data/spec/{cassettes → fixture/cassette}/show_families_xxx_found.yml +0 -3
  53. data/spec/{cassettes → fixture/cassette}/show_families_xxx_ok.yml +0 -3
  54. data/spec/{cassettes → fixture/cassette}/show_families_xxx_yyy_ok.yml +0 -3
  55. data/spec/fixture/cassette/show_families_yyy_update_kits_xxx_families_ok.yml +35 -0
  56. data/spec/fixture/cassette/show_kits_xxx_families_yyy_show_family_yyy_ok.yml +45 -0
  57. data/spec/fixture/cassette/show_kits_xxx_not_found.yml +13 -0
  58. data/spec/fixture/cassette/show_kits_xxx_show_family_yyy_ok.yml +46 -0
  59. data/spec/{cassettes → fixture/cassette}/show_libraries_xxx_ok.yml +0 -3
  60. data/spec/fixture/cassette/update_kits_xxx_empty_families_ok.yml +16 -0
  61. data/spec/fixture/cassette/update_kits_xxx_families_ok.yml +18 -0
  62. data/spec/fixture/cassette/update_kits_xxx_families_variations_ok.yml +18 -0
  63. data/spec/fixture/cassette/update_kits_xxx_name_ok.yml +17 -0
  64. data/spec/fixture/cassette/update_kits_xxx_publish_ok.yml +16 -0
  65. data/spec/fixture/record/article.rb +11 -0
  66. data/spec/fixture/record/section.rb +10 -0
  67. data/spec/lib/typekit/collection_spec.rb +22 -0
  68. data/spec/{typekit/processing → lib/typekit}/converter_spec.rb +13 -13
  69. data/spec/lib/typekit/element/association_spec.rb +96 -0
  70. data/spec/lib/typekit/element/base_spec.rb +70 -0
  71. data/spec/lib/typekit/element/persistence_spec.rb +97 -0
  72. data/spec/lib/typekit/element/serialization_spec.rb +28 -0
  73. data/spec/lib/typekit/element_spec.rb +58 -0
  74. data/spec/{typekit → lib/typekit}/helper_spec.rb +6 -8
  75. data/spec/lib/typekit/record_spec.rb +57 -0
  76. data/spec/spec_helper.rb +10 -3
  77. data/spec/support/common_helper.rb +5 -0
  78. data/spec/support/record_helper.rb +38 -0
  79. data/typekit-client.gemspec +11 -4
  80. metadata +164 -58
  81. data/lib/typekit/processing.rb +0 -8
  82. data/lib/typekit/processing/converter.rb +0 -28
  83. data/lib/typekit/processing/converter/boolean.rb +0 -14
  84. data/lib/typekit/processing/converter/collection.rb +0 -18
  85. data/lib/typekit/processing/converter/datetime.rb +0 -14
  86. data/lib/typekit/processing/converter/errors.rb +0 -24
  87. data/lib/typekit/processing/converter/record.rb +0 -16
  88. data/lib/typekit/processing/converter/unknown.rb +0 -15
  89. data/lib/typekit/processing/translator.rb +0 -13
  90. data/lib/typekit/record/base.rb +0 -52
  91. data/spec/cassettes/index_kits_ok.yml +0 -16
  92. data/spec/cassettes/show_kits_xxx_families_yyy_ok.yml +0 -16
  93. data/spec/cassettes/show_kits_xxx_ok.yml +0 -17
  94. data/spec/features/client/delete_kit_spec.rb +0 -13
  95. data/spec/features/client/index_kits_spec.rb +0 -25
  96. data/spec/features/client/show_family_spec.rb +0 -47
  97. data/spec/features/client/show_kit_spec.rb +0 -18
  98. data/spec/features/client/show_library_spec.rb +0 -18
  99. data/spec/features/client/show_variation_spec.rb +0 -22
  100. data/spec/support/resource_helper.rb +0 -34
  101. data/spec/typekit/record/base_spec.rb +0 -85
  102. data/spec/typekit/record_spec.rb +0 -80
@@ -0,0 +1,27 @@
1
+ require_relative 'element/association'
2
+ require_relative 'element/persistence'
3
+ require_relative 'element/query'
4
+ require_relative 'element/serialization'
5
+ require_relative 'element/base'
6
+
7
+ module Typekit
8
+ module Element
9
+ def self.dictionary
10
+ @dictionary ||= Hash[
11
+ ObjectSpace.each_object(Class).select do |klass|
12
+ klass < Base && klass.name
13
+ end.map do |klass|
14
+ [Helper.tokenize(klass), klass]
15
+ end
16
+ ]
17
+ end
18
+
19
+ def self.classify(name)
20
+ dictionary[Helper.tokenize(name)]
21
+ end
22
+
23
+ def self.build(name, *arguments)
24
+ classify(name).new(*arguments)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,63 @@
1
+ module Typekit
2
+ module Element
3
+ module Association
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def possessions
10
+ @possessions ||= []
11
+ end
12
+
13
+ def owners
14
+ @owners ||= []
15
+ end
16
+
17
+ def has_many(name)
18
+ possessions << name
19
+
20
+ define_method(name) do
21
+ value = attributes[name]
22
+ return value if value.is_a?(Collection::Base)
23
+
24
+ unless value.nil?
25
+ attributes[name] = Collection.build(name, self, value)
26
+ return attributes[name]
27
+ end
28
+
29
+ if feature?(:persistence) && new?
30
+ attributes[name] = Collection.build(name, self, [])
31
+ return attributes[name]
32
+ end
33
+
34
+ load!
35
+
36
+ value = attributes[name]
37
+ raise Error, 'Cannot load the association' if value.nil?
38
+
39
+ attributes[name] = Collection.build(name, self, value)
40
+ end
41
+
42
+ define_method("#{name}=") do |value|
43
+ if value.is_a?(Collection::Base)
44
+ attributes[name] = value
45
+ else
46
+ attributes[name] = Collection.build(name, self, value)
47
+ end
48
+ end
49
+ end
50
+
51
+ def belongs_to(name)
52
+ owners << name
53
+
54
+ define_method(name) do
55
+ value = attributes[name]
56
+ return value if value.nil? || value.is_a?(Element::Base)
57
+ attributes[name] = Element.build(name, self, attributes[name])
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,61 @@
1
+ module Typekit
2
+ module Element
3
+ class Base
4
+ extend Forwardable
5
+
6
+ include Client::Proxy
7
+
8
+ attr_reader :attributes
9
+ def_delegators :attributes, :to_hash, :to_h, :to_json
10
+
11
+ def initialize(*arguments)
12
+ @attributes = prepare_attributes(Helper.extract_hash!(arguments))
13
+ @attributes[:id] = nil unless @attributes.key?(:id)
14
+ connect(arguments.first)
15
+ end
16
+
17
+ def become(another)
18
+ raise ArgumentError, 'Invalid class' unless self.class == another.class
19
+ @attributes = another.attributes
20
+ true
21
+ end
22
+
23
+ def self.feature?(name)
24
+ name = Helper.constantize(name)
25
+ @features ||= {}
26
+ return @features[name] if @features.key?(name)
27
+ @features[name] = include?(Element.const_get(name))
28
+ end
29
+
30
+ def feature?(name)
31
+ self.class.feature?(name)
32
+ end
33
+
34
+ def attribute?(name)
35
+ attributes.key?(name)
36
+ end
37
+
38
+ def assign_attributes(*arguments)
39
+ prepare_attributes(*arguments).each do |name, value|
40
+ attributes[name] = value
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def prepare_attributes(attributes)
47
+ Helper.symbolize_keys(attributes)
48
+ end
49
+
50
+ def method_missing(name, *arguments)
51
+ if name.to_s =~ /^(?<name>.*)=$/
52
+ name = Regexp.last_match(:name).to_sym
53
+ attributes.send(:[]=, name, *arguments)
54
+ else
55
+ return super unless attributes.key?(name)
56
+ attributes[name]
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,61 @@
1
+ module Typekit
2
+ module Element
3
+ module Persistence
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ def new?
9
+ id.to_s.empty?
10
+ end
11
+
12
+ def persistent?
13
+ !(new? || deleted?)
14
+ end
15
+
16
+ def deleted?
17
+ !!@deleted
18
+ end
19
+
20
+ def save!
21
+ if new?
22
+ element = process(:create, serialize)
23
+ else
24
+ element = process(:update, id, serialize)
25
+ end
26
+ become(element)
27
+ true
28
+ end
29
+
30
+ def update!(*arguments)
31
+ assign_attributes(*arguments)
32
+ become(process(:update, id, serialize))
33
+ true
34
+ end
35
+
36
+ def delete!
37
+ process(:delete, id) if persistent?
38
+ @deleted = true
39
+ true
40
+ end
41
+
42
+ [:save, :update, :delete].each do |method|
43
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
44
+ def #{method}(*arguments)
45
+ #{method}!(*arguments)
46
+ rescue ServerError
47
+ false
48
+ end
49
+ CODE
50
+ end
51
+
52
+ module ClassMethods
53
+ def create(*arguments)
54
+ element = new(*arguments)
55
+ element.save
56
+ element
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ module Typekit
2
+ module Element
3
+ module Query
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def all
10
+ process(:index)
11
+ end
12
+
13
+ def find!(id)
14
+ process(:show, id)
15
+ end
16
+
17
+ def find(id)
18
+ find!(id)
19
+ rescue ServerError
20
+ nil
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Typekit
2
+ module Element
3
+ module Serialization
4
+ def serialize(options = {})
5
+ keys = attributes.keys
6
+ keys = keys & Array(options[:only]) if options.key?(:only)
7
+ Hash[
8
+ keys.map do |key|
9
+ value = attributes[key]
10
+ value = value.serialize if value.respond_to?(:serialize)
11
+ [key, value]
12
+ end
13
+ ]
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ module Typekit
2
+ class Error < StandardError
3
+ end
4
+
5
+ class ServerError < Error
6
+ ERRORS = {
7
+ 400 => 'There are errors in the data provided by your application',
8
+ 401 => 'Authentication is needed to access the requested endpoint',
9
+ 403 => 'Your application has been rate limited',
10
+ 404 => 'You are requesting a resource that does not exist',
11
+ 500 => 'The servers of Typekit are unable to process the request',
12
+ 503 => 'The Typekit API is offline for maintenance'
13
+ }
14
+ ERRORS.default = 'Unknown server error'
15
+
16
+ attr_reader :code
17
+
18
+ def initialize(code, message = '')
19
+ @code = code
20
+ super(message.empty? ? ERRORS[code] : message)
21
+ end
22
+ end
23
+ end
@@ -1,29 +1,78 @@
1
1
  module Typekit
2
2
  module Helper
3
- def self.pluralize(name)
4
- case name
3
+ # Returns the plural form of the given word.
4
+ #
5
+ # @param word [String]
6
+ # @return [String]
7
+ def self.pluralize(word)
8
+ case word
5
9
  when /^.*s$/
6
- name
10
+ word
7
11
  when /^(?<root>.*)y$/
8
- "#{ Regexp.last_match(:root) }ies"
12
+ "#{Regexp.last_match(:root)}ies"
9
13
  else
10
- "#{ name }s"
14
+ "#{word}s"
11
15
  end
12
16
  end
13
17
 
14
- def self.singularize(name)
15
- case name
18
+ # Returns the singular form of the given word.
19
+ #
20
+ # @param word [String]
21
+ # @return [String]
22
+ def self.singularize(word)
23
+ case word
16
24
  when /^(?<root>.*)ies$/
17
- "#{ Regexp.last_match(:root) }y"
25
+ "#{Regexp.last_match(:root)}y"
18
26
  when /^(?<root>.*)s$/
19
27
  Regexp.last_match(:root)
20
28
  else
21
- name
29
+ word
22
30
  end
23
31
  end
24
32
 
33
+ # Constructs a symbol suitable for looking up modules among the constants
34
+ # defined in a module.
35
+ #
36
+ # @param object [String, Symbol]
37
+ # @return [Symbol]
38
+ def self.constantize(object)
39
+ object.to_s.sub(/^.*::/, '').capitalize.to_sym
40
+ end
41
+
42
+ # Constructs a symbolic representation of the given object.
43
+ #
44
+ # @param object
45
+ # @option options [Boolean] :pluralize (true) indicates if the result
46
+ # should be plural
47
+ def self.tokenize(object, options = {})
48
+ name = object.to_s.sub(/^.*::/, '').downcase
49
+ (options.fetch(:pluralize, true) ? pluralize(name) : name).to_sym
50
+ end
51
+
52
+ # Copies the given hash while converting its keys into symbols.
53
+ #
54
+ # @param hash [Hash]
55
+ # @return [Hash]
25
56
  def self.symbolize_keys(hash)
26
- Hash[hash.map { |k, v| [ k.to_sym, v ] }]
57
+ Hash[hash.map { |k, v| [k.to_sym, v] }]
58
+ end
59
+
60
+ # Removes and returns the last element of the given array if that element
61
+ # is a hash; otherwise, a new hash is returned.
62
+ #
63
+ # @param array [Array]
64
+ # @return [Hash]
65
+ def self.extract_hash!(array)
66
+ array.last.is_a?(Hash) ? array.pop : {}
67
+ end
68
+
69
+ # Removes and returns the last element of the given array if that element
70
+ # is an array; otherwise, a new array is returned.
71
+ #
72
+ # @param array [Array]
73
+ # @return [Array]
74
+ def self.extract_array!(array)
75
+ array.last.is_a?(Array) ? array.pop : {}
27
76
  end
28
77
  end
29
78
  end
@@ -1,35 +1,31 @@
1
- require_relative 'record/base'
2
- require_relative 'record/family'
3
- require_relative 'record/variation'
4
1
  require_relative 'record/kit'
2
+ require_relative 'record/family'
5
3
  require_relative 'record/library'
4
+ require_relative 'record/variation'
6
5
 
7
6
  module Typekit
8
7
  module Record
9
- def self.mapping
10
- @mapping ||= Hash[
11
- ObjectSpace.each_object(Class).select do |klass|
12
- klass < Base && klass.name
13
- end.map do |klass|
14
- [ klass.name.downcase.sub(/^.*::/, '').to_sym, klass ]
15
- end
16
- ]
17
- end
18
-
19
- def self.classify(name)
20
- mapping[Helper.singularize(name.to_s).to_sym]
21
- end
22
-
23
8
  def self.identify(name)
24
- if mapping.include?(name.to_s.to_sym)
25
- :record
26
- elsif mapping.include?(Helper.singularize(name.to_s).to_sym)
9
+ if Element.dictionary.key?(Helper.tokenize(name, pluralize: false))
27
10
  :collection
11
+ elsif Element.dictionary.key?(Helper.tokenize(name))
12
+ :element
28
13
  end
29
14
  end
30
15
 
31
- def self.build(name, *arguments)
32
- classify(name).new(*arguments)
16
+ def self.build(name, client)
17
+ klass = Element.classify(name)
18
+ Class.new(klass) do
19
+ extend Client::Proxy
20
+
21
+ connect(client, Helper.tokenize(name))
22
+
23
+ singleton_class.instance_eval do
24
+ define_method(:new) do |*arguments|
25
+ klass.new(client, *arguments)
26
+ end
27
+ end
28
+ end
33
29
  end
34
30
  end
35
31
  end
@@ -1,8 +1,31 @@
1
1
  module Typekit
2
2
  module Record
3
- class Family < Base
3
+ class Family < Element::Base
4
+ include Element::Association
5
+ include Element::Query
6
+ include Element::Serialization
7
+
4
8
  has_many :libraries
5
9
  has_many :variations
10
+
11
+ def complete?
12
+ attribute?(:libraries) && attribute?(:variations)
13
+ end
14
+
15
+ def load!
16
+ become(process(:show, id))
17
+ true
18
+ end
19
+
20
+ def load
21
+ load!
22
+ rescue ServerError
23
+ false
24
+ end
25
+
26
+ def serialize
27
+ super(only: [:id, :subset, :variations])
28
+ end
6
29
  end
7
30
  end
8
31
  end