knod 0.4.3
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 +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +39 -0
- data/Readme.md +31 -0
- data/bin/knod +35 -0
- data/knod.gemspec +25 -0
- data/lib/knod.rb +18 -0
- data/lib/knod/request.rb +42 -0
- data/lib/knod/server.rb +168 -0
- data/lib/knod/version.rb +3 -0
- data/test/connection.rb +53 -0
- data/test/test_knod.rb +158 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 28667421fdff9da10d052b49cc759c0510e685be
|
4
|
+
data.tar.gz: b9e50bfb52f5f594ab291edd520117959e55af87
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 411502ef0abac71bfdd536c4e94e38e28ca25324b1f42fa8cf1ef988480e4787b482e6ee38bbecef713bd7b161b067c14d2165c5e5ad0f91e71da8f640a5dd9d
|
7
|
+
data.tar.gz: b881af2bbe11b777bf3661a6560f77fecfc9a01e36304cc962045dec56b84f4e0d1b9a3902ea9d080c9650fd5c9bd0cbd79368e09a0928ca8f12fa207d23ac73
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ryan Moser
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
|
3
|
+
Rake::TestTask.new do |t|
|
4
|
+
t.libs << 'test'
|
5
|
+
end
|
6
|
+
|
7
|
+
def gem_version
|
8
|
+
@version ||= Dir.glob("*.gem").sort.last
|
9
|
+
end
|
10
|
+
|
11
|
+
def report_error(task)
|
12
|
+
puts "There is no .gem file to #{task}"
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Run tests'
|
16
|
+
task :default => :test
|
17
|
+
|
18
|
+
desc 'build gem'
|
19
|
+
task :build do
|
20
|
+
puts `gem build knod.gemspec`
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Install a locally generated version of the gem'
|
24
|
+
task :install do |t|
|
25
|
+
if gem_version
|
26
|
+
puts `gem install ./#{gem_version}`
|
27
|
+
else
|
28
|
+
report_error(t.name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc 'Deploy the gem to Rubygems'
|
33
|
+
task :deploy do |t|
|
34
|
+
if gem_version
|
35
|
+
puts `gem push #{gem_version}`
|
36
|
+
else
|
37
|
+
report_error(t.name)
|
38
|
+
end
|
39
|
+
end
|
data/Readme.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#Knod
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/knod) [](https://travis-ci.org/moserrya/knod) [](https://codeclimate.com/github/moserrya/knod)
|
4
|
+
|
5
|
+
Knod is a lightweight HTTP server designed to facilitate front end development when the corresponding back end is missing or incomplete. It responds to GET, PUT, POST, PATCH, and DELETE, serving up, writing to, and deleting from the directory of your choosing. Knod has no dependencies outside of the Ruby standard library.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
```gem install knod```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
The Knod gem comes with an executable; you can run it from the command line with `knod`. Knod will default to port 4444 and the current directory. You can change these with command line arguments (-p and -d, respectively).
|
14
|
+
|
15
|
+
You can also run it by requiring `knod` and calling `Knod.start`. Knod accepts an options hash that lets you change the port, root directory, and logging:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
options = {port: 1234, root: './some/directory', logging: false}
|
19
|
+
Knod.start options
|
20
|
+
```
|
21
|
+
|
22
|
+
Logging is enabled by default. The server will select an open ephemeral port at random if you pass in 0 as the port.
|
23
|
+
|
24
|
+
Knod sanitizes the path on all requests and does not allow access to folders outside of the root directory where it is run.
|
25
|
+
|
26
|
+
GET requests map suffixes into MIME types. Data is considered to be `application/octet-stream` if the content type is unrecognized.
|
27
|
+
|
28
|
+
All data from PUT, POST, and PATCH requests is stored as JSON. If the pathway specified in the request does not exist, Knod will create it.
|
29
|
+
|
30
|
+
POST requests auto-increment in the specified path and return the id of the file written as JSON (e.g if a POST request led to the server writing 56.json, the server would respond with `"{\"id\":56}"`.
|
31
|
+
|
data/bin/knod
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'knod'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
OptionParser.new do |opts|
|
8
|
+
cmd = File.basename($0)
|
9
|
+
|
10
|
+
opts.banner = "Usage: #{cmd} [options]"
|
11
|
+
|
12
|
+
opts.separator ''
|
13
|
+
opts.separator 'Specific options:'
|
14
|
+
|
15
|
+
opts.on('-p', '--port [PORT]', 'Set the port') do |p|
|
16
|
+
options[:port] = p
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('-d', '--directory [DIR]', 'Set the root directory') do |dir|
|
20
|
+
options[:root] = dir
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('--[no-]logging', "Use this flag to disable logging") do |logging|
|
24
|
+
options[:logging] = logging
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-v', '--version', 'Show version') do
|
28
|
+
puts "#{cmd} v#{Knod::VERSION}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
|
32
|
+
end.parse!
|
33
|
+
|
34
|
+
trap("INT") { exit }
|
35
|
+
Knod.start(options)
|
data/knod.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'knod/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'knod'
|
8
|
+
gem.version = Knod::VERSION
|
9
|
+
gem.date = '2014-05-27'
|
10
|
+
gem.authors = ['Ryan Moser']
|
11
|
+
gem.email = 'ryanpmoser@gmail.com'
|
12
|
+
gem.homepage = 'https://github.com/moserrya/knod'
|
13
|
+
gem.summary = 'A tiny RESTful http server'
|
14
|
+
gem.description = 'An http server built using Ruby\'s standard library'
|
15
|
+
gem.license = 'MIT'
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ['lib']
|
21
|
+
|
22
|
+
gem.required_ruby_version = '>= 1.9.3'
|
23
|
+
|
24
|
+
gem.add_development_dependency "rake", '~> 10'
|
25
|
+
end
|
data/lib/knod.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'uri'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'json'
|
5
|
+
require 'knod/request'
|
6
|
+
require 'knod/server'
|
7
|
+
require 'knod/version'
|
8
|
+
|
9
|
+
module Knod
|
10
|
+
def self.start(options = {})
|
11
|
+
Server.new(options).start
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if __FILE__ == $0
|
16
|
+
Knod.start
|
17
|
+
end
|
18
|
+
|
data/lib/knod/request.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Knod
|
2
|
+
class Request
|
3
|
+
attr_reader :socket, :headers, :request_line
|
4
|
+
|
5
|
+
def initialize(socket)
|
6
|
+
@socket = socket
|
7
|
+
@request_line = socket.gets
|
8
|
+
parse_request
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_request
|
12
|
+
headers = {}
|
13
|
+
loop do
|
14
|
+
line = socket.gets
|
15
|
+
break if line == "\r\n"
|
16
|
+
name, value = line.strip.split(": ")
|
17
|
+
headers[name] = value
|
18
|
+
end
|
19
|
+
@headers = headers
|
20
|
+
end
|
21
|
+
|
22
|
+
def content_length
|
23
|
+
headers["Content-Length"].to_i
|
24
|
+
end
|
25
|
+
|
26
|
+
def content_type
|
27
|
+
headers["Content-Type"]
|
28
|
+
end
|
29
|
+
|
30
|
+
def uri
|
31
|
+
@uri ||= request_line.split[1]
|
32
|
+
end
|
33
|
+
|
34
|
+
def method
|
35
|
+
@verb ||= request_line.split.first.upcase
|
36
|
+
end
|
37
|
+
|
38
|
+
def body
|
39
|
+
@body ||= socket.read(content_length)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/knod/server.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
module Knod
|
2
|
+
class Server
|
3
|
+
attr_reader :server, :socket, :request
|
4
|
+
|
5
|
+
DEFAULT_PORT = 4444
|
6
|
+
DEFAULT_WEB_ROOT = './'
|
7
|
+
|
8
|
+
def initialize(options={})
|
9
|
+
port = options.fetch(:port) { DEFAULT_PORT }
|
10
|
+
@root = options.fetch(:root) { DEFAULT_WEB_ROOT }
|
11
|
+
@logging = options.fetch(:logging) { true }
|
12
|
+
@server = TCPServer.new('0.0.0.0', port)
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
log "Starting server on port #{port}"
|
17
|
+
loop do
|
18
|
+
Thread.start(server.accept) do |socket|
|
19
|
+
parse_request_and_respond(socket)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_request_and_respond(socket)
|
25
|
+
@socket = socket
|
26
|
+
@request = Request.new(socket)
|
27
|
+
log request_line
|
28
|
+
public_send "do_#{request.method}"
|
29
|
+
rescue => e
|
30
|
+
log "#{e.class}: #{e.message}"
|
31
|
+
log e.backtrace
|
32
|
+
respond 500
|
33
|
+
ensure
|
34
|
+
socket.close if socket
|
35
|
+
end
|
36
|
+
|
37
|
+
def do_GET(head=false)
|
38
|
+
path = requested_path
|
39
|
+
path = File.join(path, 'index.html') if File.directory?(path)
|
40
|
+
|
41
|
+
if File.file?(path)
|
42
|
+
File.open(path, 'rb') do |file|
|
43
|
+
socket.print file_response_header(file)
|
44
|
+
IO.copy_stream(file, socket) unless head
|
45
|
+
end
|
46
|
+
else
|
47
|
+
message = head ? '' : "\"File not found\""
|
48
|
+
respond(404, message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def do_HEAD
|
53
|
+
do_GET(head=true)
|
54
|
+
end
|
55
|
+
|
56
|
+
def do_DELETE
|
57
|
+
path = requested_path
|
58
|
+
File.delete(path) if File.file?(path)
|
59
|
+
respond 204
|
60
|
+
end
|
61
|
+
|
62
|
+
def do_PUT
|
63
|
+
write_to_path(requested_path) do |path|
|
64
|
+
File.write(path, request.body)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def do_POST
|
69
|
+
path = requested_path
|
70
|
+
FileUtils.mkdir_p(path)
|
71
|
+
records = Dir.glob(path + "/*.json")
|
72
|
+
next_id = (records.map {|r| File.basename(r, ".json") }.map(&:to_i).max || 0) + 1
|
73
|
+
File.write(File.join(path, "#{next_id}.json"), request.body)
|
74
|
+
respond(201, "{\"id\":#{next_id}}")
|
75
|
+
end
|
76
|
+
|
77
|
+
def port
|
78
|
+
server.addr[1]
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def write_to_path(path)
|
84
|
+
directory = File.dirname(path)
|
85
|
+
FileUtils.mkdir_p(directory)
|
86
|
+
yield path
|
87
|
+
respond(200, "\"Success\"")
|
88
|
+
end
|
89
|
+
|
90
|
+
def log(message)
|
91
|
+
STDERR.puts message if @logging
|
92
|
+
end
|
93
|
+
|
94
|
+
def request_line
|
95
|
+
request.request_line
|
96
|
+
end
|
97
|
+
|
98
|
+
STATUS_CODE_MAPPINGS = {
|
99
|
+
200 => "OK",
|
100
|
+
201 => "Created",
|
101
|
+
204 => "No Content",
|
102
|
+
404 => "Not Found",
|
103
|
+
500 => "Internal Server Error",
|
104
|
+
501 => "Not Implemented"
|
105
|
+
}
|
106
|
+
|
107
|
+
def response_header(status_code, message)
|
108
|
+
header = "HTTP/1.1 #{status_code} #{STATUS_CODE_MAPPINGS.fetch(status_code)}\r\n"
|
109
|
+
header << "Content-Type: application/json\r\n" unless message.empty?
|
110
|
+
header << "Content-Length: #{message.size}\r\n"
|
111
|
+
header << "Connection: close\r\n\r\n"
|
112
|
+
end
|
113
|
+
|
114
|
+
def respond(status_code, message='')
|
115
|
+
socket.print response_header(status_code, message)
|
116
|
+
socket.print message unless message.empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
def file_response_header(file)
|
120
|
+
"HTTP/1.1 200 OK\r\n" <<
|
121
|
+
"Content-Type: #{content_type(file)}\r\n" <<
|
122
|
+
"Content-Length: #{file.size}\r\n" <<
|
123
|
+
"Connection: close\r\n\r\n"
|
124
|
+
end
|
125
|
+
|
126
|
+
CONTENT_TYPE_MAPPING = {
|
127
|
+
'json' => 'application/json',
|
128
|
+
'bmp' => 'image/bmp',
|
129
|
+
'gif' => 'image/gif',
|
130
|
+
'jpg' => 'image/jpeg',
|
131
|
+
'png' => 'image/png',
|
132
|
+
'css' => 'text/css',
|
133
|
+
'html' => 'text/html',
|
134
|
+
'txt' => 'text/plain',
|
135
|
+
'xml' => 'text/xml'
|
136
|
+
}
|
137
|
+
|
138
|
+
DEFAULT_CONTENT_TYPE = 'application/octet-stream'
|
139
|
+
|
140
|
+
def content_type(path)
|
141
|
+
ext = File.extname(path).split('.').last
|
142
|
+
CONTENT_TYPE_MAPPING[ext] || DEFAULT_CONTENT_TYPE
|
143
|
+
end
|
144
|
+
|
145
|
+
def requested_path
|
146
|
+
local_path = URI.unescape(URI(request.uri).path)
|
147
|
+
|
148
|
+
clean = []
|
149
|
+
|
150
|
+
parts = local_path.split("/")
|
151
|
+
|
152
|
+
parts.each do |part|
|
153
|
+
next if part.empty? || part == '.'
|
154
|
+
part == '..' ? clean.pop : clean << part
|
155
|
+
end
|
156
|
+
|
157
|
+
File.join(@root, *clean)
|
158
|
+
end
|
159
|
+
|
160
|
+
def method_missing(method_sym, *args, &block)
|
161
|
+
if method_sym.to_s.start_with?("do_")
|
162
|
+
respond(501, "\"not implemented\"")
|
163
|
+
else
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/knod/version.rb
ADDED
data/test/connection.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
def initialize(endpoint)
|
7
|
+
uri = URI.parse(endpoint)
|
8
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
9
|
+
end
|
10
|
+
|
11
|
+
VERB_MAP = {
|
12
|
+
get: Net::HTTP::Get,
|
13
|
+
post: Net::HTTP::Post,
|
14
|
+
put: Net::HTTP::Put,
|
15
|
+
patch: Net::HTTP::Patch,
|
16
|
+
delete: Net::HTTP::Delete,
|
17
|
+
head: Net::HTTP::Head,
|
18
|
+
options: Net::HTTP::Options
|
19
|
+
}
|
20
|
+
|
21
|
+
VERB_MAP.keys.each do |method|
|
22
|
+
define_method method, ->(path, params=nil) {request_json method, path, params}
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def request_json(method, path, params)
|
28
|
+
response = request(method, path, params)
|
29
|
+
response.body = JSON.parse(response.body, symbolize_names: true)
|
30
|
+
response
|
31
|
+
rescue
|
32
|
+
response
|
33
|
+
end
|
34
|
+
|
35
|
+
def request(method, path, params = {})
|
36
|
+
case method
|
37
|
+
when :get, :head
|
38
|
+
full_path = encode_path_params(path, params)
|
39
|
+
request = VERB_MAP[method.to_sym].new(full_path)
|
40
|
+
else
|
41
|
+
request = VERB_MAP[method.to_sym].new(path)
|
42
|
+
request.body = params.to_json
|
43
|
+
end
|
44
|
+
|
45
|
+
@http.request(request)
|
46
|
+
end
|
47
|
+
|
48
|
+
def encode_path_params(path, params)
|
49
|
+
return path if params.nil?
|
50
|
+
encoded = URI.encode_www_form(params)
|
51
|
+
[path, encoded].join("?")
|
52
|
+
end
|
53
|
+
end
|
data/test/test_knod.rb
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'knod'
|
2
|
+
require 'connection'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
|
5
|
+
$knod = Knod::Server.new(port: 0, logging: false)
|
6
|
+
$port = $knod.port
|
7
|
+
|
8
|
+
Thread.new do
|
9
|
+
$knod.start
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse_json_file(file)
|
13
|
+
JSON.parse(File.read(file), symbolize_names: true)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Knod, "a tiny http server" do
|
17
|
+
let(:connection) {Connection.new("http://0.0.0.0:#{$port}")}
|
18
|
+
|
19
|
+
describe 'non-writing methods' do
|
20
|
+
before do
|
21
|
+
@path = 'index.html'
|
22
|
+
@body = "<h1>Squids are fun!</h1>"
|
23
|
+
File.write(@path, @body)
|
24
|
+
end
|
25
|
+
|
26
|
+
after do
|
27
|
+
FileUtils.remove_entry(@path, true)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'responds with 200 when the route is valid' do
|
31
|
+
response = connection.get @path
|
32
|
+
response.code.must_equal '200'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'responds with the body of the requested file' do
|
36
|
+
response = connection.get @path
|
37
|
+
response.body.must_equal @body
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'implictly serves up the index' do
|
41
|
+
response = connection.get "/"
|
42
|
+
response.body.must_equal @body
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns a 404 if the file does not exist' do
|
46
|
+
response = connection.get "/squidbat.random"
|
47
|
+
response.code.must_equal '404'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'responds to HEAD requests without a body' do
|
51
|
+
response = connection.head @path
|
52
|
+
response.body.must_be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'responds to unsupported methods with a 501' do
|
56
|
+
response = connection.options @path
|
57
|
+
response.code.must_equal '501'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'deletes files at the specified path' do
|
61
|
+
response = connection.delete @path
|
62
|
+
File.exists?(@path).must_equal false
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'responds to delete requests with a 204' do
|
66
|
+
response = connection.delete @path
|
67
|
+
response.code.must_equal '204'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'PUT' do
|
72
|
+
let(:directory) {'index'}
|
73
|
+
let(:path) {"#{directory}/81.json"}
|
74
|
+
let(:data) {{state: 'swell', predeliction: 'good challenges'}}
|
75
|
+
|
76
|
+
it 'returns a 200 on success' do
|
77
|
+
response = connection.put path, data
|
78
|
+
response.code.must_equal '200'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'writes to the local path' do
|
82
|
+
connection.put path, data
|
83
|
+
File.file?(path).must_equal true
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'writes the data to the file as json' do
|
87
|
+
connection.put path, data
|
88
|
+
parse_json_file(path).must_equal data
|
89
|
+
end
|
90
|
+
|
91
|
+
after do
|
92
|
+
FileUtils.remove_entry(directory, true)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'POST' do
|
97
|
+
let(:path) {'/items'}
|
98
|
+
let(:local_path) {File.join('.', path)}
|
99
|
+
let(:data) {{id: 81, state: 'swell', predeliction: 'good challenges'}}
|
100
|
+
|
101
|
+
before do
|
102
|
+
FileUtils.mkdir_p(local_path)
|
103
|
+
2.times {|i| File.write(File.join(".", path, "#{i+1}.json"), {state: 'noodles'})}
|
104
|
+
end
|
105
|
+
|
106
|
+
after do
|
107
|
+
FileUtils.remove_entry(local_path, true)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns a 201 on success' do
|
111
|
+
response = connection.post path, data
|
112
|
+
response.code.must_equal '201'
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'creates the required directory if it does not exist' do
|
116
|
+
FileUtils.remove_entry(local_path, true)
|
117
|
+
connection.post path, data
|
118
|
+
Dir.exists?(local_path).must_equal true
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'writes to the correct path' do
|
122
|
+
connection.post path, data
|
123
|
+
File.file?(File.join(local_path, '3.json')).must_equal true
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'responds with json' do
|
127
|
+
response = connection.post path, data
|
128
|
+
response.content_type.must_equal 'application/json'
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'returns the id of the file created' do
|
132
|
+
response = connection.post path, data
|
133
|
+
response.body.must_equal ({id: 3})
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'error handling' do
|
138
|
+
before do
|
139
|
+
def $knod.do_HEAD
|
140
|
+
raise 'boom!'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
after do
|
145
|
+
def $knod.do_HEAD
|
146
|
+
do_GET(head=true)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'responds to server errors with a 500' do
|
151
|
+
response = connection.head '/index.html'
|
152
|
+
response.code.must_equal '500'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: knod
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ryan Moser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-05-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10'
|
27
|
+
description: An http server built using Ruby's standard library
|
28
|
+
email: ryanpmoser@gmail.com
|
29
|
+
executables:
|
30
|
+
- knod
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- ".travis.yml"
|
36
|
+
- Gemfile
|
37
|
+
- Gemfile.lock
|
38
|
+
- LICENSE.txt
|
39
|
+
- Rakefile
|
40
|
+
- Readme.md
|
41
|
+
- bin/knod
|
42
|
+
- knod.gemspec
|
43
|
+
- lib/knod.rb
|
44
|
+
- lib/knod/request.rb
|
45
|
+
- lib/knod/server.rb
|
46
|
+
- lib/knod/version.rb
|
47
|
+
- test/connection.rb
|
48
|
+
- test/test_knod.rb
|
49
|
+
homepage: https://github.com/moserrya/knod
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.9.3
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 2.2.2
|
70
|
+
signing_key:
|
71
|
+
specification_version: 4
|
72
|
+
summary: A tiny RESTful http server
|
73
|
+
test_files:
|
74
|
+
- test/connection.rb
|
75
|
+
- test/test_knod.rb
|