rack-scriptstacker 0.0.1 → 0.1.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.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/README.md +24 -3
- data/lib/rack/scriptstacker.rb +62 -12
- data/lib/rack/scriptstacker/version.rb +1 -1
- data/spec/scriptstacker_spec.rb +92 -39
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bd27878e3558110c50299ac46ab58ad6dd4eb93f
|
4
|
+
data.tar.gz: 3bd7cd806c466de5c48a5022bd5d2bbc11aece15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e0248a50ebbfd9be7072736c7ef90bf91a54133a1e714e9aae3d8df38e1d734aaf714d2209a8a063a7117b2ee96539a5f63a3517410e9dd62183c6f1e275b02
|
7
|
+
data.tar.gz: 9e8702eb86e8f2a510c43ec20bda3081d12df1f3a8da17021ee7e214108588a715f85af3307986790fd6c7dad8182853cbde7117951d20887ef9d7c2523bfe51
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,8 @@ Painless static file handling for Rack apps.
|
|
4
4
|
|
5
5
|
- Automatically configures `Rack::Static` by default.
|
6
6
|
- Glob and inject JavaScript/CSS files into served HTML.
|
7
|
+
- Inserts CSS before `</head>` and JS before `</body>` by default.
|
8
|
+
- Change the inject mode to use placeholder slots instead.
|
7
9
|
|
8
10
|
# Usage
|
9
11
|
|
@@ -13,10 +15,8 @@ Painless static file handling for Rack apps.
|
|
13
15
|
<!doctype html>
|
14
16
|
<html lang="en">
|
15
17
|
<head>
|
16
|
-
<!-- ScriptStacker: CSS //-->
|
17
18
|
</head>
|
18
19
|
<body>
|
19
|
-
<!-- ScriptStacker: JAVASCRIPT //-->
|
20
20
|
</body>
|
21
21
|
</html>
|
22
22
|
```
|
@@ -32,7 +32,7 @@ class App
|
|
32
32
|
def call env
|
33
33
|
[
|
34
34
|
200,
|
35
|
-
{
|
35
|
+
{'Content-Type' => 'text/html'},
|
36
36
|
[File.read('index.html')]
|
37
37
|
]
|
38
38
|
end
|
@@ -101,3 +101,24 @@ use Rack::ScriptStacker,
|
|
101
101
|
javascript 'static/javascript'
|
102
102
|
end
|
103
103
|
```
|
104
|
+
|
105
|
+
## Use placeholder slots
|
106
|
+
|
107
|
+
```html
|
108
|
+
<!-- index.html //-->
|
109
|
+
|
110
|
+
<!doctype html>
|
111
|
+
<html lang="en">
|
112
|
+
<head>
|
113
|
+
<!-- ScriptStacker: CSS //-->
|
114
|
+
</head>
|
115
|
+
<body>
|
116
|
+
<!-- ScriptStacker: JAVASCRIPT //-->
|
117
|
+
</body>
|
118
|
+
</html>
|
119
|
+
|
120
|
+
use Rack::ScriptStacker, inject_mode: :slot do
|
121
|
+
css 'static/css'
|
122
|
+
javascript 'static/javascript'
|
123
|
+
end
|
124
|
+
```
|
data/lib/rack/scriptstacker.rb
CHANGED
@@ -12,26 +12,39 @@ end
|
|
12
12
|
|
13
13
|
module Rack
|
14
14
|
class ScriptStacker
|
15
|
+
module InjectMode
|
16
|
+
TAG = :tag
|
17
|
+
SLOT = :slot
|
18
|
+
end
|
19
|
+
|
15
20
|
DEFAULT_CONFIG = {
|
16
21
|
configure_static: true,
|
22
|
+
inject_mode: InjectMode::TAG,
|
17
23
|
stackers: {
|
18
24
|
css: {
|
19
25
|
template: '<link rel="stylesheet" type="text/css" href="%s" />',
|
20
26
|
glob: '*.css',
|
21
|
-
slot: 'CSS'
|
27
|
+
slot: 'CSS',
|
28
|
+
inject_before_tag: '</head>',
|
22
29
|
},
|
23
30
|
javascript: {
|
24
31
|
template: '<script type="text/javascript" src="%s"></script>',
|
25
32
|
glob: '*.js',
|
26
|
-
slot: 'JAVASCRIPT'
|
33
|
+
slot: 'JAVASCRIPT',
|
34
|
+
inject_before_tag: '</body>',
|
27
35
|
}
|
28
36
|
}
|
29
37
|
}
|
30
38
|
|
31
39
|
def initialize app, config={}, &stack_spec
|
32
40
|
@config = DEFAULT_CONFIG.recursive_merge config
|
33
|
-
@path_specs = ScriptStackerUtils::SpecSolidifier.new
|
34
|
-
|
41
|
+
@path_specs = ScriptStackerUtils::SpecSolidifier.new(
|
42
|
+
@config[:stackers].keys
|
43
|
+
).call stack_spec
|
44
|
+
@runner = ScriptStackerUtils::Runner.new(
|
45
|
+
@config[:stackers],
|
46
|
+
@config[:inject_mode]
|
47
|
+
)
|
35
48
|
@app = @config[:configure_static] ? configure_static(app) : app
|
36
49
|
end
|
37
50
|
|
@@ -64,7 +77,8 @@ module Rack
|
|
64
77
|
|
65
78
|
module ScriptStackerUtils
|
66
79
|
class SpecSolidifier < BasicObject
|
67
|
-
def initialize
|
80
|
+
def initialize stacker_names
|
81
|
+
@stacker_names = stacker_names
|
68
82
|
@specs = ::Hash.new { |hash, key| hash[key] = [] }
|
69
83
|
end
|
70
84
|
|
@@ -74,8 +88,13 @@ module Rack
|
|
74
88
|
end
|
75
89
|
|
76
90
|
def method_missing name, *args
|
91
|
+
if !@stacker_names.include? name
|
92
|
+
::Kernel.raise ::ArgumentError.new(
|
93
|
+
"Expected one of #{@stacker_names}, but got #{name.inspect}."
|
94
|
+
)
|
95
|
+
end
|
77
96
|
if args.size != 1
|
78
|
-
raise ::ArgumentError.new(
|
97
|
+
::Kernel.raise ::ArgumentError.new(
|
79
98
|
"Expected a path spec like 'static/css' => 'stylesheets', " +
|
80
99
|
"but got #{args.inspect} instead."
|
81
100
|
)
|
@@ -124,10 +143,11 @@ module Rack
|
|
124
143
|
end
|
125
144
|
|
126
145
|
class Runner
|
127
|
-
def initialize stacker_configs
|
146
|
+
def initialize stacker_configs, inject_mode
|
128
147
|
@stackers = stacker_configs.map do |name, config|
|
129
148
|
[name, Stacker.new(config)]
|
130
149
|
end.to_h
|
150
|
+
@inject_mode = inject_mode
|
131
151
|
end
|
132
152
|
|
133
153
|
def replace_in_body body, path_specs
|
@@ -139,10 +159,23 @@ module Rack
|
|
139
159
|
|
140
160
|
body.map do |chunk|
|
141
161
|
@stackers.values.reduce chunk do |memo, stacker|
|
142
|
-
|
162
|
+
inject_into memo, stacker
|
143
163
|
end
|
144
164
|
end
|
145
165
|
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def inject_into chunk, stacker
|
170
|
+
case @inject_mode
|
171
|
+
when Rack::ScriptStacker::InjectMode::SLOT
|
172
|
+
stacker.replace_slot chunk
|
173
|
+
when Rack::ScriptStacker::InjectMode::TAG
|
174
|
+
stacker.tag_inject chunk
|
175
|
+
else
|
176
|
+
raise ArgumentError.new "Unexpected InjectMode #{@inject_mode.inspect}."
|
177
|
+
end
|
178
|
+
end
|
146
179
|
end
|
147
180
|
|
148
181
|
class Stacker
|
@@ -150,6 +183,8 @@ module Rack
|
|
150
183
|
@template = config[:template]
|
151
184
|
@glob = config[:glob]
|
152
185
|
@slot = config[:slot]
|
186
|
+
@inject_before_tag = config[:inject_before_tag]
|
187
|
+
|
153
188
|
@files = []
|
154
189
|
end
|
155
190
|
|
@@ -159,17 +194,32 @@ module Rack
|
|
159
194
|
end
|
160
195
|
end
|
161
196
|
|
197
|
+
def tag_inject chunk
|
198
|
+
lines = chunk.split "\n", -1 # this preserves any trailing newlines
|
199
|
+
index = lines.find_index { |line| line.match @inject_before_tag }
|
200
|
+
return if index.nil?
|
201
|
+
indent = lines[index].match(/^\s*/).to_s
|
202
|
+
(
|
203
|
+
lines[0...index] +
|
204
|
+
file_list(indent + ' ') +
|
205
|
+
lines[index..-1]
|
206
|
+
).join "\n"
|
207
|
+
end
|
208
|
+
|
162
209
|
def replace_slot chunk
|
163
210
|
chunk.gsub /^(\s*)#{slot}/ do
|
164
|
-
|
165
|
-
@files.map do |line|
|
166
|
-
indent + line
|
167
|
-
end.join "\n"
|
211
|
+
file_list($1).join "\n"
|
168
212
|
end
|
169
213
|
end
|
170
214
|
|
171
215
|
private
|
172
216
|
|
217
|
+
def file_list indent
|
218
|
+
@files.map do |line|
|
219
|
+
indent + line
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
173
223
|
def slot
|
174
224
|
"<!-- ScriptStacker: #{@slot} //-->"
|
175
225
|
end
|
data/spec/scriptstacker_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'pry'
|
1
2
|
require 'rack/scriptstacker'
|
2
3
|
|
3
4
|
def smart_deindent str
|
@@ -11,13 +12,22 @@ end
|
|
11
12
|
|
12
13
|
describe Rack::ScriptStacker do
|
13
14
|
let(:js_files) { ['main.js', 'util.js'] }
|
14
|
-
let(:vendor_js_files) { ['jquery.js', '
|
15
|
+
let(:vendor_js_files) { ['jquery.js', 'blogo_web.js'] }
|
15
16
|
let(:css_files) { ['main.css', '_whatever.css'] }
|
16
|
-
let(:
|
17
|
-
|
17
|
+
let(:config) do
|
18
|
+
{
|
19
|
+
configure_static: false,
|
20
|
+
inject_mode: Rack::ScriptStacker::InjectMode::SLOT
|
21
|
+
}
|
22
|
+
end
|
23
|
+
let(:stack_spec) do
|
24
|
+
Proc.new {
|
18
25
|
css 'static/css'
|
19
26
|
javascript 'static/javascripts'
|
20
|
-
|
27
|
+
}
|
28
|
+
end
|
29
|
+
let(:middleware) do
|
30
|
+
Rack::ScriptStacker.new app_with_body(body), config, &stack_spec
|
21
31
|
end
|
22
32
|
|
23
33
|
before :each do
|
@@ -32,7 +42,6 @@ describe Rack::ScriptStacker do
|
|
32
42
|
|
33
43
|
context 'inactive' do
|
34
44
|
let(:body) { '<div>whatever</div>' }
|
35
|
-
|
36
45
|
it 'does not change without replacement slots' do
|
37
46
|
expect(@response_body).to eq('<div>whatever</div>')
|
38
47
|
end
|
@@ -47,7 +56,6 @@ describe Rack::ScriptStacker do
|
|
47
56
|
</body>
|
48
57
|
HTML
|
49
58
|
end
|
50
|
-
|
51
59
|
it 'injects tags' do
|
52
60
|
expect(@response_body).to eq(smart_deindent(<<-HTML))
|
53
61
|
<body>
|
@@ -67,7 +75,6 @@ describe Rack::ScriptStacker do
|
|
67
75
|
</head>
|
68
76
|
HTML
|
69
77
|
end
|
70
|
-
|
71
78
|
it 'injects tags' do
|
72
79
|
expect(@response_body).to eq(smart_deindent(<<-HTML))
|
73
80
|
<head>
|
@@ -78,36 +85,12 @@ describe Rack::ScriptStacker do
|
|
78
85
|
end
|
79
86
|
end
|
80
87
|
|
81
|
-
context 'path normalization' do
|
82
|
-
let(:middleware) do
|
83
|
-
Rack::ScriptStacker.new app_with_body(body), configure_static: false do
|
84
|
-
css 'static/css/' => '/stylesheets'
|
85
|
-
javascript 'static/javascripts' => 'static/js'
|
86
|
-
end
|
87
|
-
end
|
88
|
-
let(:body) do
|
89
|
-
smart_deindent(<<-HTML)
|
90
|
-
<!-- ScriptStacker: CSS //-->
|
91
|
-
<!-- ScriptStacker: JAVASCRIPT //-->
|
92
|
-
HTML
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'does not mess up the paths' do
|
96
|
-
expect(@response_body).to eq(smart_deindent(<<-HTML))
|
97
|
-
<link rel="stylesheet" type="text/css" href="/stylesheets/main.css" />
|
98
|
-
<link rel="stylesheet" type="text/css" href="/stylesheets/_whatever.css" />
|
99
|
-
<script type="text/javascript" src="/static/js/main.js"></script>
|
100
|
-
<script type="text/javascript" src="/static/js/util.js"></script>
|
101
|
-
HTML
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
88
|
context 'multiple specs for one stacker' do
|
106
|
-
let(:
|
107
|
-
|
89
|
+
let(:stack_spec) do
|
90
|
+
Proc.new {
|
108
91
|
javascript 'vendor/javascripts'
|
109
92
|
javascript 'static/javascripts'
|
110
|
-
|
93
|
+
}
|
111
94
|
end
|
112
95
|
let(:body) do
|
113
96
|
smart_deindent(<<-HTML)
|
@@ -117,13 +100,12 @@ describe Rack::ScriptStacker do
|
|
117
100
|
</body>
|
118
101
|
HTML
|
119
102
|
end
|
120
|
-
|
121
103
|
it 'injects tags in order' do
|
122
104
|
expect(@response_body).to eq(smart_deindent(<<-HTML))
|
123
105
|
<body>
|
124
106
|
<div>lmao</div>
|
125
107
|
<script type="text/javascript" src="/vendor/javascripts/jquery.js"></script>
|
126
|
-
<script type="text/javascript" src="/vendor/javascripts/
|
108
|
+
<script type="text/javascript" src="/vendor/javascripts/blogo_web.js"></script>
|
127
109
|
<script type="text/javascript" src="/static/javascripts/main.js"></script>
|
128
110
|
<script type="text/javascript" src="/static/javascripts/util.js"></script>
|
129
111
|
</body>
|
@@ -131,21 +113,92 @@ describe Rack::ScriptStacker do
|
|
131
113
|
end
|
132
114
|
end
|
133
115
|
|
134
|
-
context '
|
116
|
+
context 'static file auto-configuration' do
|
135
117
|
let(:body) { '<div>whatever</div>' }
|
136
|
-
|
137
118
|
it 'configures static automatically' do
|
138
119
|
expect(Rack::Static).to receive(:new).with(
|
139
120
|
duck_type(:call), {
|
140
121
|
urls: ['/static/css/', '/static/javascripts/']
|
141
122
|
}
|
142
123
|
)
|
143
|
-
|
144
124
|
Rack::ScriptStacker.new app_with_body(body), configure_static: true do
|
145
125
|
css 'static/css'
|
146
126
|
javascript 'static/javascripts'
|
147
127
|
end
|
148
128
|
end
|
149
129
|
end
|
130
|
+
|
131
|
+
context 'auto-inject' do
|
132
|
+
let(:config) do
|
133
|
+
{
|
134
|
+
configure_static: false,
|
135
|
+
inject_mode: Rack::ScriptStacker::InjectMode::TAG
|
136
|
+
}
|
137
|
+
end
|
138
|
+
let(:body) do
|
139
|
+
smart_deindent(<<-HTML)
|
140
|
+
<html>
|
141
|
+
<head>
|
142
|
+
<title>OH RLY</title>
|
143
|
+
</head>
|
144
|
+
<body>
|
145
|
+
<div>ya rly</div>
|
146
|
+
</body>
|
147
|
+
</html>
|
148
|
+
HTML
|
149
|
+
end
|
150
|
+
it 'injects before the <head> and <body> tags' do
|
151
|
+
expect(@response_body).to eq(smart_deindent(<<-HTML))
|
152
|
+
<html>
|
153
|
+
<head>
|
154
|
+
<title>OH RLY</title>
|
155
|
+
<link rel="stylesheet" type="text/css" href="/static/css/main.css" />
|
156
|
+
<link rel="stylesheet" type="text/css" href="/static/css/_whatever.css" />
|
157
|
+
</head>
|
158
|
+
<body>
|
159
|
+
<div>ya rly</div>
|
160
|
+
<script type="text/javascript" src="/static/javascripts/main.js"></script>
|
161
|
+
<script type="text/javascript" src="/static/javascripts/util.js"></script>
|
162
|
+
</body>
|
163
|
+
</html>
|
164
|
+
HTML
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe Rack::ScriptStackerUtils::PathSpec do
|
170
|
+
let(:css_spec) { subject.new(
|
171
|
+
'static/css/' => '/stylesheets'
|
172
|
+
)}
|
173
|
+
let(:js_spec) { subject.new(
|
174
|
+
'static/javascripts' => 'static/js/'
|
175
|
+
)}
|
176
|
+
|
177
|
+
context 'serve path' do
|
178
|
+
it 'always serves with absolute paths' do
|
179
|
+
expect(js_spec.serve_path).to start_with('/')
|
180
|
+
end
|
181
|
+
it 'appends a slash when there is none, for concatenation simplicity' do
|
182
|
+
expect(css_spec.serve_path).to end_with('/')
|
183
|
+
end
|
184
|
+
it 'does not prepend a redundant slash' do
|
185
|
+
expect(css_spec.serve_path[0..1]).to_not start_with('//')
|
186
|
+
end
|
187
|
+
it 'does not append a redundant slash' do
|
188
|
+
expect(js_spec.serve_path[-2..-1]).to_not end_with('//')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context 'source path' do
|
193
|
+
it 'appends a slash when there is none, for concatenation simplicity' do
|
194
|
+
expect(js_spec.source_path).to end_with('/')
|
195
|
+
end
|
196
|
+
it 'does not prepend a slash, because local files are relative to pwd' do
|
197
|
+
expect(css_spec.source_path).to_not start_with('/')
|
198
|
+
end
|
199
|
+
it 'does not append a redundant slash' do
|
200
|
+
expect(css_spec.source_path).to_not end_with('//')
|
201
|
+
end
|
202
|
+
end
|
150
203
|
end
|
151
204
|
|