typekit-client 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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