oaf 0.1.17 → 0.2.1

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.
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=