webservice 0.4.0 → 0.5.0

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: b43248423b9aeba87a88f63b6a708167bbf7d723
4
- data.tar.gz: 35b9cd86f34c708a4d55f7f61a08a878f4e19d12
3
+ metadata.gz: fca9552fa4818068271c81022e7ab9a4ec277d30
4
+ data.tar.gz: 23e2d0051213a5834cafff2762df4324c6907935
5
5
  SHA512:
6
- metadata.gz: def6d86899d29ca10acb0c70af15de8887005fc47dc9f1d5586103eb98033d7e68d5c44744c51b30493cbcef344a565f9dbcc21c6681aa55257e42c772c0f307
7
- data.tar.gz: 2417ece5303df96025515574a343e2b21eaa1e08015ff7cfe11822a9d9d757c918a798e149d1d03f24b74a7574e0ef6e4625778b6b63b6b42fa0f403c31adb19
6
+ metadata.gz: 6b53f3b4ea61aed918b16b5b73ad3be4e162131599d6cb1222607097d5cff3f8757222458373a2a3e30dbea91d47f32c092f6557e274bebcbdaf0ae8c121f992
7
+ data.tar.gz: b32faf56e9286e1b4283d900f59f7b1af4bfd89b70366e849200c014540c8e80c45a9110a1d51239372eb414aec5f5f308149960786d6d32f254a942fd404a85
data/README.md CHANGED
@@ -10,11 +10,151 @@ webservice gem - yet another HTTP JSON API (web service) builder
10
10
 
11
11
  ## Usage
12
12
 
13
- TBD
13
+ [Dynamic Example](#dynamic-example) •
14
+ [Classic Example](#classic-example) •
15
+ [Rackup Example](#rackup-example)
16
+
17
+
18
+ ### Dynamic Example
19
+
20
+ You can load services at-runtime from files using `Webservice.load_file`.
21
+ Example:
22
+
23
+ ```ruby
24
+ # service.rb
25
+
26
+ get '/' do
27
+ 'Hello, world!'
28
+ end
29
+ ```
30
+
31
+ and
32
+
33
+ ```ruby
34
+ # server.rb
35
+
36
+ require 'webservice'
37
+
38
+ App = Webservice.load_file( './service.rb' )
39
+ App.run!
40
+ ```
41
+
42
+ and to run type
43
+
44
+ ```
45
+ $ ruby ./server.rb
46
+ ```
47
+
48
+
49
+ ### Classic Example
50
+
51
+ ```ruby
52
+ # server.rb
53
+
54
+ require 'webservice'
55
+
56
+ class App < Webservice::Base
57
+ get '/' do
58
+ 'Hello, world!'
59
+ end
60
+ end
61
+
62
+ App.run!
63
+ ```
64
+ and to run type
65
+
66
+ ```
67
+ $ ruby ./server.rb
68
+ ```
69
+
70
+
71
+ ### Rackup Example
72
+
73
+ Use `config.ru` and `rackup`. Example:
74
+
75
+ ```ruby
76
+ # config.ru
77
+
78
+ require `webservice`
79
+
80
+ class App < Webservice::Base
81
+ get '/' do
82
+ 'Hello, world!'
83
+ end
84
+ end
85
+
86
+ run App
87
+ ```
88
+
89
+ and to run type
90
+
91
+ ```
92
+ $ rackup # will (auto-)load config.ru
93
+ ```
94
+
95
+ Note: `config.ru` is a shortcut (inline)
96
+ version of `Rack::Builder.new do ... end`:
97
+
98
+ ```ruby
99
+ # server.rb
100
+
101
+ require 'webservice'
102
+
103
+ class App < Webservice::Base
104
+ get '/' do
105
+ 'Hello, world!'
106
+ end
107
+ end
108
+
109
+ builder = Rack::Builder.new do
110
+ run App
111
+ end
112
+
113
+ Rack::Server.start builder.to_app
114
+ ```
115
+
116
+ and to run type
117
+
118
+ ```
119
+ $ ruby ./server.rb
120
+ ```
121
+
122
+
123
+
124
+ ## "Real World" Examples
125
+
126
+ See
127
+
128
+ [**`beerkit / beer.db.service`**](https://github.com/beerkit/beer.db.service) -
129
+ beer.db HTTP JSON API (web service) scripts e.g.
130
+
131
+ ```ruby
132
+ get '/beer/(r|rnd|rand|random)' do # special keys for random beer
133
+ Beer.rnd
134
+ end
135
+
136
+ get '/beer/:key'
137
+ Beer.find_by! key: params[:key]
138
+ end
139
+
140
+ get '/brewery/(r|rnd|rand|random)' do # special keys for random brewery
141
+ Brewery.rnd
142
+ end
143
+
144
+ get '/brewery/:key'
145
+ Brewery.find_by! key: params[:key]
146
+ end
147
+ ```
148
+
149
+ [**`worlddb / world.db.service`**](https://github.com/worlddb/world.db.service) -
150
+ world.db HTTP JSON API (web service) scripts
151
+
152
+ [**`sportdb / sport.db.service`**](https://github.com/sportdb/sport.db.service) -
153
+ sport.db (football.db) HTTP JSON API (web service) scripts
154
+
14
155
 
15
156
 
16
157
  ## License
17
158
 
18
159
  The `webservice` scripts are dedicated to the public domain.
19
160
  Use it as you please with no restrictions whatsoever.
20
-
@@ -3,6 +3,9 @@
3
3
  ## stdlib
4
4
  require 'json'
5
5
  require 'csv'
6
+ require 'date'
7
+ require 'time'
8
+ require 'uri'
6
9
  require 'pp'
7
10
 
8
11
 
@@ -3,31 +3,50 @@
3
3
 
4
4
  module Webservice
5
5
 
6
+ ## use (an reuse from Rack) some freezed string constants
7
+ ## HTTP verbs
8
+ GET = Rack::GET
9
+ POST = Rack::POST
10
+ PATCH = Rack::PATCH
11
+ PUT = Rack::PUT
12
+ DELETE = Rack::DELETE
13
+ HEAD = Rack::HEAD
14
+ OPTIONS = Rack::OPTIONS
15
+
16
+ ## HTTP headers
17
+ CONTENT_LENGTH = Rack::CONTENT_LENGTH
18
+ CONTENT_TYPE = Rack::CONTENT_TYPE
19
+
20
+ LOCATION = 'Location'.freeze # not available from Rack
21
+
6
22
 
7
23
  module Helpers
8
24
  ## add some more helpers
9
25
  ## "inspired" by sinatra (mostly) - for staying compatible
10
26
  ## see https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb
11
27
 
12
- ## todo -- add redirect/redirect_to
13
- ## add status -- why? why not??
28
+ ## todo -- add status -- why? why not??
14
29
 
15
30
  # Halt processing and return the error status provided.
16
- def error(code, body=nil)
17
- code = 500
18
- body = code.to_str if code.respond_to? :to_str
19
-
31
+ def error( code, body=nil )
20
32
  response.body = body unless body.nil?
21
33
  halt code
22
34
  end
23
35
 
24
36
  # Halt processing and return a 404 Not Found.
25
- def not_found(body=nil)
37
+ def not_found( body=nil )
26
38
  error 404, body
27
39
  end
28
40
 
41
+
42
+ def redirect_to( uri, status=302 ) ## Note: 302 == Found, 301 == Moved Permanently
43
+ halt status, { LOCATION => uri }
44
+ end
45
+ alias_method :redirect, :redirect_to
46
+
47
+
29
48
  # Set multiple response headers with Hash.
30
- def headers(hash=nil)
49
+ def headers( hash=nil )
31
50
  response.headers.merge! hash if hash
32
51
  response.headers
33
52
  end
@@ -36,18 +55,18 @@ module Helpers
36
55
 
37
56
  ## (simple) content_type helper - all "hard-coded" for now; always uses utf-8 too
38
57
  def content_type( type=nil )
39
- return response['Content-Type'] unless type
58
+ return response[ CONTENT_TYPE ] unless type
40
59
 
41
60
  if type.to_sym == :json
42
- response['Content-Type'] = 'application/json; charset=utf-8'
61
+ response[ CONTENT_TYPE ] = 'application/json; charset=utf-8'
43
62
  elsif type.to_sym == :js || type.to_sym == :javascript
44
- response['Content-Type'] = 'application/javascript; charset=utf-8'
63
+ response[ CONTENT_TYPE ] = 'application/javascript; charset=utf-8'
45
64
  ## use 'text/javascript; charset=utf-8' -- why? why not??
46
65
  ## note: ietf recommends application/javascript
47
66
  elsif type.to_sym == :csv || type.to_sym == :text || type.to_sym == :txt
48
- response['Content-Type'] = 'text/plain; charset=utf-8'
67
+ response[ CONTENT_TYPE ] = 'text/plain; charset=utf-8'
49
68
  elsif type.to_sym == :html || type.to_sym == :htm
50
- response['Content-Type'] = 'text/html; charset=utf-8'
69
+ response[ CONTENT_TYPE ] = 'text/html; charset=utf-8'
51
70
  else
52
71
  ### unknown type; do nothing - sorry; issue warning - why? why not??
53
72
  end
@@ -58,24 +77,38 @@ end ## module Helpers
58
77
  class Base
59
78
  include Helpers
60
79
 
80
+
61
81
  class << self
62
82
 
83
+ def call( env ) ## note self.call(env) lets you use => run Base instead of run Base.new
84
+ ## puts "calling #{self.name}.call"
85
+ prototype.call( env )
86
+ end
87
+
88
+ def prototype
89
+ ## puts "calling #{self.name}.prototype"
90
+ @prototype ||= self.new
91
+ ## pp @prototype
92
+ ## @prototype
93
+ end
94
+
95
+
63
96
  ## todo/check: all verbs needed! (supported) - why, why not??
64
97
  ## e.g. add LINK, UNLINK ??
65
98
 
66
99
  # Note: for now defining a `GET` handler also automatically defines
67
100
  # a `HEAD` handler (follows sinatra convention)
68
- def get( pattern, &block)
69
- route( 'GET', pattern, &block )
70
- route( 'HEAD', pattern, &block )
101
+ def get( pattern, &block )
102
+ route( GET, pattern, &block )
103
+ route( HEAD, pattern, &block )
71
104
  end
72
105
 
73
- def post( pattern, &block) route( 'POST', pattern, &block ); end
74
- def patch( pattern, &block) route( 'PATCH', pattern, &block ); end
75
- def put( pattern, &block) route( 'PUT', pattern, &block ); end
76
- def delete( pattern, &block) route( 'DELETE', pattern, &block ); end
77
- def head( pattern, &block) route( 'HEAD', pattern, &block ); end
78
- def options( pattern, &block) route( 'OPTIONS', pattern, &block ); end
106
+ def post( pattern, &block) route( POST, pattern, &block ); end
107
+ def patch( pattern, &block) route( PATCH, pattern, &block ); end
108
+ def put( pattern, &block) route( PUT, pattern, &block ); end
109
+ def delete( pattern, &block) route( DELETE, pattern, &block ); end
110
+ def head( pattern, &block) route( HEAD, pattern, &block ); end
111
+ def options( pattern, &block) route( OPTIONS, pattern, &block ); end
79
112
 
80
113
  def route( method, pattern, &block )
81
114
  puts "[debug] Webservice::Base.#{method.downcase} - add route #{method} '#{pattern}' to #<#{self.name}:#{self.object_id}> : #{self.class.name}"
@@ -93,7 +126,7 @@ class Base
93
126
  def environment
94
127
  ## include APP_ENV why? why not?
95
128
  ## todo -- cache value? why why not? (see/follow sinatara set machinery ??)
96
- (ENV['RACK_ENV'] || :development).to_sym
129
+ (ENV['APP_ENV'] || ENV['RACK_ENV'] || :development).to_sym
97
130
  end
98
131
 
99
132
  def development?() environment == :development; end
@@ -104,9 +137,9 @@ class Base
104
137
  ## convenience method
105
138
  def run!
106
139
  puts "[debug] Webservice::Base.run! - self = #<#{self.name}:#{self.object_id}> : #{self.class.name}" # note: assumes self is class
107
- app = self.new ## note: use self; will be derived class (e.g. App and not Base)
140
+ app = self ## note: use self; will be derived class (e.g. App and not Base)
108
141
  port = 4567
109
- Rack::Handler.get('webrick').run( app, Port:port ) do |server|
142
+ Rack::Handler::WEBrick.run( app, Port:port ) do |server|
110
143
  ## todo: add traps here - why, why not??
111
144
  end
112
145
  end
@@ -119,25 +152,35 @@ class Base
119
152
  attr_reader :params
120
153
  attr_reader :env
121
154
 
122
- def call(env)
123
- dup.call!(env)
155
+ def call( env )
156
+ dup.call!( env )
124
157
  end
125
158
 
126
- def call!(env)
159
+ def call!( env )
127
160
  env['PATH_INFO'] = '/' if env['PATH_INFO'].empty?
128
- @request = Rack::Request.new(env)
161
+
162
+ @request = Rack::Request.new( env )
129
163
  @response = Rack::Response.new
130
164
  @params = request.params
131
165
  @env = env
166
+
167
+
168
+ ## (auto-)add (merge in) cors headers
169
+ ## todo: move into a before filter ?? lets you overwrite headers - needed - why? why not??
170
+ headers 'Access-Control-Allow-Origin' => '*',
171
+ 'Access-Control-Allow-Headers' => 'Authorization,Accepts,Content-Type,X-CSRF-Token,X-Requested-With',
172
+ 'Access-Control-Allow-Methods' => 'GET,POST,PUT,DELETE,OPTIONS'
173
+
132
174
  route_eval
175
+
133
176
  @response.finish
134
177
  end
135
178
 
136
179
 
137
180
  def halt( *args )
138
- response.status = args.detect{|arg| arg.is_a?(Fixnum) } || 200
139
- response.header.merge!(args.detect{|arg| arg.is_a?(Hash) } || {})
140
- response.body = [args.detect{|arg| arg.is_a?(String) } || '']
181
+ response.status = args.detect{ |arg| arg.is_a?(Fixnum) } || 200
182
+ response.header.merge!( args.detect{ |arg| arg.is_a?(Hash) } || {} )
183
+ response.body = [args.detect{ |arg| arg.is_a?(String) } || '']
141
184
  throw :halt, response
142
185
  end
143
186
 
@@ -145,57 +188,98 @@ private
145
188
 
146
189
  def route_eval
147
190
  catch(:halt) do
148
- self.class.routes[request.request_method].each do |pattern, block|
191
+ routes = self.class.routes[ request.request_method ]
192
+ routes.each do |pattern, block|
149
193
  ## puts "trying matching route >#{request.path_info}<..."
150
194
  url_params = pattern.params( request.path_info )
151
195
  if url_params ## note: params returns nil if no match
152
196
  ## puts " BINGO! url_params: #{url_params.inspect}"
153
197
  if !url_params.empty? ## url_params hash NOT empty (e.g. {}) merge with req params
154
198
  ## todo/fix: check merge order - params overwrites url_params - why? why not??
155
- @params = url_params.merge( params )
199
+ @params = url_params.merge( @params )
156
200
  end
157
201
  handle_response( instance_eval( &block ))
158
202
  return
159
203
  end
160
204
  end
205
+ # no match found for route/request
161
206
  halt 404
162
207
  end
163
208
  end
164
209
 
165
210
 
166
211
 
167
-
168
- def handle_response( obj )
212
+ ## todo: add as_json like opts={} why? why not?
213
+ def handle_response( obj, opts={} )
169
214
  puts "[Webservice::Base#handle_response (#{request.path_info}) params: #{params.inspect}] - obj : #{obj.class.name}"
170
215
  pp obj
171
216
 
172
217
  ## "magic" param format; default to json
173
218
  format = params['format'] || 'json'
174
219
 
175
- if format == 'csv' || format == 'txt'
176
- text = generate_csv( obj )
177
- content_type :text
178
- response.write text
179
- elsif format == 'html' || format == 'htm'
180
- text = generate_html_table( obj )
181
- content_type :html
182
- response.write text
220
+ ## note: response.body must be (expects) an array!!!
221
+ ## thus, [json] etc.
222
+
223
+ if format == 'csv' || format == 'txt' ||
224
+ format == 'html' || format == 'htm'
225
+
226
+ data = as_tabular( obj )
227
+
228
+ ## note: array required!!!
229
+ # array => multiple records (array of hashes)
230
+ if data.is_a?( Array )
231
+ if format == 'csv' || format == 'txt'
232
+ content_type :txt ## use csv content type - why? why not??
233
+ response.body = [generate_csv( data )]
234
+ else
235
+ ## asume html
236
+ content_type :html
237
+ response.body = [generate_html_table( data )]
238
+ end
239
+ else
240
+ ## wrong format (expect array of hashes)
241
+ ## todo: issue warning/notice about wrong format - how?
242
+ ## use different http status code - why? why not??
243
+ content_type :txt
244
+ ## todo/check: use just data.to_s for all - why? why not?
245
+ ## for now return as is (convert to string with to_s or inspect)
246
+ response.body = [data.is_a?( String ) ? data.to_s : data.inspect]
247
+ end
183
248
  else
184
- json = generate_json( obj )
249
+ data = as_json( obj )
250
+
251
+ ## note: hash or array required!!! for now for json generation
252
+ # hash => single record
253
+ # array => multiple records (that is, array of hashes)
254
+
255
+ if data.is_a?( Hash ) || data.is_a?( Array )
256
+ json = JSON.pretty_generate( data ) ## use pretty printer
185
257
 
186
- callback = params.delete('callback')
258
+ callback = params.delete( 'callback' )
187
259
 
188
- if callback
189
- content_type :js
190
- response.write "#{callback}(#{json})"
260
+ if callback
261
+ content_type :js
262
+ response.body = ["#{callback}(#{json})"]
263
+ else
264
+ content_type :json
265
+ response.body = [json]
266
+ end
191
267
  else
192
- content_type :json
193
- response.write json
268
+ ## todo/fix/check: change http status to unprocessable entity
269
+ ## or something -- why ??? why not??
270
+ ##
271
+ ## allow "standalone" number, nils, strings - why? why not?
272
+ ## for now valid json must be wrapped in array [] or hash {}
273
+ content_type :txt
274
+ ## todo/check: use just data.to_s for all - why? why not?
275
+ ## for now return as is (convert to string with to_s or inspect)
276
+ response.body = [data.is_a?( String ) ? data.to_s : data.inspect]
194
277
  end
195
278
  end
196
279
  end # method handle_response
197
280
 
198
281
 
282
+
199
283
  def generate_csv( recs )
200
284
  ## note: for now assumes (only works with) array of hash records e.g.:
201
285
  ## [
@@ -243,14 +327,38 @@ private
243
327
  end
244
328
 
245
329
 
246
- def generate_json( obj )
247
- if obj.respond_to? :as_json_v2 ## try (our own) serializer first
330
+ ##########################################
331
+ ## auto-generate/convert "magic"
332
+
333
+ def as_tabular( obj, opts={} )
334
+ ## for now allow
335
+ ## as_tab, as_tabular - others too? e.g. as_table why? why not?
336
+ ## like as_json will return a hash or array of hashes NOT a string!!!!
337
+
338
+ if obj.respond_to? :as_tab
339
+ obj.as_tab
340
+ elsif obj.respond_to? :as_tabular
341
+ obj.as_tabular
342
+ else
343
+ ## note: use as_json will return hash (for record) or array of hashes (for records)
344
+ if obj.respond_to? :as_json
345
+ obj.as_json
346
+ else
347
+ obj ## just try/use as is (assumes array of hashesd)
348
+ end
349
+ end
350
+ end
351
+
352
+
353
+ def as_json( obj, opts={} )
354
+ if obj.respond_to? :as_json_v3 ## try (our own) serializer first
355
+ obj.as_json_v3
356
+ elsif obj.respond_to? :as_json_v2 ## try (our own) serializer first
248
357
  obj.as_json_v2
249
358
  elsif obj.respond_to? :as_json ## try (activerecord) serializer
250
- obj.as_json_v2
359
+ obj.as_json
251
360
  else
252
- ## just try/use to_json
253
- obj.to_json
361
+ obj ## just try/use as is
254
362
  end
255
363
  end
256
364
 
@@ -1,10 +1,16 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Webservice
4
- MAJOR = 0 ## todo: namespace inside version or something - why? why not??
5
- MINOR = 4
6
- PATCH = 0
7
- VERSION = [MAJOR,MINOR,PATCH].join('.')
4
+
5
+ module Version
6
+ MAJOR = 0 ## todo: namespace inside version or something - why? why not??
7
+ MINOR = 5
8
+ PATCH = 0 ## note: if not put in module will overwrite PATCH (HTTP Verb Constant)!!!
9
+ end
10
+
11
+ VERSION = [Version::MAJOR,
12
+ Version::MINOR,
13
+ Version::PATCH].join('.')
8
14
 
9
15
  def self.version
10
16
  VERSION
@@ -13,12 +13,10 @@ end
13
13
 
14
14
  get '/halt/404' do
15
15
  halt 404 # 404 - not found
16
- ## todo: check why log reports 200-OK (for status code)!!
17
16
  end
18
17
 
19
18
  get '/halt_error' do
20
19
  halt 500, "Error fatal" # 500 - internal server error
21
- ## todo: check why log reports 200-OK (for status code)!!
22
20
  end
23
21
 
24
22
 
@@ -24,46 +24,46 @@ class TestApp < MiniTest::Test
24
24
  def test_get
25
25
  get '/'
26
26
  assert last_response.ok?
27
- assert_equal %q{"Hello World"}, last_response.body
27
+ assert_equal "Hello World", last_response.body
28
28
 
29
29
  get '/hello/world'
30
30
  assert last_response.ok?
31
- assert_equal %q{"Hello world"}, last_response.body
31
+ assert_equal "Hello world", last_response.body
32
32
 
33
33
  ##############################
34
34
  ## get '/hello/:name'
35
35
  get '/hello/ruby'
36
36
  assert last_response.ok?
37
- assert_equal %q{"Hello ruby"}, last_response.body
37
+ assert_equal "Hello ruby", last_response.body
38
38
 
39
39
  get '/hello/ruby?test=t' ## try w/ extra query string/params
40
40
  assert last_response.ok?
41
- assert_equal %q{"Hello ruby"}, last_response.body
41
+ assert_equal "Hello ruby", last_response.body
42
42
 
43
43
  ##################################
44
44
  ## get '/:message/:name'
45
45
  get '/servus/wien'
46
46
  assert last_response.ok?
47
- assert_equal %q{"servus wien"}, last_response.body
47
+ assert_equal "servus wien", last_response.body
48
48
 
49
49
  get '/Hallo/Welt'
50
50
  assert last_response.ok?
51
- assert_equal %q{"Hallo Welt"}, last_response.body
51
+ assert_equal "Hallo Welt", last_response.body
52
52
  end
53
53
 
54
54
 
55
55
  def test_format
56
56
  get '/key.format'
57
57
  assert last_response.ok?
58
- assert_equal %q{"key format"}, last_response.body
58
+ assert_equal "key format", last_response.body
59
59
 
60
60
  get '/ottakringer.xxx'
61
61
  assert last_response.ok?
62
- assert_equal %q{"ottakringer xxx"}, last_response.body
62
+ assert_equal "ottakringer xxx", last_response.body
63
63
 
64
64
  get '/ottakringer'
65
65
  assert last_response.ok?
66
- assert_equal %q{"ottakringer "}, last_response.body
66
+ assert_equal "ottakringer ", last_response.body
67
67
  end
68
68
 
69
69
 
@@ -85,25 +85,38 @@ CSV
85
85
  </table>
86
86
  HTML
87
87
 
88
+
89
+ countries_json = <<JSON.strip
90
+ [
91
+ {
92
+ "key": "at",
93
+ "name": "Austria"
94
+ },
95
+ {
96
+ "key": "mx",
97
+ "name": "Mexico"
98
+ }
99
+ ]
100
+ JSON
101
+
88
102
  get '/countries.json'
89
103
  assert last_response.ok?
90
- assert_equal %q<[{"key":"at","name":"Austria"},{"key":"mx","name":"Mexico"}]>, last_response.body
104
+ assert_equal countries_json, last_response.body
91
105
 
92
106
  get '/countries'
93
107
  assert last_response.ok?
94
- assert_equal %q<[{"key":"at","name":"Austria"},{"key":"mx","name":"Mexico"}]>, last_response.body
108
+ assert_equal countries_json, last_response.body
95
109
  end # method test_countries
96
110
 
97
111
 
112
+
98
113
  def test_halt
99
- ## get '/halt/404'
100
114
  get '/halt/404'
115
+ assert_equal 404, last_response.status
101
116
 
102
- ## get '/halt_error' - 500, "Error fatal" # 500 - internal server error
103
- get '/halt_error'
104
-
105
- ## todo: check error codes
106
- assert true
117
+ get '/halt_error' ## 500, "Error fatal" # 500 - internal server error
118
+ assert_equal 500, last_response.status
119
+ assert_equal "Error fatal", last_response.body
107
120
  end
108
121
 
109
122
  end # class TestApp
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webservice
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-20 00:00:00.000000000 Z
11
+ date: 2017-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logutils