utopia 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|