webservice 0.5.0 → 0.6.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: fca9552fa4818068271c81022e7ab9a4ec277d30
4
- data.tar.gz: 23e2d0051213a5834cafff2762df4324c6907935
3
+ metadata.gz: 1cbf8b69b97842dcc85d69c5941b1df7533183d3
4
+ data.tar.gz: 21a647b38f0143f1075be6f1171b22a8f333e10b
5
5
  SHA512:
6
- metadata.gz: 6b53f3b4ea61aed918b16b5b73ad3be4e162131599d6cb1222607097d5cff3f8757222458373a2a3e30dbea91d47f32c092f6557e274bebcbdaf0ae8c121f992
7
- data.tar.gz: b32faf56e9286e1b4283d900f59f7b1af4bfd89b70366e849200c014540c8e80c45a9110a1d51239372eb414aec5f5f308149960786d6d32f254a942fd404a85
6
+ metadata.gz: 8a55ee94ae9ad07ddfe3dd1428c5e503ceaca7b9180c965f99c3891ca3952caf008b53aaf51dd18ce71f228ca5aca716377d227f112c1cbcf99c9b8caaa4fcc8
7
+ data.tar.gz: e3d677611c7240886b37380b9cd36ae546abbf561cb2ee9dbc2501eee04e3b0f888d0dd6192949d43c2fb5c3d207da2534903372c813dbe8607e82590b8ee8a0
@@ -2,8 +2,10 @@ HISTORY.md
2
2
  Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
+ assets/webservice-32x32.png
5
6
  lib/webservice.rb
6
7
  lib/webservice/base.rb
8
+ lib/webservice/response_handler.rb
7
9
  lib/webservice/version.rb
8
10
  test/helper.rb
9
11
  test/service/app.rb
@@ -24,6 +24,8 @@ require 'rack'
24
24
  require 'webservice/version' # note: let version always go first
25
25
  require 'webservice/base'
26
26
 
27
+ require 'webservice/response_handler' ## default (built-in) response handler (magic)
28
+
27
29
 
28
30
 
29
31
  module Webservice
@@ -16,8 +16,9 @@ module Webservice
16
16
  ## HTTP headers
17
17
  CONTENT_LENGTH = Rack::CONTENT_LENGTH
18
18
  CONTENT_TYPE = Rack::CONTENT_TYPE
19
-
20
- LOCATION = 'Location'.freeze # not available from Rack
19
+ # -- more HTTP headers - not available from Rack
20
+ LOCATION = 'Location'.freeze
21
+ LAST_MODIFIED = 'Last-Modified'.freeze
21
22
 
22
23
 
23
24
  module Helpers
@@ -71,6 +72,56 @@ module Helpers
71
72
  ### unknown type; do nothing - sorry; issue warning - why? why not??
72
73
  end
73
74
  end ## method content_type
75
+
76
+
77
+ ## simple send file (e.g. for images/binary blobs, etc.) helper
78
+ def send_file( path )
79
+ ## puts "send_file path=>#{path}<"
80
+
81
+ ## puts "HTTP_IF_MODIFIED_SINCE:"
82
+ ## puts request.get_header('HTTP_IF_MODIFIED_SINCE')
83
+
84
+ last_modified = File.mtime(path).httpdate
85
+ ## puts "last_modified:"
86
+ ## puts last_modified
87
+
88
+ ## HTTP 304 => Not Modified
89
+ halt 304 if request.get_header('HTTP_IF_MODIFIED_SINCE') == last_modified
90
+
91
+ headers[ LAST_MODIFIED ] = last_modified
92
+
93
+ bytes = File.open( path, 'rb' ) { |f| f.read }
94
+
95
+ ## puts "encoding:"
96
+ ## puts bytes.encoding
97
+
98
+ ## puts "size:"
99
+ ## puts bytes.size
100
+
101
+ extname = File.extname( path )
102
+ ## puts "extname:"
103
+ ## puts extname
104
+
105
+ ## puts "headers (before):"
106
+ ## pp headers
107
+
108
+ if extname == '.png'
109
+ headers[ CONTENT_TYPE ] = 'image/png'
110
+ else
111
+ ## fallback to application/octet-stream
112
+ headers[ CONTENT_TYPE ] = 'application/octet-stream'
113
+ end
114
+
115
+ headers[ CONTENT_LENGTH ] = bytes.size.to_s ## note: do NOT forget to use to_s (requires string!)
116
+
117
+ ## puts "headers (after):"
118
+ ## pp headers
119
+
120
+ halt 200, bytes
121
+ end # method send_file
122
+
123
+
124
+
74
125
  end ## module Helpers
75
126
 
76
127
 
@@ -123,6 +174,27 @@ class Base
123
174
  end
124
175
 
125
176
 
177
+ ##########################
178
+ ## support for "builtin" fallback routes
179
+ ##
180
+ ## e.g. use like
181
+ ## fallback_route GET, '/' do
182
+ ## "Hello, World!"
183
+ ## end
184
+ def fallback_route( method, pattern, &block )
185
+ puts "[debug] Webservice::Base.#{method.downcase} - add (fallback) route #{method} '#{pattern}' to #<#{self.name}:#{self.object_id}> : #{self.class.name}"
186
+
187
+ ## note: for now use the sintatra-style patterns (with mustermann)
188
+ fallback_routes[method] << [Mustermann::Sinatra.new(pattern), block]
189
+ end
190
+
191
+ def fallback_routes
192
+ ## note: !!! use @@ NOT just @ e.g.
193
+ ## routes get shared/used by all classes/subclasses
194
+ @@fallback_routes ||= Hash.new { |hash, key| hash[key]=[] }
195
+ end
196
+
197
+
126
198
  def environment
127
199
  ## include APP_ENV why? why not?
128
200
  ## todo -- cache value? why why not? (see/follow sinatara set machinery ??)
@@ -152,6 +224,9 @@ class Base
152
224
  attr_reader :params
153
225
  attr_reader :env
154
226
 
227
+ attr_reader :handler # default response_handler/respond_with handler magic
228
+
229
+
155
230
  def call( env )
156
231
  dup.call!( env )
157
232
  end
@@ -164,7 +239,9 @@ class Base
164
239
  @params = request.params
165
240
  @env = env
166
241
 
242
+ @handler = ResponseHandler.new( self ) ## for now "hard-coded"; make it a setting later - why? why not?
167
243
 
244
+ ### move cors headers to responseHandler to initialize!!!! - why? why not??
168
245
  ## (auto-)add (merge in) cors headers
169
246
  ## todo: move into a before filter ?? lets you overwrite headers - needed - why? why not??
170
247
  headers 'Access-Control-Allow-Origin' => '*',
@@ -187,7 +264,10 @@ class Base
187
264
  private
188
265
 
189
266
  def route_eval
267
+ puts " [#{self.class.name}] try matching route >#{request.request_method} #{request.path_info}<..."
268
+
190
269
  catch(:halt) do
270
+ ## pass 1
191
271
  routes = self.class.routes[ request.request_method ]
192
272
  routes.each do |pattern, block|
193
273
  ## puts "trying matching route >#{request.path_info}<..."
@@ -198,10 +278,24 @@ private
198
278
  ## todo/fix: check merge order - params overwrites url_params - why? why not??
199
279
  @params = url_params.merge( @params )
200
280
  end
201
- handle_response( instance_eval( &block ))
281
+ handler.handle_response( instance_eval( &block ))
202
282
  return
203
283
  end
204
284
  end
285
+
286
+ ## pass 2 - (builtin) fallbacks
287
+ routes = self.class.fallback_routes[ request.request_method ]
288
+ routes.each do |pattern, block|
289
+ url_params = pattern.params( request.path_info )
290
+ if url_params ## note: params returns nil if no match
291
+ if !url_params.empty? ## url_params hash NOT empty (e.g. {}) merge with req params
292
+ @params = url_params.merge( @params )
293
+ end
294
+ handler.handle_response( instance_eval( &block ))
295
+ return
296
+ end
297
+ end
298
+
205
299
  # no match found for route/request
206
300
  halt 404
207
301
  end
@@ -209,157 +303,77 @@ private
209
303
 
210
304
 
211
305
 
212
- ## todo: add as_json like opts={} why? why not?
213
- def handle_response( obj, opts={} )
214
- puts "[Webservice::Base#handle_response (#{request.path_info}) params: #{params.inspect}] - obj : #{obj.class.name}"
215
- pp obj
216
-
217
- ## "magic" param format; default to json
218
- format = params['format'] || 'json'
219
-
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 )
306
+ ##################################
307
+ ### add some fallback (builtin) routes
227
308
 
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
248
- else
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
257
-
258
- callback = params.delete( 'callback' )
309
+ fallback_route GET, '/favicon.ico' do
310
+ ## use 302 to redirect
311
+ ## note: use strg+F5 to refresh page (clear cache for favicon.ico)
312
+ redirect_to '/webservice-32x32.png'
313
+ end
259
314
 
260
- if callback
261
- content_type :js
262
- response.body = ["#{callback}(#{json})"]
263
- else
264
- content_type :json
265
- response.body = [json]
266
- end
267
- else
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]
277
- end
315
+ fallback_route GET, '/webservice-32x32.png' do
316
+ send_file "#{Webservice.root}/assets/webservice-32x32.png"
278
317
  end
279
- end # method handle_response
280
318
 
319
+ fallback_route GET, '/routes' do
320
+ msg =<<TXT
321
+ #{dump_routes}
281
322
 
323
+ #{dump_version}
324
+ TXT
325
+ end
282
326
 
283
- def generate_csv( recs )
284
- ## note: for now assumes (only works with) array of hash records e.g.:
285
- ## [
286
- ## { key: 'at', name: 'Austria', ...},
287
- ## { key: 'mx', name: 'Mexico', ...},
288
- ## ...
289
- ## ]
327
+ ## catch all (404 not found)
328
+ fallback_route GET, '/*' do
329
+ pp env
330
+ pp self.class.routes
290
331
 
291
- ## :col_sep => "\t"
292
- ## :col_sep => ";"
332
+ msg =<<TXT
333
+ 404 Not Found
293
334
 
294
- ## todo: use rec.key for headers/first row
335
+ No route matched >#{request.request_method} #{request.path_info}<:
295
336
 
296
- ## pp recs
337
+ REQUEST_METHOD: >#{request.request_method}<
338
+ PATH_INFO: >#{request.path_info}<
339
+ QUERY_STRING: >#{request.query_string}<
297
340
 
298
- CSV.generate do |csv|
299
- recs.each do |rec|
300
- csv << rec.values
301
- end
302
- end
303
- end
341
+ SCRIPT_NAME: >#{request.script_name}<
342
+ REQUEST_URI: >#{env['REQUEST_URI']}<
304
343
 
305
344
 
306
- def generate_html_table( recs )
307
- ## note: for now assumes (only works with) array of hash records e.g.:
308
- ## [
309
- ## { key: 'at', name: 'Austria', ...},
310
- ## { key: 'mx', name: 'Mexico', ...},
311
- ## ...
312
- ## ]
345
+ #{dump_routes}
313
346
 
314
- ## pp recs
347
+ #{dump_version}
348
+ TXT
315
349
 
316
- buf = ""
317
- buf << "<table>\n"
318
- recs.each do |rec|
319
- buf << " <tr>"
320
- rec.values.each do |value|
321
- buf << "<td>#{value}</td>"
322
- end
323
- buf << "</tr>\n"
324
- end
325
- buf << "</table>\n"
326
- buf
350
+ halt 404, msg
327
351
  end
328
352
 
353
+ ############################
354
+ ## fallback helpers
329
355
 
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!!!!
356
+ def dump_routes ## todo/check - rename to build_routes/show_routes/etc. - why? why not?
357
+ buf = ""
358
+ buf <<" Routes >#{self.class.name}<:\n\n"
337
359
 
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)
360
+ self.class.routes.each do |method,routes|
361
+ buf << " #{method}:\n"
362
+ routes.each do |pattern,block|
363
+ buf << " #{pattern.to_s}\n"
348
364
  end
349
365
  end
366
+ buf
350
367
  end
351
368
 
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
357
- obj.as_json_v2
358
- elsif obj.respond_to? :as_json ## try (activerecord) serializer
359
- obj.as_json
360
- else
361
- obj ## just try/use as is
362
- end
369
+ def dump_version
370
+ ## single line version string
371
+ buf = " " # note: start with two leading spaces (indent)
372
+ buf << "webservice/#{VERSION} "
373
+ buf << "(#{self.class.environment}), "
374
+ buf << "rack/#{Rack::RELEASE} (#{Rack::VERSION.join('.')}) - "
375
+ buf << "ruby/#{RUBY_VERSION} (#{RUBY_RELEASE_DATE}/#{RUBY_PLATFORM})"
376
+ buf
363
377
  end
364
378
 
365
379
  end # class Base
@@ -0,0 +1,248 @@
1
+ # encoding: utf-8
2
+
3
+ ## default (built-in) response handler
4
+
5
+ module Webservice
6
+
7
+
8
+ class ResponseHandler
9
+
10
+
11
+ def initialize( app )
12
+ @app = app
13
+ end
14
+
15
+ ## delegate request, response, params, env
16
+ ## todo/fix: use def_delegate - why? why not???
17
+ def request() @app.request; end
18
+ def response() @app.response; end
19
+ def params() @app.params; end
20
+ def env() @app.env; end
21
+
22
+ ## delegate some helpers too
23
+ def content_type( type=nil ) @app.content_type( type ); end
24
+
25
+
26
+
27
+ ## todo: add as_json like opts={} why? why not?
28
+ def handle_response( obj, opts={} )
29
+ puts "[Webservice::Handler#handle_response (#{request.path_info}) params: #{params.inspect}] - obj : #{obj.class.name}"
30
+ pp obj
31
+
32
+ ## "magic" param format; default to json
33
+ format = params['format'] || 'json'
34
+
35
+ ## note: response.body must be (expects) an array!!!
36
+ ## thus, [json] etc.
37
+
38
+ if format == 'csv' || format == 'txt' ||
39
+ format == 'html' || format == 'htm'
40
+
41
+ data = as_tabular( obj )
42
+
43
+ if data.is_a? Tabular
44
+ if format == 'csv' || format == 'txt'
45
+ content_type :txt ## use csv content type - why? why not??
46
+ response.body = [data.to_csv]
47
+ else
48
+ ## assume html
49
+ content_type :html
50
+ response.body = [data.to_html_table]
51
+ end
52
+ else
53
+ ## wrong format (expected Tabular from as_tabular - cannot convert)
54
+ ## todo: issue warning/notice about wrong format - how?
55
+ ## use different http status code - why? why not??
56
+ content_type :txt
57
+ ## todo/check: use just data.to_s for all - why? why not?
58
+ ## for now return as is (convert to string with to_s or inspect)
59
+ response.body = [data.is_a?( String ) ? data.to_s : data.inspect]
60
+ end
61
+ else ## default/assume json
62
+ data = as_json( obj )
63
+
64
+ ## note: hash or array required!!! for now for json generation
65
+ # hash => single record
66
+ # array => multiple records (that is, array of hashes)
67
+
68
+ if data.is_a?( Hash ) || data.is_a?( Array )
69
+ json = JSON.pretty_generate( data ) ## use pretty printer
70
+
71
+ callback = params.delete( 'callback' )
72
+
73
+ if callback
74
+ content_type :js
75
+ response.body = ["#{callback}(#{json})"]
76
+ else
77
+ content_type :json
78
+ response.body = [json]
79
+ end
80
+ else
81
+ ## todo/fix/check: change http status to unprocessable entity
82
+ ## or something -- why ??? why not??
83
+ ##
84
+ ## allow "standalone" number, nils, strings - why? why not?
85
+ ## for now valid json must be wrapped in array [] or hash {}
86
+ content_type :txt
87
+ ## todo/check: use just data.to_s for all - why? why not?
88
+ ## for now return as is (convert to string with to_s or inspect)
89
+ response.body = [data.is_a?( String ) ? data.to_s : data.inspect]
90
+ end
91
+ end
92
+ end # method handle_response
93
+
94
+
95
+ ##########################################
96
+ ## auto-generate/convert "magic"
97
+
98
+ def as_tabular( obj, opts={} )
99
+ headers = []
100
+ headers_clone = [] ## keep an unmodified (e.g. with symbols not string) headers/keys clone
101
+ rows = []
102
+ errors = []
103
+
104
+ if obj.respond_to? :to_a ### convert activerecord relation to array (of records)
105
+ recs = obj.to_a
106
+ elsif obj.is_a? Array
107
+ recs = obj
108
+ else
109
+ ## return as is; cannot convert
110
+ ## todo/fix: handle cannot convert different (e.g. except etc.) - why? why not??
111
+ puts "!!!! [as_tabular] sorry; can't convert <#{obj.class.name}> - Array or to_a method required"
112
+ return obj
113
+ end
114
+
115
+
116
+ recs.each do |rec|
117
+ puts "rec #{rec.class.name}"
118
+ if rec.respond_to? :as_row
119
+ row = rec.as_row
120
+ rows << row.values ## add rows as is 1:1
121
+ elsif rec.respond_to?( :as_json_v3 ) ||
122
+ rec.respond_to?( :as_json_v2 ) ||
123
+ rec.respond_to?( :as_json ) ||
124
+ rec.is_a?( Hash ) ## allow (plain) hash too - give it priority (try first?) - why? why not??
125
+
126
+ if rec.respond_to? :as_json_v3
127
+ row = rec.as_json_v3
128
+ elsif rec.respond_to? :as_json_v2
129
+ row = rec.as_json_v2
130
+ elsif rec.respond_to? :as_json
131
+ row = rec.as_json
132
+ else
133
+ row = rec ## assume it's already a hash (with key/value pairs)
134
+ end
135
+
136
+ ## convert nested values e.g. array and hash to strings
137
+ values = row.values.map do |value|
138
+ if value.is_a? Hash
139
+ ## todo: use our own "pretty printer" e.g. use unqouted strings - why? why not?
140
+ value.to_json ## use to_json "key": "value" instead of :key => "value"
141
+ elsif value.is_a? Array
142
+ ## todo: use our own "pretty printer" e.g. use unqouted strings - why? why not?
143
+ ## value = "[#{value.join('|')}]" ## use | for joins (instead of ,) - why? why not?? keep comma(,) - why? why not??
144
+ value.to_json
145
+ else
146
+ value
147
+ end
148
+ end
149
+ pp values
150
+ rows << values
151
+ else
152
+ ## todo: add record index - why? why not?
153
+ puts "sorry; can't convert - as_row or as_json method or hash required"
154
+ errors << "sorry; can't convert <#{rec.class.name}> - as_row or as_json method or hash required"
155
+ next
156
+ end
157
+
158
+ ## check headers - always must match!!!!!!!
159
+ if headers.empty?
160
+ headers_clone = row.keys
161
+ pp headers_clone
162
+ headers = headers_clone.map { |header| header.to_s }
163
+ pp headers
164
+ else
165
+ ## todo: check if headers match!!!
166
+ end
167
+ end # recs.each
168
+
169
+ if errors.empty?
170
+ Tabular.new( headers, rows )
171
+ else ## return row of errors
172
+ ## return errors as a (simple) multi-line string - why? why not??
173
+ errors.join( "\n" )
174
+ end
175
+
176
+ end # method as_tabular
177
+
178
+
179
+ def as_json( obj, opts={} )
180
+ if obj.respond_to? :as_json_v3 ## try (our own) serializer first
181
+ obj.as_json_v3
182
+ elsif obj.respond_to? :as_json_v2 ## try (our own) serializer first
183
+ obj.as_json_v2
184
+ elsif obj.respond_to? :as_json ## try (activerecord) serializer
185
+ obj.as_json
186
+ else
187
+ obj ## just try/use as is
188
+ end
189
+ end
190
+
191
+
192
+ ######
193
+ # Tabular helper/support class
194
+
195
+ class Tabular
196
+
197
+ attr_reader :headers
198
+ attr_reader :rows
199
+
200
+ def initialize( headers, rows )
201
+ @headers = headers
202
+ @rows = rows
203
+ end
204
+
205
+ def to_csv( opts={} )
206
+ ## allow changing of column/value separator (col_sep) - why? why not?
207
+ ## :col_sep => "\t"
208
+ ## :col_sep => ";"
209
+
210
+ pp self
211
+
212
+ CSV.generate( headers: true ) do |csv|
213
+ csv << headers
214
+ rows.each do |row|
215
+ csv << row
216
+ end
217
+ end
218
+ end # method to_csv
219
+
220
+
221
+ def to_html_table( opts={} )
222
+
223
+ ## todo/fix: html escape values - why? why not??
224
+
225
+ pp self
226
+
227
+ buf = ""
228
+ buf << "<table>\n"
229
+ buf << " <tr>"
230
+ headers.each do |header|
231
+ buf << "<th>#{header}</th>"
232
+ end
233
+ buf << "</tr>\n"
234
+
235
+ rows.each do |row|
236
+ buf << " <tr>"
237
+ row.each do |value|
238
+ buf << "<td>#{value}</td>"
239
+ end
240
+ buf << "</tr>\n"
241
+ end
242
+ buf << "</table>\n"
243
+ buf
244
+ end # method to_html_table
245
+ end # class Tabular
246
+
247
+ end # class ResponseHandler
248
+ end # module Webservice
@@ -4,7 +4,7 @@ module Webservice
4
4
 
5
5
  module Version
6
6
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
7
- MINOR = 5
7
+ MINOR = 6
8
8
  PATCH = 0 ## note: if not put in module will overwrite PATCH (HTTP Verb Constant)!!!
9
9
  end
10
10
 
@@ -72,6 +72,7 @@ class TestApp < MiniTest::Test
72
72
  get '/countries.csv'
73
73
  assert last_response.ok?
74
74
  assert_equal <<CSV, last_response.body
75
+ key,name
75
76
  at,Austria
76
77
  mx,Mexico
77
78
  CSV
@@ -80,32 +81,25 @@ CSV
80
81
  assert last_response.ok?
81
82
  assert_equal <<HTML, last_response.body
82
83
  <table>
84
+ <tr><th>key</th><th>name</th></tr>
83
85
  <tr><td>at</td><td>Austria</td></tr>
84
86
  <tr><td>mx</td><td>Mexico</td></tr>
85
87
  </table>
86
88
  HTML
87
89
 
88
90
 
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
91
+ countries_json = [
92
+ { 'key' => 'at', 'name' => 'Austria' },
93
+ { 'key' => 'mx', 'name' => 'Mexico' },
94
+ ]
101
95
 
102
96
  get '/countries.json'
103
97
  assert last_response.ok?
104
- assert_equal countries_json, last_response.body
98
+ assert_equal countries_json, JSON.parse( last_response.body )
105
99
 
106
100
  get '/countries'
107
101
  assert last_response.ok?
108
- assert_equal countries_json, last_response.body
102
+ assert_equal countries_json, JSON.parse( last_response.body )
109
103
  end # method test_countries
110
104
 
111
105
 
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.5.0
4
+ version: 0.6.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-26 00:00:00.000000000 Z
11
+ date: 2017-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logutils
@@ -93,8 +93,10 @@ files:
93
93
  - Manifest.txt
94
94
  - README.md
95
95
  - Rakefile
96
+ - assets/webservice-32x32.png
96
97
  - lib/webservice.rb
97
98
  - lib/webservice/base.rb
99
+ - lib/webservice/response_handler.rb
98
100
  - lib/webservice/version.rb
99
101
  - test/helper.rb
100
102
  - test/service/app.rb