ferver 1.1.0 → 1.2.0
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 +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +6 -1
- data/README.md +18 -10
- data/Rakefile +19 -17
- data/bin/ferver +19 -4
- data/ferver.gemspec +19 -19
- data/lib/ferver.rb +24 -2
- data/lib/ferver/app.rb +38 -97
- data/lib/ferver/configuration.rb +11 -0
- data/lib/ferver/directory_not_found_error.rb +4 -0
- data/lib/ferver/file_id_request.rb +28 -19
- data/lib/ferver/file_list.rb +48 -46
- data/lib/ferver/version.rb +1 -1
- data/lib/ferver/views/index.erb +28 -0
- data/spec/configuration_spec.rb +24 -0
- data/spec/ferver_spec.rb +132 -180
- data/spec/file_id_request_spec.rb +46 -67
- data/spec/file_list_spec.rb +107 -128
- data/spec/spec_helper.rb +10 -10
- metadata +49 -44
data/lib/ferver/file_list.rb
CHANGED
@@ -1,59 +1,61 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
# A representation of Ferver's file list
|
2
4
|
#
|
3
5
|
module Ferver
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
class FileList
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@files, :size, :each
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# Create a new instance with a path
|
12
|
+
#
|
13
|
+
def initialize(path)
|
14
|
+
fail ArgumentError, 'No path is specified' if path.empty?
|
15
|
+
fail DirectoryNotFoundError unless Dir.exist?(path)
|
16
|
+
|
17
|
+
@files = []
|
18
|
+
@configured_file_path = File.expand_path(path)
|
19
|
+
find_files
|
20
|
+
end
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
# Return an absolute path to a `file_name` in the `directory`
|
23
|
+
#
|
24
|
+
def self.path_for_file(directory, file_name)
|
25
|
+
File.join(directory, file_name)
|
26
|
+
end
|
18
27
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
28
|
+
# Is the file id a valid id for Ferver to serve
|
29
|
+
#
|
30
|
+
def file_id_is_valid?(file_id)
|
31
|
+
file_id < files.size
|
32
|
+
end
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
# Filename by its index
|
35
|
+
#
|
36
|
+
def file_by_id(id)
|
37
|
+
files.fetch(id)
|
38
|
+
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
@files.fetch(id)
|
35
|
-
end
|
40
|
+
def all
|
41
|
+
files
|
42
|
+
end
|
36
43
|
|
37
|
-
|
38
|
-
#
|
39
|
-
def file_count
|
40
|
-
@files.size
|
41
|
-
end
|
44
|
+
private
|
42
45
|
|
43
|
-
|
46
|
+
attr_reader :configured_file_path, :files
|
44
47
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
# Iterate through files in specified dir for files
|
49
|
+
#
|
50
|
+
def find_files
|
51
|
+
@files = []
|
49
52
|
|
50
|
-
|
51
|
-
|
53
|
+
Dir.foreach(configured_file_path) do |file|
|
54
|
+
next if file == '.' || file == '..'
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
|
56
|
+
file_path = FileList.path_for_file(configured_file_path, file)
|
57
|
+
@files << file if File.file?(file_path)
|
58
|
+
end
|
58
59
|
end
|
59
|
-
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/ferver/version.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<title>File List · Ferver</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<h3>Files served:</h3>
|
9
|
+
<ul>
|
10
|
+
<% file_list.each_with_index do |file_name, index| %>
|
11
|
+
|
12
|
+
<li><a href="/files/<%= index %>"><%= file_name %></a></li>
|
13
|
+
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<p><%= file_count %> files served from: <%= ferver_path %></p>
|
19
|
+
|
20
|
+
<hr>
|
21
|
+
|
22
|
+
<p>Served by <a href="https://github.com/rob-murray/ferver" title="Ferver: A simple Ruby app serving files over HTTP">Ferver</a> gem v<%= Ferver::VERSION %></p>
|
23
|
+
|
24
|
+
</body>
|
25
|
+
</html>
|
26
|
+
|
27
|
+
<html>
|
28
|
+
<body>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ferver::Configuration do
|
4
|
+
subject { Ferver::Configuration.new }
|
5
|
+
|
6
|
+
describe 'configured directory path' do
|
7
|
+
|
8
|
+
context 'with no path set' do
|
9
|
+
it 'should return default path' do
|
10
|
+
expect(subject.directory_path).to eq(Ferver::DEFAULT_FILE_SERVER_DIR_PATH)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when directory is set' do
|
15
|
+
let(:path) { '/foo/bar' }
|
16
|
+
|
17
|
+
it 'should return default path' do
|
18
|
+
subject.directory_path = path
|
19
|
+
|
20
|
+
expect(subject.directory_path).to eq(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/ferver_spec.rb
CHANGED
@@ -1,231 +1,183 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
context "given a request to the server root" do
|
8
|
-
|
9
|
-
before(:each) do
|
10
|
-
get "/"
|
11
|
-
end
|
12
|
-
|
13
|
-
it "should return redirect" do
|
14
|
-
expect(last_response).to be_redirect
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should redirect to file list" do
|
18
|
-
follow_redirect!
|
19
|
-
|
20
|
-
expect(last_response).to be_ok
|
21
|
-
expect(last_request.url).to match(/files/)
|
22
|
-
end
|
3
|
+
describe 'ferver' do
|
4
|
+
let(:file_list) { double('file-list') }
|
23
5
|
|
6
|
+
context 'given a request to the server root' do
|
7
|
+
before do
|
8
|
+
get '/'
|
24
9
|
end
|
25
10
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
@file_list = mock()
|
30
|
-
@file_list.stubs(:files).returns(EMPTY_FILE_LIST)
|
31
|
-
@file_list.stubs(:file_count).returns(0)
|
32
|
-
}
|
33
|
-
|
34
|
-
context "when no directory is specified" do
|
35
|
-
|
36
|
-
it "will use default directory" do
|
37
|
-
|
38
|
-
Ferver::FileList.expects(:new).with("./").returns(@file_list)
|
39
|
-
|
40
|
-
get "/files"
|
41
|
-
end
|
11
|
+
it 'should return redirect' do
|
12
|
+
expect(last_response).to be_redirect
|
13
|
+
end
|
42
14
|
|
43
|
-
|
15
|
+
it 'should redirect to file list' do
|
16
|
+
follow_redirect!
|
44
17
|
|
45
|
-
|
18
|
+
expect(last_response).to be_ok
|
19
|
+
expect(last_request.url).to match(/files/)
|
20
|
+
end
|
21
|
+
end
|
46
22
|
|
47
|
-
|
23
|
+
describe 'choosing directory to serve files from' do
|
24
|
+
before do
|
25
|
+
allow(file_list).to receive(:all).and_return(EMPTY_FILE_LIST)
|
26
|
+
allow(file_list).to receive(:size).and_return(0)
|
27
|
+
end
|
48
28
|
|
49
|
-
|
50
|
-
|
29
|
+
context 'when no directory is specified' do
|
30
|
+
it 'will use default directory' do
|
31
|
+
expect(Ferver::FileList).to receive(:new).with('./').and_return(file_list)
|
51
32
|
|
52
|
-
|
53
|
-
|
33
|
+
get '/files'
|
34
|
+
end
|
35
|
+
end
|
54
36
|
|
37
|
+
context 'when the directory passed via configuration' do
|
38
|
+
before do
|
39
|
+
Ferver.configure do |conf|
|
40
|
+
conf.directory_path = '/foo'
|
55
41
|
end
|
42
|
+
end
|
56
43
|
|
57
|
-
|
44
|
+
it 'will use directory specified' do
|
45
|
+
expect(Ferver::FileList).to receive(:new).with('/foo').and_return(file_list)
|
58
46
|
|
59
|
-
|
47
|
+
get '/files'
|
48
|
+
end
|
49
|
+
end
|
60
50
|
|
61
|
-
|
62
|
-
|
51
|
+
context 'when directory does not exist' do
|
52
|
+
before do
|
53
|
+
allow(Ferver::FileList).to receive(:new).and_raise(Ferver::DirectoryNotFoundError)
|
54
|
+
end
|
63
55
|
|
64
|
-
|
65
|
-
|
56
|
+
it 'will return server error status' do
|
57
|
+
get '/files'
|
66
58
|
|
67
|
-
|
68
|
-
|
59
|
+
expect(last_response.status).to eql 500
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
69
63
|
|
70
|
-
|
64
|
+
context 'given an empty list of files' do
|
65
|
+
before do
|
66
|
+
allow(file_list).to receive(:all).and_return(EMPTY_FILE_LIST)
|
67
|
+
allow(file_list).to receive(:size).and_return(0)
|
68
|
+
allow(Ferver::FileList).to receive(:new).and_return(file_list)
|
69
|
+
end
|
71
70
|
|
72
|
-
|
73
|
-
|
71
|
+
context 'when no content-type is requested' do
|
72
|
+
before { get '/files' }
|
74
73
|
|
75
|
-
|
74
|
+
it 'should return valid response' do
|
75
|
+
expect(last_response).to be_ok
|
76
|
+
end
|
76
77
|
|
78
|
+
it 'should contain no file list in response content' do
|
79
|
+
expect(last_response.body).not_to have_tag('li')
|
80
|
+
expect(last_response.body).to have_tag('p', text: /0 files/)
|
81
|
+
end
|
77
82
|
end
|
78
83
|
|
79
|
-
context
|
84
|
+
context 'when json content-type is requested' do
|
85
|
+
before do
|
86
|
+
get '/files', {}, 'HTTP_ACCEPT' => 'application/json'
|
87
|
+
end
|
80
88
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
Ferver::FileList.stubs(:new).returns(file_list)
|
86
|
-
}
|
89
|
+
it 'should return valid response' do
|
90
|
+
expect(last_response).to be_ok
|
91
|
+
expect(last_response.content_type).to include('application/json')
|
92
|
+
end
|
87
93
|
|
88
|
-
|
94
|
+
it 'should contain no file list in response content' do
|
95
|
+
list = JSON.parse last_response.body
|
96
|
+
expect(list).to eq(EMPTY_FILE_LIST)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
89
100
|
|
90
|
-
|
101
|
+
context 'given a list of files' do
|
102
|
+
before do
|
103
|
+
allow(file_list).to receive(:all).and_return(%w(file1 file2))
|
104
|
+
allow(file_list).to receive(:size).and_return(2)
|
105
|
+
allow(Ferver::FileList).to receive(:new).and_return(file_list)
|
106
|
+
end
|
91
107
|
|
92
|
-
|
93
|
-
expect(last_response).to be_ok
|
94
|
-
#todo test html
|
95
|
-
end
|
108
|
+
describe 'file list request' do
|
96
109
|
|
97
|
-
|
98
|
-
|
99
|
-
expect(last_response.body).to contain(/0 files/)
|
100
|
-
end
|
110
|
+
context 'when no content-type is requested' do
|
111
|
+
before { get '/files' }
|
101
112
|
|
113
|
+
it 'should return valid response' do
|
114
|
+
expect(last_response).to be_ok
|
102
115
|
end
|
103
116
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
get "/files", {}, {"HTTP_ACCEPT" => "application/json" }
|
108
|
-
}
|
109
|
-
|
110
|
-
it "should return valid response" do
|
111
|
-
expect(last_response).to be_ok
|
112
|
-
expect(last_response.content_type).to include("application/json")
|
113
|
-
end
|
114
|
-
|
115
|
-
it "should contain no file list in response content" do
|
116
|
-
list = JSON.parse last_response.body
|
117
|
-
expect(list).to eq(EMPTY_FILE_LIST)
|
118
|
-
end
|
119
|
-
|
117
|
+
it 'should contain no file list in response content' do
|
118
|
+
expect(last_response.body).to have_tag('li', count: 2)
|
119
|
+
expect(last_response.body).to have_tag('p', text: /2 files/)
|
120
120
|
end
|
121
121
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
file_list = mock()
|
128
|
-
file_list.stubs(:files).returns(["file1", "file2"])
|
129
|
-
file_list.stubs(:file_count).returns(2)
|
130
|
-
Ferver::FileList.stubs(:new).returns(file_list)
|
131
|
-
}
|
132
|
-
|
133
|
-
context "when no content-type is requested" do
|
134
|
-
|
135
|
-
before { get "/files" }
|
136
|
-
|
137
|
-
it "should return valid response" do
|
138
|
-
expect(last_response).to be_ok
|
139
|
-
#todo test html
|
140
|
-
end
|
141
|
-
|
142
|
-
it "should contain no file list in response content" do
|
143
|
-
expect(last_response.body).to have_selector("li", :count => 2)
|
144
|
-
expect(last_response.body).to contain(/2 files/)
|
145
|
-
end
|
146
|
-
|
147
|
-
it "should list filenames" do
|
148
|
-
expect(last_response.body).to have_selector("li") do |node|
|
149
|
-
|
150
|
-
expect(node.first).to have_selector("a", :content => "file1")
|
151
|
-
expect(node.last).to have_selector("a", :content => "file2")
|
152
|
-
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
122
|
+
it 'should list filenames' do
|
123
|
+
expect(last_response.body).to have_tag('li') do
|
124
|
+
with_tag('a', text: 'file1')
|
125
|
+
with_tag('a', text: 'file2')
|
126
|
+
end
|
156
127
|
end
|
128
|
+
end
|
157
129
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
}
|
163
|
-
|
164
|
-
it "should return valid response" do
|
165
|
-
expect(last_response).to be_ok
|
166
|
-
expect(last_response.content_type).to include("application/json")
|
167
|
-
end
|
168
|
-
|
169
|
-
it "should contain no file list in response content" do
|
170
|
-
list = JSON.parse last_response.body
|
171
|
-
expect(list.count).to eq(2)
|
172
|
-
expect(list).to match_array(["file1", "file2"])
|
173
|
-
end
|
130
|
+
context 'when json content-type is requested' do
|
131
|
+
before do
|
132
|
+
get '/files', {}, 'HTTP_ACCEPT' => 'application/json'
|
133
|
+
end
|
174
134
|
|
135
|
+
it 'should return valid response' do
|
136
|
+
expect(last_response).to be_ok
|
137
|
+
expect(last_response.content_type).to include('application/json')
|
175
138
|
end
|
176
139
|
|
140
|
+
it 'should contain no file list in response content' do
|
141
|
+
list = JSON.parse last_response.body
|
142
|
+
expect(list.count).to eq(2)
|
143
|
+
expect(list).to match_array(%w(file1 file2))
|
144
|
+
end
|
145
|
+
end
|
177
146
|
end
|
178
147
|
|
148
|
+
describe 'downloading a file' do
|
179
149
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
@file_list.stubs(:files).returns(["file1", "file2"])
|
185
|
-
@file_list.stubs(:file_count).returns(2)
|
186
|
-
Ferver::FileList.stubs(:new).returns(@file_list)
|
187
|
-
}
|
188
|
-
|
189
|
-
context "when requesting a file out of range" do
|
190
|
-
|
191
|
-
before {
|
192
|
-
@file_list.expects(:file_id_is_valid?).with(3).returns(false)
|
193
|
-
get "/files/3"
|
194
|
-
}
|
195
|
-
|
196
|
-
it "should return not_found" do
|
197
|
-
expect(last_response).to be_not_found
|
198
|
-
end
|
199
|
-
|
150
|
+
context 'when requesting a file out of range' do
|
151
|
+
before do
|
152
|
+
allow(file_list).to receive(:file_id_is_valid?).with(3).and_return(false)
|
153
|
+
get '/files/3'
|
200
154
|
end
|
201
155
|
|
202
|
-
|
203
|
-
|
204
|
-
before {
|
205
|
-
@file_list.expects(:file_id_is_valid?).never
|
206
|
-
get "/files/foo"
|
207
|
-
}
|
208
|
-
|
209
|
-
it "should return not_found" do
|
210
|
-
expect(last_response).to be_bad_request
|
211
|
-
end
|
212
|
-
|
156
|
+
it 'should return not_found' do
|
157
|
+
expect(last_response).to be_not_found
|
213
158
|
end
|
159
|
+
end
|
214
160
|
|
215
|
-
|
161
|
+
context 'when requesting invalid file id' do
|
162
|
+
before { get '/files/foo' }
|
216
163
|
|
217
|
-
|
164
|
+
it 'should return not_found' do
|
165
|
+
expect(last_response).to be_bad_request
|
166
|
+
end
|
167
|
+
end
|
218
168
|
|
219
|
-
|
220
|
-
|
221
|
-
end
|
169
|
+
context 'when requesting a valid file id' do
|
170
|
+
before { get '/files/0' }
|
222
171
|
|
223
|
-
|
224
|
-
|
225
|
-
|
172
|
+
xit 'should return ok response' do
|
173
|
+
expect(last_response).to be_ok
|
174
|
+
end
|
226
175
|
|
176
|
+
xit 'should return octet-stream content-type' do
|
177
|
+
expect(last_response.headers['Content-Type']).to eq('application/octet-stream')
|
227
178
|
end
|
228
179
|
|
180
|
+
end
|
229
181
|
end
|
230
|
-
|
231
|
-
end
|
182
|
+
end
|
183
|
+
end
|