typekit-client 0.0.2

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