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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e890fc9a6623a37de61efc6f09c3684ec341dcb3
4
- data.tar.gz: 2b8bc414cecc3afeb0a00f1cd2cc4aa5a66b2d13
3
+ metadata.gz: 05a627944971257f2082ae5953e5df01c2f65965
4
+ data.tar.gz: edeb2413e71cd8139c9d44618be83f2c2ddfb450
5
5
  SHA512:
6
- metadata.gz: ddcf6487fe8a4d35c2e24d5f5364a28acacc1bc92b395f2f285f7c6e9543bac3ae8cd9c6d273f286e4af354f0e156ac8a456f817f5274f828a543910aea34e71
7
- data.tar.gz: 844dafe2fb40aa786c3465a02a3d5a1c0c2289c916796d00e35a62b29ca0158d56b49523a6a70536921a5dfbf85a83941c8e2d8d7de527f800a901a97b641094
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
- <p>The code:
23
- </p>
24
- <code>stylesheet_tag "/css/screen.css"</code>
25
- <p>Output:</p>
26
- <samp>
27
- <%= Rack::Utils.escape_html( stylesheet_tag "/css/screen.css") %>
28
- </samp>
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
- <p>The code:
41
- </p>
42
- <code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
43
- <p>Output:</p>
44
- <samp>
45
- <%= Rack::Utils.escape_html( javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js") %>
46
- </samp>
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>
@@ -17,5 +17,9 @@ module Example
17
17
  get "/deeper" do
18
18
  erb :index
19
19
  end
20
+
21
+ get "/deeper/and-deeper" do
22
+ erb :index
23
+ end
20
24
  end
21
25
  end
@@ -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 url_options: {script_tag: false} ) %>
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,6 +1,6 @@
1
1
  module Sinatra
2
2
  module Exstatic
3
3
  # Library version
4
- VERSION = "3.0.0"
4
+ VERSION = "3.1.0"
5
5
  end
6
6
  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 < ::String
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? && mtime_int
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=nil)
139
- options ||= {}
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
- !addr.is_uri? ||
145
- opts.delete(:script_name) && addr.start_with?("/")
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
- asset_dir = options.delete(:asset_dir) || settings.public_folder
172
- asset = Asset.new source, asset_dir
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, settings.public_folder
190
- href = sss_url_for asset, options.delete(:url_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
- opts ||= {}
203
- [a, opts]
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, settings.public_folder ), options.delete(:url_options)
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 |*sources|
264
- list, options = sss_extract_options sources
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
- send "sss_#{method_name}", source, options
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.treat_symbols_as_metadata_keys_with_true_values = true
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
 
@@ -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
- subject(:asset){ Asset.new filename, asset_dir }
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
- it { should_not be_nil }
19
- it { should == expected }
20
- its(:fullpath) { should == fullpath }
21
- its(:timestamp) { should == Time.now.to_i }
22
- its(:"is_uri?") { should be_falsy }
23
- its(:querystring) { should == "?ts=#{Time.now.to_i}" }
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 uri( addr, absolute, script_tag )
73
- script_tag ? File.join( ENV["SCRIPT_NAME"], addr) : addr
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
- let(:script_name) { "/bar" }
90
- let(:fullpath) { File.join asset_dir, filename }
91
- let(:asset_dir) { "app/public/" }
92
- let(:time) { Time.now.to_i }
93
- before do
94
- ENV["SCRIPT_NAME"] = script_name
95
- File.stub(:"exists?").with(fullpath).and_return(true)
96
- File.stub(:mtime).with(fullpath).and_return(time)
97
- end
98
- context "Stylesheets" do
99
- let(:url) { "/stylesheets/winter.css" }
100
- let(:filename) { "/stylesheets/winter.css" }
101
- context "Given a filename" do
102
- context "But no options" do
103
- let(:expected) { %Q!<link charset="utf-8" href="/bar/stylesheets/winter.css?ts=#{time}" media="screen" rel="stylesheet" />! }
104
- subject { o.send :sss_stylesheet_tag, url }
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
- context "with options" do
108
- context "media=print" do
109
- let(:expected) { %Q!<link charset="utf-8" href="/bar/stylesheets/winter.css?ts=#{time}" media="print" rel="stylesheet" />! }
110
- subject { o.send :sss_stylesheet_tag, url, media: "print" }
111
- it { should == expected }
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
- it { should_not be_nil }
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
- it { should_not be_nil }
132
- it { should == expected }
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
- .stub(:exists?)
168
- .and_return(true)
222
+ .expects(:exists?)
223
+ .at_least_once
224
+ .returns(true)
169
225
 
170
226
  Sinatra::Exstatic::Asset.any_instance
171
- .stub(:mtime_int)
172
- .and_return(1367612251)
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
- <p>The code:
54
- </p>
55
- <code>stylesheet_tag "/css/screen.css"</code>
56
- <p>Output:</p>
57
- <samp>
58
- &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=1367612251&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
59
- </samp>
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
+ &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=1367612251&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
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
+ &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=702cc1975cea5e2e6558680532502615c01e762a&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
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
- <p>The code:
72
- </p>
73
- <code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
74
- <p>Output:</p>
75
- <samp>
76
- &lt;script charset=&quot;utf-8&quot; src=&quot;http:&#x2F;&#x2F;code.jquery.com&#x2F;jquery-1.9.1.min.js&quot;&gt;&lt;&#x2F;script&gt;
77
- </samp>
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
+ &lt;script charset=&quot;utf-8&quot; src=&quot;http:&#x2F;&#x2F;code.jquery.com&#x2F;jquery-1.9.1.min.js&quot;&gt;&lt;&#x2F;script&gt;
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="/app2/css/base.css?ts=1367612251" media="screen" rel="stylesheet" />
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="/app2/css/base.css?ts=1367612251" media="screen" rel="stylesheet" />
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
- <p>The code:
54
- </p>
55
- <code>stylesheet_tag "/css/screen.css"</code>
56
- <p>Output:</p>
57
- <samp>
58
- &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=1367612251&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
59
- </samp>
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
+ &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=1367612251&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
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
+ &lt;link charset=&quot;utf-8&quot; href=&quot;&#x2F;css&#x2F;screen.css?ts=702cc1975cea5e2e6558680532502615c01e762a&quot; media=&quot;screen&quot; rel=&quot;stylesheet&quot; &#x2F;&gt;
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
- <p>The code:
72
- </p>
73
- <code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
74
- <p>Output:</p>
75
- <samp>
76
- &lt;script charset=&quot;utf-8&quot; src=&quot;http:&#x2F;&#x2F;code.jquery.com&#x2F;jquery-1.9.1.min.js&quot;&gt;&lt;&#x2F;script&gt;
77
- </samp>
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
+ &lt;script charset=&quot;utf-8&quot; src=&quot;http:&#x2F;&#x2F;code.jquery.com&#x2F;jquery-1.9.1.min.js&quot;&gt;&lt;&#x2F;script&gt;
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.0.0
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: 2015-08-26 00:00:00.000000000 Z
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
- }