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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +350 -0
  8. data/Rakefile +7 -0
  9. data/bin/typekit +146 -0
  10. data/lib/typekit.rb +13 -0
  11. data/lib/typekit/client.rb +38 -0
  12. data/lib/typekit/configuration.rb +16 -0
  13. data/lib/typekit/configuration/base.rb +21 -0
  14. data/lib/typekit/configuration/default.rb +34 -0
  15. data/lib/typekit/connection.rb +10 -0
  16. data/lib/typekit/connection/adaptor.rb +13 -0
  17. data/lib/typekit/connection/adaptor/standard.rb +32 -0
  18. data/lib/typekit/connection/dispatcher.rb +17 -0
  19. data/lib/typekit/connection/request.rb +23 -0
  20. data/lib/typekit/connection/response.rb +20 -0
  21. data/lib/typekit/core.rb +16 -0
  22. data/lib/typekit/helper.rb +43 -0
  23. data/lib/typekit/parser.rb +14 -0
  24. data/lib/typekit/parser/json.rb +13 -0
  25. data/lib/typekit/parser/yaml.rb +13 -0
  26. data/lib/typekit/processor.rb +38 -0
  27. data/lib/typekit/routing.rb +9 -0
  28. data/lib/typekit/routing/map.rb +43 -0
  29. data/lib/typekit/routing/node.rb +5 -0
  30. data/lib/typekit/routing/node/base.rb +45 -0
  31. data/lib/typekit/routing/node/collection.rb +34 -0
  32. data/lib/typekit/routing/node/operation.rb +32 -0
  33. data/lib/typekit/routing/node/root.rb +8 -0
  34. data/lib/typekit/routing/node/scope.rb +19 -0
  35. data/lib/typekit/routing/proxy.rb +17 -0
  36. data/lib/typekit/version.rb +3 -0
  37. data/spec/cassettes/index_kits_ok.yml +16 -0
  38. data/spec/cassettes/index_kits_unauthorized.yml +16 -0
  39. data/spec/cassettes/show_families_calluna_found.yml +16 -0
  40. data/spec/spec_helper.rb +20 -0
  41. data/spec/support/rest_helper.rb +22 -0
  42. data/spec/typekit/client_spec.rb +36 -0
  43. data/spec/typekit/configuration_spec.rb +34 -0
  44. data/spec/typekit/connection/adaptor_spec.rb +24 -0
  45. data/spec/typekit/connection/dispatcher_spec.rb +36 -0
  46. data/spec/typekit/connection/request_spec.rb +13 -0
  47. data/spec/typekit/helper_spec.rb +93 -0
  48. data/spec/typekit/processor_spec.rb +34 -0
  49. data/spec/typekit/routing/map_spec.rb +106 -0
  50. data/spec/typekit/routing/node_spec.rb +40 -0
  51. data/typekit-client.gemspec +33 -0
  52. metadata +208 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c787138c7a90aa58f94a0f9cfd0080aeac67440d
4
+ data.tar.gz: 24df2e9aa560fb343e385198510af8c235afa72e
5
+ SHA512:
6
+ metadata.gz: 6adc44648829ecd93949689f00765376f61a9d8f7b6b3cd5e5976220dabb229c7a359ed26c12e8c9f46b7095fa928010d1732a32b88744131701cbebb688b2f9
7
+ data.tar.gz: 5adb42620a3c05627243b0e834ab12249a56cc62f48c0afe9ed5c87135f9ddc2a4fa03a206b3bc0d2106819027e701b75b0470d15194eea72eef06cf617d4235
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.swp
2
+ .DS_Store
3
+ Gemfile.lock
4
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## Typekit 0.0.2 (May 28, 2014)
2
+ * Renamed the gem into `typekit-client` (still `require 'typekit'`).
3
+ * Rewrote the whole library (refactoring, preparing for new features).
4
+ * Implemented a DSL for describing the resources provided by Typekit.
5
+ * Introduced a client-side verification of user requests.
6
+
7
+ ## Typekit 0.0.1 (May 16, 2014)
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright 2014 Ivan Ukhov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,350 @@
1
+ # Typekit Client
2
+ A Ruby library for accessing the [Typekit API](https://typekit.com/docs/api).
3
+
4
+ ## Installation
5
+ `Ruby >= 2.1` is required. Make sure you have it installed:
6
+ ```bash
7
+ $ ruby -v
8
+ ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
9
+ ```
10
+
11
+ In case you don’t:
12
+ ```bash
13
+ $ curl -sSL https://get.rvm.io | bash
14
+ $ rvm install 2.1
15
+ ```
16
+
17
+ Add the gem into your `Gemfile`:
18
+ ```ruby
19
+ gem 'typekit-client', require: 'typekit'
20
+ ```
21
+
22
+ Then run `bundler`:
23
+ ```bash
24
+ $ bundle
25
+ ```
26
+
27
+ In order to interact with the Typekit API, one should have a valid API token.
28
+ You can generate such a token [here](https://typekit.com/account/tokens).
29
+ For convenience, let us create a shortcut for it:
30
+ ```bash
31
+ $ export tk_token=YOUR_TOKEN_GOES_HERE
32
+ ```
33
+
34
+ ## Usage
35
+ Here is the basic setup in a Ruby script:
36
+ ```ruby
37
+ require 'typekit'
38
+
39
+ client = Typekit::Client.new(token: ENV['tk_token'])
40
+ ```
41
+
42
+ And here is how to run it, assuming you name your script `app.rb`:
43
+ ```bash
44
+ $ bundle exec ruby app.rb
45
+ ```
46
+
47
+ The main method of `client` is `perform(action, *path, parameters = {})`.
48
+ The arguments are as follows:
49
+ * `action` is the action that you would like to perform on a resource, and
50
+ it can be one of `:index`, `:show`, `:create`, `:update`, or `:delete`;
51
+ * `*path` refers to an arbitrary number of arguments needed to identify
52
+ the desired resource (a plenty of examples are given below), and it
53
+ always begins with one of `:families`, `:kits`, or `:libraries`;
54
+ * `parameters` is a hash of parameters needed to perform the action.
55
+
56
+ `perform` has an alias for each of the actions: `index(*path, parameters = {})`,
57
+ `show(*path, parameters = {})`, `create(*path, parameters = {})`, and so on.
58
+ The result of a method call is returned as a hash, and its content is exactly
59
+ what the Typekit API sends back to `client`. The only exception is when
60
+ the API returns an error, in which case an appropriate exception is being
61
+ raised.
62
+
63
+ Before sending the actual request to the Typekit API, the library checks
64
+ whether the resource given by `*path` makes sense and, if it does, whether
65
+ `action` can be performed on that resource. So, if you receive an exception,
66
+ check out the [API reference](https://typekit.com/docs/api/).
67
+
68
+ Now, let us have a look at some typical use cases. For clarity, the code
69
+ below makes use of the following auxiliary function:
70
+ ```ruby
71
+ def p(data)
72
+ puts JSON.pretty_generate(data)
73
+ end
74
+ ```
75
+
76
+ ### Show all kits
77
+ Code:
78
+ ```ruby
79
+ p client.index(:kits)
80
+ ```
81
+
82
+ Output:
83
+ ```json
84
+ {
85
+ "kits": [
86
+ {
87
+ "id": "bas4cfe",
88
+ "link": "/api/v1/json/kits/bas4cfe"
89
+ },
90
+ ...
91
+ ]
92
+ }
93
+ ```
94
+
95
+ ### Show the description of a variant of a font family
96
+ Code:
97
+ ```ruby
98
+ p client.show(:families, 'vcsm', 'i9')
99
+ ```
100
+
101
+ Output:
102
+ ```json
103
+ {
104
+ "variation": {
105
+ "id": "vcsm:i9",
106
+ "name": "Proxima Nova Black Italic",
107
+ "family": {
108
+ "id": "vcsm",
109
+ "link": "/api/v1/json/families/vcsm",
110
+ "name": "Proxima Nova"
111
+ },
112
+ "font_style": "italic",
113
+ "font_variant": "normal",
114
+ "font_weight": "900",
115
+ ...
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Show the font families in the trial library with pagination
121
+ Code:
122
+ ```ruby
123
+ p client.show(:libraries, 'trial', page: 10, per_page: 5)
124
+ ```
125
+
126
+ Output:
127
+ ```json
128
+ {
129
+ "library": {
130
+ "id": "trial",
131
+ "link": "/api/v1/json/libraries/trial",
132
+ "name": "Trial Library",
133
+ "families": [
134
+ {
135
+ "id": "qnhl",
136
+ "link": "/api/v1/json/families/qnhl",
137
+ "name": "Caliban Std"
138
+ },
139
+ {
140
+ "id": "vybr",
141
+ "link": "/api/v1/json/families/vybr",
142
+ "name": "Calluna"
143
+ },
144
+ ...
145
+ ],
146
+ "pagination": {
147
+ "count": 261,
148
+ "on": "families",
149
+ "page": 10,
150
+ "page_count": 53,
151
+ "per_page": 5
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### Create a new kit
158
+ Code:
159
+ ```ruby
160
+ p result = client.create(:kits, name: 'Megakit', domains: 'localhost')
161
+ kit_id = result['kit']['id']
162
+ ```
163
+
164
+ Output:
165
+ ```json
166
+ {
167
+ "kit": {
168
+ "id": "izw0qiq",
169
+ "name": "Megakit",
170
+ "analytics": false,
171
+ "badge": true,
172
+ "domains": [
173
+ "localhost"
174
+ ],
175
+ "families": [
176
+
177
+ ]
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### Disable the badge of a kit
183
+ Code:
184
+ ```ruby
185
+ p client.update(:kits, kit_id, badge: false)
186
+ ```
187
+
188
+ Output:
189
+ ```json
190
+ {
191
+ "kit": {
192
+ "id": "izw0qiq",
193
+ "name": "Megakit",
194
+ "analytics": false,
195
+ "badge": false,
196
+ "domains": [
197
+ "localhost"
198
+ ],
199
+ "families": [
200
+
201
+ ]
202
+ }
203
+ }
204
+ ```
205
+
206
+ ### Look up the id of a font family by its slug
207
+ Code:
208
+ ```ruby
209
+ p result = client.show(:families, 'proxima-nova')
210
+ family_id = result['family']['id']
211
+ ```
212
+
213
+ Output:
214
+ ```json
215
+ {
216
+ "family": {
217
+ "id": "vcsm",
218
+ "link": "/api/v1/json/families/vcsm"
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### Add a font family into a kit
224
+ Code:
225
+ ```ruby
226
+ p client.update(:kits, kit_id, families: { "0" => { id: family_id } })
227
+ ```
228
+
229
+ Output:
230
+ ```json
231
+ {
232
+ "kit": {
233
+ "id": "nys8sny",
234
+ "name": "Megakit",
235
+ "analytics": false,
236
+ "badge": false,
237
+ "domains": [
238
+ "localhost"
239
+ ],
240
+ "families": [
241
+ {
242
+ "id": "vcsm",
243
+ "name": "Proxima Nova",
244
+ "slug": "proxima-nova",
245
+ "css_names": [
246
+ "proxima-nova-1",
247
+ "proxima-nova-2"
248
+ ],
249
+ ...
250
+ }
251
+ ]
252
+ }
253
+ }
254
+ ```
255
+
256
+ ### Delete a kit
257
+ Command:
258
+ ```ruby
259
+ p client.delete(:kits, kit_id)
260
+ ```
261
+
262
+ Output:
263
+ ```json
264
+ {
265
+ "ok": true
266
+ }
267
+ ```
268
+
269
+ ## Command-line Interface (CLI)
270
+ There is a simple CLI provided in order to demonstrate the usage of the
271
+ library and to give the ability to perform basic operations without writing
272
+ any code. The tool is called `typekit`, and it should get installed along
273
+ with the gem. Try running:
274
+ ```
275
+ $ typekit -h
276
+ Usage: typekit [options] [command]
277
+
278
+ Required options:
279
+ -t, --token TOKEN Set the API token
280
+
281
+ Other options:
282
+ -v, --version VERSION Set the API version
283
+ -f, --format FORMAT Set the data format
284
+ -h, --help Show this message
285
+ ```
286
+
287
+ Alternatively, you can install `typekit` in the `bin` directory of your
288
+ project using the following command:
289
+ ```bash
290
+ $ bundle binstubs typekit
291
+ ```
292
+
293
+ The tool has two modes: normal and interactive. If `command` is provided,
294
+ the tool executes only that particular command and terminates:
295
+ ```
296
+ $ typekit -t $tk_token index kits
297
+ {
298
+ "kits": [
299
+ {
300
+ "id": "bas4cfe",
301
+ "link": "/api/v1/json/kits/bas4cfe"
302
+ },
303
+ ...
304
+ ]
305
+ }
306
+ $
307
+ ```
308
+
309
+ If `command` is not provided, the tool gives a command prompt wherein one
310
+ can enter multiple commands:
311
+ ```
312
+ $ typekit -t $tk_token
313
+ Type 'help' for help and 'exit' to exit.
314
+ > help
315
+ Usage: <action> <resource> [parameters]
316
+
317
+ <action> index, show, create, update, or delete
318
+ <resource> a list separated by whitespaces
319
+ [parameters] a JSON-encoded hash (optional)
320
+
321
+ Examples:
322
+ index kits
323
+ show kits bas4cfe families vcsm
324
+ show families vcsm i9
325
+ show libraries trial { "page": 10, "per_page": 5 }
326
+ create kits { "name": "Megakit", "domains": "localhost" }
327
+ update kits bas4cfe { "name": "Ultrakit" }
328
+ delete kits bas4cfe
329
+ > index kits
330
+ {
331
+ "kits": [
332
+ {
333
+ "id": "bas4cfe",
334
+ "link": "/api/v1/json/kits/bas4cfe"
335
+ },
336
+ ...
337
+ ]
338
+ }
339
+ > exit
340
+ Bye.
341
+ $
342
+ ```
343
+
344
+ ## Contributing
345
+
346
+ 1. Fork it ( https://github.com/IvanUkhov/typekit-client/fork )
347
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
348
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
349
+ 4. Push to the branch (`git push origin my-new-feature`)
350
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/bin/typekit ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
4
+
5
+ require 'typekit'
6
+ require 'optparse'
7
+ require 'json'
8
+
9
+ class Controller
10
+ def initialize(**options)
11
+ @client = Typekit::Client.new(**options)
12
+ end
13
+
14
+ def process(command)
15
+ action, path, parameters = parse(command)
16
+ print @client.perform(action, path, parameters)
17
+ rescue Exception => e
18
+ puts e
19
+ end
20
+
21
+ def help
22
+ puts <<-HELP
23
+ Usage: <action> <resource> [parameters]
24
+
25
+ <action> show, create, update, or delete
26
+ <resource> a list separated by whitespaces
27
+ [parameters] a JSON-encoded hash (optional)
28
+
29
+ Examples:
30
+ index kits
31
+ show kits bas4cfe families vcsm
32
+ show families vcsm i9
33
+ show libraries trial { "page": 10, "per_page": 5 }
34
+ create kits { "name": "Megakit", "domains": "localhost" }
35
+ update kits bas4cfe { "name": "Ultrakit" }
36
+ delete kits bas4cfe
37
+ HELP
38
+ end
39
+
40
+ protected
41
+
42
+ def print(output)
43
+ puts JSON.pretty_generate(output)
44
+ end
45
+
46
+ def parse(command)
47
+ parameters = extract_parameters!(command)
48
+
49
+ chunks = command.split(/\s+/).compact.reject(&:empty?)
50
+ action, path = chunks[0], chunks[1..-1]
51
+
52
+ raise 'Missing action name' if action.nil?
53
+ raise 'Invalid action name' unless action =~ /^\w+$/
54
+
55
+ raise 'Missing resource name' if path.empty?
56
+ unless path.all?{ |chunk| chunk =~ /^[-\w\d]+$/ }
57
+ raise 'Invalid resource name'
58
+ end
59
+
60
+ [ action, path, parameters ]
61
+ end
62
+
63
+ def extract_parameters!(command)
64
+ if command =~ /(?<parameters>{.*})\s*$/
65
+ parameters = JSON.parse(Regexp.last_match(:parameters))
66
+ raise unless parameters.is_a?(Hash)
67
+ command.gsub!(/{.*}/, '')
68
+ parameters
69
+ else
70
+ {}
71
+ end
72
+ rescue
73
+ raise 'Invalid parameters'
74
+ end
75
+ end
76
+
77
+ options = {}
78
+
79
+ parser = OptionParser.new do |o|
80
+ o.banner = 'Usage: typekit [options] [command]'
81
+
82
+ o.separator ''
83
+ o.separator 'Required options:'
84
+
85
+ o.on('-t', '--token TOKEN', 'Set the API token') do |value|
86
+ options[:token] = value
87
+ end
88
+
89
+ o.separator ''
90
+ o.separator 'Other options:'
91
+
92
+ o.on('-v', '--version VERSION', 'Set the API version') do |value|
93
+ options[:version] = value
94
+ end
95
+
96
+ o.on('-f', '--format FORMAT', 'Set the data format') do |value|
97
+ options[:format] = value
98
+ end
99
+
100
+ o.on_tail('-h', '--help', 'Show this message') do
101
+ raise
102
+ end
103
+ end
104
+
105
+ begin
106
+ parser.parse!
107
+ rescue
108
+ puts parser
109
+ exit
110
+ end
111
+
112
+ begin
113
+ controller = Controller.new(options)
114
+ rescue Exception => e
115
+ puts e
116
+ exit
117
+ end
118
+
119
+ unless ARGV.empty?
120
+ controller.process(ARGV.join(' '))
121
+ exit
122
+ end
123
+
124
+ puts %{Type 'help' for help and 'exit' to exit.}
125
+
126
+ loop do
127
+ begin
128
+ print '> '
129
+ command = $stdin.gets.strip
130
+ next if command.empty?
131
+ case command
132
+ when 'exit'
133
+ break
134
+ when 'help'
135
+ controller.help
136
+ else
137
+ controller.process(command)
138
+ end
139
+ rescue Interrupt
140
+ break
141
+ end
142
+ end
143
+
144
+ puts 'Bye.'
145
+
146
+ # vim: set ft=ruby