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.

Files changed (64) hide show
  1. data/LICENSE +21 -0
  2. data/README.md +356 -0
  3. data/lib/sprockets.rb +26 -37
  4. data/lib/sprockets/asset.rb +212 -0
  5. data/lib/sprockets/asset_attributes.rb +158 -0
  6. data/lib/sprockets/base.rb +163 -0
  7. data/lib/sprockets/bundled_asset.rb +258 -0
  8. data/lib/sprockets/cache/file_store.rb +41 -0
  9. data/lib/sprockets/caching.rb +123 -0
  10. data/lib/sprockets/charset_normalizer.rb +41 -0
  11. data/lib/sprockets/context.rb +217 -0
  12. data/lib/sprockets/digest.rb +67 -0
  13. data/lib/sprockets/directive_processor.rb +380 -0
  14. data/lib/sprockets/eco_template.rb +38 -0
  15. data/lib/sprockets/ejs_template.rb +37 -0
  16. data/lib/sprockets/engines.rb +98 -0
  17. data/lib/sprockets/environment.rb +81 -40
  18. data/lib/sprockets/errors.rb +17 -0
  19. data/lib/sprockets/index.rb +79 -0
  20. data/lib/sprockets/jst_processor.rb +26 -0
  21. data/lib/sprockets/mime.rb +38 -0
  22. data/lib/sprockets/processing.rb +280 -0
  23. data/lib/sprockets/processor.rb +32 -0
  24. data/lib/sprockets/safety_colons.rb +28 -0
  25. data/lib/sprockets/server.rb +272 -0
  26. data/lib/sprockets/static_asset.rb +86 -0
  27. data/lib/sprockets/trail.rb +114 -0
  28. data/lib/sprockets/utils.rb +67 -0
  29. data/lib/sprockets/version.rb +1 -7
  30. metadata +212 -64
  31. data/Rakefile +0 -19
  32. data/bin/sprocketize +0 -54
  33. data/ext/nph-sprockets.cgi +0 -127
  34. data/lib/sprockets/concatenation.rb +0 -36
  35. data/lib/sprockets/error.rb +0 -5
  36. data/lib/sprockets/pathname.rb +0 -37
  37. data/lib/sprockets/preprocessor.rb +0 -91
  38. data/lib/sprockets/secretary.rb +0 -106
  39. data/lib/sprockets/source_file.rb +0 -54
  40. data/lib/sprockets/source_line.rb +0 -82
  41. data/test/fixtures/assets/images/script_with_assets/one.png +0 -1
  42. data/test/fixtures/assets/images/script_with_assets/two.png +0 -1
  43. data/test/fixtures/assets/stylesheets/script_with_assets.css +0 -1
  44. data/test/fixtures/constants.yml +0 -1
  45. data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js +0 -8
  46. data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js +0 -2
  47. data/test/fixtures/multiline_comments_should_be_removed_by_default.js +0 -4
  48. data/test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js +0 -5
  49. data/test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js +0 -1
  50. data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +0 -3
  51. data/test/fixtures/requiring_the_current_file_should_do_nothing.js +0 -1
  52. data/test/fixtures/src/constants.yml +0 -3
  53. data/test/fixtures/src/foo.js +0 -1
  54. data/test/fixtures/src/foo/bar.js +0 -4
  55. data/test/fixtures/src/foo/foo.js +0 -1
  56. data/test/fixtures/src/script_with_assets.js +0 -3
  57. data/test/test_concatenation.rb +0 -28
  58. data/test/test_environment.rb +0 -64
  59. data/test/test_helper.rb +0 -55
  60. data/test/test_pathname.rb +0 -43
  61. data/test/test_preprocessor.rb +0 -107
  62. data/test/test_secretary.rb +0 -83
  63. data/test/test_source_file.rb +0 -34
  64. 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.
@@ -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 &copy; 2011 Sam Stephenson <<sstephenson@gmail.com>>
352
+
353
+ Copyright &copy; 2011 Joshua Peek <<josh@joshpeek.com>>
354
+
355
+ Sprockets is distributed under an MIT-style license. See LICENSE for
356
+ details.
@@ -1,42 +1,31 @@
1
- $:.unshift File.dirname(__FILE__)
2
-
3
- require "yaml"
4
- require "fileutils"
1
+ require 'sprockets/version'
5
2
 
6
3
  module Sprockets
7
- class << self
8
- def running_on_windows?
9
- RUBY_PLATFORM =~ /(win|w)32$/
10
- end
11
-
12
- def absolute?(location)
13
- same_when_expanded?(location) || platform_absolute_path?(location)
14
- end
15
-
16
- protected
17
- def same_when_expanded?(location)
18
- location[0, 1] == File.expand_path(location)[0, 1]
19
- end
20
-
21
- def platform_absolute_path?(location)
22
- false
23
- end
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
- if Sprockets.running_on_windows?
26
- def platform_absolute_path?(location)
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