oaf 0.1.17 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -55,8 +55,7 @@ Hello, world!
55
55
  ```
56
56
 
57
57
  ### HTTP Methods
58
- Files must carry the extension of the HTTP method used to invoke them. Some
59
- examples: `hello.GET`, `hello.POST`, `hello.PUT`, `hello.DELETE`
58
+ Files must carry the extension of the HTTP method used to invoke them.
60
59
 
61
60
  ### Headers and Status
62
61
  You can indicate HTTP headers and status using stdout from your script.
@@ -71,12 +70,36 @@ content-type: text/plain
71
70
  EOF
72
71
  ```
73
72
 
74
- Separated by 3 dashes on a line of their own (`\n---\n`), the very last block
73
+ Separated by 3 dashes on a line of their own (`---`), the very last block
75
74
  of output can contain headers and response status.
76
75
 
77
- ### Getting request headers and body
78
- The headers and body of the HTTP request will be passed to the script as
79
- arguments. The headers will be passed as $1, and the body as $2.
76
+ ### Getting request headers, query parameters, and body
77
+ Headers, query parameters, and request body are all passed to executables using
78
+ the environment. To defeat overlap in variables, they are namespaced using a
79
+ prefix. The prefixes for environment variables are as follows:
80
+
81
+ * Headers: `oaf_header_`
82
+ * Query parameters: `oaf_query_`
83
+ * Request body: `oaf_request_body`
84
+
85
+ Below is a quick example of a shell script which makes use of the request data:
86
+ ```bash
87
+ #!/bin/bash
88
+ if [ -n "$oaf_header_accept" ]; then
89
+ echo "You passed the Accept header: $oaf_header_accept"
90
+ fi
91
+ if [ -n "$oaf_query_myparam" ]; then
92
+ echo "You passed the 'myparam' value: $oaf_query_myparam"
93
+ fi
94
+ if [ -n "$oaf_request_body" ]; then
95
+ echo "You passed the request body: $oaf_request_body"
96
+ fi
97
+ ```
98
+
99
+ Headers query parameter names are converted to all-lowercase, and dashes are
100
+ replaced with underscores. This is due to the way the environment works. For
101
+ example, if you wanted to get at the `Content-Type` header, you could with the
102
+ environment variable `$oaf_header_content_type`.
80
103
 
81
104
  ### Catch-all methods
82
105
  Catch-all's can be defined by naming a file inside of a directory, beginning and
data/lib/oaf/http.rb CHANGED
@@ -54,7 +54,6 @@ module Oaf
54
54
  # == Parameters:
55
55
  # path::
56
56
  # The path in which to search for files
57
- #
58
57
  # port::
59
58
  # The TCP port to listen on
60
59
  #
@@ -62,24 +61,23 @@ module Oaf
62
61
  server = WEBrick::HTTPServer.new :Port => port
63
62
  server.mount_proc '/' do |req, res|
64
63
  req_body = ''
65
- req_headers = Oaf::Util.format_hash req.header
64
+ req_headers = req.header
65
+ req_query = req.query
66
66
  if ['POST', 'PUT'].member? req.request_method
67
67
  begin
68
68
  req_body = req.body
69
69
  rescue WEBrick::HTTPStatus::LengthRequired
70
70
  true # without this, coverage is not collected.
71
71
  end
72
- else
73
- req_body = req.query
74
72
  end
75
73
  file = Oaf::Util.get_request_file path, req.path, req.request_method
76
- out = Oaf::Util.get_output file, req_headers, req_body
77
- headers, status, body = Oaf::HTTP.parse_response out
78
- headers.each do |name, value|
74
+ out = Oaf::Util.get_output file, req_headers, req_body, req_query
75
+ res_headers, res_status, res_body = Oaf::HTTP.parse_response out
76
+ res_headers.each do |name, value|
79
77
  res.header[name] = value
80
78
  end
81
- res.status = status
82
- res.body = body
79
+ res.status = res_status
80
+ res.body = res_body
83
81
  end
84
82
  trap 'INT' do server.shutdown end
85
83
  server.start
data/lib/oaf/util.rb CHANGED
@@ -21,6 +21,7 @@
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
23
  require 'open3'
24
+ require 'json'
24
25
 
25
26
  module Oaf
26
27
 
@@ -85,23 +86,79 @@ module Oaf
85
86
  .concat((500..505).to_a).include? code.to_i
86
87
  end
87
88
 
88
- # Format a hash in preparation for passing it to an executable program as
89
- # an argument on the command line.
89
+ # Convert various pieces of data about the request into a single hash, which
90
+ # can then be passed on as environment data to a script.
90
91
  #
91
92
  # == Parameters:
92
93
  # headers::
93
- # A hash in the form `name` => `value` containing pairs of headers.
94
+ # A hash of request headers
95
+ # query::
96
+ # A hash of query parameters
97
+ # body::
98
+ # A string containing the request body
99
+ #
100
+ # == Returns:
101
+ # A flat hash containing namespaced environment parameters
102
+ #
103
+ def prepare_environment headers, query, body
104
+ result = Hash.new
105
+ headers.each do |name, value|
106
+ name = Oaf::Util.prepare_key name
107
+ result["oaf_header_#{name}"] = Oaf::Util.flatten value
108
+ end
109
+ query.each do |name, value|
110
+ name = Oaf::Util.prepare_key name
111
+ result["oaf_query_#{name}"] = Oaf::Util.flatten value
112
+ end
113
+ result["oaf_request_body"] = Oaf::Util.flatten body
114
+ result
115
+ end
116
+
117
+ # Replace characters that would not be suitable for an environment variable
118
+ # name. Currently this only replaces dashes with underscores. If the need
119
+ # arises, more can be added here later.
120
+ #
121
+ # == Parameters:
122
+ # key::
123
+ # The key to sanitize
94
124
  #
95
125
  # == Returns:
96
- # A comma-delimited, colon-separated list of header names and values. The
97
- # return value of this function should be parsed according to RFC2616.
126
+ # A string with the prepared value
98
127
  #
99
- def format_hash hash
128
+ def prepare_key key
129
+ key.gsub('-', '_').downcase
130
+ end
131
+
132
+ # Flatten a hash or array into a string. This is useful for preparing some
133
+ # data for passing in via the environment, because multi-dimension data
134
+ # structures are not supported for that.
135
+ #
136
+ # == Parameters:
137
+ # data::
138
+ # The data to flatten
139
+ #
140
+ # == Returns:
141
+ # A flattened string. It will be empty if the object passed in was not
142
+ # flatten-able.
143
+ #
144
+ def flatten data
100
145
  result = ''
101
- hash.each do |name, value|
102
- result += "#{name}:#{value},"
146
+ if data.kind_of? Hash
147
+ data.each do |key, val|
148
+ val = Oaf::Util.flatten val if not val.kind_of? String
149
+ result += "#{key}#{val}"
150
+ end
151
+ elsif data.kind_of? Array
152
+ data.each do |item|
153
+ item = Oaf::Util.flatten item if not item.kind_of? String
154
+ result += item
155
+ end
156
+ elsif data.kind_of? String
157
+ result = data
158
+ else
159
+ result = ''
103
160
  end
104
- result.sub!(/,$/, '')
161
+ result
105
162
  end
106
163
 
107
164
  # Given an array of text lines, iterate over each of them and determine if
@@ -187,8 +244,8 @@ module Oaf
187
244
  # == Returns:
188
245
  # A string of stderr concatenated to stdout.
189
246
  #
190
- def run_buffered command
191
- stdin, stdout, stderr = Open3.popen3 "#{command} 2>&1"
247
+ def run_buffered env, command
248
+ stdin, stdout, stderr = Open3.popen3 env, "#{command} 2>&1"
192
249
  stdout.read
193
250
  end
194
251
 
@@ -202,15 +259,18 @@ module Oaf
202
259
  # The HTTP request headers to pass
203
260
  # body::
204
261
  # The HTTP request body to pass
262
+ # query::
263
+ # The HTTP query parameters to pass
205
264
  #
206
265
  # == Returns:
207
266
  # The result from the file, or a default result if the file is not found.
208
267
  #
209
- def get_output file, headers, body
268
+ def get_output file, headers=[], body=[], query=[]
210
269
  if file.nil?
211
270
  out = Oaf::Util.get_default_response
212
271
  elsif File.executable? file
213
- out = Oaf::Util.run_buffered "#{file} '#{headers}' '#{body}'"
272
+ env = Oaf::Util.prepare_environment headers, query, body
273
+ out = Oaf::Util.run_buffered env, file
214
274
  else
215
275
  out = File.open(file).read
216
276
  end
data/lib/oaf/version.rb CHANGED
@@ -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.1.17'
24
+ VERSION = '0.2.1'
25
25
  end
data/oaf.gemspec CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.required_ruby_version = '>= 1.8'
20
20
 
21
21
  s.add_runtime_dependency 'bundler'
22
+ s.add_runtime_dependency 'json'
22
23
 
23
24
  s.add_development_dependency 'rake'
24
25
  s.add_development_dependency 'rspec'
@@ -80,23 +80,45 @@ module Oaf
80
80
  end
81
81
  end
82
82
 
83
- describe "Format Request Headers" do
84
- it "should return a single key/value for just one header" do
85
- headers = [['x-powered-by', 'oaf']]
86
- result = Oaf::Util.format_hash headers
87
- result.should eq('x-powered-by:oaf')
83
+ describe "Argument Sanitizers" do
84
+ it "should replace dashes with underscores in key names" do
85
+ result = Oaf::Util.prepare_key 'x-powered-by'
86
+ result.should eq('x_powered_by')
88
87
  end
89
88
 
90
- it "should return a comma-delimited list for multiple headers" do
91
- headers = [['x-powered-by', 'oaf'], ['content-type', 'text/plain']]
92
- result = Oaf::Util.format_hash headers
93
- result.should eq('x-powered-by:oaf,content-type:text/plain')
89
+ it "should convert all letters to lowercase in key names" do
90
+ result = Oaf::Util.prepare_key 'X-Powered-By'
91
+ result.should eq('x_powered_by')
94
92
  end
95
93
 
96
- it "should return nil if no headers present" do
97
- headers = []
98
- result = Oaf::Util.format_hash headers
99
- result.should be_nil
94
+ it "should flatten a hash into a string" do
95
+ result = Oaf::Util.flatten({'item1' => 'value1'})
96
+ result.should eq('item1value1')
97
+ end
98
+
99
+ it "should flatten hashes recursively into a string" do
100
+ result = Oaf::Util.flatten({'one' => {'two' => {'three' => 'four'}}})
101
+ result.should eq('onetwothreefour')
102
+ end
103
+
104
+ it "should flatten an array of multiple values" do
105
+ result = Oaf::Util.flatten(['one', 'two'])
106
+ result.should eq('onetwo')
107
+ end
108
+
109
+ it "should flatten arrays of multiple values recursively" do
110
+ result = Oaf::Util.flatten(['one', ['two', 'three', ['four']]])
111
+ result.should eq('onetwothreefour')
112
+ end
113
+
114
+ it "should not modify strings" do
115
+ result = Oaf::Util.flatten 'oaf'
116
+ result.should eq('oaf')
117
+ end
118
+
119
+ it "should flatten unknown object types into empty strings" do
120
+ result = Oaf::Util.flatten true
121
+ result.should eq('')
100
122
  end
101
123
  end
102
124
 
@@ -194,32 +216,47 @@ module Oaf
194
216
  @f3.chmod 0755
195
217
  @f3.write "#!/bin/bash\necho 'test1'\necho 'test2' 1>&2\n"
196
218
  @f3.close
219
+
220
+ @f4 = Tempfile.new 'oaf'
221
+ @f4.chmod 0755
222
+ @f4.write "#!/bin/bash\necho \"$oaf_header_x_powered_by\"\n" +
223
+ "echo \"$oaf_query_myparam\"\necho \"$oaf_request_body\"\n"
224
+ @f4.close
197
225
  end
198
226
 
199
227
  after(:all) do
200
228
  @f1.delete
201
229
  @f2.delete
202
230
  @f3.delete
231
+ @f4.delete
203
232
  end
204
233
 
205
234
  it "should execute a file if it is executable" do
206
- result = Oaf::Util.get_output @f1.path, nil, nil
235
+ result = Oaf::Util.get_output @f1.path
207
236
  result.should eq("This is a test\n")
208
237
  end
209
238
 
210
239
  it "should read file contents if it is not executable" do
211
- result = Oaf::Util.get_output @f2.path, nil, nil
240
+ result = Oaf::Util.get_output @f2.path
212
241
  result.should eq("This is a test\n")
213
242
  end
214
243
 
215
244
  it "should assume safe defaults if the file doesnt exist" do
216
- result = Oaf::Util.get_output nil, nil, nil
245
+ result = Oaf::Util.get_output nil
217
246
  result.should eq(Oaf::Util.get_default_response)
218
247
  end
219
248
 
220
249
  it "should catch stderr output instead of dumping it" do
221
- result = Oaf::Util.get_output @f3.path, nil, nil
250
+ result = Oaf::Util.get_output @f3.path
222
251
  result.should eq("test1\ntest2\n")
223
252
  end
253
+
254
+ it "should register environment variables for headers, query, and body" do
255
+ headers = {'x-powered-by' => 'oaf'}
256
+ query = {'myparam' => 'myval'}
257
+ body = 'Test Body'
258
+ result = Oaf::Util.get_output @f4.path, headers, body, query
259
+ result.should eq("oaf\nmyval\nTest Body\n")
260
+ end
224
261
  end
225
262
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require 'simplecov'
2
2
  require 'coveralls'
3
3
  Coveralls.wear!
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
4
8
  SimpleCov.start do
5
9
  add_filter '/spec/'
6
10
  end
metadata CHANGED
@@ -1,85 +1,82 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oaf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.17
4
+ version: 0.2.1
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Ryan Uber
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-08-29 00:00:00.000000000 Z
12
+ date: 2013-08-30 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &13011880 !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
24
+ version_requirements: *13011880
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &12995680 !ruby/object:Gem::Requirement
28
+ none: false
23
29
  requirements:
24
30
  - - ! '>='
25
31
  - !ruby/object:Gem::Version
26
32
  version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *12995680
27
36
  - !ruby/object:Gem::Dependency
28
37
  name: rake
29
- requirement: !ruby/object:Gem::Requirement
38
+ requirement: &12995140 !ruby/object:Gem::Requirement
39
+ none: false
30
40
  requirements:
31
41
  - - ! '>='
32
42
  - !ruby/object:Gem::Version
33
43
  version: '0'
34
44
  type: :development
35
45
  prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ! '>='
39
- - !ruby/object:Gem::Version
40
- version: '0'
46
+ version_requirements: *12995140
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rspec
43
- requirement: !ruby/object:Gem::Requirement
49
+ requirement: &12994440 !ruby/object:Gem::Requirement
50
+ none: false
44
51
  requirements:
45
52
  - - ! '>='
46
53
  - !ruby/object:Gem::Version
47
54
  version: '0'
48
55
  type: :development
49
56
  prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
57
+ version_requirements: *12994440
55
58
  - !ruby/object:Gem::Dependency
56
59
  name: simplecov
57
- requirement: !ruby/object:Gem::Requirement
60
+ requirement: &12993820 !ruby/object:Gem::Requirement
61
+ none: false
58
62
  requirements:
59
63
  - - ! '>='
60
64
  - !ruby/object:Gem::Version
61
65
  version: '0'
62
66
  type: :development
63
67
  prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ! '>='
67
- - !ruby/object:Gem::Version
68
- version: '0'
68
+ version_requirements: *12993820
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: coveralls
71
- requirement: !ruby/object:Gem::Requirement
71
+ requirement: &12993160 !ruby/object:Gem::Requirement
72
+ none: false
72
73
  requirements:
73
74
  - - ! '>='
74
75
  - !ruby/object:Gem::Version
75
76
  version: '0'
76
77
  type: :development
77
78
  prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ! '>='
81
- - !ruby/object:Gem::Version
82
- version: '0'
79
+ version_requirements: *12993160
83
80
  description: Care-free web app prototyping using files and scripts
84
81
  email:
85
82
  - ru@ryanuber.com
@@ -106,26 +103,27 @@ files:
106
103
  homepage: https://github.com/ryanuber/oaf
107
104
  licenses:
108
105
  - MIT
109
- metadata: {}
110
106
  post_install_message:
111
107
  rdoc_options: []
112
108
  require_paths:
113
109
  - lib
114
110
  required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
115
112
  requirements:
116
113
  - - ! '>='
117
114
  - !ruby/object:Gem::Version
118
115
  version: '1.8'
119
116
  required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
120
118
  requirements:
121
119
  - - ! '>='
122
120
  - !ruby/object:Gem::Version
123
121
  version: '0'
124
122
  requirements: []
125
123
  rubyforge_project:
126
- rubygems_version: 2.0.7
124
+ rubygems_version: 1.8.11
127
125
  signing_key:
128
- specification_version: 4
126
+ specification_version: 3
129
127
  summary: Web app prototyping
130
128
  test_files:
131
129
  - spec/oaf/http_spec.rb
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OGM5OWUzYzJlZjlhMDRhYzc2YmVmM2I4YTEyZjkwMzMxMDZkMTc0OQ==
5
- data.tar.gz: !binary |-
6
- YmE3ZDZlNWE2NjgxYjE0N2E1NjdmOTAzZWU5NDRmOGE5OWUxMWY2MA==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- NGU0MjkzNzYyNmYwMjY1MGQ2Y2FkZDdhMDIwNTM5NDMwOWE5YTE1YzA4MWQ4
10
- ODEwNGJhZjJiNjA1MmFlYTk0NzEwNTkxOWM2ODBjNWZjOWRhZmVlZDU5YWI4
11
- YzI1YTI4M2M5ZjMzYjI3MGZiNWYzNGMyOTM5MzgxNzE4OGFmMDk=
12
- data.tar.gz: !binary |-
13
- ZTUxODJhMmEzNjE4YmJhMzg1NmIyMWJhM2E4OGI1YjAzNDNhMmRkYmJkYTVl
14
- M2JjNGYxMzBlNDE2OTI5OGM2NzljNzMzYTk4ZmEzYWJlOWM2OTA0N2I3YzEx
15
- YTMyNTNmYmUzNjczZTJiY2Q3YjE0YmMyODkwZWJmZmFlNzFkZWY=