heroics 0.0.12 → 0.0.13

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