oaf 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTc3ZjQwMGU4N2M5MDkyZGNhN2JkY2YxNWYwN2Q3MWUyNzRkZWI4OA==
4
+ Y2M2YmMyNGY2Zjg4MmU2MWJjN2Y3ZDc0MzhjMDMxZmEyZjRkY2RkNw==
5
5
  data.tar.gz: !binary |-
6
- NTg3YWZjMTYwYjcyMmI1NDU3M2QxOGI5YmM0MjliOTcwNDk3Nzg0NQ==
6
+ ZjY4YTM5NjdiYzg3YzA0NTk4NmFjMzdiOGMzYzYwYmZjZWM0NWJkMA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MGM5M2IzOTMwZDhkNTUyZDBiMTE1YjFiYjM2Y2ExNmVhZTQwNDBjZjQxNGU5
10
- NDUzNWNmZmQwMzFmZTI0NDc3NjdlY2FhNDNlNWFhNzFhZmI5Zjc5NzkxZmE5
11
- M2U3YmE5ZWZkMjNmYTBiZThkMjRkMDE2OWVjMWU0OWVkNDk3MzY=
9
+ OWQ0NzRhMGJiYjYxNGQxNmJmNjFmZTBiMDRhODc4NmU0ZDFjNGQyZTExZDI2
10
+ ZjJhMDkyM2NkMGRhZmU0NzZmYWY2M2IwYjM1NzI2YmQ3NWJkMzc1MzJiMjk5
11
+ NzQwYmJiMjMyN2I0NzJlOWRkMDU3ZjRiYmRhZDA4MDk3NGNjYWE=
12
12
  data.tar.gz: !binary |-
13
- MGY5ODIwOGIzM2I4MGM0NDkwZTBiZWVmNjU3NTFjNjg2ZDRkYTU1MGM1ZWMy
14
- ODE3ZDA3YmY5ZGQwMmY2ODAwMTQzNWFiYWQwMTVmM2ZjNDkyYjQ0NGI5MmU4
15
- ZDZkZWFmMTRkOGEwNDY5NmYyNjVjODk3NWIwYzFlNmVhODE3ZTk=
13
+ YWVmMzQ5NWE1M2FiYzQ4YmQyM2E3YjdjMjkyOWZiMDkxMDJiMDA1NGI2YWU1
14
+ NTg0ODdlZGU0NTQ1NTljNjU5MGE2NjMwZGY0MTNjOTY4NDdkZmZiYjJmYjYx
15
+ MGVkMDZkMTg0MTlhMWYxZTUzMWI3YjMxMDJhY2M1ODM0MTdjZjM=
data/bin/oaf CHANGED
@@ -61,4 +61,4 @@ else
61
61
  exit 1
62
62
  end
63
63
 
64
- Oaf::HTTP::Server.serve options[:path], options[:port]
64
+ Oaf::HTTPServer.serve options[:path], options[:port]
data/lib/oaf.rb CHANGED
@@ -22,5 +22,5 @@
22
22
 
23
23
  require 'oaf/version'
24
24
  require 'oaf/util'
25
- require 'oaf/http/handler'
26
- require 'oaf/http/server'
25
+ require 'oaf/httphandler'
26
+ require 'oaf/httpserver'
@@ -21,16 +21,16 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
23
  require 'oaf/util'
24
- require 'oaf/http/server'
24
+ require 'oaf/httpserver'
25
25
  require 'webrick'
26
26
 
27
- module Oaf::HTTP
27
+ module Oaf
28
28
 
29
29
  # Provides all required handlers to WEBrick for serving all basic HTTP
30
30
  # methods. WEBrick handles GET, POST, HEAD, and OPTIONS out of the box,
31
31
  # but to mock most RESTful applications we are going to want PUT and
32
32
  # DELETE undoubtedly.
33
- class Handler < WEBrick::HTTPServlet::AbstractServlet
33
+ class HTTPHandler < WEBrick::HTTPServlet::AbstractServlet
34
34
 
35
35
  # Remove the predefined WEBrick methods. WEBrick comes with some defaults
36
36
  # for GET, POST, OPTIONS, and HEAD, but let's use our own instead.
@@ -64,12 +64,12 @@ module Oaf::HTTP
64
64
  def process_request req, res
65
65
  req_headers = req.header
66
66
  req_query = req.query
67
- req_body = Oaf::HTTP::Server.get_request_body req
67
+ req_body = Oaf::HTTPServer.get_request_body req
68
68
  file = Oaf::Util.get_request_file @path, req.path, req.request_method
69
69
  out = Oaf::Util.get_output(file, req.path, req_headers, req_body,
70
70
  req_query)
71
- res_headers, res_status, res_body = Oaf::HTTP::Server.parse_response out
72
- Oaf::HTTP::Server.set_response! res, res_headers, res_body, res_status
71
+ res_headers, res_status, res_body = Oaf::HTTPServer.parse_response out
72
+ Oaf::HTTPServer.set_response! res, res_headers, res_body, res_status
73
73
  end
74
74
 
75
75
  # A magic respond_to? implementation to trick WEBrick into thinking that any
@@ -0,0 +1,106 @@
1
+ # oaf - Care-free web app prototyping using files and scripts
2
+ # Copyright 2013 Ryan Uber <ru@ryanuber.com>
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'oaf/util'
24
+ require 'oaf/httphandler'
25
+ require 'webrick'
26
+
27
+ module Oaf::HTTPServer
28
+ extend self
29
+
30
+ # Given output from a script, parse HTTP response details and return them
31
+ # as a hash, including body, status, and headers.
32
+ #
33
+ # == Parameters:
34
+ # output::
35
+ # The output text data returned by a script.
36
+ #
37
+ # == Returns:
38
+ # A hash containing the HTTP response details (body, status, and headers).
39
+ #
40
+ def parse_response output
41
+ has_meta = false
42
+ headers = {'content-type' => 'text/plain'}
43
+ status = 200
44
+ headers, status, meta_size = Oaf::Util.parse_http_meta output
45
+ lines = output.split("\n")
46
+ body = lines.take(lines.length - meta_size).join("\n")+"\n"
47
+ [headers, status, body]
48
+ end
49
+
50
+ # Safely retrieves the request body, and assumes an empty string if it
51
+ # cannot be retrieved. This helps get around a nasty exception in WEBrick.
52
+ #
53
+ # == Parameters:
54
+ # req::
55
+ # A WEBrick::HTTPRequest object
56
+ #
57
+ # == Returns:
58
+ # A string containing the request body
59
+ #
60
+ def get_request_body req
61
+ if ['POST', 'PUT'].member? req.request_method
62
+ begin
63
+ result = req.body
64
+ rescue WEBrick::HTTPStatus::LengthRequired
65
+ result = '' # needs to be in rescue for coverage
66
+ end
67
+ end
68
+ result
69
+ end
70
+
71
+ # Consume HTTP response details and set them into a response object.
72
+ #
73
+ # == Parameters:
74
+ # res::
75
+ # A WEBrick::HTTPResponse object
76
+ # headers::
77
+ # A hash containing HTTP response headers
78
+ # body::
79
+ # A string containing the HTTP response body
80
+ # status::
81
+ # An integer indicating the response status
82
+ #
83
+ def set_response! res, headers, body, status
84
+ headers.each do |name, value|
85
+ res.header[name] = value
86
+ end
87
+ res.body = body
88
+ res.status = status
89
+ end
90
+
91
+ # Invokes the Webrick web server library to handle incoming requests, and
92
+ # routes them to the appropriate scripts if they exist on the filesystem.
93
+ #
94
+ # == Parameters:
95
+ # path::
96
+ # The path in which to search for files
97
+ # port::
98
+ # The TCP port to listen on
99
+ #
100
+ def serve path, port
101
+ server = WEBrick::HTTPServer.new :Port => port
102
+ server.mount '/', Oaf::HTTPHandler, path
103
+ trap 'INT' do server.shutdown end
104
+ server.start
105
+ end
106
+ end
@@ -20,285 +20,281 @@
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
- module Oaf
23
+ module Oaf::Util
24
+ extend self
24
25
 
25
- module Util
26
- extend Oaf
27
- extend self
28
-
29
- # Determines if a line of output looks anything like an HTTP header
30
- # declaration.
31
- #
32
- # == Parameters:
33
- # line::
34
- # A line of text to examine
35
- #
36
- # == Returns:
37
- # A boolean, true if it can be read as a header string, else false.
38
- #
39
- def is_http_header? line
40
- line.split(':').length == 2
41
- end
26
+ # Determines if a line of output looks anything like an HTTP header
27
+ # declaration.
28
+ #
29
+ # == Parameters:
30
+ # line::
31
+ # A line of text to examine
32
+ #
33
+ # == Returns:
34
+ # A boolean, true if it can be read as a header string, else false.
35
+ #
36
+ def is_http_header? line
37
+ line.split(':').length == 2
38
+ end
42
39
 
43
- # Retrieves a hash in the form `name` => `value` from a string that
44
- # describes an HTTP response header.
45
- #
46
- # == Parameters:
47
- # line::
48
- # A line of text to parse
49
- #
50
- # == Returns
51
- # A hash in the form `name` => `value`, or `nil`
52
- #
53
- def get_http_header line
54
- return nil if not is_http_header? line
55
- parts = line.split(':')
56
- {parts[0].strip => parts[1].strip}
57
- end
40
+ # Retrieves a hash in the form `name` => `value` from a string that
41
+ # describes an HTTP response header.
42
+ #
43
+ # == Parameters:
44
+ # line::
45
+ # A line of text to parse
46
+ #
47
+ # == Returns
48
+ # A hash in the form `name` => `value`, or `nil`
49
+ #
50
+ def get_http_header line
51
+ return nil if not is_http_header? line
52
+ parts = line.split(':')
53
+ {parts[0].strip => parts[1].strip}
54
+ end
58
55
 
59
- # Retrieves the numeric value from a line of text as an HTTP status code.
60
- #
61
- # == Parameters:
62
- # line::
63
- # A line of text to parse
64
- #
65
- # == Returns:
66
- # An integer if valid, else `nil`.
67
- #
68
- def get_http_status line
69
- is_http_status?(line) ? line.to_i : nil
70
- end
56
+ # Retrieves the numeric value from a line of text as an HTTP status code.
57
+ #
58
+ # == Parameters:
59
+ # line::
60
+ # A line of text to parse
61
+ #
62
+ # == Returns:
63
+ # An integer if valid, else `nil`.
64
+ #
65
+ def get_http_status line
66
+ is_http_status?(line) ? line.to_i : nil
67
+ end
71
68
 
72
- # Determines if an HTTP status code is valid per RFC2616
73
- #
74
- # == Parameters:
75
- # code::
76
- # A number to validate
77
- #
78
- # == Returns
79
- # A boolean, true if valid, else false.
80
- #
81
- def is_http_status? code
82
- (200..206).to_a.concat((300..307).to_a).concat((400..417).to_a) \
83
- .concat((500..505).to_a).include? code.to_i
84
- end
69
+ # Determines if an HTTP status code is valid per RFC2616
70
+ #
71
+ # == Parameters:
72
+ # code::
73
+ # A number to validate
74
+ #
75
+ # == Returns
76
+ # A boolean, true if valid, else false.
77
+ #
78
+ def is_http_status? code
79
+ (200..206).to_a.concat((300..307).to_a).concat((400..417).to_a) \
80
+ .concat((500..505).to_a).include? code.to_i
81
+ end
85
82
 
86
- # Convert various pieces of data about the request into a single hash, which
87
- # can then be passed on as environment data to a script.
88
- #
89
- # == Parameters:
90
- # req_path::
91
- # A string with the HTTP request path
92
- # headers::
93
- # A hash of request headers
94
- # params::
95
- # A hash of request parameters
96
- # body::
97
- # A string containing the request body
98
- #
99
- # == Returns:
100
- # A flat hash containing namespaced environment parameters
101
- #
102
- def prepare_environment req_path, headers, params, body
103
- result = Hash.new
104
- {'header' => headers, 'param' => params}.each do |prefix, data|
105
- data.each do |name, value|
106
- result.merge! Oaf::Util.environment_item prefix, name, value
107
- end
83
+ # Convert various pieces of data about the request into a single hash, which
84
+ # can then be passed on as environment data to a script.
85
+ #
86
+ # == Parameters:
87
+ # req_path::
88
+ # A string with the HTTP request path
89
+ # headers::
90
+ # A hash of request headers
91
+ # params::
92
+ # A hash of request parameters
93
+ # body::
94
+ # A string containing the request body
95
+ #
96
+ # == Returns:
97
+ # A flat hash containing namespaced environment parameters
98
+ #
99
+ def prepare_environment req_path, headers, params, body
100
+ result = Hash.new
101
+ {'header' => headers, 'param' => params}.each do |prefix, data|
102
+ data.each do |name, value|
103
+ result.merge! Oaf::Util.environment_item prefix, name, value
108
104
  end
109
- result.merge! Oaf::Util.environment_item 'request', 'path', req_path
110
- result.merge Oaf::Util.environment_item 'request', 'body', body
111
105
  end
106
+ result.merge! Oaf::Util.environment_item 'request', 'path', req_path
107
+ result.merge Oaf::Util.environment_item 'request', 'body', body
108
+ end
112
109
 
113
- # Prepares a key for placement in the execution environment. This includes
114
- # namespacing variables and converting characters to predictable and
115
- # easy-to-use names.
116
- #
117
- # == Parameters:
118
- # prefix::
119
- # A prefix for the key. This helps with separation.
120
- # key::
121
- # The key to sanitize
122
- #
123
- # == Returns:
124
- # A string with the prepared value
125
- #
126
- def prepare_key prefix, key
127
- "oaf_#{prefix}_#{key.gsub('-', '_').downcase}"
128
- end
110
+ # Prepares a key for placement in the execution environment. This includes
111
+ # namespacing variables and converting characters to predictable and
112
+ # easy-to-use names.
113
+ #
114
+ # == Parameters:
115
+ # prefix::
116
+ # A prefix for the key. This helps with separation.
117
+ # key::
118
+ # The key to sanitize
119
+ #
120
+ # == Returns:
121
+ # A string with the prepared value
122
+ #
123
+ def prepare_key prefix, key
124
+ "oaf_#{prefix}_#{key.gsub('-', '_').downcase}"
125
+ end
129
126
 
130
- # Formats a single environment item into a hash, which can be merged into a
131
- # collective environment mapping later on.
132
- #
133
- # == Parameters:
134
- # prefix::
135
- # The prefix for the type of item being added.
136
- # key::
137
- # The key name of the environment property
138
- # value::
139
- # The value for the environment property
140
- #
141
- # == Returns:
142
- # A hash with prepared values ready to merge into an environment hash
143
- #
144
- def environment_item prefix, key, value
145
- {Oaf::Util.prepare_key(prefix, key) => Oaf::Util.flatten(value)}
146
- end
127
+ # Formats a single environment item into a hash, which can be merged into a
128
+ # collective environment mapping later on.
129
+ #
130
+ # == Parameters:
131
+ # prefix::
132
+ # The prefix for the type of item being added.
133
+ # key::
134
+ # The key name of the environment property
135
+ # value::
136
+ # The value for the environment property
137
+ #
138
+ # == Returns:
139
+ # A hash with prepared values ready to merge into an environment hash
140
+ #
141
+ def environment_item prefix, key, value
142
+ {Oaf::Util.prepare_key(prefix, key) => Oaf::Util.flatten(value)}
143
+ end
147
144
 
148
- # Flatten a hash or array into a string. This is useful for preparing some
149
- # data for passing in via the environment, because multi-dimension data
150
- # structures are not supported for that.
151
- #
152
- # == Parameters:
153
- # data::
154
- # The data to flatten
155
- #
156
- # == Returns:
157
- # A flattened string. It will be empty if the object passed in was not
158
- # flatten-able.
159
- #
160
- def flatten data
161
- result = ''
162
- if data.kind_of? Hash
163
- data.each do |key, val|
164
- val = Oaf::Util.flatten val if not val.kind_of? String
165
- result += "#{key}#{val}"
166
- end
167
- elsif data.kind_of? Array
168
- data.each do |item|
169
- item = Oaf::Util.flatten item if not item.kind_of? String
170
- result += item
171
- end
172
- elsif data.kind_of? String
173
- result = data
174
- else
175
- result = ''
145
+ # Flatten a hash or array into a string. This is useful for preparing some
146
+ # data for passing in via the environment, because multi-dimension data
147
+ # structures are not supported for that.
148
+ #
149
+ # == Parameters:
150
+ # data::
151
+ # The data to flatten
152
+ #
153
+ # == Returns:
154
+ # A flattened string. It will be empty if the object passed in was not
155
+ # flatten-able.
156
+ #
157
+ def flatten data
158
+ result = ''
159
+ if data.kind_of? Hash
160
+ data.each do |key, val|
161
+ val = Oaf::Util.flatten val if not val.kind_of? String
162
+ result += "#{key}#{val}"
163
+ end
164
+ elsif data.kind_of? Array
165
+ data.each do |item|
166
+ item = Oaf::Util.flatten item if not item.kind_of? String
167
+ result += item
176
168
  end
177
- result
169
+ elsif data.kind_of? String
170
+ result = data
171
+ else
172
+ result = ''
178
173
  end
174
+ result
175
+ end
179
176
 
180
- # Given an array of text lines, iterate over each of them and determine if
181
- # they may be interpreted as headers or status. If they can, add them to
182
- # the result.
183
- #
184
- # == Parameters:
185
- # text::
186
- # Plain text data to examine
187
- #
188
- # == Returns:
189
- # A 3-item structure containing headers, status, and the number of lines
190
- # which the complete metadata (including the "---" delimiter) occupies.
191
- #
192
- def parse_http_meta text
193
- headers = {}
194
- status = 200
195
- size = 0
196
- parts = text.split /^---$/
197
- if parts.length > 1
198
- for part in parts.last.split "\n"
199
- if Oaf::Util.is_http_header? part
200
- headers.merge! Oaf::Util.get_http_header part
201
- elsif Oaf::Util.is_http_status? part
202
- status = Oaf::Util.get_http_status part
203
- else next
204
- end
205
- size += size == 0 ? 2 : 1 # compensate for delimiter
177
+ # Given an array of text lines, iterate over each of them and determine if
178
+ # they may be interpreted as headers or status. If they can, add them to
179
+ # the result.
180
+ #
181
+ # == Parameters:
182
+ # text::
183
+ # Plain text data to examine
184
+ #
185
+ # == Returns:
186
+ # A 3-item structure containing headers, status, and the number of lines
187
+ # which the complete metadata (including the "---" delimiter) occupies.
188
+ #
189
+ def parse_http_meta text
190
+ headers = {}
191
+ status = 200
192
+ size = 0
193
+ parts = text.split /^---$/
194
+ if parts.length > 1
195
+ for part in parts.last.split "\n"
196
+ if Oaf::Util.is_http_header? part
197
+ headers.merge! Oaf::Util.get_http_header part
198
+ elsif Oaf::Util.is_http_status? part
199
+ status = Oaf::Util.get_http_status part
200
+ else next
206
201
  end
202
+ size += size == 0 ? 2 : 1 # compensate for delimiter
207
203
  end
208
- [headers, status, size]
209
204
  end
205
+ [headers, status, size]
206
+ end
210
207
 
211
- # Return a default response string indicating that nothing could be
212
- # done and no response files were found.
213
- #
214
- # == Returns:
215
- # A string with response output for parsing.
216
- #
217
- def get_default_response
218
- "oaf: Not Found\n---\n404"
219
- end
208
+ # Return a default response string indicating that nothing could be
209
+ # done and no response files were found.
210
+ #
211
+ # == Returns:
212
+ # A string with response output for parsing.
213
+ #
214
+ def get_default_response
215
+ "oaf: Not Found\n---\n404"
216
+ end
220
217
 
221
- # Returns the path to the file to use for the request. If the provided
222
- # file path does not exist, this method will search for a file within
223
- # the same directory matching the default convention "_*_".
224
- #
225
- # == Parameters:
226
- # root::
227
- # The root path to search within.
228
- # req_path::
229
- # The HTTP request path
230
- # method::
231
- # The HTTP method of the request
232
- #
233
- # == Returns:
234
- # The path to a file to use, or `nil` if none is found.
235
- #
236
- def get_request_file root, req_path, method
237
- file = File.join root, "#{req_path}.#{method}"
238
- if not File.exist? file
239
- Dir.glob(File.join(File.dirname(file), "_*_.#{method}")).each do |f|
240
- file = f
241
- break
242
- end
218
+ # Returns the path to the file to use for the request. If the provided
219
+ # file path does not exist, this method will search for a file within
220
+ # the same directory matching the default convention "_*_".
221
+ #
222
+ # == Parameters:
223
+ # root::
224
+ # The root path to search within.
225
+ # req_path::
226
+ # The HTTP request path
227
+ # method::
228
+ # The HTTP method of the request
229
+ #
230
+ # == Returns:
231
+ # The path to a file to use, or `nil` if none is found.
232
+ #
233
+ def get_request_file root, req_path, method
234
+ file = File.join root, "#{req_path}.#{method}"
235
+ if not File.exist? file
236
+ Dir.glob(File.join(File.dirname(file), "_*_.#{method}")).each do |f|
237
+ file = f
238
+ break
243
239
  end
244
- File.exist?(file) ? file : nil
245
240
  end
241
+ File.exist?(file) ? file : nil
242
+ end
246
243
 
247
- # Fork a new process, in which we can safely modify the running environment
248
- # and run a command, sending data back to the parent process using an IO
249
- # pipe. This can be done in a single line in ruby >= 1.9, but we will do it
250
- # the hard way to maintain compatibility with older rubies.
251
- #
252
- # == Parameters:
253
- # env::
254
- # The environment data to use in the subprocess.
255
- # command::
256
- # The command to execute against the server
257
- #
258
- # == Returns:
259
- # A string of stderr concatenated to stdout.
260
- #
261
- def run_buffered env, command
262
- out, wout = IO.pipe
263
- pid = fork do
264
- out.close
265
- ENV.replace env
266
- wout.write %x(#{command} 2>&1)
267
- at_exit { exit! }
268
- end
269
- wout.close
270
- Process.wait pid
271
- out.read
244
+ # Fork a new process, in which we can safely modify the running environment
245
+ # and run a command, sending data back to the parent process using an IO
246
+ # pipe. This can be done in a single line in ruby >= 1.9, but we will do it
247
+ # the hard way to maintain compatibility with older rubies.
248
+ #
249
+ # == Parameters:
250
+ # env::
251
+ # The environment data to use in the subprocess.
252
+ # command::
253
+ # The command to execute against the server
254
+ #
255
+ # == Returns:
256
+ # A string of stderr concatenated to stdout.
257
+ #
258
+ def run_buffered env, command
259
+ out, wout = IO.pipe
260
+ pid = fork do
261
+ out.close
262
+ ENV.replace env
263
+ wout.write %x(#{command} 2>&1)
264
+ at_exit { exit! }
272
265
  end
266
+ wout.close
267
+ Process.wait pid
268
+ out.read
269
+ end
273
270
 
274
- # Executes a file, or reads its contents if it is not executable, passing
275
- # it the request headers and body as arguments, and returns the result.
276
- #
277
- # == Parameters:
278
- # req_path::
279
- # The HTTP request path
280
- # file::
281
- # The path to the file to use for output
282
- # headers::
283
- # The HTTP request headers to pass
284
- # body::
285
- # The HTTP request body to pass
286
- # params::
287
- # The HTTP request parameters to pass
288
- #
289
- # == Returns:
290
- # The result from the file, or a default result if the file is not found.
291
- #
292
- def get_output file, req_path=nil, headers=[], body=[], params=[]
293
- if file.nil?
294
- out = Oaf::Util.get_default_response
295
- elsif File.executable? file
296
- env = Oaf::Util.prepare_environment req_path, headers, params, body
297
- out = Oaf::Util.run_buffered env, file
298
- else
299
- out = File.open(file).read
300
- end
301
- out
271
+ # Executes a file, or reads its contents if it is not executable, passing
272
+ # it the request headers and body as arguments, and returns the result.
273
+ #
274
+ # == Parameters:
275
+ # req_path::
276
+ # The HTTP request path
277
+ # file::
278
+ # The path to the file to use for output
279
+ # headers::
280
+ # The HTTP request headers to pass
281
+ # body::
282
+ # The HTTP request body to pass
283
+ # params::
284
+ # The HTTP request parameters to pass
285
+ #
286
+ # == Returns:
287
+ # The result from the file, or a default result if the file is not found.
288
+ #
289
+ def get_output file, req_path=nil, headers=[], body=[], params=[]
290
+ if file.nil?
291
+ out = Oaf::Util.get_default_response
292
+ elsif File.executable? file
293
+ env = Oaf::Util.prepare_environment req_path, headers, params, body
294
+ out = Oaf::Util.run_buffered env, file
295
+ else
296
+ out = File.open(file).read
302
297
  end
298
+ out
303
299
  end
304
300
  end
@@ -21,5 +21,5 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
23
  module Oaf
24
- VERSION = '0.3.0'
24
+ VERSION = '0.3.1'
25
25
  end
@@ -25,7 +25,7 @@ require 'spec_helper'
25
25
  module Oaf
26
26
  describe "Returning HTTP Responses" do
27
27
  it "should return safe defaults if output is empty" do
28
- headers, status, body = Oaf::HTTP::Server.parse_response ''
28
+ headers, status, body = Oaf::HTTPServer.parse_response ''
29
29
  headers.should eq({})
30
30
  status.should eq(200)
31
31
  body.should eq("\n")
@@ -33,7 +33,7 @@ module Oaf
33
33
 
34
34
  it "should return safe defaults when only body is present" do
35
35
  text = "This is a test\n"
36
- headers, status, body = Oaf::HTTP::Server.parse_response text
36
+ headers, status, body = Oaf::HTTPServer.parse_response text
37
37
  headers.should eq({})
38
38
  status.should eq(200)
39
39
  body.should eq("This is a test\n")
@@ -41,25 +41,25 @@ module Oaf
41
41
 
42
42
  it "should return headers correctly" do
43
43
  text = "---\nx-powered-by: oaf"
44
- headers, status, body = Oaf::HTTP::Server.parse_response text
44
+ headers, status, body = Oaf::HTTPServer.parse_response text
45
45
  headers.should eq({'x-powered-by' => 'oaf'})
46
46
  end
47
47
 
48
48
  it "should return status correctly" do
49
49
  text = "---\n201"
50
- headers, status, body = Oaf::HTTP::Server.parse_response text
50
+ headers, status, body = Oaf::HTTPServer.parse_response text
51
51
  status.should eq(201)
52
52
  end
53
53
 
54
54
  it "should return body correctly" do
55
55
  text = "This is a test\n---\n200"
56
- headers, status, body = Oaf::HTTP::Server.parse_response text
56
+ headers, status, body = Oaf::HTTPServer.parse_response text
57
57
  body.should eq("This is a test\n")
58
58
  end
59
59
 
60
60
  it "should return body correctly when no metadata is present" do
61
61
  text = "This is a test"
62
- headers, status, body = Oaf::HTTP::Server.parse_response text
62
+ headers, status, body = Oaf::HTTPServer.parse_response text
63
63
  body.should eq("This is a test\n")
64
64
  end
65
65
  end
@@ -90,15 +90,15 @@ module Oaf
90
90
  @webrick.should_receive(:start).once.and_return(true)
91
91
  WEBrick::HTTPServer.stub(:new).and_return(@webrick)
92
92
  @webrick.should_receive(:mount) \
93
- .with('/', Oaf::HTTP::Handler, '/tmp').once \
93
+ .with('/', Oaf::HTTPHandler, '/tmp').once \
94
94
  .and_return(true)
95
- Oaf::HTTP::Server.serve '/tmp', 9000
95
+ Oaf::HTTPServer.serve '/tmp', 9000
96
96
  end
97
97
 
98
98
  it "should parse the request properly" do
99
99
  req = Oaf::FakeReq.new :path => @f1request
100
100
  res = Oaf::FakeRes.new
101
- handler = Oaf::HTTP::Handler.new Oaf::FakeServlet.new, @tempdir1
101
+ handler = Oaf::HTTPHandler.new Oaf::FakeServlet.new, @tempdir1
102
102
  handler.process_request req, res
103
103
  res.body.should eq("This is a test.\n")
104
104
  res.status.should eq(201)
@@ -108,7 +108,7 @@ module Oaf
108
108
  it "should accept containable methods properly" do
109
109
  req = Oaf::FakeReq.new({:path => @f2request, :method => 'PUT'})
110
110
  res = Oaf::FakeRes.new
111
- handler = Oaf::HTTP::Handler.new Oaf::FakeServlet.new, @tempdir1
111
+ handler = Oaf::HTTPHandler.new Oaf::FakeServlet.new, @tempdir1
112
112
  handler.process_request req, res
113
113
  res.body.should eq("Containable Test\n")
114
114
  res.status.should eq(202)
@@ -118,8 +118,8 @@ module Oaf
118
118
  it "should respond to any HTTP method" do
119
119
  req = Oaf::FakeReq.new :path => @f1request
120
120
  res = Oaf::FakeRes.new
121
- Oaf::HTTP::Handler.any_instance.stub(:process_request).and_return(true)
122
- handler = Oaf::HTTP::Handler.new Oaf::FakeServlet.new, @tempdir1
121
+ Oaf::HTTPHandler.any_instance.stub(:process_request).and_return(true)
122
+ handler = Oaf::HTTPHandler.new Oaf::FakeServlet.new, @tempdir1
123
123
  handler.should_receive(:process_request).with(req, res).once
124
124
  handler.respond_to?(:do_GET).should be_true
125
125
  handler.respond_to?(:do_get).should be_false
@@ -130,8 +130,8 @@ module Oaf
130
130
  it "should call our custom methods for built-ins" do
131
131
  req = Oaf::FakeReq.new :path => @f1request
132
132
  res = Oaf::FakeRes.new
133
- Oaf::HTTP::Handler.any_instance.stub(:process_request).and_return(true)
134
- handler = Oaf::HTTP::Handler.new Oaf::FakeServlet.new, @tempdir1
133
+ Oaf::HTTPHandler.any_instance.stub(:process_request).and_return(true)
134
+ handler = Oaf::HTTPHandler.new Oaf::FakeServlet.new, @tempdir1
135
135
  handler.should_receive(:process_request).with(req, res).exactly(4).times
136
136
  handler.do_GET(req, res)
137
137
  handler.do_POST(req, res)
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oaf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Uber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-09-01 00:00:00.000000000 Z
11
+ date: 2013-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ! '>='
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ! '>='
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rake
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -81,25 +67,18 @@ dependencies:
81
67
  - !ruby/object:Gem::Version
82
68
  version: '0'
83
69
  description: Care-free web app prototyping using files and scripts
84
- email:
85
- - ru@ryanuber.com
70
+ email: ru@ryanuber.com
86
71
  executables:
87
72
  - oaf
88
73
  extensions: []
89
74
  extra_rdoc_files: []
90
75
  files:
91
- - .travis.yml
92
- - Gemfile
93
- - LICENSE
94
- - README.md
95
- - Rakefile
96
76
  - bin/oaf
97
- - lib/oaf.rb
98
- - lib/oaf/http/handler.rb
99
- - lib/oaf/http/server.rb
77
+ - lib/oaf/httphandler.rb
78
+ - lib/oaf/httpserver.rb
100
79
  - lib/oaf/util.rb
101
80
  - lib/oaf/version.rb
102
- - oaf.gemspec
81
+ - lib/oaf.rb
103
82
  - spec/oaf/http_spec.rb
104
83
  - spec/oaf/util_spec.rb
105
84
  - spec/spec_helper.rb
@@ -1,8 +0,0 @@
1
- branches:
2
- only:
3
- - master
4
- language: ruby
5
- rvm:
6
- - 1.9.3
7
- - 1.9.2
8
- - 1.8.7
data/Gemfile DELETED
@@ -1,2 +0,0 @@
1
- source "https://rubygems.org"
2
- gemspec
data/LICENSE DELETED
@@ -1,20 +0,0 @@
1
- MIT LICENSE
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md DELETED
@@ -1,150 +0,0 @@
1
- Oaf
2
- ---
3
-
4
- Care-free web app prototyping using files and scripts
5
-
6
- [![Gem Version](https://badge.fury.io/rb/oaf.png)](http://badge.fury.io/rb/oaf)
7
- [![Build Status](https://travis-ci.org/ryanuber/oaf.png)](https://travis-ci.org/ryanuber/oaf)
8
- [![Coverage Status](https://coveralls.io/repos/ryanuber/oaf/badge.png)](https://coveralls.io/r/ryanuber/oaf)
9
- [![Code Climate](https://codeclimate.com/github/ryanuber/oaf.png)](https://codeclimate.com/github/ryanuber/oaf)
10
-
11
- [Documentation](http://rubydoc.info/gems/oaf/frames)
12
-
13
- `Oaf` provides stupid-easy way of creating dynamic web applications by setting
14
- all best practices and security considerations aside until you are sure that you
15
- want to invest your time doing so.
16
-
17
- `Oaf` was created as a weekend project to create a small, simple HTTP server
18
- program that uses script execution as its primary mechanism for generating
19
- responses.
20
-
21
- Example
22
- -------
23
-
24
- Create an executable file named `hello.GET`:
25
-
26
- ```
27
- #!/bin/bash
28
- echo "Hello, ${USER}!"
29
- ```
30
-
31
- Start the server by running the `oaf` command, then make a request:
32
-
33
- ```
34
- $ curl localhost:9000/hello
35
- Hello, ryanuber!
36
- ```
37
-
38
- Installation
39
- ------------
40
-
41
- Oaf is available on [rubygems](http://rubygems.org/gems/oaf), which means you
42
- can do:
43
-
44
- ```
45
- gem install oaf
46
- ```
47
-
48
- ### Accepted files
49
-
50
- `Oaf` will run *ANY* file with the executable bit set, be it shell, Python, Ruby,
51
- compiled binary, or whatever else you might have.
52
-
53
- `Oaf` can also use plain text files.
54
-
55
- ### How file permissions affect output
56
-
57
- * If the file in your request is executable, the output of its execution will
58
- be used as the return data.
59
- * If the file is *NOT* executable, then the contents of the file will be used
60
- as the return data.
61
-
62
- ### Nested methods
63
-
64
- You can create nested methods using simple directories. Example:
65
-
66
- ```
67
- $ ls ./examples/
68
- hello.GET
69
-
70
- $ curl http://localhost:8000/examples/hello
71
- Hello, world!
72
- ```
73
-
74
- ### HTTP Methods
75
- Files must carry the extension of the HTTP method used to invoke them.
76
- Oaf should support any HTTP method, including custom methods.
77
-
78
- ### Headers and Status
79
- You can indicate HTTP headers and status using stdout from your script.
80
-
81
- ```
82
- #!/bin/bash
83
- cat <<EOF
84
- Hello, world!
85
- ---
86
- content-type: text/plain
87
- 200
88
- EOF
89
- ```
90
-
91
- Separated by 3 dashes on a line of their own (`---`), the very last block
92
- of output can contain headers and response status.
93
-
94
- ### Getting request headers, query parameters, and body
95
-
96
- Headers, query parameters, and request body are all passed to executables using
97
- the environment. To defeat overlap in variables, they are namespaced using a
98
- prefix. The environment variables are as follows:
99
-
100
- * Headers: `oaf_header_*`
101
- * Request parameters: `oaf_param_*`
102
- * Request body: `oaf_request_body`
103
- * Request path: `oaf_request_path`
104
-
105
- Below is a quick example of a shell script which makes use of the request data:
106
-
107
- ```
108
- #!/bin/bash
109
- echo "You passed the Accept header: $oaf_header_accept"
110
- echo "You passed the 'message' value: $oaf_param_message"
111
- echo "You passed the request body: $oaf_request_body"
112
- echo "This method is located at: $oaf_request_path"
113
- ```
114
-
115
- Headers query parameter names are converted to all-lowercase, and dashes are
116
- replaced with underscores. This is due to the way the environment works. For
117
- example, if you wanted to get at the `Content-Type` header, you could with the
118
- environment variable `$oaf_header_content_type`.
119
-
120
- ### Catch-all methods
121
-
122
- Catch-all's can be defined by naming a file inside of a directory, beginning and
123
- ending with underscores (`_`). So for example, `test/_default_.GET` will match:
124
- `GET /test/anything`, `GET /test/blah`, etc.
125
-
126
- If you want to define a top-level method for `/test`, you would do so in the
127
- file at `/test.GET`.
128
-
129
- Q&A
130
- ---
131
-
132
- **Why are the headers and status at the bottom of the response?**
133
- Because it is much easier to echo these last. Since Oaf reads response
134
- data directly from stdout/stderr, it is very easy for debug or error messages
135
- to interfere with them. By specifying the headers and status last, we minimize
136
- the chances of unexpected output damaging the response, as all processing is
137
- already complete.
138
-
139
- **Why the name `Oaf`?**
140
- It's a bit of a clumsy and "oafish" approach at web application prototyping. I
141
- constantly find myself trying to write server parts of programs before I have
142
- even completed basic functionality, and sometimes even before I have a clear
143
- idea of what it is I want the program to do.
144
-
145
- Acknowledgements
146
- ----------------
147
-
148
- `Oaf` is inspired by [Stubb](https://github.com/knuton/stubb). A number of ideas
149
- and conventions were borrowed from it. Kudos to
150
- [@knuton](https://github.com/knuton) for having a great idea.
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require 'bundler/gem_tasks'
2
- require 'rspec/core/rake_task'
3
-
4
- RSpec::Core::RakeTask.new
5
-
6
- task :default => [:spec]
@@ -1,110 +0,0 @@
1
- # oaf - Care-free web app prototyping using files and scripts
2
- # Copyright 2013 Ryan Uber <ru@ryanuber.com>
3
- #
4
- # Permission is hereby granted, free of charge, to any person obtaining
5
- # a copy of this software and associated documentation files (the
6
- # "Software"), to deal in the Software without restriction, including
7
- # without limitation the rights to use, copy, modify, merge, publish,
8
- # distribute, sublicense, and/or sell copies of the Software, and to
9
- # permit persons to whom the Software is furnished to do so, subject to
10
- # the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be
13
- # included in all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
-
23
- require 'oaf/util'
24
- require 'oaf/http/handler'
25
- require 'webrick'
26
-
27
- module Oaf::HTTP
28
-
29
- module Server
30
- extend Oaf
31
- extend self
32
-
33
- # Given output from a script, parse HTTP response details and return them
34
- # as a hash, including body, status, and headers.
35
- #
36
- # == Parameters:
37
- # output::
38
- # The output text data returned by a script.
39
- #
40
- # == Returns:
41
- # A hash containing the HTTP response details (body, status, and headers).
42
- #
43
- def parse_response output
44
- has_meta = false
45
- headers = {'content-type' => 'text/plain'}
46
- status = 200
47
- headers, status, meta_size = Oaf::Util.parse_http_meta output
48
- lines = output.split("\n")
49
- body = lines.take(lines.length - meta_size).join("\n")+"\n"
50
- [headers, status, body]
51
- end
52
-
53
- # Safely retrieves the request body, and assumes an empty string if it
54
- # cannot be retrieved. This helps get around a nasty exception in WEBrick.
55
- #
56
- # == Parameters:
57
- # req::
58
- # A WEBrick::HTTPRequest object
59
- #
60
- # == Returns:
61
- # A string containing the request body
62
- #
63
- def get_request_body req
64
- if ['POST', 'PUT'].member? req.request_method
65
- begin
66
- result = req.body
67
- rescue WEBrick::HTTPStatus::LengthRequired
68
- result = '' # needs to be in rescue for coverage
69
- end
70
- end
71
- result
72
- end
73
-
74
- # Consume HTTP response details and set them into a response object.
75
- #
76
- # == Parameters:
77
- # res::
78
- # A WEBrick::HTTPResponse object
79
- # headers::
80
- # A hash containing HTTP response headers
81
- # body::
82
- # A string containing the HTTP response body
83
- # status::
84
- # An integer indicating the response status
85
- #
86
- def set_response! res, headers, body, status
87
- headers.each do |name, value|
88
- res.header[name] = value
89
- end
90
- res.body = body
91
- res.status = status
92
- end
93
-
94
- # Invokes the Webrick web server library to handle incoming requests, and
95
- # routes them to the appropriate scripts if they exist on the filesystem.
96
- #
97
- # == Parameters:
98
- # path::
99
- # The path in which to search for files
100
- # port::
101
- # The TCP port to listen on
102
- #
103
- def serve path, port
104
- server = WEBrick::HTTPServer.new :Port => port
105
- server.mount '/', Oaf::HTTP::Handler, path
106
- trap 'INT' do server.shutdown end
107
- server.start
108
- end
109
- end
110
- end
@@ -1,27 +0,0 @@
1
- lib = File.expand_path("../lib", __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'oaf/version'
4
-
5
- Gem::Specification.new do |s|
6
- s.name = 'oaf'
7
- s.version = Oaf::VERSION
8
- s.summary = 'Web app prototyping'
9
- s.description = 'Care-free web app prototyping using files and scripts'
10
- s.authors = ["Ryan Uber"]
11
- s.email = ['ru@ryanuber.com']
12
- s.files = %x(git ls-files).split($/)
13
- s.homepage = 'https://github.com/ryanuber/oaf'
14
- s.license = 'MIT'
15
- s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- s.test_files = s.files.grep(%r{^spec/})
17
- s.require_paths = ['lib']
18
-
19
- s.required_ruby_version = '>= 1.8'
20
-
21
- s.add_runtime_dependency 'bundler'
22
-
23
- s.add_development_dependency 'rake'
24
- s.add_development_dependency 'rspec'
25
- s.add_development_dependency 'simplecov'
26
- s.add_development_dependency 'coveralls'
27
- end