utopia 2.0.0 → 2.0.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.
- checksums.yaml +4 -4
- data/lib/utopia/command/server.rb +1 -0
- data/lib/utopia/content/links.rb +3 -2
- data/lib/utopia/static.rb +2 -192
- data/lib/utopia/static/local_file.rb +123 -0
- data/lib/utopia/static/mime_types.rb +114 -0
- data/lib/utopia/version.rb +1 -1
- data/spec/utopia/content/links_spec.rb +69 -53
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45aef2de7e3895af0d07047f106c870ddf6f52a9
|
4
|
+
data.tar.gz: c44105d12552392fc82e952e1084cc56f563e5cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84bcd1fde7888daeae19321e11eae4cddf9ad83968994e12f838f1972387b6aee51612a2ba642f449bbeecb0364b014983c75020f7be045bfac1bd25a7d2f9a0
|
7
|
+
data.tar.gz: 9da83f29130302129c6d4928bf621d9db153b3ae937b5dc428e9266ba32d6dac125562ff1500e9705884b2b9e6b3303a900ae43e8ca123a3dc862744fc6ef785
|
@@ -69,6 +69,7 @@ module Utopia
|
|
69
69
|
# Copy git hooks:
|
70
70
|
system("cp", "-r", File.join(Setup::Server::ROOT, 'git', 'hooks'), File.join(destination_root, '.git')) or fail "could not copy git hooks"
|
71
71
|
# finally set everything in the .git directory to be group writable
|
72
|
+
# This failed for me and I had to do sudo chown http:http .git -R first.
|
72
73
|
system("chmod", "-Rf", "g+w", File.join(destination_root, '.git')) or fail "could not update permissions of .git directory"
|
73
74
|
end
|
74
75
|
end
|
data/lib/utopia/content/links.rb
CHANGED
@@ -54,7 +54,8 @@ module Utopia
|
|
54
54
|
|
55
55
|
# Named:
|
56
56
|
if name = options[:name]
|
57
|
-
|
57
|
+
# We use pattern === name, which matches either the whole string, or matches a regexp.
|
58
|
+
ordered.select!{|link| name === link.name}
|
58
59
|
end
|
59
60
|
|
60
61
|
if locale = options[:locale]
|
@@ -155,7 +156,7 @@ module Utopia
|
|
155
156
|
end
|
156
157
|
|
157
158
|
def indices(path, &block)
|
158
|
-
Dir.entries(path).
|
159
|
+
Dir.entries(path).select{|filename| filename.match(INDEX_XNODE_FILTER)}
|
159
160
|
end
|
160
161
|
|
161
162
|
def load_indices(name, path, metadata)
|
data/lib/utopia/static.rb
CHANGED
@@ -21,202 +21,12 @@
|
|
21
21
|
require_relative 'middleware'
|
22
22
|
require_relative 'localization'
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
require 'digest/sha1'
|
27
|
-
require 'mime/types'
|
24
|
+
require_relative 'static/local_file'
|
25
|
+
require_relative 'static/mime_types'
|
28
26
|
|
29
27
|
module Utopia
|
30
28
|
# A middleware which serves static files from the specified root directory.
|
31
29
|
class Static
|
32
|
-
# Default mime-types which are common for files served over HTTP:
|
33
|
-
MIME_TYPES = {
|
34
|
-
:xiph => {
|
35
|
-
"ogx" => "application/ogg",
|
36
|
-
"ogv" => "video/ogg",
|
37
|
-
"oga" => "audio/ogg",
|
38
|
-
"ogg" => "audio/ogg",
|
39
|
-
"spx" => "audio/ogg",
|
40
|
-
"flac" => "audio/flac",
|
41
|
-
"anx" => "application/annodex",
|
42
|
-
"axa" => "audio/annodex",
|
43
|
-
"xspf" => "application/xspf+xml",
|
44
|
-
},
|
45
|
-
:media => [
|
46
|
-
:xiph, "mp3", "mp4", "wav", "aiff", ["aac", "audio/x-aac"], "mov", "avi", "wmv", "mpg"
|
47
|
-
],
|
48
|
-
:text => [
|
49
|
-
"html", "css", "js", ["map", "application/json"], "txt", "rtf", "xml", "pdf"
|
50
|
-
],
|
51
|
-
:fonts => [
|
52
|
-
"otf", ["eot", "application/vnd.ms-fontobject"], "ttf", "woff"
|
53
|
-
],
|
54
|
-
:archive => [
|
55
|
-
"zip", "tar", "tgz", "tar.gz", "tar.bz2", ["dmg", "application/x-apple-diskimage"],
|
56
|
-
["torrent", "application/x-bittorrent"]
|
57
|
-
],
|
58
|
-
:images => [
|
59
|
-
"png", "gif", "jpeg", "tiff", "svg"
|
60
|
-
],
|
61
|
-
:default => [
|
62
|
-
:media, :text, :archive, :images, :fonts
|
63
|
-
]
|
64
|
-
}
|
65
|
-
|
66
|
-
# A class to assist with loading mime-type metadata.
|
67
|
-
class MimeTypeLoader
|
68
|
-
def initialize(library)
|
69
|
-
@extensions = {}
|
70
|
-
@library = library
|
71
|
-
end
|
72
|
-
|
73
|
-
attr :extensions
|
74
|
-
|
75
|
-
def self.extensions_for(types, library = MIME_TYPES)
|
76
|
-
loader = self.new(library)
|
77
|
-
loader.expand(types)
|
78
|
-
return loader.extensions
|
79
|
-
end
|
80
|
-
|
81
|
-
def extract_extensions(mime_types)
|
82
|
-
mime_types.select{|mime_type| !mime_type.obsolete?}.each do |mime_type|
|
83
|
-
mime_type.extensions.each do |ext|
|
84
|
-
@extensions["." + ext] = mime_type.content_type
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class ExpansionError < ArgumentError
|
90
|
-
end
|
91
|
-
|
92
|
-
def expand(types)
|
93
|
-
types.each do |type|
|
94
|
-
current_count = @extensions.size
|
95
|
-
|
96
|
-
begin
|
97
|
-
case type
|
98
|
-
when Symbol
|
99
|
-
self.expand(MIME_TYPES[type])
|
100
|
-
when Array
|
101
|
-
@extensions["." + type[0]] = type[1]
|
102
|
-
when String
|
103
|
-
self.extract_extensions MIME::Types.of(type)
|
104
|
-
when Regexp
|
105
|
-
self.extract_extensions MIME::Types[type]
|
106
|
-
when MIME::Type
|
107
|
-
self.extract_extensions.call([type])
|
108
|
-
end
|
109
|
-
rescue
|
110
|
-
raise ExpansionError.new("#{self.class.name}: Error while processing #{type.inspect}!")
|
111
|
-
end
|
112
|
-
|
113
|
-
if @extensions.size == current_count
|
114
|
-
raise ExpansionError.new("#{self.class.name}: Could not find any mime type for #{type.inspect}")
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
private
|
121
|
-
|
122
|
-
# Represents a local file on disk which can be served directly, or passed upstream to sendfile.
|
123
|
-
class LocalFile
|
124
|
-
def initialize(root, path)
|
125
|
-
@root = root
|
126
|
-
@path = path
|
127
|
-
@etag = Digest::SHA1.hexdigest("#{File.size(full_path)}#{mtime_date}")
|
128
|
-
|
129
|
-
@range = nil
|
130
|
-
end
|
131
|
-
|
132
|
-
attr :root
|
133
|
-
attr :path
|
134
|
-
attr :etag
|
135
|
-
attr :range
|
136
|
-
|
137
|
-
# Fit in with Rack::Sendfile
|
138
|
-
def to_path
|
139
|
-
full_path
|
140
|
-
end
|
141
|
-
|
142
|
-
def full_path
|
143
|
-
File.join(@root, @path.components)
|
144
|
-
end
|
145
|
-
|
146
|
-
def mtime_date
|
147
|
-
File.mtime(full_path).httpdate
|
148
|
-
end
|
149
|
-
|
150
|
-
def bytesize
|
151
|
-
File.size(full_path)
|
152
|
-
end
|
153
|
-
|
154
|
-
# This reflects whether calling each would yield anything.
|
155
|
-
def empty?
|
156
|
-
bytesize == 0
|
157
|
-
end
|
158
|
-
|
159
|
-
alias size bytesize
|
160
|
-
|
161
|
-
def each
|
162
|
-
File.open(full_path, "rb") do |file|
|
163
|
-
file.seek(@range.begin)
|
164
|
-
remaining = @range.end - @range.begin+1
|
165
|
-
|
166
|
-
while remaining > 0
|
167
|
-
break unless part = file.read([8192, remaining].min)
|
168
|
-
|
169
|
-
remaining -= part.length
|
170
|
-
|
171
|
-
yield part
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def modified?(env)
|
177
|
-
if modified_since = env['HTTP_IF_MODIFIED_SINCE']
|
178
|
-
return false if File.mtime(full_path) <= Time.parse(modified_since)
|
179
|
-
end
|
180
|
-
|
181
|
-
if etags = env['HTTP_IF_NONE_MATCH']
|
182
|
-
etags = etags.split(/\s*,\s*/)
|
183
|
-
return false if etags.include?(etag) || etags.include?('*')
|
184
|
-
end
|
185
|
-
|
186
|
-
return true
|
187
|
-
end
|
188
|
-
|
189
|
-
CONTENT_LENGTH = Rack::CONTENT_LENGTH
|
190
|
-
CONTENT_RANGE = 'Content-Range'.freeze
|
191
|
-
|
192
|
-
def serve(env, response_headers)
|
193
|
-
ranges = Rack::Utils.get_byte_ranges(env['HTTP_RANGE'], size)
|
194
|
-
response = [200, response_headers, self]
|
195
|
-
|
196
|
-
# puts "Requesting ranges: #{ranges.inspect} (#{size})"
|
197
|
-
|
198
|
-
if ranges == nil or ranges.size != 1
|
199
|
-
# No ranges, or multiple ranges (which we don't support).
|
200
|
-
# TODO: Support multiple byte-ranges, for now just send entire file:
|
201
|
-
response[0] = 200
|
202
|
-
response[1][CONTENT_LENGTH] = size.to_s
|
203
|
-
@range = 0...size
|
204
|
-
else
|
205
|
-
# Partial content:
|
206
|
-
@range = ranges[0]
|
207
|
-
partial_size = @range.count
|
208
|
-
|
209
|
-
response[0] = 206
|
210
|
-
response[1][CONTENT_LENGTH] = partial_size.to_s
|
211
|
-
response[1][CONTENT_RANGE] = "bytes #{@range.min}-#{@range.max}/#{size}"
|
212
|
-
end
|
213
|
-
|
214
|
-
return response
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
public
|
219
|
-
|
220
30
|
DEFAULT_CACHE_CONTROL = 'public, max-age=3600'.freeze
|
221
31
|
|
222
32
|
# @param root [String] The root directory to serve files from.
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'time'
|
22
|
+
require 'digest/sha1'
|
23
|
+
|
24
|
+
module Utopia
|
25
|
+
# A middleware which serves static files from the specified root directory.
|
26
|
+
class Static
|
27
|
+
# Represents a local file on disk which can be served directly, or passed upstream to sendfile.
|
28
|
+
class LocalFile
|
29
|
+
def initialize(root, path)
|
30
|
+
@root = root
|
31
|
+
@path = path
|
32
|
+
@etag = Digest::SHA1.hexdigest("#{File.size(full_path)}#{mtime_date}")
|
33
|
+
|
34
|
+
@range = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
attr :root
|
38
|
+
attr :path
|
39
|
+
attr :etag
|
40
|
+
attr :range
|
41
|
+
|
42
|
+
# Fit in with Rack::Sendfile
|
43
|
+
def to_path
|
44
|
+
full_path
|
45
|
+
end
|
46
|
+
|
47
|
+
def full_path
|
48
|
+
File.join(@root, @path.components)
|
49
|
+
end
|
50
|
+
|
51
|
+
def mtime_date
|
52
|
+
File.mtime(full_path).httpdate
|
53
|
+
end
|
54
|
+
|
55
|
+
def bytesize
|
56
|
+
File.size(full_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
# This reflects whether calling each would yield anything.
|
60
|
+
def empty?
|
61
|
+
bytesize == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
alias size bytesize
|
65
|
+
|
66
|
+
def each
|
67
|
+
File.open(full_path, "rb") do |file|
|
68
|
+
file.seek(@range.begin)
|
69
|
+
remaining = @range.end - @range.begin+1
|
70
|
+
|
71
|
+
while remaining > 0
|
72
|
+
break unless part = file.read([8192, remaining].min)
|
73
|
+
|
74
|
+
remaining -= part.length
|
75
|
+
|
76
|
+
yield part
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def modified?(env)
|
82
|
+
if modified_since = env['HTTP_IF_MODIFIED_SINCE']
|
83
|
+
return false if File.mtime(full_path) <= Time.parse(modified_since)
|
84
|
+
end
|
85
|
+
|
86
|
+
if etags = env['HTTP_IF_NONE_MATCH']
|
87
|
+
etags = etags.split(/\s*,\s*/)
|
88
|
+
return false if etags.include?(etag) || etags.include?('*')
|
89
|
+
end
|
90
|
+
|
91
|
+
return true
|
92
|
+
end
|
93
|
+
|
94
|
+
CONTENT_LENGTH = Rack::CONTENT_LENGTH
|
95
|
+
CONTENT_RANGE = 'Content-Range'.freeze
|
96
|
+
|
97
|
+
def serve(env, response_headers)
|
98
|
+
ranges = Rack::Utils.get_byte_ranges(env['HTTP_RANGE'], size)
|
99
|
+
response = [200, response_headers, self]
|
100
|
+
|
101
|
+
# puts "Requesting ranges: #{ranges.inspect} (#{size})"
|
102
|
+
|
103
|
+
if ranges == nil or ranges.size != 1
|
104
|
+
# No ranges, or multiple ranges (which we don't support).
|
105
|
+
# TODO: Support multiple byte-ranges, for now just send entire file:
|
106
|
+
response[0] = 200
|
107
|
+
response[1][CONTENT_LENGTH] = size.to_s
|
108
|
+
@range = 0...size
|
109
|
+
else
|
110
|
+
# Partial content:
|
111
|
+
@range = ranges[0]
|
112
|
+
partial_size = @range.count
|
113
|
+
|
114
|
+
response[0] = 206
|
115
|
+
response[1][CONTENT_LENGTH] = partial_size.to_s
|
116
|
+
response[1][CONTENT_RANGE] = "bytes #{@range.min}-#{@range.max}/#{size}"
|
117
|
+
end
|
118
|
+
|
119
|
+
return response
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'mime/types'
|
22
|
+
|
23
|
+
module Utopia
|
24
|
+
# A middleware which serves static files from the specified root directory.
|
25
|
+
class Static
|
26
|
+
# Default mime-types which are common for files served over HTTP:
|
27
|
+
MIME_TYPES = {
|
28
|
+
:xiph => {
|
29
|
+
"ogx" => "application/ogg",
|
30
|
+
"ogv" => "video/ogg",
|
31
|
+
"oga" => "audio/ogg",
|
32
|
+
"ogg" => "audio/ogg",
|
33
|
+
"spx" => "audio/ogg",
|
34
|
+
"flac" => "audio/flac",
|
35
|
+
"anx" => "application/annodex",
|
36
|
+
"axa" => "audio/annodex",
|
37
|
+
"xspf" => "application/xspf+xml",
|
38
|
+
},
|
39
|
+
:media => [
|
40
|
+
:xiph, "mp3", "mp4", "wav", "aiff", ["aac", "audio/x-aac"], "mov", "avi", "wmv", "mpg"
|
41
|
+
],
|
42
|
+
:text => [
|
43
|
+
"html", "css", "js", ["map", "application/json"], "txt", "rtf", "xml", "pdf"
|
44
|
+
],
|
45
|
+
:fonts => [
|
46
|
+
"otf", ["eot", "application/vnd.ms-fontobject"], "ttf", "woff"
|
47
|
+
],
|
48
|
+
:archive => [
|
49
|
+
"zip", "tar", "tgz", "tar.gz", "tar.bz2", ["dmg", "application/x-apple-diskimage"],
|
50
|
+
["torrent", "application/x-bittorrent"]
|
51
|
+
],
|
52
|
+
:images => [
|
53
|
+
"png", "gif", "jpeg", "tiff", "svg"
|
54
|
+
],
|
55
|
+
:default => [
|
56
|
+
:media, :text, :archive, :images, :fonts
|
57
|
+
]
|
58
|
+
}
|
59
|
+
|
60
|
+
# A class to assist with loading mime-type metadata.
|
61
|
+
class MimeTypeLoader
|
62
|
+
def initialize(library)
|
63
|
+
@extensions = {}
|
64
|
+
@library = library
|
65
|
+
end
|
66
|
+
|
67
|
+
attr :extensions
|
68
|
+
|
69
|
+
def self.extensions_for(types, library = MIME_TYPES)
|
70
|
+
loader = self.new(library)
|
71
|
+
loader.expand(types)
|
72
|
+
return loader.extensions
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_extensions(mime_types)
|
76
|
+
mime_types.select{|mime_type| !mime_type.obsolete?}.each do |mime_type|
|
77
|
+
mime_type.extensions.each do |ext|
|
78
|
+
@extensions["." + ext] = mime_type.content_type
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class ExpansionError < ArgumentError
|
84
|
+
end
|
85
|
+
|
86
|
+
def expand(types)
|
87
|
+
types.each do |type|
|
88
|
+
current_count = @extensions.size
|
89
|
+
|
90
|
+
begin
|
91
|
+
case type
|
92
|
+
when Symbol
|
93
|
+
self.expand(MIME_TYPES[type])
|
94
|
+
when Array
|
95
|
+
@extensions["." + type[0]] = type[1]
|
96
|
+
when String
|
97
|
+
self.extract_extensions MIME::Types.of(type)
|
98
|
+
when Regexp
|
99
|
+
self.extract_extensions MIME::Types[type]
|
100
|
+
when MIME::Type
|
101
|
+
self.extract_extensions.call([type])
|
102
|
+
end
|
103
|
+
rescue
|
104
|
+
raise ExpansionError.new("#{self.class.name}: Error while processing #{type.inspect}!")
|
105
|
+
end
|
106
|
+
|
107
|
+
if @extensions.size == current_count
|
108
|
+
raise ExpansionError.new("#{self.class.name}: Could not find any mime type for #{type.inspect}")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/utopia/version.rb
CHANGED
@@ -22,64 +22,80 @@
|
|
22
22
|
|
23
23
|
require 'utopia/content/links'
|
24
24
|
|
25
|
-
|
26
|
-
describe
|
27
|
-
|
28
|
-
links = Utopia::Content::Links.index(File.expand_path("links", __dir__), Utopia::Path.create("/"))
|
29
|
-
|
30
|
-
expect(links.size).to be == 3
|
31
|
-
|
32
|
-
expect(links[0].kind).to be == :virtual
|
33
|
-
expect(links[0].href).to be == nil
|
34
|
-
|
35
|
-
expect(links[1].title).to be == "Welcome"
|
36
|
-
expect(links[1].to_href).to be == '<a class="link" href="/welcome">Welcome</a>'
|
37
|
-
expect(links[1].kind).to be == :file
|
38
|
-
expect(links[1].href).to be == "/welcome"
|
39
|
-
expect(links[1].name).to be == 'welcome'
|
40
|
-
|
41
|
-
expect(links[2].title).to be == 'Foo Bar'
|
42
|
-
expect(links[2].kind).to be == :directory
|
43
|
-
expect(links[2].href).to be == "/foo/index"
|
44
|
-
expect(links[2].name).to be == 'foo'
|
45
|
-
|
46
|
-
expect(links[1]).to be_eql links[1]
|
47
|
-
expect(links[0]).to_not be_eql links[1]
|
48
|
-
end
|
25
|
+
RSpec.describe Utopia::Content::Links do
|
26
|
+
describe 'INDEX_XNODE_FILTER' do
|
27
|
+
subject{Utopia::Content::Links::INDEX_XNODE_FILTER}
|
49
28
|
|
50
|
-
it "should
|
51
|
-
|
52
|
-
|
53
|
-
expect(links.size).to be == 1
|
29
|
+
it "should match index" do
|
30
|
+
expect("index.xnode").to match(subject)
|
54
31
|
end
|
55
32
|
|
56
|
-
it "should
|
57
|
-
|
58
|
-
|
59
|
-
# Select both test links
|
60
|
-
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"))
|
61
|
-
expect(links.size).to be == 2
|
62
|
-
|
63
|
-
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"), locale: 'en')
|
64
|
-
expect(links.size).to be == 1
|
33
|
+
it "should not match invalid index" do
|
34
|
+
expect("old-index.xnode").to_not match(subject)
|
65
35
|
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not match partial strings" do
|
39
|
+
links = Utopia::Content::Links.index(File.expand_path("links", __dir__), Utopia::Path.create("/"), name: "come")
|
66
40
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
expect(links.collect(&:title)).to be == ['One', 'Two', 'Three', 'Four', 'Five']
|
74
|
-
end
|
41
|
+
expect(links).to be_empty
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should give a list of links" do
|
45
|
+
links = Utopia::Content::Links.index(File.expand_path("links", __dir__), Utopia::Path.create("/"))
|
75
46
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
47
|
+
expect(links.size).to be == 3
|
48
|
+
|
49
|
+
expect(links[0].kind).to be == :virtual
|
50
|
+
expect(links[0].href).to be == nil
|
51
|
+
|
52
|
+
expect(links[1].title).to be == "Welcome"
|
53
|
+
expect(links[1].to_href).to be == '<a class="link" href="/welcome">Welcome</a>'
|
54
|
+
expect(links[1].kind).to be == :file
|
55
|
+
expect(links[1].href).to be == "/welcome"
|
56
|
+
expect(links[1].name).to be == 'welcome'
|
57
|
+
|
58
|
+
expect(links[2].title).to be == 'Foo Bar'
|
59
|
+
expect(links[2].kind).to be == :directory
|
60
|
+
expect(links[2].href).to be == "/foo/index"
|
61
|
+
expect(links[2].name).to be == 'foo'
|
62
|
+
|
63
|
+
expect(links[1]).to be_eql links[1]
|
64
|
+
expect(links[0]).to_not be_eql links[1]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should filter links by name" do
|
68
|
+
links = Utopia::Content::Links.index(File.expand_path("links", __dir__), Utopia::Path.create("/"), name: /foo/)
|
69
|
+
|
70
|
+
expect(links.size).to be == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should select localized links" do
|
74
|
+
root = File.expand_path("links", __dir__)
|
75
|
+
|
76
|
+
# Select both test links
|
77
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"))
|
78
|
+
expect(links.size).to be == 2
|
79
|
+
|
80
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/foo"), locale: 'en')
|
81
|
+
expect(links.size).to be == 1
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should read correct link order for en" do
|
85
|
+
root = File.expand_path("localized", __dir__)
|
86
|
+
|
87
|
+
# Select both test links
|
88
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"), locale: 'en')
|
89
|
+
|
90
|
+
expect(links.collect(&:title)).to be == ['One', 'Two', 'Three', 'Four', 'Five']
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should read correct link order for zh" do
|
94
|
+
root = File.expand_path("localized", __dir__)
|
95
|
+
|
96
|
+
# Select both test links
|
97
|
+
links = Utopia::Content::Links.index(root, Utopia::Path.create("/"), locale: 'zh')
|
98
|
+
|
99
|
+
expect(links.collect(&:title)).to be == ['One', 'Two', 'Three', '四']
|
84
100
|
end
|
85
101
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: utopia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trenni
|
@@ -394,6 +394,8 @@ files:
|
|
394
394
|
- lib/utopia/session/lazy_hash.rb
|
395
395
|
- lib/utopia/setup.rb
|
396
396
|
- lib/utopia/static.rb
|
397
|
+
- lib/utopia/static/local_file.rb
|
398
|
+
- lib/utopia/static/mime_types.rb
|
397
399
|
- lib/utopia/version.rb
|
398
400
|
- materials/utopia.png
|
399
401
|
- materials/utopia.svg
|