webservice 0.3.1 → 0.4.0

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: 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