vitrine 0.0.13 → 0.0.14
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/README.md +19 -18
- data/lib/asset_compiler.rb +148 -0
- data/lib/version.rb +1 -1
- data/lib/vitrine.rb +5 -82
- data/test/test_vitrine.rb +6 -82
- data/test/test_vitrine_asset_compiler.rb +96 -0
- data/test/test_vitrine_in_rack_stack.rb +6 -3
- data/vitrine.gemspec +4 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ee018a5ccd0dc9855944a5383d88ea37bde9b68
|
4
|
+
data.tar.gz: 318885cc0cdb952a5e0da8374493ad73df9919f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3ffd8d9dc3d901e9f05c8b5abfd9ff0a94f860f910934918015b55eaf82382876baca2610b31e233f854377812c0ff181c3de74e9ef21b4b802f7ac4145403d
|
7
|
+
data.tar.gz: 87fd5a399ecd9d46ce855ff408361f7d226d6f1571ac3fad59ab82052e25c32ef105c5cc364381427878237aadd5db47282251a4f4d652552ce0f887a4b3b510
|
data/README.md
CHANGED
@@ -23,24 +23,16 @@ Vitrine assumes that there are two directories under the current tree:
|
|
23
23
|
|
24
24
|
Vitrine is for **development**. It takes runtime compilation to 11 and beyound. Running tasks
|
25
25
|
is all fine and good when you build out the app for production, but when iterating on UI it's essential
|
26
|
-
to be able to just yank the file
|
26
|
+
to be able to just yank the file in there and carry on. THe compilation perks include:
|
27
27
|
|
28
|
-
|
28
|
+
* Any `.scss` file you shove into the `public` directory can be referenced as `.css` from your HTML.
|
29
|
+
Ask for `foo.css` and `foo.scss` will be compiled on the fly.
|
30
|
+
* Any `.coffee` file you shove into the `public` directory can be references as `.js` from your HTML.
|
31
|
+
Ask for `bar.js` and `bar.coffee` will be compiled on the fly.
|
32
|
+
* CoffeeScript files will have source-maps out of the box for pleasant browser debugging.
|
33
|
+
* Decent error messages will be shown for both invalid SCSS and invalid CoffeeScript.
|
29
34
|
|
30
|
-
|
31
|
-
Vitrine will automatically compile it via SASS.
|
32
|
-
|
33
|
-
### For CoffeeScript, with source maps
|
34
|
-
|
35
|
-
Same thing applies to CoffeeScript - put `.coffee` files in "public", and reference them as `.js` files.
|
36
|
-
Vitrine will generate you source maps on the fly for pleasant browser debugging.
|
37
|
-
|
38
|
-
## Sensible error messages when automatic compilation fails
|
39
|
-
|
40
|
-
Vitrine will try to show you sensible errors if your SCSS or CoffeeScript fail to compile due to syntax errors and
|
41
|
-
the like. CoffeeScript errors go to the browser console, and Sass errors go in a generated element on top of your page.
|
42
|
-
|
43
|
-
## Do not recompile on every request
|
35
|
+
## Asset caching
|
44
36
|
|
45
37
|
Succesfully compiled assets will be cached to save time on next reload, and ETagged based on their
|
46
38
|
mtime.
|
@@ -51,8 +43,8 @@ If you have the "views" directory available, Vitrine will try to pick up any usa
|
|
51
43
|
From there on, it's going to try to render it with the automatically picked template engine using the
|
52
44
|
standard Sinatra facilities. You can use HAML, LESS, Slim, ERB, Builder or anything else you like.
|
53
45
|
|
54
|
-
If you are writing an SPA, you can make a file called "catch_all.erb" which is going to be
|
55
|
-
for all missing URLs without extension.
|
46
|
+
If you are writing an SPA, you can make a file called "catch_all.erb" which is going to be
|
47
|
+
the fall-through template for all missing URLs without extension.
|
56
48
|
|
57
49
|
## Automatic reload via Guard
|
58
50
|
|
@@ -80,6 +72,15 @@ forget to set `:root` - like so:
|
|
80
72
|
vitrine.settings.set :root => File.dirname(__FILE__)
|
81
73
|
end
|
82
74
|
|
75
|
+
You can also only opt-in to the asset compilation system of Vitrine only once you have migrated your app from
|
76
|
+
the prototype stage into, say, a Sinatra application.
|
77
|
+
|
78
|
+
Note that you _need_ to have an `ExecJS` environment on your server for this:
|
79
|
+
|
80
|
+
use Vitrine::AssetCompiler.new do | ac |
|
81
|
+
vitrine.settings.set :root => File.dirname(__FILE__)
|
82
|
+
end
|
83
|
+
|
83
84
|
## Contributing to vitrine
|
84
85
|
|
85
86
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# The part of Vitrine responsible for SASS and CoffeeScript
|
2
|
+
# compilation and caching. By default it will assume your
|
3
|
+
# public directory is set on the inner app in the Rack stack,
|
4
|
+
# and can be retreived using the standard Sinatra settings
|
5
|
+
# protocol, like so:
|
6
|
+
#
|
7
|
+
# @app.settings.public_folder
|
8
|
+
#
|
9
|
+
# However, you can also set this setting on the app object using the
|
10
|
+
# AssetCompiler#public_dir accessor.
|
11
|
+
#
|
12
|
+
# use Vitrine::AssetCompiler do | compiler |
|
13
|
+
# compiler.public_dir = File.dirname(__FILE__) + '/public'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# This allows you to use the asset compiler when the inner app
|
17
|
+
# is not a Sinatra application.
|
18
|
+
#
|
19
|
+
# Obviously, the usual limitation apply for this kind of workflow -
|
20
|
+
# you pretty much have to have an ExecJS env on yourserver, or else...
|
21
|
+
class Vitrine::AssetCompiler < Sinatra::Base
|
22
|
+
set :show_exceptions, false
|
23
|
+
set :raise_errors, true
|
24
|
+
|
25
|
+
# An explicit override for +public_folder+ setting,
|
26
|
+
# if set will take precedence over the setting
|
27
|
+
attr_accessor :public_dir
|
28
|
+
|
29
|
+
# Try to find SCSS replacement for missing CSS
|
30
|
+
get /(.+)\.css/ do | basename |
|
31
|
+
begin
|
32
|
+
content_type 'text/css', :charset => 'utf-8'
|
33
|
+
# TODO: has no handling for .sass
|
34
|
+
scss_source_path = File.join(get_public, "#{basename}.scss")
|
35
|
+
mtime_cache(scss_source_path) do
|
36
|
+
# TODO: Examine http://sass-lang.com/documentation/file.SASS_REFERENCE.html
|
37
|
+
# It already has provisions for error display, among other things
|
38
|
+
Sass.compile_file(scss_source_path, cache_location: '/tmp/vitrine/sass-cache')
|
39
|
+
end
|
40
|
+
rescue Errno::ENOENT # Missing SCSS
|
41
|
+
forward_or_halt "No such CSS or SCSS file found"
|
42
|
+
rescue Exception => e # CSS syntax error or something alike
|
43
|
+
# Add a generated DOM element before <body/> to inject
|
44
|
+
# a visible error message
|
45
|
+
error_tpl = 'body:before {
|
46
|
+
background: white; padding: 3px; font-family: monospaced; color: red;
|
47
|
+
font-size: 14px; content: %s }'
|
48
|
+
css_message = error_tpl % [e.class, "\n", "--> ", e.message].join.inspect
|
49
|
+
# If we halt with 500 this will not be shown as CSS
|
50
|
+
halt 200, css_message
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generate a sourcemap for CoffeeScript files
|
55
|
+
get /(.+)\.js\.map$/ do | basename |
|
56
|
+
begin
|
57
|
+
coffee_source = File.join(get_public, "#{basename}.coffee")
|
58
|
+
content_type 'application/json', :charset => 'utf-8'
|
59
|
+
mtime_cache(coffee_source) do
|
60
|
+
Vitrine.build_coffeescript_source_map_body(coffee_source, get_public)
|
61
|
+
end
|
62
|
+
rescue Errno::ENOENT # Missing CoffeeScript
|
63
|
+
forward_or_halt "No coffeescript file found to generate the map for"
|
64
|
+
rescue Exception => e # CS syntax error or something alike
|
65
|
+
halt 400, 'Compliation of the related CoffeeScript file failed'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Try to find CoffeeScript replacement for missing JS
|
70
|
+
get /(.+)\.js$/ do | basename |
|
71
|
+
# If this file is not found resort back to a coffeescript
|
72
|
+
begin
|
73
|
+
coffee_source = File.join(get_public, "#{basename}.coffee")
|
74
|
+
content_type 'text/javascript'
|
75
|
+
mtime_cache(coffee_source) do
|
76
|
+
source_body = File.read(coffee_source)
|
77
|
+
# We could have sent a header, but it's a nice idea to have the
|
78
|
+
# sourcemap header saved if we write out the compiled JS,
|
79
|
+
# whereas otherwise it would have been discarded
|
80
|
+
[
|
81
|
+
"//# sourceMappingURL=#{basename}.js.map",
|
82
|
+
CoffeeScript.compile(source_body)
|
83
|
+
].join("\n")
|
84
|
+
end
|
85
|
+
rescue Errno::ENOENT # Missing CoffeeScript
|
86
|
+
forward_or_halt "No such JS file and could not find a .coffee replacement"
|
87
|
+
rescue Exception => e # CS syntax error or something alike
|
88
|
+
# Inject the syntax error into the browser console
|
89
|
+
console_message = 'console.error(%s)' % [e.class, "\n", "--> ", e.message].join.inspect
|
90
|
+
# Avoid 500 because it plays bad with LiveReload
|
91
|
+
halt 200, console_message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def mtime_cache(path, &blk)
|
96
|
+
# Mix in the request URL into the cache key so that we can hash
|
97
|
+
# .map sourcemaps and .js compiles based off of the same file path
|
98
|
+
# and mtime
|
99
|
+
key = [File.expand_path(path), File.mtime(path), request.path_info, get_public]
|
100
|
+
cache_sha = Digest::SHA1.hexdigest(Marshal.dump(key))
|
101
|
+
|
102
|
+
# Store in a temp dir
|
103
|
+
FileUtils.mkdir_p '/tmp/vitrine'
|
104
|
+
p = '/tmp/vitrine/%s' % cache_sha
|
105
|
+
|
106
|
+
# Only write it out unless a file with the same SHA does not exist
|
107
|
+
unless File.exist?(p)
|
108
|
+
Vitrine.atomic_write(p) do |f|
|
109
|
+
log "---> Recompiling #{path} for #{request.path_info}"
|
110
|
+
f.write(yield)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# And send out the file that's been written
|
115
|
+
last_modified(File.mtime(p))
|
116
|
+
etag File.mtime(p).to_i.to_s
|
117
|
+
File.read(p)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# Get path to the public directory, trying (in order:)
|
122
|
+
# self.public_dir reader
|
123
|
+
# the inner app's public_folder setting
|
124
|
+
# my own public_folder setting
|
125
|
+
def get_public
|
126
|
+
inner_public = if @app && @app.respond_to?(:settings)
|
127
|
+
@app.settings.public_folder
|
128
|
+
else
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
choices = [@public_dir, inner_public, settings.public_dir]
|
132
|
+
choices.compact.shift
|
133
|
+
end
|
134
|
+
|
135
|
+
def forward_or_halt msg
|
136
|
+
if @app
|
137
|
+
log "Forwarding, #{msg} -> pub #{get_public.inspect}"
|
138
|
+
forward
|
139
|
+
else
|
140
|
+
halt 404, msg
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def log(mgs)
|
145
|
+
env['captivity.logger'].debug(msg) if env['captivity.logger']
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
data/lib/version.rb
CHANGED
data/lib/vitrine.rb
CHANGED
@@ -3,10 +3,12 @@ require 'coffee-script'
|
|
3
3
|
require 'rack/contrib/try_static'
|
4
4
|
require 'sass'
|
5
5
|
require 'pathname'
|
6
|
+
require 'fileutils'
|
6
7
|
|
7
8
|
require_relative 'version'
|
8
9
|
require_relative 'atomic_write'
|
9
10
|
require_relative 'sourcemaps'
|
11
|
+
require_relative 'asset_compiler'
|
10
12
|
|
11
13
|
# A little idiosyncrastic asset server.
|
12
14
|
# Does very simple things:
|
@@ -14,14 +16,15 @@ require_relative 'sourcemaps'
|
|
14
16
|
# * automatic compilation of CoffeeScript and SASS assets - just request them with .js and .css
|
15
17
|
# and Vitrine will find them and compile them for you on the spot
|
16
18
|
class Vitrine::App < Sinatra::Base
|
17
|
-
set :static, true
|
18
19
|
|
19
20
|
set :show_exceptions, false
|
20
21
|
set :raise_errors, true
|
21
22
|
|
22
23
|
# Sets whether Vitrine will output messages about dynamic assets
|
23
24
|
set :silent, true
|
24
|
-
set :public_folder, ->{ File.join(settings.root, 'public') }
|
25
|
+
set :public_folder, ->{ File.join(settings.root, 'public') }
|
26
|
+
|
27
|
+
use Vitrine::AssetCompiler
|
25
28
|
|
26
29
|
# For extensionless things try to pick out the related templates
|
27
30
|
# from the views directory, and render them with a default layout.
|
@@ -88,88 +91,8 @@ class Vitrine::App < Sinatra::Base
|
|
88
91
|
template_engine = File.extname(template_path).gsub(/^\./, '')
|
89
92
|
render(template_engine, File.read(template_path), :layout => get_layout, :locals => locals)
|
90
93
|
end
|
91
|
-
|
92
|
-
|
93
|
-
# Try to find SCSS replacement for missing CSS
|
94
|
-
get /(.+)\.css/ do | basename |
|
95
|
-
begin
|
96
|
-
content_type 'text/css', :charset => 'utf-8'
|
97
|
-
# TODO: has no handling for .sass
|
98
|
-
scss_source_path = File.join(settings.root, 'public', "#{basename}.scss")
|
99
|
-
mtime_cache(scss_source_path) do
|
100
|
-
# TODO: Examine http://sass-lang.com/documentation/file.SASS_REFERENCE.html
|
101
|
-
Sass.compile_file(scss_source_path, cache_location: '/tmp/vitrine/sass-cache')
|
102
|
-
end
|
103
|
-
rescue Errno::ENOENT # Missing SCSS
|
104
|
-
halt 404, "No such CSS or SCSS file found"
|
105
|
-
rescue Exception => e # CSS syntax error or something alike
|
106
|
-
# Add a generated DOM element before <body/> to inject
|
107
|
-
# a visible error message
|
108
|
-
error_tpl = 'body:before { background: white; font-family: sans-serif; color: red; font-size: 14px; content: %s }'
|
109
|
-
css_message = error_tpl % [e.class, "\n", "--> ", e.message].join.inspect
|
110
|
-
# If we halt with 500 this will not be shown as CSS
|
111
|
-
halt 200, css_message
|
112
|
-
end
|
113
|
-
end
|
114
94
|
|
115
|
-
# Generate a sourcemap for CoffeeScript files
|
116
|
-
get /(.+)\.js\.map$/ do | basename |
|
117
|
-
begin
|
118
|
-
coffee_source = File.join(settings.root, 'public', "#{basename}.coffee")
|
119
|
-
content_type 'application/json', :charset => 'utf-8'
|
120
|
-
mtime_cache(coffee_source) do
|
121
|
-
Vitrine.build_coffeescript_source_map_body(coffee_source, File.join(settings.root, 'public'))
|
122
|
-
end
|
123
|
-
rescue Errno::ENOENT # Missing CoffeeScript
|
124
|
-
halt 404, "No coffeescript file found to generate the map for"
|
125
|
-
rescue Exception => e # CS syntax error or something alike
|
126
|
-
halt 400, 'Compliation of the related CoffeeScript file failed'
|
127
|
-
end
|
128
|
-
end
|
129
95
|
|
130
|
-
# Try to find CoffeeScript replacement for missing JS
|
131
|
-
get /(.+)\.js$/ do | basename |
|
132
|
-
# If this file is not found resort back to a coffeescript
|
133
|
-
begin
|
134
|
-
coffee_source = File.join(settings.root, 'public', "#{basename}.coffee")
|
135
|
-
content_type 'text/javascript'
|
136
|
-
mtime_cache(coffee_source) do
|
137
|
-
["//# sourceMappingURL=#{basename}.js.map", CoffeeScript.compile(File.read(coffee_source))].join("\n")
|
138
|
-
end
|
139
|
-
rescue Errno::ENOENT # Missing CoffeeScript
|
140
|
-
halt 404, "No such JS file and could not find a .coffee replacement"
|
141
|
-
rescue Exception => e # CS syntax error or something alike
|
142
|
-
# Inject the syntax error into the browser console
|
143
|
-
console_message = 'console.error(%s)' % [e.class, "\n", "--> ", e.message].join.inspect
|
144
|
-
halt 500, console_message
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
require 'fileutils'
|
149
|
-
|
150
|
-
def mtime_cache(path, &blk)
|
151
|
-
# Mix in the request URL into the cache key so that we can hash
|
152
|
-
# .map sourcemaps and .js compiles based off of the same file path
|
153
|
-
# and mtime
|
154
|
-
key = [File.expand_path(path), File.mtime(path), request.path_info, settings.root]
|
155
|
-
cache_sha = Digest::SHA1.hexdigest(Marshal.dump(key))
|
156
|
-
|
157
|
-
# Store in a temp dir
|
158
|
-
FileUtils.mkdir_p '/tmp/vitrine'
|
159
|
-
p = '/tmp/vitrine/%s' % cache_sha
|
160
|
-
if File.exist?(p)
|
161
|
-
etag File.mtime(p)
|
162
|
-
File.read(p)
|
163
|
-
else
|
164
|
-
yield.tap do | body |
|
165
|
-
Vitrine.atomic_write(p) do |f|
|
166
|
-
log "---> Recompiling #{path} for #{request.path_info}"
|
167
|
-
f.write body
|
168
|
-
end
|
169
|
-
etag File.mtime(p)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
96
|
|
174
97
|
def get_layout
|
175
98
|
layouts = Dir.glob(File.join(settings.views, 'layout.*'))
|
data/test/test_vitrine.rb
CHANGED
@@ -77,70 +77,9 @@ class TestVitrine < Test::Unit::TestCase
|
|
77
77
|
end
|
78
78
|
|
79
79
|
get '/nice.js'
|
80
|
-
|
81
|
-
assert_not_nil last_response.headers['ETag'], 'Should set ETag for the compiled version'
|
82
|
-
assert_equal 200, last_response.status
|
83
|
-
assert_equal 'text/javascript;charset=utf-8', last_response.content_type
|
84
|
-
|
85
|
-
assert last_response.body.include?( 'alert("rockage!")'), 'Should include the compiled function'
|
86
|
-
assert last_response.body.include?( '//# sourceMappingURL=/nice.js.map'),
|
87
|
-
'Should include the reference to the source map'
|
88
|
-
end
|
89
|
-
|
90
|
-
def test_compiles_coffeescript_sourcemap
|
91
|
-
|
92
|
-
FileUtils.mkdir_p File.join(@tempdir, 'public', 'js')
|
93
|
-
|
94
|
-
write_public 'js/nice.coffee' do | f |
|
95
|
-
f.puts 'alert "rockage!"'
|
96
|
-
end
|
97
|
-
|
98
|
-
# Sourcemap will only ever get requested AFTER the corresponding JS file
|
99
|
-
get '/js/nice.js'
|
100
|
-
assert last_response.ok?
|
101
|
-
|
102
|
-
get '/js/nice.js.map'
|
103
|
-
ref = {"version"=>3, "file"=>"", "sourceRoot"=>"", "sources"=>["/js/nice.coffee"],
|
104
|
-
"names"=>[], "mappings"=>"AAAA;CAAA,CAAA,GAAA,KAAA;CAAA"}
|
105
|
-
assert_equal ref, JSON.parse(last_response.body)
|
106
|
-
end
|
107
|
-
|
108
|
-
def test_sends_vanilla_js_if_its_present
|
109
|
-
write_public 'vanilla.js' do | f |
|
110
|
-
f.puts 'vanilla();'
|
111
|
-
end
|
112
|
-
|
113
|
-
get '/vanilla.js'
|
114
|
-
assert_equal 200, last_response.status
|
115
|
-
assert_equal "vanilla();\n", last_response.body
|
116
|
-
end
|
117
|
-
|
118
|
-
def test_invalid_coffeescript_creates_decent_error_alerts
|
119
|
-
write_public 'faulty.coffee' do | f |
|
120
|
-
f.puts 'function() { junked up }'
|
121
|
-
end
|
122
|
-
|
123
|
-
get '/faulty.js'
|
124
|
-
|
125
|
-
assert_equal 500, last_response.status
|
126
|
-
assert_equal 'text/javascript;charset=utf-8', last_response.content_type
|
127
|
-
err = 'console.error("ExecJS::RuntimeError\n--> SyntaxError: reserved word \"function\"")'
|
128
|
-
assert_equal err, last_response.body
|
80
|
+
assert_include last_response.body, 'alert("rockage!")', 'Should include the compiled function'
|
129
81
|
end
|
130
82
|
|
131
|
-
def test_caches_compiled_js_by_etag_and_responds_with_304_when_requested_again
|
132
|
-
write_public 'nice.coffee' do | f |
|
133
|
-
f.puts 'alert "rockage!"'
|
134
|
-
end
|
135
|
-
|
136
|
-
get '/nice.js'
|
137
|
-
assert_equal 200, last_response.status
|
138
|
-
assert_not_nil last_response.headers['ETag']
|
139
|
-
|
140
|
-
etag = last_response.headers['ETag']
|
141
|
-
get '/nice.js', {}, rack_env = {'HTTP_IF_NONE_MATCH' => etag}
|
142
|
-
assert_equal 304, last_response.status
|
143
|
-
end
|
144
83
|
|
145
84
|
def test_sends_vanilla_css_if_present
|
146
85
|
write_public 'vanilla.css' do | f |
|
@@ -153,29 +92,14 @@ class TestVitrine < Test::Unit::TestCase
|
|
153
92
|
assert_equal '/* vanilla CSS kode */', last_response.body
|
154
93
|
end
|
155
94
|
|
156
|
-
def test_compiles_scss_when_requested_as_css
|
157
|
-
write_public 'styles.scss' do | f |
|
158
|
-
f.puts '.foo {'
|
159
|
-
f.puts '.bar { font-size: 10px; }'
|
160
|
-
f.puts '}'
|
161
|
-
end
|
162
|
-
|
163
|
-
get '/styles.css'
|
164
|
-
|
165
|
-
assert last_response.ok?
|
166
|
-
assert_not_nil last_response.headers['ETag'], 'Should set ETag for the compiled version'
|
167
|
-
assert last_response.body.include?('.foo .bar {'), 'Should have compiled the CSS rule'
|
168
|
-
end
|
169
95
|
|
170
|
-
def
|
171
|
-
write_public '
|
172
|
-
f.puts '
|
96
|
+
def test_sends_vanilla_js_if_its_present
|
97
|
+
write_public 'vanilla.js' do | f |
|
98
|
+
f.puts 'vanilla();'
|
173
99
|
end
|
174
100
|
|
175
|
-
get '/
|
176
|
-
|
101
|
+
get '/vanilla.js'
|
177
102
|
assert_equal 200, last_response.status
|
178
|
-
|
179
|
-
assert last_response.body.include?('Sass::SyntaxError'), 'Should include the syntax error class'
|
103
|
+
assert_equal "vanilla();\n", last_response.body
|
180
104
|
end
|
181
105
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
class TestVitrineAssetCompiler < Test::Unit::TestCase
|
4
|
+
include Rack::Test::Methods, VitrineTesting
|
5
|
+
|
6
|
+
def app # overridden
|
7
|
+
Vitrine::AssetCompiler.new.tap { |a| a.settings.set :root, @tempdir }
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_compiles_coffeescript_to_js_when_addressed_by_js_extension
|
11
|
+
write_public 'nice.coffee' do | f |
|
12
|
+
f.puts 'alert "rockage!"'
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/nice.js'
|
16
|
+
|
17
|
+
assert_not_nil last_response.headers['ETag'], 'Should set ETag for the compiled version'
|
18
|
+
assert_equal 200, last_response.status
|
19
|
+
assert_equal 'text/javascript;charset=utf-8', last_response.content_type
|
20
|
+
|
21
|
+
assert last_response.body.include?( 'alert("rockage!")'), 'Should include the compiled function'
|
22
|
+
assert last_response.body.include?( '//# sourceMappingURL=/nice.js.map'),
|
23
|
+
'Should include the reference to the source map'
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_compiles_coffeescript_sourcemap
|
27
|
+
|
28
|
+
FileUtils.mkdir_p File.join(@tempdir, 'public', 'js')
|
29
|
+
|
30
|
+
write_public 'js/nice.coffee' do | f |
|
31
|
+
f.puts 'alert "rockage!"'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sourcemap will only ever get requested AFTER the corresponding JS file
|
35
|
+
get '/js/nice.js'
|
36
|
+
assert last_response.ok?
|
37
|
+
|
38
|
+
get '/js/nice.js.map'
|
39
|
+
ref = {"version"=>3, "file"=>"", "sourceRoot"=>"", "sources"=>["/js/nice.coffee"],
|
40
|
+
"names"=>[], "mappings"=>"AAAA;CAAA,CAAA,GAAA,KAAA;CAAA"}
|
41
|
+
assert_equal ref, JSON.parse(last_response.body)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_invalid_coffeescript_creates_decent_error_alerts
|
45
|
+
write_public 'faulty.coffee' do | f |
|
46
|
+
f.puts 'function() { junked up }'
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/faulty.js'
|
50
|
+
|
51
|
+
assert_equal 200, last_response.status
|
52
|
+
assert_equal 'text/javascript;charset=utf-8', last_response.content_type
|
53
|
+
err = 'console.error("ExecJS::RuntimeError\n--> SyntaxError: reserved word \"function\"")'
|
54
|
+
assert_equal err, last_response.body
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_caches_compiled_js_by_etag_and_responds_with_304_when_requested_again
|
58
|
+
write_public 'nice.coffee' do | f |
|
59
|
+
f.puts 'alert "rockage!"'
|
60
|
+
end
|
61
|
+
|
62
|
+
get '/nice.js'
|
63
|
+
assert_equal 200, last_response.status
|
64
|
+
assert_not_nil last_response.headers['ETag']
|
65
|
+
|
66
|
+
etag = last_response.headers['ETag']
|
67
|
+
get '/nice.js', {}, rack_env = {'HTTP_IF_NONE_MATCH' => etag}
|
68
|
+
assert_equal 304, last_response.status
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_compiles_scss_when_requested_as_css
|
72
|
+
write_public 'styles.scss' do | f |
|
73
|
+
f.puts '.foo {'
|
74
|
+
f.puts '.bar { font-size: 10px; }'
|
75
|
+
f.puts '}'
|
76
|
+
end
|
77
|
+
|
78
|
+
get '/styles.css'
|
79
|
+
|
80
|
+
assert last_response.ok?
|
81
|
+
assert_not_nil last_response.headers['ETag'], 'Should set ETag for the compiled version'
|
82
|
+
assert last_response.body.include?('.foo .bar {'), 'Should have compiled the CSS rule'
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_displays_decent_alerts_for_scss_errors
|
86
|
+
write_public 'faulty.scss' do | f |
|
87
|
+
f.puts '.foo {{ junkiness-factor: 24pem; }'
|
88
|
+
end
|
89
|
+
|
90
|
+
get '/faulty.css'
|
91
|
+
|
92
|
+
assert_equal 200, last_response.status
|
93
|
+
assert last_response.body.include?('body:before {'), 'Should include the generated element selector'
|
94
|
+
assert last_response.body.include?('Sass::SyntaxError'), 'Should include the syntax error class'
|
95
|
+
end
|
96
|
+
end
|
@@ -7,9 +7,12 @@ class TestVitrineInRackStack < Test::Unit::TestCase
|
|
7
7
|
def app
|
8
8
|
td = temporary_app_dir
|
9
9
|
outer = Rack::Builder.new do
|
10
|
-
|
11
|
-
|
10
|
+
# The outer app
|
11
|
+
use Vitrine::App do |v|
|
12
|
+
v.settings.set root: td
|
12
13
|
end
|
14
|
+
|
15
|
+
# And a lobster downstream
|
13
16
|
map "/lobster" do
|
14
17
|
run Rack::Lobster.new
|
15
18
|
end
|
@@ -25,7 +28,7 @@ class TestVitrineInRackStack < Test::Unit::TestCase
|
|
25
28
|
|
26
29
|
def test_fetch_js
|
27
30
|
write_public('hello.coffee') do | f |
|
28
|
-
f << 'window.alert
|
31
|
+
f << 'window.alert "Hello Coffee"'
|
29
32
|
end
|
30
33
|
|
31
34
|
get '/hello.js'
|
data/vitrine.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "vitrine"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.14"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Julik Tarkhanov"]
|
12
|
-
s.date = "2013-12-
|
12
|
+
s.date = "2013-12-23"
|
13
13
|
s.description = " Serves ERB templates with live CoffeeScript and SASS "
|
14
14
|
s.email = "me@julik.nl"
|
15
15
|
s.executables = ["vitrine"]
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.md",
|
26
26
|
"Rakefile",
|
27
27
|
"bin/vitrine",
|
28
|
+
"lib/asset_compiler.rb",
|
28
29
|
"lib/atomic_write.rb",
|
29
30
|
"lib/server.rb",
|
30
31
|
"lib/sourcemaps.rb",
|
@@ -32,6 +33,7 @@ Gem::Specification.new do |s|
|
|
32
33
|
"lib/vitrine.rb",
|
33
34
|
"test/helper.rb",
|
34
35
|
"test/test_vitrine.rb",
|
36
|
+
"test/test_vitrine_asset_compiler.rb",
|
35
37
|
"test/test_vitrine_in_rack_stack.rb",
|
36
38
|
"vitrine.gemspec"
|
37
39
|
]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vitrine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -222,6 +222,7 @@ files:
|
|
222
222
|
- README.md
|
223
223
|
- Rakefile
|
224
224
|
- bin/vitrine
|
225
|
+
- lib/asset_compiler.rb
|
225
226
|
- lib/atomic_write.rb
|
226
227
|
- lib/server.rb
|
227
228
|
- lib/sourcemaps.rb
|
@@ -229,6 +230,7 @@ files:
|
|
229
230
|
- lib/vitrine.rb
|
230
231
|
- test/helper.rb
|
231
232
|
- test/test_vitrine.rb
|
233
|
+
- test/test_vitrine_asset_compiler.rb
|
232
234
|
- test/test_vitrine_in_rack_stack.rb
|
233
235
|
- vitrine.gemspec
|
234
236
|
homepage: http://github.com/julik/vitrine
|