heroics 0.0.12 → 0.0.13

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ecdb1c591d9352b73ba1b720cead8bcd9c690b49
4
- data.tar.gz: be82ea121fecbcf6bf9761521518a999d888fe33
3
+ metadata.gz: e1f56fc7f7100a44079eaaec68a2e7194992fc9c
4
+ data.tar.gz: 0c3730316fa3f9329b3406cc4fa25ce44331d409
5
5
  SHA512:
6
- metadata.gz: 3ac51fd3d654d02de41058362d324cac0835974d3b602c1845ab56b1563a40f6c1a995409e74ae1b9ec6b4496eef02feabe82f8068cb7eb3c1b4f8094e9d942c
7
- data.tar.gz: 8ef27a5f558e7e09c1cad67f46971db41b8cbf0ba0c777305d6c05e86761522514345cc4a2ecbce0755bbb6cec1ba1644bebabf9ec897ae2014cc9db2bf29458
6
+ metadata.gz: 461418c693655d9d717f7a9cdf00dc678f6c569655a3eb954678be101c5da93ba1f242cfa25e3a1c3c4683b3dd0e9a0b29467a9813428f5089c39792496adba9
7
+ data.tar.gz: 83c9808500e1927af547fa5fa2bb679667c9812133a6065dcf6714d6b32f8539e76477d2ccfaf02e26f28e7388c83c8de2b9aefba7c91354c357834798a71cd6
data/.travis.yml CHANGED
@@ -1,3 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ - 2.0.0-p598
5
+ - 2.1.5
6
+ - 2.2.0
data/bin/heroics-generate CHANGED
@@ -2,10 +2,11 @@
2
2
 
3
3
  require 'optparse'
4
4
  require 'heroics'
5
+ require 'open-uri'
5
6
 
6
7
  options = {headers: {}, cache_path: nil}
7
8
  option_parser = OptionParser.new do |opts|
8
- opts.banner = 'Usage: heroics-generate module_name schema_filename url'
9
+ opts.banner = 'Usage: heroics-generate module_name schema_filepath url'
9
10
 
10
11
  opts.on('-h', '--help', 'Display this screen') do
11
12
  puts opts
@@ -28,8 +29,8 @@ option_parser.parse!
28
29
  if ARGV.length != 3
29
30
  puts option_parser
30
31
  else
31
- module_name, schema_filename, url = ARGV
32
- schema = Heroics::Schema.new(MultiJson.decode(File.read(schema_filename)))
32
+ module_name, schema_filepath, url = ARGV
33
+ schema = Heroics::Schema.new(MultiJson.decode(open(schema_filepath).read))
33
34
  cache = 'Moneta.new(:Memory)'
34
35
  if options[:cache_path]
35
36
  cache = "Moneta.new(:File, dir: \"#{options[:cache_path]}\")"
data/lib/heroics/link.rb CHANGED
@@ -47,8 +47,8 @@ module Heroics
47
47
  path = "#{@path_prefix}#{path}" unless @path_prefix == '/'
48
48
  headers = @default_headers
49
49
  if body
50
- headers = headers.merge({'Content-Type' => 'application/json'})
51
- body = MultiJson.dump(body)
50
+ headers = headers.merge({'Content-Type' => @link_schema.content_type})
51
+ body = @link_schema.encode(body)
52
52
  end
53
53
  cache_key = "#{path}:#{headers.hash}"
54
54
  if @link_schema.method == :get
@@ -59,11 +59,11 @@ module Heroics
59
59
  connection = Excon.new(@root_url)
60
60
  response = connection.request(method: @link_schema.method, path: path,
61
61
  headers: headers, body: body,
62
- expects: [200, 201, 202, 206, 304])
62
+ expects: [200, 201, 202, 204, 206, 304])
63
63
  content_type = response.headers['Content-Type']
64
64
  if response.status == 304
65
65
  MultiJson.load(@cache["data:#{cache_key}"])
66
- elsif content_type && content_type.include?('application/json')
66
+ elsif content_type && content_type =~ /application\/.*json/
67
67
  etag = response.headers['ETag']
68
68
  if etag
69
69
  @cache["etag:#{cache_key}"] = etag
@@ -24,7 +24,6 @@ module Heroics
24
24
  # @param name [String] The name of the resource.
25
25
  # @raise [SchemaError] Raised if an unknown resource name is provided.
26
26
  def resource(name)
27
- resource_schema = @resources[name]
28
27
  if @schema['definitions'].has_key?(name)
29
28
  ResourceSchema.new(@schema, name)
30
29
  else
@@ -58,7 +57,8 @@ module Heroics
58
57
  def initialize(schema, name)
59
58
  @schema = schema
60
59
  @name = name
61
- link_schema = schema['definitions'][name]['links']
60
+ link_schema = schema['definitions'][name]['links'] || []
61
+
62
62
  @links = Hash[link_schema.each_with_index.map do |link, link_index|
63
63
  link_name = Heroics.ruby_name(link['title'])
64
64
  [link_name, LinkSchema.new(schema, name, link_index)]
@@ -128,6 +128,22 @@ module Heroics
128
128
  link_schema['method'].downcase.to_sym
129
129
  end
130
130
 
131
+ # Get the Content-Type for this link.
132
+ #
133
+ # @return [String] The Content-Type value
134
+ def content_type
135
+ link_schema['encType'] || 'application/json'
136
+ end
137
+
138
+ def encode(body)
139
+ case content_type
140
+ when 'application/x-www-form-urlencoded'
141
+ URI.encode_www_form(body)
142
+ when /application\/.*json/
143
+ MultiJson.dump(body)
144
+ end
145
+ end
146
+
131
147
  # Get the names of the parameters this link expects.
132
148
  #
133
149
  # @return [Array<String>] The parameters.
@@ -307,7 +323,8 @@ module Heroics
307
323
  # @param [Fixnum,String,TrueClass,FalseClass,Time] The parameter to format.
308
324
  # @return [String] The formatted parameter.
309
325
  def format_parameter(parameter)
310
- parameter.instance_of?(Time) ? iso_format(parameter) : parameter.to_s
326
+ formatted_parameter = parameter.instance_of?(Time) ? iso_format(parameter) : parameter.to_s
327
+ URI.escape formatted_parameter
311
328
  end
312
329
 
313
330
  # Convert a time to an ISO 8601 combined data and time format.
@@ -1,3 +1,3 @@
1
1
  module Heroics
2
- VERSION = '0.0.12'
2
+ VERSION = '0.0.13'
3
3
  end
data/test/helper.rb CHANGED
@@ -127,6 +127,25 @@ SAMPLE_SCHEMA = {
127
127
  'email_field' => {
128
128
  '$ref' => '#/definitions/resource/definitions/email_field'}}}},
129
129
 
130
+ {'description' => 'Submit a sample resource as form data',
131
+ 'encType' => 'application/x-www-form-urlencoded',
132
+ 'href' => '/resource',
133
+ 'method' => 'POST',
134
+ 'rel' => 'submit',
135
+ 'title' => 'Submit',
136
+ 'schema' => {
137
+ 'properties' => {
138
+ 'date_field' => {
139
+ '$ref' => '#/definitions/resource/definitions/date_field'},
140
+ 'string_field' => {
141
+ '$ref' => '#/definitions/resource/definitions/string_field'},
142
+ 'boolean_field' => {
143
+ '$ref' => '#/definitions/resource/definitions/boolean_field'},
144
+ 'uuid_field' => {
145
+ '$ref' => '#/definitions/resource/definitions/uuid_field'},
146
+ 'email_field' => {
147
+ '$ref' => '#/definitions/resource/definitions/email_field'}}}},
148
+
130
149
  {'description' => 'Update a sample resource',
131
150
  'href' => '/resource/{(%23%2Fdefinitions%2Fresource%2Fdefinitions%2Fuuid_field)}',
132
151
  'method' => 'PATCH',
data/test/link_test.rb CHANGED
@@ -38,6 +38,20 @@ class LinkTest < MiniTest::Unit::TestCase
38
38
  assert_equal(nil, link.run('44724831-bf66-4bc2-865f-e2c4c2b14c78'))
39
39
  end
40
40
 
41
+ # Link.run URL-escapes special characters in parameters.
42
+ def test_run_with_parameters_needing_escaping
43
+ Excon.stub(method: :get) do |request|
44
+ assert_equal('/resource/foo%23bar', request[:path])
45
+ Excon.stubs.pop
46
+ {status: 200, body: ''}
47
+ end
48
+
49
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
50
+ link = Heroics::Link.new('https://example.com',
51
+ schema.resource('resource').link('info'))
52
+ assert_equal(nil, link.run('foo#bar'))
53
+ end
54
+
41
55
  # Link.run converts Time parameters to UTC before sending them to the
42
56
  # server.
43
57
  def test_run_converts_time_parameters_to_utc
@@ -71,6 +85,25 @@ class LinkTest < MiniTest::Unit::TestCase
71
85
  assert_equal(nil, link.run(body))
72
86
  end
73
87
 
88
+ # Link.run optionally takes an extra parameter to send in the request body.
89
+ # It automatically converts the specified object to the specified encoding
90
+ # type and includes a Content-Type header in the request
91
+ def test_run_without_parameters_and_with_non_json_request_body
92
+ body = {'Hello' => 'world!'}
93
+ Excon.stub(method: :post) do |request|
94
+ assert_equal('application/x-www-form-urlencoded', request[:headers]['Content-Type'])
95
+ assert_equal('Hello=world%21', request[:body])
96
+ Excon.stubs.pop
97
+ {status: 200, body: ''}
98
+ end
99
+
100
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
101
+ link = Heroics::Link.new('https://example.com',
102
+ schema.resource('resource').link('submit'))
103
+ assert_equal(nil, link.run(body))
104
+ end
105
+
106
+
74
107
  # Link.run passes custom headers to the server when they've been provided.
75
108
  def test_run_with_custom_request_headers
76
109
  Excon.stub(method: :get) do |request|
@@ -178,7 +211,7 @@ class LinkTest < MiniTest::Unit::TestCase
178
211
  assert_equal('/resource', request[:path])
179
212
  Excon.stubs.pop
180
213
  {status: 200,
181
- headers: {'Content-Type' => 'application/json;charset=utf-8'},
214
+ headers: {'Content-Type' => 'application/vnd.api+json;charset=utf-8'},
182
215
  body: MultiJson.dump(body)}
183
216
  end
184
217
 
@@ -204,6 +237,20 @@ class LinkTest < MiniTest::Unit::TestCase
204
237
  assert_equal(body, link.run)
205
238
  end
206
239
 
240
+ # Link.run considers HTTP 204 No Content responses as successful.
241
+ def test_run_with_no_content_response
242
+ Excon.stub(method: :delete) do |request|
243
+ assert_equal("/resource/2013-01-01T08:00:00Z", request[:path])
244
+ Excon.stubs.pop
245
+ {status: 204, body: ''}
246
+ end
247
+
248
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
249
+ link = Heroics::Link.new('https://example.com',
250
+ schema.resource('resource').link('delete'))
251
+ assert_equal(nil, link.run(Time.parse('2013-01-01 00:00:00-0800')))
252
+ end
253
+
207
254
  # Link.run raises an Excon error if anything other than a 200 or 201 HTTP
208
255
  # status code was returned by the server.
209
256
  def test_run_with_failed_request
@@ -251,8 +298,9 @@ class LinkTest < MiniTest::Unit::TestCase
251
298
  {status: 200}
252
299
  end
253
300
 
301
+ headers = {}
254
302
  cache = Moneta.new(:Memory)
255
- cache['etag:/resource:0'] = 'etag-contents'
303
+ cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
256
304
  schema = Heroics::Schema.new(SAMPLE_SCHEMA)
257
305
  link = Heroics::Link.new('https://example.com',
258
306
  schema.resource('resource').link('list'),
@@ -287,9 +335,10 @@ class LinkTest < MiniTest::Unit::TestCase
287
335
  {status: 304, headers: {'Content-Type' => 'application/json'}}
288
336
  end
289
337
 
338
+ headers = {}
290
339
  cache = Moneta.new(:Memory)
291
- cache['etag:/resource:0'] = 'etag-contents'
292
- cache['data:/resource:0'] = MultiJson.dump(body)
340
+ cache["etag:/resource:#{headers.hash}"] = 'etag-contents'
341
+ cache["data:/resource:#{headers.hash}"] = MultiJson.dump(body)
293
342
  schema = Heroics::Schema.new(SAMPLE_SCHEMA)
294
343
  link = Heroics::Link.new('https://example.com',
295
344
  schema.resource('resource').link('list'),
data/test/schema_test.rb CHANGED
@@ -54,7 +54,7 @@ class ResourceSchemaTest < MiniTest::Unit::TestCase
54
54
  def test_links
55
55
  schema = Heroics::Schema.new(SAMPLE_SCHEMA)
56
56
  assert_equal(
57
- ['list', 'info', 'identify_resource', 'create', 'update', 'delete'],
57
+ ['list', 'info', 'identify_resource', 'create', 'submit', 'update', 'delete'],
58
58
  schema.resource('resource').links.map { |link| link.name })
59
59
  end
60
60
  end
@@ -167,6 +167,14 @@ class LinkSchemaTest < MiniTest::Unit::TestCase
167
167
  link.format_path(['44724831-bf66-4bc2-865f-e2c4c2b14c78']))
168
168
  end
169
169
 
170
+ # LinkSchema.format_path escapes special URL characters in parameters.
171
+ def test_format_path_with_illegal_literals
172
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
173
+ link = schema.resource('resource').link('info')
174
+ assert_equal(['/resource/foobar%25', nil],
175
+ link.format_path(['foobar%']))
176
+ end
177
+
170
178
  # LinkSchema.format_path correctly returns a parameter as a body if a path
171
179
  # doesn't have any parameters.
172
180
  def test_format_path_with_body
@@ -224,6 +232,21 @@ class LinkSchemaTest < MiniTest::Unit::TestCase
224
232
  link = schema.resource('resource').link('identify_resource')
225
233
  assert_equal('identify-resource', link.pretty_name)
226
234
  end
235
+
236
+ # LinkSchema.content_type returns the media type associated with this
237
+ # resource.
238
+ def test_content_type
239
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
240
+ link = schema.resource('resource').link('submit')
241
+ assert_equal('application/x-www-form-urlencoded', link.content_type)
242
+ end
243
+
244
+ # The content type should default to application/json
245
+ def test_default_content_type
246
+ schema = Heroics::Schema.new(SAMPLE_SCHEMA)
247
+ link = schema.resource('resource').link('identify_resource')
248
+ assert_equal('application/json', link.content_type)
249
+ end
227
250
  end
228
251
 
229
252
  class DownloadSchemaTest < MiniTest::Unit::TestCase
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: heroics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - geemus
@@ -9,20 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-22 00:00:00.000000000 Z
12
+ date: 2015-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ~>
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
20
  version: '1.3'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ~>
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.3'
28
28
  - !ruby/object:Gem::Dependency
@@ -43,98 +43,98 @@ dependencies:
43
43
  name: rake
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - '>='
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: turn
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - '>='
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - '>='
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: erubis
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - ~>
74
+ - - "~>"
75
75
  - !ruby/object:Gem::Version
76
76
  version: 2.7.0
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ~>
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: 2.7.0
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: excon
86
86
  requirement: !ruby/object:Gem::Requirement
87
87
  requirements:
88
- - - '>='
88
+ - - ">="
89
89
  - !ruby/object:Gem::Version
90
90
  version: '0'
91
91
  type: :runtime
92
92
  prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - '>='
95
+ - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  - !ruby/object:Gem::Dependency
99
99
  name: moneta
100
100
  requirement: !ruby/object:Gem::Requirement
101
101
  requirements:
102
- - - '>='
102
+ - - ">="
103
103
  - !ruby/object:Gem::Version
104
104
  version: '0'
105
105
  type: :runtime
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - '>='
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: multi_json
114
114
  requirement: !ruby/object:Gem::Requirement
115
115
  requirements:
116
- - - '>='
116
+ - - ">="
117
117
  - !ruby/object:Gem::Version
118
118
  version: 1.9.2
119
119
  type: :runtime
120
120
  prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - '>='
123
+ - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: 1.9.2
126
126
  - !ruby/object:Gem::Dependency
127
127
  name: netrc
128
128
  requirement: !ruby/object:Gem::Requirement
129
129
  requirements:
130
- - - '>='
130
+ - - ">="
131
131
  - !ruby/object:Gem::Version
132
132
  version: '0'
133
133
  type: :runtime
134
134
  prerelease: false
135
135
  version_requirements: !ruby/object:Gem::Requirement
136
136
  requirements:
137
- - - '>='
137
+ - - ">="
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  description: A Ruby client generator for HTTP APIs described with a JSON schema
@@ -147,8 +147,8 @@ executables:
147
147
  extensions: []
148
148
  extra_rdoc_files: []
149
149
  files:
150
- - .gitignore
151
- - .travis.yml
150
+ - ".gitignore"
151
+ - ".travis.yml"
152
152
  - CONTRIBUTING.md
153
153
  - CONTRIBUTORS.md
154
154
  - Gemfile
@@ -192,17 +192,17 @@ require_paths:
192
192
  - lib
193
193
  required_ruby_version: !ruby/object:Gem::Requirement
194
194
  requirements:
195
- - - '>='
195
+ - - ">="
196
196
  - !ruby/object:Gem::Version
197
197
  version: '0'
198
198
  required_rubygems_version: !ruby/object:Gem::Requirement
199
199
  requirements:
200
- - - '>='
200
+ - - ">="
201
201
  - !ruby/object:Gem::Version
202
202
  version: '0'
203
203
  requirements: []
204
204
  rubyforge_project:
205
- rubygems_version: 2.0.14
205
+ rubygems_version: 2.2.2
206
206
  signing_key:
207
207
  specification_version: 4
208
208
  summary: A Ruby client generator for HTTP APIs described with a JSON schema