sprockets 1.0.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprockets might be problematic. Click here for more details.
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/lib/sprockets.rb +26 -37
- data/lib/sprockets/asset.rb +212 -0
- data/lib/sprockets/asset_attributes.rb +158 -0
- data/lib/sprockets/base.rb +163 -0
- data/lib/sprockets/bundled_asset.rb +258 -0
- data/lib/sprockets/cache/file_store.rb +41 -0
- data/lib/sprockets/caching.rb +123 -0
- data/lib/sprockets/charset_normalizer.rb +41 -0
- data/lib/sprockets/context.rb +217 -0
- data/lib/sprockets/digest.rb +67 -0
- data/lib/sprockets/directive_processor.rb +380 -0
- data/lib/sprockets/eco_template.rb +38 -0
- data/lib/sprockets/ejs_template.rb +37 -0
- data/lib/sprockets/engines.rb +98 -0
- data/lib/sprockets/environment.rb +81 -40
- data/lib/sprockets/errors.rb +17 -0
- data/lib/sprockets/index.rb +79 -0
- data/lib/sprockets/jst_processor.rb +26 -0
- data/lib/sprockets/mime.rb +38 -0
- data/lib/sprockets/processing.rb +280 -0
- data/lib/sprockets/processor.rb +32 -0
- data/lib/sprockets/safety_colons.rb +28 -0
- data/lib/sprockets/server.rb +272 -0
- data/lib/sprockets/static_asset.rb +86 -0
- data/lib/sprockets/trail.rb +114 -0
- data/lib/sprockets/utils.rb +67 -0
- data/lib/sprockets/version.rb +1 -7
- metadata +212 -64
- data/Rakefile +0 -19
- data/bin/sprocketize +0 -54
- data/ext/nph-sprockets.cgi +0 -127
- data/lib/sprockets/concatenation.rb +0 -36
- data/lib/sprockets/error.rb +0 -5
- data/lib/sprockets/pathname.rb +0 -37
- data/lib/sprockets/preprocessor.rb +0 -91
- data/lib/sprockets/secretary.rb +0 -106
- data/lib/sprockets/source_file.rb +0 -54
- data/lib/sprockets/source_line.rb +0 -82
- data/test/fixtures/assets/images/script_with_assets/one.png +0 -1
- data/test/fixtures/assets/images/script_with_assets/two.png +0 -1
- data/test/fixtures/assets/stylesheets/script_with_assets.css +0 -1
- data/test/fixtures/constants.yml +0 -1
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js +0 -8
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js +0 -2
- data/test/fixtures/multiline_comments_should_be_removed_by_default.js +0 -4
- data/test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js +0 -5
- data/test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js +0 -1
- data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +0 -3
- data/test/fixtures/requiring_the_current_file_should_do_nothing.js +0 -1
- data/test/fixtures/src/constants.yml +0 -3
- data/test/fixtures/src/foo.js +0 -1
- data/test/fixtures/src/foo/bar.js +0 -4
- data/test/fixtures/src/foo/foo.js +0 -1
- data/test/fixtures/src/script_with_assets.js +0 -3
- data/test/test_concatenation.rb +0 -28
- data/test/test_environment.rb +0 -64
- data/test/test_helper.rb +0 -55
- data/test/test_pathname.rb +0 -43
- data/test/test_preprocessor.rb +0 -107
- data/test/test_secretary.rb +0 -83
- data/test/test_source_file.rb +0 -34
- data/test/test_source_line.rb +0 -89
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 Sam Stephenson
|
2
|
+
Copyright (c) 2011 Joshua Peek
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
# Sprockets: Rack-based asset packaging
|
2
|
+
|
3
|
+
Sprockets is a Ruby library for compiling and serving web assets.
|
4
|
+
It features declarative dependency management for JavaScript and CSS
|
5
|
+
assets, as well as a powerful preprocessor pipeline that allows you to
|
6
|
+
write assets in languages like CoffeeScript, Sass, SCSS and LESS.
|
7
|
+
|
8
|
+
# Installation #
|
9
|
+
|
10
|
+
Install Sprockets from RubyGems:
|
11
|
+
|
12
|
+
$ gem install sprockets
|
13
|
+
|
14
|
+
Or include it in your project's `Gemfile` with Bundler:
|
15
|
+
|
16
|
+
gem 'sprockets', '~> 2.0'
|
17
|
+
|
18
|
+
# Understanding the Sprockets Environment #
|
19
|
+
|
20
|
+
You'll need an instance of the `Sprockets::Environment` class to
|
21
|
+
access and serve assets from your application. Under Rails 3.1 and
|
22
|
+
later, `YourApp::Application.assets` is a preconfigured
|
23
|
+
`Sprockets::Environment` instance. For Rack-based applications, create
|
24
|
+
an instance in `config.ru`.
|
25
|
+
|
26
|
+
The Sprockets `Environment` has methods for retrieving and serving
|
27
|
+
assets, manipulating the load path, and registering processors. It is
|
28
|
+
also a Rack application that can be mounted at a URL to serve assets
|
29
|
+
over HTTP.
|
30
|
+
|
31
|
+
## The Load Path ##
|
32
|
+
|
33
|
+
The *load path* is an ordered list of directories that Sprockets uses
|
34
|
+
to search for assets.
|
35
|
+
|
36
|
+
In the simplest case, a Sprockets environment's load path will consist
|
37
|
+
of a single directory containing your application's asset source
|
38
|
+
files. When mounted, the environment will serve assets from this
|
39
|
+
directory as if they were static files in your public root.
|
40
|
+
|
41
|
+
The power of the load path is that it lets you organize your source
|
42
|
+
files into multiple directories -- even directories that live outside
|
43
|
+
your application -- and combine those directories into a single
|
44
|
+
virtual filesystem. That means you can easily bundle JavaScript, CSS
|
45
|
+
and images into a Ruby library and import them into your application.
|
46
|
+
|
47
|
+
### Manipulating the Load Path ###
|
48
|
+
|
49
|
+
To add a directory to your environment's load path, use the
|
50
|
+
`append_path` and `prepend_path` methods. Directories at the beginning
|
51
|
+
of the load path have precedence over subsequent directories.
|
52
|
+
|
53
|
+
environment = Sprockets::Environment.new
|
54
|
+
environment.append_path 'app/assets/javascripts'
|
55
|
+
environment.append_path 'lib/assets/javascripts'
|
56
|
+
environment.append_path 'vendor/assets/jquery'
|
57
|
+
|
58
|
+
In general, you should append to the path by default and reserve
|
59
|
+
prepending for cases where you need to override existing assets.
|
60
|
+
|
61
|
+
## Accessing Assets ##
|
62
|
+
|
63
|
+
Once you've set up your environment's load path, you can mount the
|
64
|
+
environment as a Rack server and request assets via HTTP. You can also
|
65
|
+
access assets programmatically from within your application.
|
66
|
+
|
67
|
+
### Logical Paths ###
|
68
|
+
|
69
|
+
Assets in Sprockets are always referenced by their *logical path*.
|
70
|
+
|
71
|
+
The logical path is the path of the asset source file relative to its
|
72
|
+
containing directory in the load path. For example, if your load path
|
73
|
+
contains the directory `app/assets/javascripts`:
|
74
|
+
|
75
|
+
<table>
|
76
|
+
<tr>
|
77
|
+
<th>Asset source file</th>
|
78
|
+
<th>Logical path</th>
|
79
|
+
</tr>
|
80
|
+
<tr>
|
81
|
+
<td>app/assets/javascripts/application.js</td>
|
82
|
+
<td>application.js</td>
|
83
|
+
</tr>
|
84
|
+
<tr>
|
85
|
+
<td>app/assets/javascripts/models/project.js</td>
|
86
|
+
<td>models/project.js</td>
|
87
|
+
</tr>
|
88
|
+
</table>
|
89
|
+
|
90
|
+
In this way, all directories in the load path are merged to create a
|
91
|
+
virtual filesystem whose entries are logical paths.
|
92
|
+
|
93
|
+
### Serving Assets Over HTTP ###
|
94
|
+
|
95
|
+
When you mount an environment, all of its assets are accessible as
|
96
|
+
logical paths underneath the *mount point*. For example, if you mount
|
97
|
+
your environment at `/assets` and request the URL
|
98
|
+
`/assets/application.js`, Sprockets will search your load path for the
|
99
|
+
file named `application.js` and serve it.
|
100
|
+
|
101
|
+
Under Rails 3.1 and later, your Sprockets environment is automatically
|
102
|
+
mounted at `/assets`. If you are using Sprockets with a Rack
|
103
|
+
application, you will need to mount the environment yourself. A good
|
104
|
+
way to do this is with the `map` method in `config.ru`:
|
105
|
+
|
106
|
+
require 'sprockets'
|
107
|
+
map '/assets' do
|
108
|
+
environment = Sprockets::Environment.new
|
109
|
+
environment.append_path 'app/assets/javascripts'
|
110
|
+
environment.append_path 'app/assets/stylesheets'
|
111
|
+
run environment
|
112
|
+
end
|
113
|
+
|
114
|
+
### Accessing Assets Programmatically ###
|
115
|
+
|
116
|
+
You can use the `find_asset` method (aliased as `[]`) to retrieve an
|
117
|
+
asset from a Sprockets environment. Pass it a logical path and you'll
|
118
|
+
get a `Sprockets::BundledAsset` instance back:
|
119
|
+
|
120
|
+
environment['application.js']
|
121
|
+
# => #<Sprockets::BundledAsset ...>
|
122
|
+
|
123
|
+
Call `to_s` on the resulting asset to access its contents, `length` to
|
124
|
+
get its length in bytes, `mtime` to query its last-modified time, and
|
125
|
+
`pathname` to get its full path on the filesystem.
|
126
|
+
|
127
|
+
# Using Engines #
|
128
|
+
|
129
|
+
Asset source files can be written in another language, like SCSS or
|
130
|
+
CoffeeScript, and automatically compiled to CSS or JavaScript by
|
131
|
+
Sprockets. Compilers for these languages are called *engines*.
|
132
|
+
|
133
|
+
Engines are specified by additional extensions on the asset source
|
134
|
+
filename. For example, a CSS file written in SCSS might have the name
|
135
|
+
`layout.css.scss`, while a JavaScript file written in CoffeeScript
|
136
|
+
might have the name `dialog.js.coffee`.
|
137
|
+
|
138
|
+
## Styling with Sass and SCSS ##
|
139
|
+
|
140
|
+
[Sass](http://sass-lang.com/) is a language that compiles to CSS and
|
141
|
+
adds features like nested rules, variables, mixins and selector
|
142
|
+
inheritance.
|
143
|
+
|
144
|
+
If the `sass` gem is available to your application, you can use Sass
|
145
|
+
to write CSS assets in Sprockets.
|
146
|
+
|
147
|
+
Sprockets supports both Sass syntaxes. For the original
|
148
|
+
whitespace-sensitive syntax, use the extension `.css.sass`. For the
|
149
|
+
new SCSS syntax, use the extension `.css.scss`.
|
150
|
+
|
151
|
+
## Styling with LESS ##
|
152
|
+
|
153
|
+
[LESS](http://lesscss.org/) extends CSS with dynamic behavior such as
|
154
|
+
variables, mixins, operations and functions.
|
155
|
+
|
156
|
+
If the `less` gem is available to your application, you can use LESS
|
157
|
+
to write CSS assets in Sprockets. Note that the LESS compiler is
|
158
|
+
written in JavaScript, and at the time of this writing, the `less` gem
|
159
|
+
depends on `therubyracer` which embeds the V8 JavaScript runtime in
|
160
|
+
Ruby.
|
161
|
+
|
162
|
+
To write CSS assets with LESS, use the extension `.css.less`.
|
163
|
+
|
164
|
+
## Scripting with CoffeeScript ##
|
165
|
+
|
166
|
+
[CoffeeScript](http://jashkenas.github.com/coffee-script/) is a
|
167
|
+
language that compiles to the "good parts" of JavaScript, featuring a
|
168
|
+
cleaner syntax with array comprehensions, classes, and function
|
169
|
+
binding.
|
170
|
+
|
171
|
+
If the `coffee-script` gem is available to your application, you can
|
172
|
+
use CoffeeScript to write JavaScript assets in Sprockets. Note that
|
173
|
+
the CoffeeScript compiler is written in JavaScript, and you will need
|
174
|
+
an [ExecJS](https://github.com/sstephenson/execjs)-supported runtime
|
175
|
+
on your system to invoke it.
|
176
|
+
|
177
|
+
To write JavaScript assets with CoffeeScript, use the extension
|
178
|
+
`.js.coffee`.
|
179
|
+
|
180
|
+
## JavaScript Templating with EJS and Eco ##
|
181
|
+
|
182
|
+
Sprockets supports *JavaScript templates* for client-side rendering of
|
183
|
+
strings or markup. JavaScript templates have the special format
|
184
|
+
extension `.jst` and are compiled to JavaScript functions.
|
185
|
+
|
186
|
+
When loaded, a JavaScript template function can be accessed by its
|
187
|
+
logical path as a property on the global `JST` object. Invoke a
|
188
|
+
template function to render the template as a string. The resulting
|
189
|
+
string can then be inserted into the DOM.
|
190
|
+
|
191
|
+
<!-- templates/hello.jst.ejs -->
|
192
|
+
<div>Hello, <span><%= name %></span>!</div>
|
193
|
+
|
194
|
+
// application.js
|
195
|
+
//= require templates/hello
|
196
|
+
$("#hello").html(JST["templates/hello"]({ name: "Sam" }));
|
197
|
+
|
198
|
+
Sprockets supports two JavaScript template languages:
|
199
|
+
[EJS](https://github.com/sstephenson/ruby-ejs), for embedded
|
200
|
+
JavaScript, and [Eco](https://github.com/sstephenson/ruby-eco), for
|
201
|
+
embedded CoffeeScript. Both languages use the familiar `<% … %>`
|
202
|
+
syntax for embedding logic in templates.
|
203
|
+
|
204
|
+
If the `ejs` gem is available to your application, you can use EJS
|
205
|
+
templates in Sprockets. EJS templates have the extension `.jst.ejs`.
|
206
|
+
|
207
|
+
If the `eco` gem is available to your application, you can use [Eco
|
208
|
+
templates](https://github.com/sstephenson/eco) in Sprockets. Eco
|
209
|
+
templates have the extension `.jst.eco`. Note that the `eco` gem
|
210
|
+
depends on the CoffeeScript compiler, so the same caveats apply as
|
211
|
+
outlined above for the CoffeeScript engine.
|
212
|
+
|
213
|
+
## Invoking Ruby with ERB ##
|
214
|
+
|
215
|
+
Sprockets provides an ERB engine for preprocessing assets using
|
216
|
+
embedded Ruby code. Append `.erb` to a CSS or JavaScript asset's
|
217
|
+
filename to enable the ERB engine.
|
218
|
+
|
219
|
+
**Note**: Sprockets processes multiple engine extensions in order from
|
220
|
+
right to left, so you can use multiple engines with a single
|
221
|
+
asset. For example, to have a CoffeeScript asset that is first
|
222
|
+
preprocessed with ERB, use the extension `.js.coffee.erb`.
|
223
|
+
|
224
|
+
Ruby code embedded in an asset is evaluated in the context of a
|
225
|
+
`Sprockets::Context` instance for the given asset. Common uses for ERB
|
226
|
+
include:
|
227
|
+
|
228
|
+
- embedding another asset as a Base64-encoded `data:` URI with the
|
229
|
+
`asset_data_uri` helper
|
230
|
+
- inserting the URL to another asset, such as with the `asset_path`
|
231
|
+
helper provided by the Sprockets Rails plugin
|
232
|
+
- embedding other application resources, such as a localized string
|
233
|
+
database, in a JavaScript asset via JSON
|
234
|
+
- embedding version constants loaded from another file
|
235
|
+
|
236
|
+
See the [Helper Methods](#FIXME) section for more information about
|
237
|
+
interacting with `Sprockets::Context` instances via ERB.
|
238
|
+
|
239
|
+
### String Interpolation Syntax ###
|
240
|
+
|
241
|
+
If you need access to Ruby from an asset but cannot use ERB's `<% …
|
242
|
+
%>` syntax, Sprockets also supports Ruby string interpolation syntax
|
243
|
+
(`#{ … }`) with the `.str` engine extension.
|
244
|
+
|
245
|
+
# Managing and Bundling Dependencies #
|
246
|
+
|
247
|
+
You can create *asset bundles* -- ordered concatenations of asset
|
248
|
+
source files -- by specifying dependencies in a special comment syntax
|
249
|
+
at the top of each source file.
|
250
|
+
|
251
|
+
Sprockets reads these comments, called *directives*, and processes
|
252
|
+
them to recursively build a dependency graph. When you request an
|
253
|
+
asset with dependencies, the dependencies will be included in order at
|
254
|
+
the top of the file.
|
255
|
+
|
256
|
+
## The Directive Processor ##
|
257
|
+
|
258
|
+
Sprockets runs the *directive processor* on each CSS and JavaScript
|
259
|
+
source file. The directive processor scans for comment lines beginning
|
260
|
+
with `=` in comment blocks at the top of the file.
|
261
|
+
|
262
|
+
//= require jquery
|
263
|
+
//= require jquery-ui
|
264
|
+
//= require backbone
|
265
|
+
//= require_tree .
|
266
|
+
|
267
|
+
The first word immediately following `=` specifies the directive
|
268
|
+
name. Any words following the directive name are treated as
|
269
|
+
arguments. Arguments may be placed in single or double quotes if they
|
270
|
+
contain spaces, similar to commands in the Unix shell.
|
271
|
+
|
272
|
+
**Note**: Non-directive comment lines will be preserved in the final
|
273
|
+
asset, but directive comments are stripped after
|
274
|
+
processing. Sprockets will not look for directives in comment blocks
|
275
|
+
that occur after the first line of code.
|
276
|
+
|
277
|
+
### Supported Comment Types ###
|
278
|
+
|
279
|
+
The directive processor understands comment blocks in three formats:
|
280
|
+
|
281
|
+
/* Multi-line comment blocks (CSS, SCSS, JavaScript)
|
282
|
+
*= require foo
|
283
|
+
*/
|
284
|
+
|
285
|
+
// Single-line comment blocks (SCSS, JavaScript)
|
286
|
+
//= require foo
|
287
|
+
|
288
|
+
# Single-line comment blocks (CoffeeScript)
|
289
|
+
#= require foo
|
290
|
+
|
291
|
+
## Sprockets Directives ##
|
292
|
+
|
293
|
+
You can use the following directives to declare dependencies in asset
|
294
|
+
source files.
|
295
|
+
|
296
|
+
For directives that take a *path* argument, you may specify either a
|
297
|
+
logical path or a relative path. Relative paths begin with `./` and
|
298
|
+
reference files relative to the location of the current file.
|
299
|
+
|
300
|
+
### The `require` Directive ###
|
301
|
+
|
302
|
+
`require` *path* inserts the contents of the asset source file
|
303
|
+
specified by *path*. If the file is required multiple times, it will
|
304
|
+
appear in the bundle only once.
|
305
|
+
|
306
|
+
### The `include` Directive ###
|
307
|
+
|
308
|
+
`include` *path* works like `require`, but inserts the contents of the
|
309
|
+
specified source file even if it has already been included or
|
310
|
+
required.
|
311
|
+
|
312
|
+
### The `require_directory` Directive ###
|
313
|
+
|
314
|
+
`require_directory` *path* requires all source files of the same
|
315
|
+
format in the directory specified by *path*. Files are required in
|
316
|
+
alphabetical order.
|
317
|
+
|
318
|
+
### The `require_tree` Directive ###
|
319
|
+
|
320
|
+
`require_tree` *path* works like `require_directory`, but operates
|
321
|
+
recursively to require all files in all subdirectories of the
|
322
|
+
directory specified by *path*.
|
323
|
+
|
324
|
+
### The `require_self` Directive ###
|
325
|
+
|
326
|
+
`require_self` tells Sprockets to insert the body of the current
|
327
|
+
source file before any subsequent `require` or `include` directives.
|
328
|
+
|
329
|
+
### The `depend_on` Directive ###
|
330
|
+
|
331
|
+
`depend_on` *path* declares a dependency on the given *path* without
|
332
|
+
including it in the bundle. This is useful when you need to expire an
|
333
|
+
asset's cache in response to a change in another file.
|
334
|
+
|
335
|
+
# Contributing #
|
336
|
+
|
337
|
+
The Sprockets source code is [hosted on
|
338
|
+
GitHub](https://github.com/sstephenson/sprockets). You can check out a
|
339
|
+
copy of the latest code using Git:
|
340
|
+
|
341
|
+
$ git clone https://github.com/sstephenson/sprockets.git
|
342
|
+
|
343
|
+
If you've found a bug or have a question, please open an issue on the
|
344
|
+
[Sprockets issue
|
345
|
+
tracker](https://github.com/sstephenson/sprockets/issues). Or, clone
|
346
|
+
the Sprockets repository, write a failing test case, fix the bug and
|
347
|
+
submit a pull request.
|
348
|
+
|
349
|
+
# License #
|
350
|
+
|
351
|
+
Copyright © 2011 Sam Stephenson <<sstephenson@gmail.com>>
|
352
|
+
|
353
|
+
Copyright © 2011 Joshua Peek <<josh@joshpeek.com>>
|
354
|
+
|
355
|
+
Sprockets is distributed under an MIT-style license. See LICENSE for
|
356
|
+
details.
|
data/lib/sprockets.rb
CHANGED
@@ -1,42 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "yaml"
|
4
|
-
require "fileutils"
|
1
|
+
require 'sprockets/version'
|
5
2
|
|
6
3
|
module Sprockets
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
4
|
+
autoload :ArgumentError, "sprockets/errors"
|
5
|
+
autoload :Asset, "sprockets/asset"
|
6
|
+
autoload :AssetAttributes, "sprockets/asset_attributes"
|
7
|
+
autoload :BundledAsset, "sprockets/bundled_asset"
|
8
|
+
autoload :CharsetNormalizer, "sprockets/charset_normalizer"
|
9
|
+
autoload :CircularDependencyError, "sprockets/errors"
|
10
|
+
autoload :ContentTypeMismatch, "sprockets/errors"
|
11
|
+
autoload :Context, "sprockets/context"
|
12
|
+
autoload :DirectiveProcessor, "sprockets/directive_processor"
|
13
|
+
autoload :EcoTemplate, "sprockets/eco_template"
|
14
|
+
autoload :EjsTemplate, "sprockets/ejs_template"
|
15
|
+
autoload :EngineError, "sprockets/errors"
|
16
|
+
autoload :Engines, "sprockets/engines"
|
17
|
+
autoload :Environment, "sprockets/environment"
|
18
|
+
autoload :Error, "sprockets/errors"
|
19
|
+
autoload :FileNotFound, "sprockets/errors"
|
20
|
+
autoload :Index, "sprockets/index"
|
21
|
+
autoload :JstProcessor, "sprockets/jst_processor"
|
22
|
+
autoload :Processing, "sprockets/processing"
|
23
|
+
autoload :Processor, "sprockets/processor"
|
24
|
+
autoload :Server, "sprockets/server"
|
25
|
+
autoload :StaticAsset, "sprockets/static_asset"
|
26
|
+
autoload :Utils, "sprockets/utils"
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
location[0, 1] == File::SEPARATOR && File.expand_path(location) =~ /[A-Za-z]:[\/\\]/
|
28
|
-
end
|
29
|
-
end
|
28
|
+
module Cache
|
29
|
+
autoload :FileStore, "sprockets/cache/file_store"
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
33
|
-
require "sprockets/version"
|
34
|
-
require "sprockets/error"
|
35
|
-
require "sprockets/environment"
|
36
|
-
require "sprockets/pathname"
|
37
|
-
require "sprockets/source_line"
|
38
|
-
require "sprockets/source_file"
|
39
|
-
require "sprockets/concatenation"
|
40
|
-
require "sprockets/preprocessor"
|
41
|
-
require "sprockets/secretary"
|
42
|
-
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# `Asset` is the base class for `BundledAsset` and `StaticAsset`.
|
5
|
+
class Asset
|
6
|
+
# Internal initializer to load `Asset` from serialized `Hash`.
|
7
|
+
def self.from_hash(environment, hash)
|
8
|
+
asset = allocate
|
9
|
+
asset.init_with(environment, hash)
|
10
|
+
asset
|
11
|
+
end
|
12
|
+
|
13
|
+
# Define base set of attributes to be serialized.
|
14
|
+
def self.serialized_attributes
|
15
|
+
%w( id logical_path pathname )
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :environment
|
19
|
+
attr_reader :id, :logical_path, :pathname
|
20
|
+
|
21
|
+
def initialize(environment, logical_path, pathname)
|
22
|
+
@environment = environment
|
23
|
+
@logical_path = logical_path.to_s
|
24
|
+
@pathname = Pathname.new(pathname)
|
25
|
+
@id = environment.digest.update(object_id.to_s).to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initialize `Asset` from serialized `Hash`.
|
29
|
+
def init_with(environment, coder)
|
30
|
+
@environment = environment
|
31
|
+
@pathname = @mtime = @length = nil
|
32
|
+
|
33
|
+
self.class.serialized_attributes.each do |attr|
|
34
|
+
instance_variable_set("@#{attr}", coder[attr].to_s) if coder[attr]
|
35
|
+
end
|
36
|
+
|
37
|
+
if @pathname && @pathname.is_a?(String)
|
38
|
+
# Expand `$root` placeholder and wrapper string in a `Pathname`
|
39
|
+
@pathname = Pathname.new(expand_root_path(@pathname))
|
40
|
+
end
|
41
|
+
|
42
|
+
if @mtime && @mtime.is_a?(String)
|
43
|
+
# Parse time string
|
44
|
+
@mtime = Time.parse(@mtime)
|
45
|
+
end
|
46
|
+
|
47
|
+
if @length && @length.is_a?(String)
|
48
|
+
# Convert length to an `Integer`
|
49
|
+
@length = Integer(@length)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Copy serialized attributes to the coder object
|
54
|
+
def encode_with(coder)
|
55
|
+
coder['class'] = self.class.name.sub(/Sprockets::/, '')
|
56
|
+
|
57
|
+
self.class.serialized_attributes.each do |attr|
|
58
|
+
value = send(attr)
|
59
|
+
coder[attr] = case value
|
60
|
+
when Time
|
61
|
+
value.iso8601
|
62
|
+
else
|
63
|
+
value.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
coder['pathname'] = relativize_root_path(coder['pathname'])
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns `Content-Type` from pathname.
|
71
|
+
def content_type
|
72
|
+
@content_type ||= environment.content_type_of(pathname)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get mtime at the time the `Asset` is built.
|
76
|
+
def mtime
|
77
|
+
@mtime ||= environment.stat(pathname).mtime
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get length at the time the `Asset` is built.
|
81
|
+
def length
|
82
|
+
@length ||= environment.stat(pathname).size
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get content digest at the time the `Asset` is built.
|
86
|
+
def digest
|
87
|
+
@digest ||= environment.file_digest(pathname).hexdigest
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return logical path with digest spliced in.
|
91
|
+
#
|
92
|
+
# "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
|
93
|
+
#
|
94
|
+
def digest_path
|
95
|
+
environment.attributes_for(logical_path).path_with_fingerprint(digest)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Return an `Array` of `Asset` files that are declared dependencies.
|
99
|
+
def dependencies
|
100
|
+
[]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Expand asset into an `Array` of parts.
|
104
|
+
#
|
105
|
+
# Appending all of an assets body parts together should give you
|
106
|
+
# the asset's contents as a whole.
|
107
|
+
#
|
108
|
+
# This allows you to link to individual files for debugging
|
109
|
+
# purposes.
|
110
|
+
def to_a
|
111
|
+
[self]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Add enumerator to allow `Asset` instances to be used as Rack
|
115
|
+
# compatible body objects.
|
116
|
+
def each
|
117
|
+
yield to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
# Checks if Asset is fresh by comparing the actual mtime and
|
121
|
+
# digest to the inmemory model.
|
122
|
+
#
|
123
|
+
# Used to test if cached models need to be rebuilt.
|
124
|
+
#
|
125
|
+
# Subclass must override `fresh?` or `stale?`.
|
126
|
+
def fresh?
|
127
|
+
!stale?
|
128
|
+
end
|
129
|
+
|
130
|
+
# Checks if Asset is stale by comparing the actual mtime and
|
131
|
+
# digest to the inmemory model.
|
132
|
+
#
|
133
|
+
# Subclass must override `fresh?` or `stale?`.
|
134
|
+
def stale?
|
135
|
+
!fresh?
|
136
|
+
end
|
137
|
+
|
138
|
+
# Pretty inspect
|
139
|
+
def inspect
|
140
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} " +
|
141
|
+
"pathname=#{pathname.to_s.inspect}, " +
|
142
|
+
"mtime=#{mtime.inspect}, " +
|
143
|
+
"digest=#{digest.inspect}" +
|
144
|
+
">"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Assets are equal if they share the same path, mtime and digest.
|
148
|
+
def eql?(other)
|
149
|
+
other.class == self.class &&
|
150
|
+
other.relative_pathname == self.relative_pathname &&
|
151
|
+
other.mtime.to_i == self.mtime.to_i &&
|
152
|
+
other.digest == self.digest
|
153
|
+
end
|
154
|
+
alias_method :==, :eql?
|
155
|
+
|
156
|
+
protected
|
157
|
+
# Get pathname with its root stripped.
|
158
|
+
def relative_pathname
|
159
|
+
Pathname.new(relativize_root_path(pathname))
|
160
|
+
end
|
161
|
+
|
162
|
+
# Replace `$root` placeholder with actual environment root.
|
163
|
+
def expand_root_path(path)
|
164
|
+
environment.attributes_for(path).expand_root
|
165
|
+
end
|
166
|
+
|
167
|
+
# Replace actual environment root with `$root` placeholder.
|
168
|
+
def relativize_root_path(path)
|
169
|
+
environment.attributes_for(path).relativize_root
|
170
|
+
end
|
171
|
+
|
172
|
+
# Check if dependency is fresh.
|
173
|
+
#
|
174
|
+
# `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
|
175
|
+
#
|
176
|
+
# A `Hash` is used rather than other `Asset` object because we
|
177
|
+
# want to test non-asset files and directories.
|
178
|
+
def dependency_fresh?(dep = {})
|
179
|
+
path, mtime, hexdigest = dep.values_at('path', 'mtime', 'hexdigest')
|
180
|
+
|
181
|
+
stat = environment.stat(path)
|
182
|
+
|
183
|
+
# If path no longer exists, its definitely stale.
|
184
|
+
if stat.nil?
|
185
|
+
return false
|
186
|
+
end
|
187
|
+
|
188
|
+
# Compare dependency mime to the actual mtime. If the
|
189
|
+
# dependency mtime is newer than the actual mtime, the file
|
190
|
+
# hasn't changed since we created this `Asset` instance.
|
191
|
+
#
|
192
|
+
# However, if the mtime is newer it doesn't mean the asset is
|
193
|
+
# stale. Many deployment environments may recopy or recheckout
|
194
|
+
# assets on each deploy. In this case the mtime would be the
|
195
|
+
# time of deploy rather than modified time.
|
196
|
+
if mtime >= stat.mtime
|
197
|
+
return true
|
198
|
+
end
|
199
|
+
|
200
|
+
digest = environment.file_digest(path)
|
201
|
+
|
202
|
+
# If the mtime is newer, do a full digest comparsion. Return
|
203
|
+
# fresh if the digests match.
|
204
|
+
if hexdigest == digest.hexdigest
|
205
|
+
return true
|
206
|
+
end
|
207
|
+
|
208
|
+
# Otherwise, its stale.
|
209
|
+
false
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|