sinatra-exstatic-assets 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/.travis.yml +11 -0
- data/CHANGES.md +9 -0
- data/Gemfile +29 -0
- data/LICENCE +27 -0
- data/README.md +76 -0
- data/Rakefile +38 -0
- data/examples/app/main.rb +13 -0
- data/examples/app/public/css/base.css +93 -0
- data/examples/app/public/css/screen.css +8 -0
- data/examples/app/public/favicon.ico +0 -0
- data/examples/app/public/images/3609420787_f7fc0e53c7.jpg +0 -0
- data/examples/app/views/index.erb +90 -0
- data/examples/app/views/layout.erb +40 -0
- data/examples/app2/main.rb +12 -0
- data/examples/app2/public/css/base.css +93 -0
- data/examples/app2/public/css/screen.css +8 -0
- data/examples/app2/public/favicon +0 -0
- data/examples/app2/views/index.erb +105 -0
- data/examples/app2/views/layout.erb +40 -0
- data/examples/config.rb +17 -0
- data/examples/config.ru +11 -0
- data/examples/views/index.erb +83 -0
- data/examples/views/layout.erb +40 -0
- data/lib/sinatra/exstatic-assets.rb +1 -0
- data/lib/sinatra/exstatic_assets.rb +304 -0
- data/lib/sinatra/exstatic_assets/version.rb +6 -0
- data/sinatra-exstatic-assets.gemspec +20 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/static_assets_spec.rb +156 -0
- data/spec/support/fixtures/app2.txt +144 -0
- data/spec/support/fixtures/main.txt +129 -0
- data/spec/support/shared/all_routes.rb +20 -0
- metadata +101 -0
@@ -0,0 +1,93 @@
|
|
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
|
+
}
|
Binary file
|
@@ -0,0 +1,105 @@
|
|
1
|
+
<header>
|
2
|
+
<h1>
|
3
|
+
<a href="#" title="Link to this post"
|
4
|
+
rel="bookmark">Usage</a>
|
5
|
+
</h1>
|
6
|
+
</header>
|
7
|
+
<p>Note that there is a Main App and an App2. This is to demonstrate that you can give links relative to the app, but if they are mounted with an extra prefix (using Rack#map, for example) the helper will respect that and produce the right href attribute.</p>
|
8
|
+
<section><header><h2>Installation and loading</h2></header>
|
9
|
+
<p>Start by installing:</p>
|
10
|
+
<code>gem 'sinatra-exstatic-assets'</code>
|
11
|
+
<p>Then require it in your Sinatra app.</p>
|
12
|
+
<code>require 'sinatra/exstatic_assets'</code>
|
13
|
+
</section>
|
14
|
+
<section>
|
15
|
+
<header><h2>The helpers</h2></header>
|
16
|
+
<p>Use these helpers in your views.</p>
|
17
|
+
<p>To add an attribute to a helper, pass it as an option, e.g. <code>width: "500"</code></p>
|
18
|
+
<p>Sometimes, you won't want the script tag (e.g. "/app2") prepended to the url, like in the case of a favicon. In that case you can pass in <code>url_options: {script_tag: false}</code> to the helper, e.g. <code>favicon_tag url_options: {script_tag: false}</code></p>
|
19
|
+
<section id='stylesheet_tag'>
|
20
|
+
<header>
|
21
|
+
<h3>stylesheet_tag</h3>
|
22
|
+
</header>
|
23
|
+
<p>The code:
|
24
|
+
</p>
|
25
|
+
<code>stylesheet_tag "/css/screen.css"</code>
|
26
|
+
<p>Output:</p>
|
27
|
+
<samp>
|
28
|
+
<%= Rack::Utils.escape_html( stylesheet_tag "/css/screen.css") %>
|
29
|
+
</samp>
|
30
|
+
<footer><p>Also known as:
|
31
|
+
<ul>
|
32
|
+
<li><code>css_tag</code></li>
|
33
|
+
<li><code>stylesheet</code></li>
|
34
|
+
</ul>
|
35
|
+
</footer>
|
36
|
+
</section>
|
37
|
+
<section id='javascript_tag'>
|
38
|
+
<header>
|
39
|
+
<h3>javascript_tag</h3>
|
40
|
+
</header>
|
41
|
+
<p>The code:
|
42
|
+
</p>
|
43
|
+
<code>javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
|
44
|
+
<p>Output:</p>
|
45
|
+
<samp>
|
46
|
+
<%= Rack::Utils.escape_html( javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js") %>
|
47
|
+
</samp>
|
48
|
+
<p>The code:
|
49
|
+
</p>
|
50
|
+
<code>javascript_tag "/js/helpers.js"</code>
|
51
|
+
<p>Output:</p>
|
52
|
+
<samp>
|
53
|
+
<%= Rack::Utils.escape_html( javascript_tag "/js/helpers.js" ) %>
|
54
|
+
</samp>
|
55
|
+
<footer><p>Also known as:
|
56
|
+
<ul>
|
57
|
+
<li><code>javascript_include_tag</code></li>
|
58
|
+
<li><code>js_tag</code></li>
|
59
|
+
<li><code>script_tag</code></li>
|
60
|
+
</ul>
|
61
|
+
</footer>
|
62
|
+
</section>
|
63
|
+
<section id='image_tag'>
|
64
|
+
<header>
|
65
|
+
<h3>image_tag</h3>
|
66
|
+
</header>
|
67
|
+
<p>The code:
|
68
|
+
</p>
|
69
|
+
<code>image_tag "http://farm1.staticflickr.com/10/12470738_fc0212bf8c.jpg", width: "500", height: "375", alt: "Greased Lightning"</code>
|
70
|
+
<p>Output:</p>
|
71
|
+
<samp>
|
72
|
+
<%= Rack::Utils.escape_html( image_tag "http://farm1.staticflickr.com/10/12470738_fc0212bf8c.jpg", width: "500", height: "375", alt: "Greased Lightning" ) %>
|
73
|
+
</samp>
|
74
|
+
<p>The code:
|
75
|
+
</p>
|
76
|
+
<code>image_tag "/images/turnip.jpg", width: "500", height: "375", alt: "Turnip"</code>
|
77
|
+
<p>Output:</p>
|
78
|
+
<samp>
|
79
|
+
<%= Rack::Utils.escape_html( image_tag "/images/turnip.jpg", width: "500", height: "375", alt: "Turnip" ) %>
|
80
|
+
</samp>
|
81
|
+
<footer><p>Also known as:
|
82
|
+
<ul>
|
83
|
+
<li><code>img_tag</code></li>
|
84
|
+
<li><code>img</code></li>
|
85
|
+
</ul>
|
86
|
+
</footer>
|
87
|
+
</section>
|
88
|
+
<section id='favicon_tag'>
|
89
|
+
<header>
|
90
|
+
<h3>favicon_tag</h3>
|
91
|
+
</header>
|
92
|
+
<p>The code:
|
93
|
+
</p>
|
94
|
+
<code>favicon_tag</code>
|
95
|
+
<p>Output:</p>
|
96
|
+
<samp>
|
97
|
+
<%= Rack::Utils.escape_html( favicon_tag url_options: {script_tag: false} ) %>
|
98
|
+
</samp>
|
99
|
+
<footer><p>Also known as:
|
100
|
+
<ul>
|
101
|
+
<li><code>favicon</code></li>
|
102
|
+
</ul>
|
103
|
+
</footer>
|
104
|
+
</section>
|
105
|
+
</section>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>Example</title>
|
6
|
+
<%= favicon_tag url_options: {script_tag: false} %>
|
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" %>
|
9
|
+
<%= stylesheet_tag "/css/screen.css" %>
|
10
|
+
<%= javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js" %>
|
11
|
+
|
12
|
+
</head>
|
13
|
+
|
14
|
+
<body>
|
15
|
+
<header role='banner'>
|
16
|
+
<a href="http://www.flickr.com/photos/tjt195/12470738/" title="Greased Lightning by tarotastic, on Flickr"><%= image_tag "http://farm1.staticflickr.com/10/12470738_fc0212bf8c.jpg", width: "500", height: "375", alt: "Greased Lightning" %></a>
|
17
|
+
<hgroup>
|
18
|
+
<h1>Sinatra Exstatic Assets</h1>
|
19
|
+
<h2>Helpers for your JS, CSS and anything static</h2>
|
20
|
+
</hgroup>
|
21
|
+
</header>
|
22
|
+
|
23
|
+
<nav>
|
24
|
+
<ul>
|
25
|
+
<li><a href="/">Main app</a></li>
|
26
|
+
<li><a href="/app2">App 2</a></li>
|
27
|
+
<li><a href="https://rubygems.org/gems/sinatra-exstatic-assets">Rubygems</a></li>
|
28
|
+
<li><a href="https://github.com/yb66/sinatra-exstatic-assets">Source code</a></li>
|
29
|
+
</ul>
|
30
|
+
</nav>
|
31
|
+
<article>
|
32
|
+
<%= yield %>
|
33
|
+
</article>
|
34
|
+
|
35
|
+
|
36
|
+
<footer>
|
37
|
+
<p>© <%= Time.now.year %> See the LICENCE file for more.</p>
|
38
|
+
</footer>
|
39
|
+
</body>
|
40
|
+
</html>
|
data/examples/config.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path "./app/main.rb", File.dirname(__FILE__)
|
2
|
+
require File.expand_path "./app2/main.rb", File.dirname(__FILE__)
|
3
|
+
|
4
|
+
module Example
|
5
|
+
def self.app
|
6
|
+
app = Rack::Builder.app do
|
7
|
+
map "/app2" do
|
8
|
+
run Example::App2
|
9
|
+
end
|
10
|
+
|
11
|
+
map "/" do
|
12
|
+
run Example::App
|
13
|
+
end
|
14
|
+
end
|
15
|
+
#run app
|
16
|
+
end
|
17
|
+
end
|
data/examples/config.ru
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
<header>
|
2
|
+
<time datetime="<%= Time.now.httpdate %>" pubdate>
|
3
|
+
<span><%= Time.now.strftime "%B"%></span> <%= Time.now.mday %>
|
4
|
+
</time>
|
5
|
+
<h1>
|
6
|
+
<a href="#" title="Link to this post"
|
7
|
+
rel="bookmark">Usage</a>
|
8
|
+
</h1>
|
9
|
+
</header>
|
10
|
+
<p>This is an article that demonstrates how to use the library. Notice that this is a Main App and an App2. This is to demonstrate that you can give links relative to the app, but if they are mounted with an extra prefix (using Rack#map, for example) the helper will respect that and produce the right href attribute.</p>
|
11
|
+
<section><header><h2>Installation and loading</h2></header>
|
12
|
+
<p>Start by installing:</p>
|
13
|
+
<code>gem 'sinatra-static-assets'</code>
|
14
|
+
<p>Then require it in your Sinatra app.</p>
|
15
|
+
<code>require 'sinatra/static_assets'</code>
|
16
|
+
</section>
|
17
|
+
<section>
|
18
|
+
<section>
|
19
|
+
<header><h2>The helpers</h2></header>
|
20
|
+
<p>Use these helpers in your views.<p>
|
21
|
+
|
22
|
+
<section>
|
23
|
+
<header>
|
24
|
+
<h3>stylesheet_link_tag</h3>
|
25
|
+
</header>
|
26
|
+
<p>The code:
|
27
|
+
</p>
|
28
|
+
<code>stylesheet_link_tag "/css/screen.css"</code>
|
29
|
+
<p>Output:</p>
|
30
|
+
<samp>
|
31
|
+
<%= Rack::Utils.escape_html( stylesheet_link_tag "/css/screen.css") %>
|
32
|
+
</samp>
|
33
|
+
</section>
|
34
|
+
<section>
|
35
|
+
<header>
|
36
|
+
<h3>javascript_script_tag</h3>
|
37
|
+
</header>
|
38
|
+
<p>The code:
|
39
|
+
</p>
|
40
|
+
<code>javascript_script_tag "http://code.jquery.com/jquery-1.9.1.min.js"</code>
|
41
|
+
<p>Output:</p>
|
42
|
+
<samp>
|
43
|
+
<%= Rack::Utils.escape_html( javascript_script_tag "http://code.jquery.com/jquery-1.9.1.min.js") %>
|
44
|
+
</samp>
|
45
|
+
<footer><p>Also known as:
|
46
|
+
<ul>
|
47
|
+
<li><code>javascript_include_tag</code></li>
|
48
|
+
<li><code>js_tag</code></li>
|
49
|
+
<li><code>script_tag</code></li>
|
50
|
+
</ul>
|
51
|
+
</footer>
|
52
|
+
</section>
|
53
|
+
<section>
|
54
|
+
<header>
|
55
|
+
<h3>image_tag</h3>
|
56
|
+
</header>
|
57
|
+
<p>The code:
|
58
|
+
</p>
|
59
|
+
<code>image_tag "http://farm3.staticflickr.com/2474/3609420787_f7fc0e53c7.jpg", width: "500", height: "275", alt: "Magic Ball"</code>
|
60
|
+
<p>Output:</p>
|
61
|
+
<samp>
|
62
|
+
<%= Rack::Utils.escape_html( image_tag "http://farm3.staticflickr.com/2474/3609420787_f7fc0e53c7.jpg", width: "500", height: "275", alt: "Magic Ball") %>
|
63
|
+
</samp>
|
64
|
+
</section>
|
65
|
+
<section>
|
66
|
+
<header>
|
67
|
+
<h3>image_tag</h3>
|
68
|
+
</header>
|
69
|
+
<p>The code:
|
70
|
+
</p>
|
71
|
+
<code>favicon_tag</code>
|
72
|
+
<p>Output:</p>
|
73
|
+
<samp>
|
74
|
+
<%= Rack::Utils.escape_html( favicon_link_tag ) %>
|
75
|
+
</samp>
|
76
|
+
<footer><p>Also known as:
|
77
|
+
<ul>
|
78
|
+
<li><code>favicon_link_tag</code></li>
|
79
|
+
<li><code>link_favicon_tag</code></li>
|
80
|
+
</ul>
|
81
|
+
</footer>
|
82
|
+
</section>
|
83
|
+
</section>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>Example</title>
|
6
|
+
<%= favicon_link_tag %>
|
7
|
+
<%= stylesheet_link_tag 'http://fonts.googleapis.com/css?family=Quicksand|Faster+One|Cherry+Swash:700|Titillium+Web', rel: 'stylesheet', type: 'text/css' %>
|
8
|
+
<%= stylesheet_link_tag "/css/base.css" %>
|
9
|
+
<%= stylesheet_link_tag "/css/screen.css" %>
|
10
|
+
<%= javascript_script_tag "http://code.jquery.com/jquery-1.9.1.min.js" %>
|
11
|
+
|
12
|
+
</head>
|
13
|
+
|
14
|
+
<body>
|
15
|
+
<header role='banner'>
|
16
|
+
<a href="http://www.flickr.com/photos/28931095@N03/3609420787/" title="Magic Ball by Sam Bald, on Flickr"><%= image_tag "http://farm3.staticflickr.com/2474/3609420787_f7fc0e53c7.jpg", width: "500", height: "375", alt: "Magic Ball" %></a>
|
17
|
+
<hgroup>
|
18
|
+
<h1>Sinatra Static Assets</h1>
|
19
|
+
<h2>Helpers for your JS, CSS and anything static</h2>
|
20
|
+
</hgroup>
|
21
|
+
</header>
|
22
|
+
|
23
|
+
<nav>
|
24
|
+
<ul>
|
25
|
+
<li><a href="/">Main app</a></li>
|
26
|
+
<li><a href="/app2">App 2</a></li>
|
27
|
+
<li><a href="https://rubygems.org/gems/sinatra-static-assets">Rubygems</a></li>
|
28
|
+
<li><a href="https://github.com/wbzyl/sinatra-static-assets">Source code</a></li>
|
29
|
+
</ul>
|
30
|
+
</nav>
|
31
|
+
<article>
|
32
|
+
<%= yield %>
|
33
|
+
</article>
|
34
|
+
|
35
|
+
|
36
|
+
<footer>
|
37
|
+
<p>© <%= Time.now.year %> See the LICENCE file for more.</p>
|
38
|
+
</footer>
|
39
|
+
</body>
|
40
|
+
</html>
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'exstatic_assets.rb'
|
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
# @see https://sinatrarb.com/intro The framework
|
4
|
+
module Sinatra
|
5
|
+
|
6
|
+
# A Sinatra extension for helping with static assets. You probably want to start with {Helpers}.
|
7
|
+
module Exstatic
|
8
|
+
|
9
|
+
# For creating HTML tags.
|
10
|
+
class Tag < ::String
|
11
|
+
|
12
|
+
# @param [String] name The tag name e.g. `link`.
|
13
|
+
# @param [Hash] options With the exception of any options listed here, these are passed to the tag to make the HTML attributes.
|
14
|
+
# @option options [TrueClass] :closed Whether to self-close the link XHTML style or not.
|
15
|
+
# @param [#call] block The contents of the block are wrapped by the HTML tag e.g. <p>This is from the block</p>
|
16
|
+
# @example
|
17
|
+
# Tag.new "img", {src: "/images/foo.jpg", width: "500"}
|
18
|
+
# # => "<img src="/images/foo.jpg" width="500" />"
|
19
|
+
def initialize( name, options={}, &block )
|
20
|
+
@name = name
|
21
|
+
@closed = (c = options.delete(:closed)).nil? ? true : c
|
22
|
+
@options = options
|
23
|
+
@attributes = self.class.make_attributes @options
|
24
|
+
@block = block
|
25
|
+
super tag
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :attributes, :options, :name
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
|
33
|
+
# @yield Its return value is used as the contents of the HTML tag.
|
34
|
+
def tag
|
35
|
+
return @tag if @tag
|
36
|
+
start_tag = "<#{@name} #{@attributes}".strip
|
37
|
+
@tag = if @block
|
38
|
+
"#{start_tag}>#{@block.call}</#{name}>"
|
39
|
+
elsif @closed
|
40
|
+
"#{start_tag} />"
|
41
|
+
else
|
42
|
+
"#{start_tag}>"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# Takes a hash and transforms it into a string of HTML attributes.
|
48
|
+
# @param [Hash] options
|
49
|
+
# @return [String]
|
50
|
+
def self.make_attributes( options )
|
51
|
+
options.sort
|
52
|
+
.map {|key, value| %(#{key}="#{value}") }
|
53
|
+
.join " "
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Encapsulates an asset, be it a stylesheet, an image…
|
60
|
+
class Asset < ::String
|
61
|
+
|
62
|
+
attr_reader :fullpath
|
63
|
+
|
64
|
+
# @param [String] filename Either the file name (and path relative to the public folder) or the external HTTP link.
|
65
|
+
# @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
|
+
if asset_dir.nil?
|
68
|
+
filename, asset_dir = [File.basename(filename), File.dirname(filename)]
|
69
|
+
end
|
70
|
+
# TODO fail if asset_dir.nil?
|
71
|
+
super filename
|
72
|
+
@fullpath = File.join( asset_dir, filename ) unless is_uri?
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# If the asset is a local file this gets the timestamp.
|
77
|
+
# @return [Integer]
|
78
|
+
def timestamp
|
79
|
+
@timestamp ||= !is_uri? && exists? && mtime_int
|
80
|
+
end
|
81
|
+
|
82
|
+
# Takes the timestamp and returns it as a querystring.
|
83
|
+
# @return [String] `?ts=TIMESTAMP`
|
84
|
+
def querystring
|
85
|
+
timestamp ? "?ts=#{timestamp}" : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# We only need to check for a scheme/protocol to know
|
89
|
+
# it's not a file.
|
90
|
+
URI_PATTERN = %r{\A
|
91
|
+
(?:
|
92
|
+
[A-z]+
|
93
|
+
\:
|
94
|
+
)? # The protocol part. It's optional.
|
95
|
+
// /? # There will always be at least 2 //
|
96
|
+
}x
|
97
|
+
|
98
|
+
# Tests whether the asset is a file or an HTTP link by checking the scheme portion.
|
99
|
+
# @note
|
100
|
+
# A url will match:
|
101
|
+
# http://example.com
|
102
|
+
# //example.com
|
103
|
+
# but www.example.com or example.com will be treated as a file.
|
104
|
+
# @return [TrueClass]
|
105
|
+
def is_uri?
|
106
|
+
self =~ URI_PATTERN ? true : false
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
# Convenience, mainly for my convenience in stubbing during tests.
|
111
|
+
# @return [TrueClass]
|
112
|
+
def exists?
|
113
|
+
File.exists? fullpath
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Convenience, mainly for my convenience in stubbing during tests.
|
118
|
+
# @return [Int]
|
119
|
+
def mtime_int
|
120
|
+
File.mtime( fullpath ).to_i
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# The private instance methods.
|
126
|
+
# @api private
|
127
|
+
module Private
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# Wraps around Sinatra::Helpers#uri. Appends a querystring if passed an Asset object.
|
132
|
+
# @param [String,#querystring] addr
|
133
|
+
# @param [Hash] options
|
134
|
+
# @option options [TrueClass] :absolute see Sinatra::Helpers#uri
|
135
|
+
# @option options [TrueClass] :script_tag Whether to prepend the SCRIPT_TAG env variable.
|
136
|
+
# @return [String]
|
137
|
+
# @see Sinatra::Helpers#uri
|
138
|
+
def sss_url_for(addr, options=nil)
|
139
|
+
options ||= {}
|
140
|
+
absolute = options.delete :absolute
|
141
|
+
absolute = false if absolute.nil?
|
142
|
+
script_tag = options.delete(:script_tag)
|
143
|
+
script_tag = true if script_tag.nil?
|
144
|
+
href = uri addr, absolute, script_tag
|
145
|
+
addr.respond_to?(:querystring) ?
|
146
|
+
"#{href}#{addr.querystring}" :
|
147
|
+
href
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# The default options passed with a stylesheet asset.
|
152
|
+
DEFAULT_CSS = {
|
153
|
+
# :type => "text/css", # not needed with HTML5
|
154
|
+
:charset => "utf-8",
|
155
|
+
:media => "screen",
|
156
|
+
:rel => "stylesheet"
|
157
|
+
}
|
158
|
+
|
159
|
+
|
160
|
+
# Produce a stylesheet link tag.
|
161
|
+
# @param [String] source The file or HTML resource.
|
162
|
+
# @param [Hash] options
|
163
|
+
# @option options [String] :asset_dir The directory the asset is held. Defaults to Sinatra's `public_folder` setting.
|
164
|
+
# @option options [Hash] :url_options Options for devising the URL.
|
165
|
+
# @option options [TrueClass] :script_tag Whether to prepend the SCRIPT_TAG env variable.
|
166
|
+
# @return [Tag]
|
167
|
+
def sss_stylesheet_tag(source, options = {})
|
168
|
+
asset_dir = options.delete(:asset_dir) || settings.public_folder
|
169
|
+
asset = Asset.new source, asset_dir
|
170
|
+
href = sss_url_for( asset, options.delete(:url_options) )
|
171
|
+
Tag.new "link", DEFAULT_CSS.merge(:href => href)
|
172
|
+
.merge(options)
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Default options for the javascript script tags.
|
177
|
+
DEFAULT_JS = {
|
178
|
+
# :type => "text/javascript",
|
179
|
+
:charset => "utf-8"
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
# Produce a javascript script tag.
|
184
|
+
# @see #sss_stylesheet_tag but there is no `closed` option here.
|
185
|
+
def sss_javascript_tag(source, options = {})
|
186
|
+
asset = Asset.new source, settings.public_folder
|
187
|
+
href = sss_url_for asset, options.delete(:url_options)
|
188
|
+
Tag.new("script", DEFAULT_JS.merge(:src => href)
|
189
|
+
.merge(options)
|
190
|
+
) {}
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
# Make's sure the options don't get mixed up with the other args.
|
195
|
+
def sss_extract_options(a)
|
196
|
+
opts = a.last.respond_to?(:keys) ? a.pop : {}
|
197
|
+
[a, opts]
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
# @see #sss_stylesheet_tag
|
202
|
+
def sss_image_tag(source, options = {})
|
203
|
+
options[:src] = sss_url_for Asset.new( source, settings.public_folder ), options.delete(:url_options)
|
204
|
+
Tag.new "img", options
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
# These are the helpers available to a Sinatra app using the extension.
|
210
|
+
# @example
|
211
|
+
# # For a classic app
|
212
|
+
# require 'sinatra/exstatic'
|
213
|
+
# # That's all for a classic app, the helpers
|
214
|
+
# # are now available.
|
215
|
+
#
|
216
|
+
# # For a modular app
|
217
|
+
# require 'sinatra/base'
|
218
|
+
# require 'sinatra/exstatic'
|
219
|
+
# class MyApp < Sinatra::Base
|
220
|
+
# helpers Sinatra::Exstatic
|
221
|
+
module Helpers
|
222
|
+
include Private
|
223
|
+
|
224
|
+
# @!method image_tag(*sources)
|
225
|
+
# Produce an HTML img tag.
|
226
|
+
# @param [String] sources The file or HTML resource.
|
227
|
+
# @param [Hash] options
|
228
|
+
# @option options [String] :asset_dir The directory the asset is held. Defaults to Sinatra's `public_folder` setting.
|
229
|
+
# @option options [Hash] :url_options Options for devising the URL. (see sss_url_for)
|
230
|
+
# @return [#to_s]
|
231
|
+
# @example
|
232
|
+
# image_tag "/images/big-fish.jpg", width: "500", height: "250", alt: "The biggest fish in the world!"
|
233
|
+
# # => <img alt="The biggest fish in the world!" height="250" src="/images/big-fish.jpg?ts=1367933468" width="500" />
|
234
|
+
|
235
|
+
# @!method stylesheet_tag(*sources)
|
236
|
+
# Produce an HTML link tag to a stylesheet.
|
237
|
+
# @param [String] sources The file or HTML resource.
|
238
|
+
# @param [Hash] options
|
239
|
+
# @option options [String] :asset_dir The directory the asset is held. Defaults to Sinatra's `public_folder` setting.
|
240
|
+
# @option options [Hash] :url_options Options for devising the URL. (see sss_url_for)
|
241
|
+
# @return [#to_s]
|
242
|
+
# @example
|
243
|
+
# stylesheet_tag "/css/screen.css"
|
244
|
+
# # => <link charset="utf-8" href="/css/screen.css?ts=1367678587" media="screen" rel="stylesheet">
|
245
|
+
|
246
|
+
# @!method javascript_tag(*sources)
|
247
|
+
# Produce an HTML script tag.
|
248
|
+
# @param [String] sources The file or HTML resource.
|
249
|
+
# @param [Hash] options
|
250
|
+
# @option options [String] :asset_dir The directory the asset is held. Defaults to Sinatra's `public_folder` setting.
|
251
|
+
# @option options [Hash] :url_options Options for devising the URL. (see sss_url_for)
|
252
|
+
# @return [#to_s]
|
253
|
+
# @example
|
254
|
+
# javascript_tag "http://code.jquery.com/jquery-1.9.1.min.js"
|
255
|
+
# # => <script charset="utf-8" src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
256
|
+
%w{image_tag stylesheet_tag javascript_tag}.each do |method_name|
|
257
|
+
define_method method_name do |*sources|
|
258
|
+
list, options = sss_extract_options sources
|
259
|
+
list.map {|source|
|
260
|
+
send "sss_#{method_name}", source, options
|
261
|
+
}.join "\n"
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
alias_method :img_tag, :image_tag
|
267
|
+
alias_method :css_tag, :stylesheet_tag
|
268
|
+
alias_method :stylesheet, :stylesheet_tag
|
269
|
+
alias_method :javascript_include_tag, :javascript_tag
|
270
|
+
alias_method :js_tag, :javascript_tag
|
271
|
+
alias_method :script_tag, :javascript_tag
|
272
|
+
|
273
|
+
# @param [String] source
|
274
|
+
# @param [Hash] options
|
275
|
+
# @option options [Hash] :url_options script_tag
|
276
|
+
# @example
|
277
|
+
# favicon_tag
|
278
|
+
# # => <link href="/favicon.ico" rel="icon">
|
279
|
+
def favicon_tag(*args)
|
280
|
+
source, options = sss_extract_options args
|
281
|
+
source = "favicon.ico" if source.nil? or source.empty?
|
282
|
+
|
283
|
+
# xhtml style like <link rel="shortcut icon" href="http://example.com/myicon.ico" />
|
284
|
+
options[:rel] ||= settings.xhtml ? "shortcut icon" : "icon"
|
285
|
+
|
286
|
+
options[:href] = sss_url_for(source, options.delete(:url_options))
|
287
|
+
|
288
|
+
Tag.new "link", options
|
289
|
+
end
|
290
|
+
|
291
|
+
alias_method :link_favicon_tag, :favicon_tag
|
292
|
+
alias_method :favicon, :favicon_tag
|
293
|
+
|
294
|
+
end
|
295
|
+
|
296
|
+
# Extending
|
297
|
+
def self.registered(app)
|
298
|
+
app.helpers Exstatic::Helpers
|
299
|
+
app.disable :xhtml
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
register Exstatic
|
304
|
+
end
|