sinatra-exstatic-assets 3.0.0 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +8 -0
- data/Gemfile +2 -0
- data/README.md +16 -0
- data/examples/app/views/index.erb +37 -14
- data/examples/app2/main.rb +4 -0
- data/examples/app2/views/index.erb +1 -1
- data/examples/app2/views/layout.erb +1 -1
- data/lib/sinatra/exstatic_assets/formats.rb +31 -0
- data/lib/sinatra/exstatic_assets/version.rb +1 -1
- data/lib/sinatra/exstatic_assets.rb +84 -46
- data/spec/spec_helper.rb +7 -2
- data/spec/static_assets_spec.rb +117 -51
- data/spec/support/fixtures/app-deeper.html +37 -14
- data/spec/support/fixtures/app2-deeper.html +1 -1
- data/spec/support/fixtures/app2.html +1 -1
- data/spec/support/fixtures/main.html +37 -14
- metadata +3 -3
- data/examples/app2/public/css/base.css +0 -93
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05a627944971257f2082ae5953e5df01c2f65965
|
4
|
+
data.tar.gz: edeb2413e71cd8139c9d44618be83f2c2ddfb450
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecd83526a18cc3f4221186585705acb464f3f317a7318fe99f21eda4c890daa933621eb3147b46868faf848fa8361427d36b5ef36f2fd904c8b1fa95e613064a
|
7
|
+
data.tar.gz: b82d28f9c9eba146b27803ba630030783e8812f54b4e4a5f75db00f8eef6838843ca9fe701bb4562cb0516d0fee98dccaeb04fd4c066765281cc6a942a21e693
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# CH CH CH CHANGES! #
|
2
2
|
|
3
|
+
## Friday the 2nd of December 2016, v3.1.0 ##
|
4
|
+
|
5
|
+
- Fixed a subtle bug with script_name.
|
6
|
+
- Improved favicon method.
|
7
|
+
- Added timestamp formatting.
|
8
|
+
|
9
|
+
----
|
10
|
+
|
3
11
|
## Wednesday the 26th of August 2015, v3.0.0 ##
|
4
12
|
|
5
13
|
- Fixed typo where `script_tag` had overwritten `script_name`.
|
data/Gemfile
CHANGED
@@ -10,6 +10,7 @@ end
|
|
10
10
|
group :test do
|
11
11
|
gem "rspec"
|
12
12
|
gem "rspec-its"
|
13
|
+
gem "mocha"
|
13
14
|
gem "rack-test"
|
14
15
|
gem "simplecov"
|
15
16
|
gem 'turn', :require => false
|
@@ -22,6 +23,7 @@ group :development do
|
|
22
23
|
gem "rake"
|
23
24
|
unless RUBY_ENGINE == 'jruby' || RUBY_ENGINE == "rbx"
|
24
25
|
gem "pry-byebug"
|
26
|
+
gem "pry-doc"
|
25
27
|
end
|
26
28
|
gem 'webrick', '~> 1.3.1' # get rid of stupid warnings.
|
27
29
|
end
|
data/README.md
CHANGED
@@ -85,6 +85,22 @@ Here's a quick example, but there are more in the `examples` directory:
|
|
85
85
|
|
86
86
|
There is also more detailed documentation on each helper in the {Sinatra::Exstatic::Helpers} API docs.
|
87
87
|
|
88
|
+
## Formats ##
|
89
|
+
|
90
|
+
The time format is the result of the file's `mtime`. If you wish for a different kind of format, SHA1 of the file is available (and I may add more). To use:
|
91
|
+
|
92
|
+
require 'sinatra/exstatic_assets/formats'
|
93
|
+
register Sinatra::Exstatic
|
94
|
+
|
95
|
+
configure do
|
96
|
+
set :timestamp_format, :sha1
|
97
|
+
end
|
98
|
+
|
99
|
+
And now the value returned will be the SHA1 hash of the file. You can override the choice by passing the `timestamp_format` to the method:
|
100
|
+
|
101
|
+
= css_tag "/css/screen.css", timestamp_format: :mtime
|
102
|
+
|
103
|
+
|
88
104
|
### TODO ###
|
89
105
|
|
90
106
|
* Make it easy to pass in caching options.
|
@@ -19,13 +19,32 @@ rel="bookmark">Usage</a>
|
|
19
19
|
<header>
|
20
20
|
<h3>stylesheet_tag</h3>
|
21
21
|
</header>
|
22
|
-
<
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
<section>
|
23
|
+
<h3>Default use:</h3>
|
24
|
+
<p>By default, the querystring is appended with parameter of "ts" (for <strong>t</strong>ime<strong>s</strong>tamp) and the mtime of the file.
|
25
|
+
<p>The code:
|
26
|
+
</p>
|
27
|
+
<code>stylesheet_tag "/css/screen.css"</code>
|
28
|
+
<p>Output:</p>
|
29
|
+
<samp>
|
30
|
+
<%= Rack::Utils.escape_html( stylesheet_tag "/css/screen.css") %>
|
31
|
+
</samp>
|
32
|
+
</section>
|
33
|
+
<section>
|
34
|
+
<h3>Alternative timestamp format</h3>
|
35
|
+
<p>You can choose to take an SHA1 of the file instead:</p>
|
36
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :sha1</code>
|
37
|
+
<p>Output:</p>
|
38
|
+
<samp>
|
39
|
+
<%= Rack::Utils.escape_html( stylesheet_tag "/css/screen.css", timestamp_format: :sha1) %>
|
40
|
+
</samp>
|
41
|
+
<p>To set the timestamp for all calls, use the settings object:</p>
|
42
|
+
<code>set :timestamp_format, :sha1</code>
|
43
|
+
<p>It's set to :mtime_int by default. The timestamp formatting applies to all method calls (except for favicon, because favicons aren't cached in the same manner) but can be overridden by including the option in the method call e.g.</p>
|
44
|
+
<code>set :timestamp_format, :sha1</code>
|
45
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :mtime_int</code>
|
46
|
+
<p>Would mean that the resultant stylesheet would have an SHA1 string appended, but other method calls without the "timestamp_format: :mtime_int" would have an mtime appended.</p>
|
47
|
+
</section>
|
29
48
|
<footer><p>Also known as:
|
30
49
|
<ul>
|
31
50
|
<li><code>css_tag</code></li>
|
@@ -37,13 +56,17 @@ rel="bookmark">Usage</a>
|
|
37
56
|
<header>
|
38
57
|
<h3>javascript_tag</h3>
|
39
58
|
</header>
|
40
|
-
<
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
59
|
+
<section>
|
60
|
+
<h3>Using a URL</h3>
|
61
|
+
<p>The code:
|
62
|
+
</p>
|
63
|
+
<code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
|
64
|
+
<p>Output:</p>
|
65
|
+
<samp>
|
66
|
+
<%= Rack::Utils.escape_html( javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js") %>
|
67
|
+
</samp>
|
68
|
+
<p>When a URL is given, no timestamp is appended. If the javascript was a file, then like stylesheet_tag above, it would have a timestamp appended.</p>
|
69
|
+
</section>
|
47
70
|
<footer><p>Also known as:
|
48
71
|
<ul>
|
49
72
|
<li><code>javascript_include_tag</code></li>
|
data/examples/app2/main.rb
CHANGED
@@ -94,7 +94,7 @@ rel="bookmark">Usage</a>
|
|
94
94
|
<code>favicon_tag</code>
|
95
95
|
<p>Output:</p>
|
96
96
|
<samp>
|
97
|
-
<%= Rack::Utils.escape_html( favicon_tag
|
97
|
+
<%= Rack::Utils.escape_html( favicon_tag ) %>
|
98
98
|
</samp>
|
99
99
|
<footer><p>Also known as:
|
100
100
|
<ul>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<title>Example</title>
|
6
6
|
<%= favicon_tag %>
|
7
7
|
<%= stylesheet_tag 'http://fonts.googleapis.com/css?family=Quicksand|Faster+One|Cherry+Swash:700|Titillium+Web', rel: 'stylesheet', type: 'text/css' %>
|
8
|
-
<%= stylesheet_tag "/css/base.css" %>
|
8
|
+
<%= stylesheet_tag "/css/base.css", script_name: false %>
|
9
9
|
<%= stylesheet_tag "/css/screen.css" %>
|
10
10
|
<%= javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js" %>
|
11
11
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require_relative "../exstatic_assets.rb"
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module Exstatic
|
5
|
+
# Extends the Asset class, for using types of format
|
6
|
+
# other than mtime_int
|
7
|
+
# Just require this file and then use the format of your choice.
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# require 'sinatra/exstatic_assets/formats'
|
11
|
+
#
|
12
|
+
# configure do
|
13
|
+
# # Set all timestamps to use SHA1 of the file.
|
14
|
+
# app.set :timestamp_format, :sha1
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# # or just call it on an individual basis:
|
18
|
+
# stylesheet_tag "css/main.css", timestamp_format: :sha1
|
19
|
+
module Formats
|
20
|
+
|
21
|
+
def sha1
|
22
|
+
Digest::SHA1.file(fullpath).hexdigest
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class Asset
|
28
|
+
include Formats
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
+
require 'digest/sha1'
|
2
3
|
|
3
4
|
# @see https://sinatrarb.com/intro The framework
|
4
5
|
module Sinatra
|
@@ -57,18 +58,20 @@ module Sinatra
|
|
57
58
|
|
58
59
|
|
59
60
|
# Encapsulates an asset, be it a stylesheet, an image…
|
60
|
-
class Asset <
|
61
|
+
class Asset < String
|
61
62
|
|
62
63
|
attr_reader :fullpath
|
63
64
|
|
64
65
|
# @param [String] filename Either the file name (and path relative to the public folder) or the external HTTP link.
|
65
66
|
# @param [String] asset_dir The asset directory. When used with Sinatra this will default to the directory defined by the `public_folder` setting.
|
66
|
-
def initialize( filename, asset_dir=nil ) # TODO failure strategy
|
67
|
+
def initialize( filename, asset_dir=nil, timestamp_format=nil ) # TODO failure strategy
|
67
68
|
if asset_dir.nil?
|
69
|
+
# TODO should this strip the leading slash?
|
68
70
|
filename, asset_dir = [File.basename(filename), File.dirname(filename)]
|
69
71
|
end
|
70
72
|
# TODO fail if asset_dir.nil?
|
71
73
|
super filename
|
74
|
+
@timestamp_format = timestamp_format
|
72
75
|
@fullpath = File.join( asset_dir, filename ) unless is_uri?
|
73
76
|
end
|
74
77
|
|
@@ -76,7 +79,7 @@ module Sinatra
|
|
76
79
|
# If the asset is a local file this gets the timestamp.
|
77
80
|
# @return [Integer]
|
78
81
|
def timestamp
|
79
|
-
@timestamp ||= !is_uri? && exists? &&
|
82
|
+
@timestamp ||= !is_uri? && exists? && send(@timestamp_format)
|
80
83
|
end
|
81
84
|
|
82
85
|
# Takes the timestamp and returns it as a querystring.
|
@@ -135,16 +138,38 @@ module Sinatra
|
|
135
138
|
# @option options [TrueClass] :script_name Whether to prepend the SCRIPT_NAME env variable.
|
136
139
|
# @return [String]
|
137
140
|
# @see Sinatra::Helpers#uri
|
138
|
-
def sss_url_for(addr, options
|
139
|
-
|
140
|
-
opts = {timestamp: true}.merge options
|
141
|
-
absolute = opts.delete :absolute
|
141
|
+
def sss_url_for(addr, options, url_opts)
|
142
|
+
absolute = url_opts.delete :absolute
|
142
143
|
absolute = false if absolute.nil?
|
144
|
+
scr = url_opts.delete(:script_name)
|
143
145
|
script_name =
|
144
|
-
|
145
|
-
|
146
|
-
|
146
|
+
if addr.is_uri?
|
147
|
+
false
|
148
|
+
elsif addr.start_with?("/")
|
149
|
+
scr.nil? ?
|
150
|
+
true :
|
151
|
+
scr
|
152
|
+
else
|
153
|
+
false
|
154
|
+
end
|
147
155
|
href = uri addr, absolute, script_name
|
156
|
+
|
157
|
+
timestamp =
|
158
|
+
if scr == false
|
159
|
+
false
|
160
|
+
else
|
161
|
+
# timestamp |
|
162
|
+
# nil t
|
163
|
+
# f | f
|
164
|
+
# t | t
|
165
|
+
if options[:timestamp] && options[:timestamp] == false
|
166
|
+
false
|
167
|
+
else
|
168
|
+
true
|
169
|
+
end
|
170
|
+
end
|
171
|
+
opts = {timestamp: timestamp}.merge options
|
172
|
+
|
148
173
|
addr.respond_to?(:querystring) && opts[:timestamp] ?
|
149
174
|
"#{href}#{addr.querystring}" :
|
150
175
|
href
|
@@ -167,10 +192,9 @@ module Sinatra
|
|
167
192
|
# @option options [Hash] :url_options Options for devising the URL.
|
168
193
|
# @option options [TrueClass] :script_name Whether to prepend the SCRIPT_NAME env variable.
|
169
194
|
# @return [Tag]
|
170
|
-
def sss_stylesheet_tag(source, options
|
171
|
-
|
172
|
-
|
173
|
-
href = sss_url_for( asset, options.delete(:url_options) )
|
195
|
+
def sss_stylesheet_tag(source, options, url_opts)
|
196
|
+
asset = Asset.new source, options.delete(:asset_dir), options.delete(:timestamp_format)
|
197
|
+
href = sss_url_for asset, options, url_opts
|
174
198
|
Tag.new "link", DEFAULT_CSS.merge(:href => href)
|
175
199
|
.merge(options)
|
176
200
|
end
|
@@ -179,15 +203,15 @@ module Sinatra
|
|
179
203
|
# Default options for the javascript script tags.
|
180
204
|
DEFAULT_JS = {
|
181
205
|
# :type => "text/javascript",
|
182
|
-
:charset => "utf-8"
|
206
|
+
:charset => "utf-8",
|
183
207
|
}
|
184
208
|
|
185
209
|
|
186
210
|
# Produce a javascript script tag.
|
187
211
|
# @see #sss_stylesheet_tag but there is no `closed` option here.
|
188
|
-
def sss_javascript_tag(source, options
|
189
|
-
asset = Asset.new source,
|
190
|
-
href = sss_url_for asset, options
|
212
|
+
def sss_javascript_tag(source, options, url_opts)
|
213
|
+
asset = Asset.new source, options.delete(:asset_dir), options.delete(:timestamp_format)
|
214
|
+
href = sss_url_for asset, options, url_opts
|
191
215
|
Tag.new("script", DEFAULT_JS.merge(:src => href)
|
192
216
|
.merge(options)
|
193
217
|
) {}
|
@@ -197,19 +221,43 @@ module Sinatra
|
|
197
221
|
# Make's sure the options don't get mixed up with the other args.
|
198
222
|
def sss_extract_options(a)
|
199
223
|
opts = a.last.respond_to?(:keys) ?
|
200
|
-
a.pop :
|
224
|
+
a.pop || {} :
|
201
225
|
{}
|
202
|
-
|
203
|
-
|
226
|
+
|
227
|
+
url_opts = opts.empty? ?
|
228
|
+
{} :
|
229
|
+
{
|
230
|
+
absolute: opts.delete(:absolute),
|
231
|
+
script_name: opts.delete(:script_name),
|
232
|
+
}.reject{|k,v| v.nil? }
|
233
|
+
a = [nil] if a == []
|
234
|
+
[a, opts, url_opts]
|
204
235
|
end
|
205
236
|
|
206
237
|
|
207
238
|
# @see #sss_stylesheet_tag
|
208
|
-
def sss_image_tag(source, options
|
209
|
-
options[:src] = sss_url_for Asset.new( source,
|
239
|
+
def sss_image_tag(source, options, url_opts)
|
240
|
+
options[:src] = sss_url_for Asset.new( source, options.delete(:asset_dir), options.delete(:timestamp_format) ), options, url_opts
|
210
241
|
Tag.new "img", options
|
211
242
|
end
|
212
243
|
|
244
|
+
|
245
|
+
# @param [String] source
|
246
|
+
# @param [Hash] options
|
247
|
+
# @option options [TrueClass] :script_name whether to append the script_name environment variable or not
|
248
|
+
# @example
|
249
|
+
# favicon_tag
|
250
|
+
# # => <link href="/favicon.ico" rel="icon">
|
251
|
+
def sss_favicon_tag(source, options, url_opts)
|
252
|
+
source = "/favicon.ico" if source.nil? or source.empty?
|
253
|
+
|
254
|
+
# xhtml style like <link rel="shortcut icon" href="http://example.com/myicon.ico" />
|
255
|
+
options[:rel] ||= settings.xhtml ? "shortcut icon" : "icon"
|
256
|
+
asset = Asset.new source, options.delete(:asset_dir), options.delete(:timestamp_format)
|
257
|
+
options[:href] = sss_url_for asset, options.merge(timestamp: false), url_opts
|
258
|
+
Tag.new "link", options
|
259
|
+
end
|
260
|
+
|
213
261
|
end
|
214
262
|
|
215
263
|
# These are the helpers available to a Sinatra app using the extension.
|
@@ -259,11 +307,17 @@ module Sinatra
|
|
259
307
|
# @example
|
260
308
|
# javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"
|
261
309
|
# # => <script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
262
|
-
%w{image_tag stylesheet_tag javascript_tag}.each do |method_name|
|
263
|
-
define_method method_name do |*
|
264
|
-
list, options = sss_extract_options
|
310
|
+
%w{image_tag stylesheet_tag javascript_tag favicon_tag}.each do |method_name|
|
311
|
+
define_method method_name do |*args|
|
312
|
+
list, options, url_opts = sss_extract_options args
|
265
313
|
list.map {|source|
|
266
|
-
|
314
|
+
asset_dir = options.delete(:asset_dir) ||
|
315
|
+
settings.static_assets_dir ||
|
316
|
+
settings.public_folder
|
317
|
+
timestamp_format = options.delete(:timestamp_format) ||
|
318
|
+
settings.timestamp_format ||
|
319
|
+
:mtime_int # default
|
320
|
+
send "sss_#{method_name}", source, options.merge({asset_dir: asset_dir, timestamp_format: timestamp_format}), url_opts
|
267
321
|
}.join "\n"
|
268
322
|
end
|
269
323
|
end
|
@@ -276,34 +330,18 @@ module Sinatra
|
|
276
330
|
alias_method :js_tag, :javascript_tag
|
277
331
|
alias_method :script_tag, :javascript_tag
|
278
332
|
|
279
|
-
# @param [String] source
|
280
|
-
# @param [Hash] options
|
281
|
-
# @option options [Hash] :url_options script_name
|
282
|
-
# @example
|
283
|
-
# favicon_tag
|
284
|
-
# # => <link href="/favicon.ico" rel="icon">
|
285
|
-
def favicon_tag(*args)
|
286
|
-
source, options = sss_extract_options args
|
287
|
-
source = "favicon.ico" if source.nil? or source.empty?
|
288
|
-
|
289
|
-
# xhtml style like <link rel="shortcut icon" href="http://example.com/myicon.ico" />
|
290
|
-
options[:rel] ||= settings.xhtml ? "shortcut icon" : "icon"
|
291
|
-
|
292
|
-
url_options = options.delete(:url_options) || {}
|
293
|
-
options[:href] = sss_url_for(Asset.new(source), url_options.merge(timestamp: false))
|
294
|
-
|
295
|
-
Tag.new "link", options
|
296
|
-
end
|
297
|
-
|
298
333
|
alias_method :link_favicon_tag, :favicon_tag
|
299
334
|
alias_method :favicon, :favicon_tag
|
300
335
|
|
301
336
|
end
|
302
337
|
|
303
338
|
# Extending
|
339
|
+
# @see {Sinatra::Exstatic::Formats} to extend timestamp_format
|
304
340
|
def self.registered(app)
|
305
341
|
app.helpers Exstatic::Helpers
|
306
342
|
app.disable :xhtml
|
343
|
+
app.set :static_assets_dir, nil
|
344
|
+
app.set :timestamp_format, :mtime_int
|
307
345
|
end
|
308
346
|
end
|
309
347
|
|
data/spec/spec_helper.rb
CHANGED
@@ -3,12 +3,13 @@
|
|
3
3
|
require 'rspec'
|
4
4
|
require 'rspec/its'
|
5
5
|
Spec_dir = File.expand_path( File.dirname __FILE__ )
|
6
|
-
|
6
|
+
require 'pry-byebug' if ENV["DEBUG"]
|
7
7
|
|
8
8
|
# code coverage
|
9
9
|
require 'simplecov'
|
10
10
|
SimpleCov.start do
|
11
11
|
add_filter "/vendor/"
|
12
|
+
add_filter "/vendor.noindex/"
|
12
13
|
add_filter "/bin/"
|
13
14
|
end
|
14
15
|
|
@@ -33,7 +34,11 @@ require 'rack/test/accepts'
|
|
33
34
|
require 'timecop'
|
34
35
|
|
35
36
|
RSpec.configure do |config|
|
36
|
-
config.
|
37
|
+
config.mock_with :mocha
|
38
|
+
|
39
|
+
config.expect_with :rspec do |c|
|
40
|
+
c.syntax = [:should, :expect]
|
41
|
+
end
|
37
42
|
|
38
43
|
config.include Rack::Test::Accepts, :type => :request
|
39
44
|
|
data/spec/static_assets_spec.rb
CHANGED
@@ -1,30 +1,62 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require_relative "../lib/sinatra/exstatic_assets.rb"
|
2
|
+
#require_relative "../lib/sinatra/exstatic_assets.rb"
|
3
|
+
require_relative "../lib/sinatra/exstatic_assets/formats.rb"
|
3
4
|
|
4
5
|
module Sinatra
|
5
6
|
module Exstatic
|
6
7
|
|
8
|
+
shared_context "mtime timestamp" do
|
9
|
+
before(:each) do
|
10
|
+
File.expects(:"exists?").with(fullpath)
|
11
|
+
.at_least_once
|
12
|
+
.returns(true)
|
13
|
+
File.expects(:mtime).with(fullpath)
|
14
|
+
.returns(time)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
7
18
|
describe Asset, :time_sensitive do
|
19
|
+
shared_examples "for Asset file" do
|
20
|
+
its(:fullpath) { should == fullpath }
|
21
|
+
its(:"is_uri?") { should be_falsy }
|
22
|
+
it { should_not be_nil }
|
23
|
+
it { should == expected }
|
24
|
+
end
|
8
25
|
let(:asset_dir) { "app/public" }
|
9
|
-
|
26
|
+
let(:time) { Time.now }
|
27
|
+
subject(:asset){ Asset.new filename, asset_dir, timestamp_format }
|
10
28
|
context "Given a file" do
|
11
|
-
let(:fullpath) { File.join asset_dir, filename }
|
12
|
-
before do
|
13
|
-
File.stub(:"exists?").with(fullpath).and_return(true)
|
14
|
-
File.stub(:mtime).with(fullpath).and_return(Time.now)
|
15
|
-
end
|
16
29
|
let(:filename) { "image.jpg" }
|
17
30
|
let(:expected) { "image.jpg" }
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
31
|
+
let(:fullpath) { File.join asset_dir, filename }
|
32
|
+
context "Using mtime as the timestamp" do
|
33
|
+
let(:timestamp_format) { :mtime_int }
|
34
|
+
context "" do
|
35
|
+
include_context "mtime timestamp"
|
36
|
+
its(:timestamp) { should == Time.now.to_i }
|
37
|
+
its(:querystring) { should == "?ts=#{Time.now.to_i}" }
|
38
|
+
end
|
39
|
+
include_examples "for Asset file"
|
40
|
+
end
|
41
|
+
context "Using sha1 as the timestamp" do
|
42
|
+
let(:timestamp_format) { :sha1 }
|
43
|
+
context "" do
|
44
|
+
before do
|
45
|
+
digest_mock = mock()
|
46
|
+
digest_mock.expects(:hexdigest).returns( "871cb4397c5f5f146cc5583088b12c7d0a8ddc97" )
|
47
|
+
File.expects(:"exists?").with(fullpath).returns(true)
|
48
|
+
Digest::SHA1.expects(:file).with(fullpath).returns(digest_mock)
|
49
|
+
end
|
50
|
+
its(:timestamp) { should == "871cb4397c5f5f146cc5583088b12c7d0a8ddc97" }
|
51
|
+
its(:querystring) { should == %Q!?ts=#{"871cb4397c5f5f146cc5583088b12c7d0a8ddc97"}! }
|
52
|
+
end
|
53
|
+
include_examples "for Asset file"
|
54
|
+
end
|
24
55
|
end
|
25
56
|
context "Given a url" do
|
26
57
|
let(:filename) { "http://code.jquery.com/jquery-1.9.1.min.js" }
|
27
58
|
let(:expected) { "http://code.jquery.com/jquery-1.9.1.min.js" }
|
59
|
+
let(:timestamp_format) { :mtime_int }
|
28
60
|
it { should_not be_nil }
|
29
61
|
it { should == expected }
|
30
62
|
its(:fullpath) { should be_nil }
|
@@ -69,8 +101,11 @@ end
|
|
69
101
|
|
70
102
|
class FakeObject
|
71
103
|
include Sinatra::Exstatic::Private
|
72
|
-
def
|
73
|
-
|
104
|
+
def initialize script_name=nil
|
105
|
+
@script_name = script_name || public_folder
|
106
|
+
end
|
107
|
+
def uri( addr, absolute, script_name )
|
108
|
+
script_name ? File.join( @script_name, addr) : addr
|
74
109
|
end
|
75
110
|
def settings
|
76
111
|
self
|
@@ -78,58 +113,78 @@ class FakeObject
|
|
78
113
|
def public_folder
|
79
114
|
"app/public"
|
80
115
|
end
|
116
|
+
def xhtml
|
117
|
+
@xhtml ||= false
|
118
|
+
end
|
81
119
|
end
|
120
|
+
|
82
121
|
describe "Private methods", :time_sensitive do
|
122
|
+
let(:script_name) { "/bar" }
|
123
|
+
let(:fullpath) { File.join asset_dir, filename }
|
124
|
+
let(:asset_dir) { "app/public/" }
|
125
|
+
let(:time) { Time.now.to_i }
|
126
|
+
let(:timestamp_format) { :mtime_int }
|
83
127
|
let(:o) {
|
84
128
|
# A double, I couldn't get RSpec's to work with this
|
85
129
|
# probably because they're not well documented
|
86
130
|
# hint hint RSpec team
|
87
|
-
o = FakeObject.new
|
131
|
+
o = FakeObject.new script_name
|
88
132
|
}
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
it { should == expected }
|
133
|
+
|
134
|
+
context "Favicon" do
|
135
|
+
let(:url) { "/favicon.ico" }
|
136
|
+
let(:filename) { "favicon.ico" }
|
137
|
+
let(:expected) { %Q!<link href="/bar/favicon.ico" rel="icon" />! }
|
138
|
+
subject {
|
139
|
+
o.send :sss_favicon_tag, url, {asset_dir: asset_dir}, {}
|
140
|
+
}
|
141
|
+
it { should == expected }
|
142
|
+
end
|
143
|
+
context "Accessing the file system" do
|
144
|
+
include_context "mtime timestamp" do
|
145
|
+
end
|
146
|
+
context "Stylesheets" do
|
147
|
+
before do
|
148
|
+
ENV["SCRIPT_NAME"] = script_name
|
106
149
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
150
|
+
let(:url) { "/stylesheets/winter.css" }
|
151
|
+
let(:filename) { "/stylesheets/winter.css" }
|
152
|
+
context "Given a filename" do
|
153
|
+
context "But no options" do
|
154
|
+
let(:expected) { %Q!<link charset="utf-8" href="/bar/stylesheets/winter.css?ts=#{time}" media="screen" rel="stylesheet" />! }
|
155
|
+
subject { o.send :sss_stylesheet_tag, url, {asset_dir: asset_dir, timestamp_format: timestamp_format}, {} }
|
156
|
+
it { should == expected }
|
157
|
+
end
|
158
|
+
context "with options" do
|
159
|
+
context "media=print" do
|
160
|
+
let(:expected) { %Q!<link charset="utf-8" href="/bar/stylesheets/winter.css?ts=#{time}" media="print" rel="stylesheet" />! }
|
161
|
+
subject { o.send :sss_stylesheet_tag, url, {asset_dir: asset_dir,media: "print", timestamp_format: timestamp_format}, {} }
|
162
|
+
it { should == expected }
|
163
|
+
end
|
112
164
|
end
|
113
165
|
end
|
114
166
|
end
|
115
|
-
|
116
|
-
end
|
117
|
-
context "Javascripts" do
|
167
|
+
context "Javascripts" do
|
118
168
|
let(:url) { "/js/get_stuff.js" }
|
119
169
|
let(:filename) { "/js/get_stuff.js" }
|
120
170
|
let(:expected) { %Q!<script charset="utf-8" src="/bar/js/get_stuff.js?ts=#{time}"></script>! }
|
121
|
-
subject { o.send :sss_javascript_tag, url }
|
171
|
+
subject { o.send :sss_javascript_tag, url, {asset_dir: asset_dir, timestamp_format: timestamp_format}, {} }
|
122
172
|
it { should_not be_nil }
|
123
173
|
it { should == expected }
|
124
174
|
end
|
175
|
+
end
|
125
176
|
context "Images" do
|
126
177
|
context "Local" do
|
127
178
|
let(:url) { "/images/foo.png" }
|
128
179
|
let(:filename) { "/images/foo.png" }
|
129
180
|
let(:expected) { %Q!<img src="/bar/images/foo.png?ts=#{time}" />! }
|
130
|
-
subject { o.send :sss_image_tag, url }
|
131
|
-
|
132
|
-
|
181
|
+
subject { o.send :sss_image_tag, url, {asset_dir: asset_dir, timestamp_format: :mtime_int}, {} }
|
182
|
+
|
183
|
+
context "Using mtime as the timestamp" do
|
184
|
+
include_context "mtime timestamp"
|
185
|
+
it { should_not be_nil }
|
186
|
+
it { should == expected }
|
187
|
+
end
|
133
188
|
end
|
134
189
|
context "Remote" do
|
135
190
|
let(:url) { "http://example.org/images/foo.png" }
|
@@ -137,7 +192,7 @@ describe "Private methods", :time_sensitive do
|
|
137
192
|
let(:expected) { %Q!<img src="#{url}" />! }
|
138
193
|
subject {
|
139
194
|
o.send :sss_image_tag,
|
140
|
-
url
|
195
|
+
url, {asset_dir: asset_dir}, {}
|
141
196
|
}
|
142
197
|
it { should_not be_nil }
|
143
198
|
it { should == expected }
|
@@ -148,7 +203,7 @@ describe "Private methods", :time_sensitive do
|
|
148
203
|
let(:expected) { %Q!<img src="#{url}" />! }
|
149
204
|
subject {
|
150
205
|
o.send :sss_image_tag,
|
151
|
-
url
|
206
|
+
url, {asset_dir: asset_dir}, {}
|
152
207
|
}
|
153
208
|
it { should_not be_nil }
|
154
209
|
it { should == expected }
|
@@ -164,12 +219,14 @@ describe "Using them with a Sinatra app", :time_sensitive do
|
|
164
219
|
let(:expected) { File.read File.expand_path(fixture_file, File.dirname(__FILE__)) }
|
165
220
|
before do
|
166
221
|
Sinatra::Exstatic::Asset.any_instance
|
167
|
-
.
|
168
|
-
.
|
222
|
+
.expects(:exists?)
|
223
|
+
.at_least_once
|
224
|
+
.returns(true)
|
169
225
|
|
170
226
|
Sinatra::Exstatic::Asset.any_instance
|
171
|
-
.
|
172
|
-
.
|
227
|
+
.expects(:mtime_int)
|
228
|
+
.at_least_once
|
229
|
+
.returns(1367612251)
|
173
230
|
end
|
174
231
|
context "Main" do
|
175
232
|
context "/" do
|
@@ -210,5 +267,14 @@ describe "Using them with a Sinatra app", :time_sensitive do
|
|
210
267
|
subject { last_response.body }
|
211
268
|
it { should == expected }
|
212
269
|
end
|
270
|
+
context "/app2/deeper" do
|
271
|
+
let(:fixture_file) { "./support/fixtures/app2-deeper.html" }
|
272
|
+
before do
|
273
|
+
get "/app2/deeper/and-deeper"
|
274
|
+
end
|
275
|
+
it_should_behave_like "Any route"
|
276
|
+
subject { last_response.body }
|
277
|
+
it { should == expected }
|
278
|
+
end
|
213
279
|
end
|
214
280
|
end
|
@@ -50,13 +50,32 @@ rel="bookmark">Usage</a>
|
|
50
50
|
<header>
|
51
51
|
<h3>stylesheet_tag</h3>
|
52
52
|
</header>
|
53
|
-
<
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
<section>
|
54
|
+
<h3>Default use:</h3>
|
55
|
+
<p>By default, the querystring is appended with parameter of "ts" (for <strong>t</strong>ime<strong>s</strong>tamp) and the mtime of the file.
|
56
|
+
<p>The code:
|
57
|
+
</p>
|
58
|
+
<code>stylesheet_tag "/css/screen.css"</code>
|
59
|
+
<p>Output:</p>
|
60
|
+
<samp>
|
61
|
+
<link charset="utf-8" href="/css/screen.css?ts=1367612251" media="screen" rel="stylesheet" />
|
62
|
+
</samp>
|
63
|
+
</section>
|
64
|
+
<section>
|
65
|
+
<h3>Alternative timestamp format</h3>
|
66
|
+
<p>You can choose to take an SHA1 of the file instead:</p>
|
67
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :sha1</code>
|
68
|
+
<p>Output:</p>
|
69
|
+
<samp>
|
70
|
+
<link charset="utf-8" href="/css/screen.css?ts=702cc1975cea5e2e6558680532502615c01e762a" media="screen" rel="stylesheet" />
|
71
|
+
</samp>
|
72
|
+
<p>To set the timestamp for all calls, use the settings object:</p>
|
73
|
+
<code>set :timestamp_format, :sha1</code>
|
74
|
+
<p>It's set to :mtime_int by default. The timestamp formatting applies to all method calls (except for favicon, because favicons aren't cached in the same manner) but can be overridden by including the option in the method call e.g.</p>
|
75
|
+
<code>set :timestamp_format, :sha1</code>
|
76
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :mtime_int</code>
|
77
|
+
<p>Would mean that the resultant stylesheet would have an SHA1 string appended, but other method calls without the "timestamp_format: :mtime_int" would have an mtime appended.</p>
|
78
|
+
</section>
|
60
79
|
<footer><p>Also known as:
|
61
80
|
<ul>
|
62
81
|
<li><code>css_tag</code></li>
|
@@ -68,13 +87,17 @@ rel="bookmark">Usage</a>
|
|
68
87
|
<header>
|
69
88
|
<h3>javascript_tag</h3>
|
70
89
|
</header>
|
71
|
-
<
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
90
|
+
<section>
|
91
|
+
<h3>Using a URL</h3>
|
92
|
+
<p>The code:
|
93
|
+
</p>
|
94
|
+
<code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
|
95
|
+
<p>Output:</p>
|
96
|
+
<samp>
|
97
|
+
<script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
98
|
+
</samp>
|
99
|
+
<p>When a URL is given, no timestamp is appended. If the javascript was a file, then like stylesheet_tag above, it would have a timestamp appended.</p>
|
100
|
+
</section>
|
78
101
|
<footer><p>Also known as:
|
79
102
|
<ul>
|
80
103
|
<li><code>javascript_include_tag</code></li>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<title>Example</title>
|
6
6
|
<link href="/app2/favicon.ico" rel="icon" />
|
7
7
|
<link charset="utf-8" href="http://fonts.googleapis.com/css?family=Quicksand|Faster+One|Cherry+Swash:700|Titillium+Web" media="screen" rel="stylesheet" type="text/css" />
|
8
|
-
<link charset="utf-8" href="/
|
8
|
+
<link charset="utf-8" href="/css/base.css" media="screen" rel="stylesheet" />
|
9
9
|
<link charset="utf-8" href="/app2/css/screen.css?ts=1367612251" media="screen" rel="stylesheet" />
|
10
10
|
<script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
11
11
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<title>Example</title>
|
6
6
|
<link href="/app2/favicon.ico" rel="icon" />
|
7
7
|
<link charset="utf-8" href="http://fonts.googleapis.com/css?family=Quicksand|Faster+One|Cherry+Swash:700|Titillium+Web" media="screen" rel="stylesheet" type="text/css" />
|
8
|
-
<link charset="utf-8" href="/
|
8
|
+
<link charset="utf-8" href="/css/base.css" media="screen" rel="stylesheet" />
|
9
9
|
<link charset="utf-8" href="/app2/css/screen.css?ts=1367612251" media="screen" rel="stylesheet" />
|
10
10
|
<script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
11
11
|
|
@@ -50,13 +50,32 @@ rel="bookmark">Usage</a>
|
|
50
50
|
<header>
|
51
51
|
<h3>stylesheet_tag</h3>
|
52
52
|
</header>
|
53
|
-
<
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
53
|
+
<section>
|
54
|
+
<h3>Default use:</h3>
|
55
|
+
<p>By default, the querystring is appended with parameter of "ts" (for <strong>t</strong>ime<strong>s</strong>tamp) and the mtime of the file.
|
56
|
+
<p>The code:
|
57
|
+
</p>
|
58
|
+
<code>stylesheet_tag "/css/screen.css"</code>
|
59
|
+
<p>Output:</p>
|
60
|
+
<samp>
|
61
|
+
<link charset="utf-8" href="/css/screen.css?ts=1367612251" media="screen" rel="stylesheet" />
|
62
|
+
</samp>
|
63
|
+
</section>
|
64
|
+
<section>
|
65
|
+
<h3>Alternative timestamp format</h3>
|
66
|
+
<p>You can choose to take an SHA1 of the file instead:</p>
|
67
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :sha1</code>
|
68
|
+
<p>Output:</p>
|
69
|
+
<samp>
|
70
|
+
<link charset="utf-8" href="/css/screen.css?ts=702cc1975cea5e2e6558680532502615c01e762a" media="screen" rel="stylesheet" />
|
71
|
+
</samp>
|
72
|
+
<p>To set the timestamp for all calls, use the settings object:</p>
|
73
|
+
<code>set :timestamp_format, :sha1</code>
|
74
|
+
<p>It's set to :mtime_int by default. The timestamp formatting applies to all method calls (except for favicon, because favicons aren't cached in the same manner) but can be overridden by including the option in the method call e.g.</p>
|
75
|
+
<code>set :timestamp_format, :sha1</code>
|
76
|
+
<code>stylesheet_tag "/css/screen.css", timestamp_format: :mtime_int</code>
|
77
|
+
<p>Would mean that the resultant stylesheet would have an SHA1 string appended, but other method calls without the "timestamp_format: :mtime_int" would have an mtime appended.</p>
|
78
|
+
</section>
|
60
79
|
<footer><p>Also known as:
|
61
80
|
<ul>
|
62
81
|
<li><code>css_tag</code></li>
|
@@ -68,13 +87,17 @@ rel="bookmark">Usage</a>
|
|
68
87
|
<header>
|
69
88
|
<h3>javascript_tag</h3>
|
70
89
|
</header>
|
71
|
-
<
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
90
|
+
<section>
|
91
|
+
<h3>Using a URL</h3>
|
92
|
+
<p>The code:
|
93
|
+
</p>
|
94
|
+
<code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
|
95
|
+
<p>Output:</p>
|
96
|
+
<samp>
|
97
|
+
<script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
98
|
+
</samp>
|
99
|
+
<p>When a URL is given, no timestamp is appended. If the javascript was a file, then like stylesheet_tag above, it would have a timestamp appended.</p>
|
100
|
+
</section>
|
78
101
|
<footer><p>Also known as:
|
79
102
|
<ul>
|
80
103
|
<li><code>javascript_include_tag</code></li>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-exstatic-assets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Włodek Bzyl
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2016-12-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -49,7 +49,6 @@ files:
|
|
49
49
|
- examples/app/views/index.erb
|
50
50
|
- examples/app/views/layout.erb
|
51
51
|
- examples/app2/main.rb
|
52
|
-
- examples/app2/public/css/base.css
|
53
52
|
- examples/app2/public/css/screen.css
|
54
53
|
- examples/app2/public/favicon.ico
|
55
54
|
- examples/app2/public/js/helpers.js
|
@@ -61,6 +60,7 @@ files:
|
|
61
60
|
- examples/views/layout.erb
|
62
61
|
- lib/sinatra/exstatic-assets.rb
|
63
62
|
- lib/sinatra/exstatic_assets.rb
|
63
|
+
- lib/sinatra/exstatic_assets/formats.rb
|
64
64
|
- lib/sinatra/exstatic_assets/version.rb
|
65
65
|
- sinatra-exstatic-assets.gemspec
|
66
66
|
- spec/spec_helper.rb
|
@@ -1,93 +0,0 @@
|
|
1
|
-
body {
|
2
|
-
width: 480px; margin: 0px 30px;
|
3
|
-
}
|
4
|
-
|
5
|
-
|
6
|
-
header[role=banner] h2 {
|
7
|
-
font-size: 18px; margin: 0px; color: #888;
|
8
|
-
font-style: italic;
|
9
|
-
}
|
10
|
-
|
11
|
-
ul { list-style-type: none }
|
12
|
-
|
13
|
-
nav ul {
|
14
|
-
list-style: none; padding: 0px; display: block;
|
15
|
-
clear: right; background-color: #888;
|
16
|
-
padding-left: 4px; height: 24px;
|
17
|
-
}
|
18
|
-
|
19
|
-
nav ul li {
|
20
|
-
display: inline; padding: 0px 20px 5px 10px;
|
21
|
-
height: 24px; border-right: 1px solid #ccc;
|
22
|
-
}
|
23
|
-
|
24
|
-
nav ul li a {
|
25
|
-
color: #993333; text-decoration: none;
|
26
|
-
font-size: 13px; font-weight: bold;
|
27
|
-
}
|
28
|
-
|
29
|
-
nav ul li a:hover {
|
30
|
-
color: #fff;
|
31
|
-
}
|
32
|
-
|
33
|
-
article > header time {
|
34
|
-
font-size: 14px; display: block; width: 26px;
|
35
|
-
padding: 2px; text-align: center; background-color: #993333;
|
36
|
-
color: #fff; font-weight: bold; -moz-border-radius: 6px;
|
37
|
-
-webkit-border-radius: 6px; border-radius: 6px; float: left;
|
38
|
-
margin-bottom: 10px;
|
39
|
-
}
|
40
|
-
|
41
|
-
article > header time span {
|
42
|
-
font-size: 10px; font-weight: normal;
|
43
|
-
text-transform: uppercase;
|
44
|
-
}
|
45
|
-
|
46
|
-
article > header h1 {
|
47
|
-
font-size: 30px; float: left;
|
48
|
-
margin:0;
|
49
|
-
margin-left: 14px;
|
50
|
-
text-shadow: 2px 2px 5px #333;
|
51
|
-
}
|
52
|
-
|
53
|
-
article > header h1 a {
|
54
|
-
color: #993333;
|
55
|
-
}
|
56
|
-
|
57
|
-
section > header h3 {
|
58
|
-
border-bottom: 1px dotted;
|
59
|
-
border-left: 1px dotted;
|
60
|
-
padding-left: 5px;
|
61
|
-
}
|
62
|
-
|
63
|
-
article > section {
|
64
|
-
margin-top: 30px;
|
65
|
-
}
|
66
|
-
article section section{
|
67
|
-
margin-bottom: 40px;
|
68
|
-
}
|
69
|
-
|
70
|
-
article > section header h1 {
|
71
|
-
font-size: 16px;
|
72
|
-
}
|
73
|
-
|
74
|
-
article p {
|
75
|
-
clear: both;
|
76
|
-
}
|
77
|
-
|
78
|
-
footer p {
|
79
|
-
text-align: center; font-size: 12px;
|
80
|
-
color: #888; margin-top: 24px;
|
81
|
-
}
|
82
|
-
|
83
|
-
code {
|
84
|
-
background: #E6E6E6;
|
85
|
-
border: 1px solid #D8D8D8;
|
86
|
-
display: block;
|
87
|
-
margin-bottom: 10px;
|
88
|
-
}
|
89
|
-
|
90
|
-
samp {
|
91
|
-
background: #F5F6CE;
|
92
|
-
display: block;
|
93
|
-
}
|