webservice 0.3.1 → 0.4.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: 64db4f0483ecfe48d4e3cfd610da49d8a9d8f855
4
- data.tar.gz: d4d0db3429872b4eb0a19075431cb6a4876a8ac4
3
+ metadata.gz: b43248423b9aeba87a88f63b6a708167bbf7d723
4
+ data.tar.gz: 35b9cd86f34c708a4d55f7f61a08a878f4e19d12
5
5
  SHA512:
6
- metadata.gz: 3d62d10f13fead1c48ed70b2ff03cadfd185046446f0478cc9ddb5b4da7ac03a68dddbfe418bfde039279a43fec946c5dba692996857b020de5fb186fc6ba5ca
7
- data.tar.gz: f89d934fcc0efc22e3a3a9ae86ff3f07fb0e26fb8f606e178e7b6aabdcbf1a50a4c6c8c1e8f2acc529133560d8a5a9ac218403ef50fc1945ac31a4fe252ac68f
6
+ metadata.gz: def6d86899d29ca10acb0c70af15de8887005fc47dc9f1d5586103eb98033d7e68d5c44744c51b30493cbcef344a565f9dbcc21c6681aa55257e42c772c0f307
7
+ data.tar.gz: 2417ece5303df96025515574a343e2b21eaa1e08015ff7cfe11822a9d9d757c918a798e149d1d03f24b74a7574e0ef6e4625778b6b63b6b42fa0f403c31adb19
data/lib/webservice.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## stdlib
4
4
  require 'json'
5
+ require 'csv'
5
6
  require 'pp'
6
7
 
7
8
 
@@ -3,21 +3,85 @@
3
3
 
4
4
  module Webservice
5
5
 
6
- class Base
7
6
 
8
- HTTP_VERBS = %w(GET POST PATCH PUT DELETE HEAD OPTIONS)
7
+ module Helpers
8
+ ## add some more helpers
9
+ ## "inspired" by sinatra (mostly) - for staying compatible
10
+ ## see https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb
11
+
12
+ ## todo -- add redirect/redirect_to
13
+ ## add status -- why? why not??
14
+
15
+ # 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
+
20
+ response.body = body unless body.nil?
21
+ halt code
22
+ end
23
+
24
+ # Halt processing and return a 404 Not Found.
25
+ def not_found(body=nil)
26
+ error 404, body
27
+ end
28
+
29
+ # Set multiple response headers with Hash.
30
+ def headers(hash=nil)
31
+ response.headers.merge! hash if hash
32
+ response.headers
33
+ end
34
+
35
+
36
+
37
+ ## (simple) content_type helper - all "hard-coded" for now; always uses utf-8 too
38
+ def content_type( type=nil )
39
+ return response['Content-Type'] unless type
40
+
41
+ if type.to_sym == :json
42
+ response['Content-Type'] = 'application/json; charset=utf-8'
43
+ elsif type.to_sym == :js || type.to_sym == :javascript
44
+ response['Content-Type'] = 'application/javascript; charset=utf-8'
45
+ ## use 'text/javascript; charset=utf-8' -- why? why not??
46
+ ## note: ietf recommends application/javascript
47
+ elsif type.to_sym == :csv || type.to_sym == :text || type.to_sym == :txt
48
+ response['Content-Type'] = 'text/plain; charset=utf-8'
49
+ elsif type.to_sym == :html || type.to_sym == :htm
50
+ response['Content-Type'] = 'text/html; charset=utf-8'
51
+ else
52
+ ### unknown type; do nothing - sorry; issue warning - why? why not??
53
+ end
54
+ end ## method content_type
55
+ end ## module Helpers
9
56
 
10
57
 
58
+ class Base
59
+ include Helpers
60
+
11
61
  class << self
12
62
 
13
63
  ## todo/check: all verbs needed! (supported) - why, why not??
14
- HTTP_VERBS.each do |method|
15
- define_method( method.downcase ) do |pattern, &block|
16
- puts "[debug] Webservice::Base.#{method.downcase} - add route #{method} '#{pattern}' to #<#{self.name}:#{self.object_id}> : #{self.class.name}"
64
+ ## e.g. add LINK, UNLINK ??
17
65
 
18
- ## note: for now use the sintatra-style patterns (with mustermann)
19
- routes[method] << [Mustermann::Sinatra.new(pattern), block]
20
- end
66
+ # Note: for now defining a `GET` handler also automatically defines
67
+ # a `HEAD` handler (follows sinatra convention)
68
+ def get( pattern, &block)
69
+ route( 'GET', pattern, &block )
70
+ route( 'HEAD', pattern, &block )
71
+ end
72
+
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
79
+
80
+ def route( method, pattern, &block )
81
+ puts "[debug] Webservice::Base.#{method.downcase} - add route #{method} '#{pattern}' to #<#{self.name}:#{self.object_id}> : #{self.class.name}"
82
+
83
+ ## note: for now use the sintatra-style patterns (with mustermann)
84
+ routes[method] << [Mustermann::Sinatra.new(pattern), block]
21
85
  end
22
86
 
23
87
 
@@ -26,6 +90,17 @@ class Base
26
90
  end
27
91
 
28
92
 
93
+ def environment
94
+ ## include APP_ENV why? why not?
95
+ ## todo -- cache value? why why not? (see/follow sinatara set machinery ??)
96
+ (ENV['RACK_ENV'] || :development).to_sym
97
+ end
98
+
99
+ def development?() environment == :development; end
100
+ def production?() environment == :production; end
101
+ def test?() environment == :test; end
102
+
103
+
29
104
  ## convenience method
30
105
  def run!
31
106
  puts "[debug] Webservice::Base.run! - self = #<#{self.name}:#{self.object_id}> : #{self.class.name}" # note: assumes self is class
@@ -39,7 +114,6 @@ class Base
39
114
  end ## class << self
40
115
 
41
116
 
42
-
43
117
  attr_reader :request
44
118
  attr_reader :response
45
119
  attr_reader :params
@@ -67,7 +141,6 @@ class Base
67
141
  throw :halt, response
68
142
  end
69
143
 
70
-
71
144
  private
72
145
 
73
146
  def route_eval
@@ -78,6 +151,7 @@ private
78
151
  if url_params ## note: params returns nil if no match
79
152
  ## puts " BINGO! url_params: #{url_params.inspect}"
80
153
  if !url_params.empty? ## url_params hash NOT empty (e.g. {}) merge with req params
154
+ ## todo/fix: check merge order - params overwrites url_params - why? why not??
81
155
  @params = url_params.merge( params )
82
156
  end
83
157
  handle_response( instance_eval( &block ))
@@ -89,20 +163,97 @@ private
89
163
  end
90
164
 
91
165
 
166
+
167
+
92
168
  def handle_response( obj )
93
- puts "[Webservice::Base#handle_response] - obj : #{obj.class.name}"
169
+ puts "[Webservice::Base#handle_response (#{request.path_info}) params: #{params.inspect}] - obj : #{obj.class.name}"
170
+ pp obj
94
171
 
95
- ### todo/fix: set content type to json
172
+ ## "magic" param format; default to json
173
+ format = params['format'] || 'json'
96
174
 
97
- if obj.respond_to?( :as_json_v2 )
98
- response.write obj.as_json_v2
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
99
183
  else
100
- ## just try/use to_json
101
- response.write obj.to_json
184
+ json = generate_json( obj )
185
+
186
+ callback = params.delete('callback')
187
+
188
+ if callback
189
+ content_type :js
190
+ response.write "#{callback}(#{json})"
191
+ else
192
+ content_type :json
193
+ response.write json
194
+ end
195
+ end
196
+ end # method handle_response
197
+
198
+
199
+ def generate_csv( recs )
200
+ ## note: for now assumes (only works with) array of hash records e.g.:
201
+ ## [
202
+ ## { key: 'at', name: 'Austria', ...},
203
+ ## { key: 'mx', name: 'Mexico', ...},
204
+ ## ...
205
+ ## ]
206
+
207
+ ## :col_sep => "\t"
208
+ ## :col_sep => ";"
209
+
210
+ ## todo: use rec.key for headers/first row
211
+
212
+ ## pp recs
213
+
214
+ CSV.generate do |csv|
215
+ recs.each do |rec|
216
+ csv << rec.values
217
+ end
102
218
  end
103
219
  end
104
220
 
105
221
 
222
+ def generate_html_table( recs )
223
+ ## note: for now assumes (only works with) array of hash records e.g.:
224
+ ## [
225
+ ## { key: 'at', name: 'Austria', ...},
226
+ ## { key: 'mx', name: 'Mexico', ...},
227
+ ## ...
228
+ ## ]
229
+
230
+ ## pp recs
231
+
232
+ buf = ""
233
+ buf << "<table>\n"
234
+ recs.each do |rec|
235
+ buf << " <tr>"
236
+ rec.values.each do |value|
237
+ buf << "<td>#{value}</td>"
238
+ end
239
+ buf << "</tr>\n"
240
+ end
241
+ buf << "</table>\n"
242
+ buf
243
+ end
244
+
245
+
246
+ def generate_json( obj )
247
+ if obj.respond_to? :as_json_v2 ## try (our own) serializer first
248
+ obj.as_json_v2
249
+ elsif obj.respond_to? :as_json ## try (activerecord) serializer
250
+ obj.as_json_v2
251
+ else
252
+ ## just try/use to_json
253
+ obj.to_json
254
+ end
255
+ end
256
+
106
257
  end # class Base
107
258
 
108
259
  end # module Webservice
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Webservice
4
4
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
5
- MINOR = 3
6
- PATCH = 1
5
+ MINOR = 4
6
+ PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
data/test/service/app.rb CHANGED
@@ -22,6 +22,14 @@ get '/halt_error' do
22
22
  end
23
23
 
24
24
 
25
+ get '/countries(.:format)?' do
26
+ ## array of hash records
27
+ [ { key: 'at', name: 'Austria' },
28
+ { key: 'mx', name: 'Mexico' } ]
29
+ end
30
+
31
+
32
+
25
33
  get '/:message/:name' do
26
34
  message = params['message']
27
35
  name = params['name']
data/test/test_app.rb CHANGED
@@ -57,9 +57,9 @@ class TestApp < MiniTest::Test
57
57
  assert last_response.ok?
58
58
  assert_equal %q{"key format"}, last_response.body
59
59
 
60
- get '/ottakringer.csv'
60
+ get '/ottakringer.xxx'
61
61
  assert last_response.ok?
62
- assert_equal %q{"ottakringer csv"}, last_response.body
62
+ assert_equal %q{"ottakringer xxx"}, last_response.body
63
63
 
64
64
  get '/ottakringer'
65
65
  assert last_response.ok?
@@ -67,6 +67,34 @@ class TestApp < MiniTest::Test
67
67
  end
68
68
 
69
69
 
70
+ def test_countries
71
+
72
+ get '/countries.csv'
73
+ assert last_response.ok?
74
+ assert_equal <<CSV, last_response.body
75
+ at,Austria
76
+ mx,Mexico
77
+ CSV
78
+
79
+ get '/countries.html'
80
+ assert last_response.ok?
81
+ assert_equal <<HTML, last_response.body
82
+ <table>
83
+ <tr><td>at</td><td>Austria</td></tr>
84
+ <tr><td>mx</td><td>Mexico</td></tr>
85
+ </table>
86
+ HTML
87
+
88
+ get '/countries.json'
89
+ assert last_response.ok?
90
+ assert_equal %q<[{"key":"at","name":"Austria"},{"key":"mx","name":"Mexico"}]>, last_response.body
91
+
92
+ get '/countries'
93
+ assert last_response.ok?
94
+ assert_equal %q<[{"key":"at","name":"Austria"},{"key":"mx","name":"Mexico"}]>, last_response.body
95
+ end # method test_countries
96
+
97
+
70
98
  def test_halt
71
99
  ## get '/halt/404'
72
100
  get '/halt/404'
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.3.1
4
+ version: 0.4.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-19 00:00:00.000000000 Z
11
+ date: 2017-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logutils