Skip to content

Commit

Permalink
Adding directive depends_on_directory
Browse files Browse the repository at this point in the history
`depends_on_directory` allows you to specify all files (non-recurisve)
in a directory to watch for changes. This is helpful in cases where
files may be dynamic or there are many files.
  • Loading branch information
jessecollier committed Apr 3, 2020
1 parent 9909da6 commit 7b492a6
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Get upgrade notes from Sprockets 3.x to 4.x at https://github.com/rails/sprocket
## Master

- Fix for Ruby 2.7 keyword arguments warning in `base.rb`. [#660](https://github.com/rails/sprockets/pull/660)
- Adding new directive `depend_on_directory` [#668](https://github.com/rails/sprockets/pull/668)

## 4.0.0

Expand Down
48 changes: 44 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Here is a list of the available directives:
- [`link_directory`](#link_directory) - Make target directory compile and be publicly available without adding contents to current
- [`link_tree`](#link_tree) - Make target tree compile and be publicly available without adding contents to current
- [`depend_on`](#depend_on) - Recompile current file if target has changed
- [`depend_on_directory`](#depend_on_directory) - Recompile current file if any files in target directory has changed
- [`stub`](#stub) - Ignore target file

You can see what each of these does below.
Expand Down Expand Up @@ -239,7 +240,7 @@ The first time this file is compiled the `application.js` output will be written

So, if `b.js` changes it will get recompiled. However instead of having to recompile the other files from `a.js` to `z.js` since they did not change, we can use the prior intermediary files stored in the cached values . If these files were expensive to generate, then this "partial" asset cache strategy can save a lot of time.

Directives such as `require`, `link`, and `depend_on` tell Sprockets what assets need to be re-compiled when a file changes. Files are considered "fresh" based on their mtime on disk and a combination of cache keys.
Directives such as `require`, `link`, `depend_on`, and `depend_on_directory` tell Sprockets what assets need to be re-compiled when a file changes. Files are considered "fresh" based on their mtime on disk and a combination of cache keys.

On Rails you can force a "clean" install by clearing the `public/assets` and `tmp/cache/assets` directories.

Expand Down Expand Up @@ -445,11 +446,50 @@ you need to tell sprockets that it needs to re-compile the file if `bar.data` ch
var bar = '<%= File.read("bar.data") %>'
```

To depend on an entire directory containing multiple files, use `depend_on_directory`

### depend_on_asset

`depend_on_asset` *path* works like `depend_on`, but operates
recursively reading the file and following the directives found. This is automatically implied if you use `link`, so consider if it just makes sense using `link` instead of `depend_on_asset`.

### depend_on_directory

`depend_on_directory` *path* declares all files in the given *path* without
including them in the bundle. This is useful when you need to expire an
asset's cache in response to a change in multiple files in a single directory.

All paths are relative to your declaration and must begin with `./`

Also, your must include these directories in your [load path](guides/building_an_asset_processing_framework.md#the-load-path).

**Example:**

If we've got a directory called `data` with files `a.data` and `b.data`

```a.data
A
```

```b.data
B
```

```js.erb
//= depend_on_directory ./data
var a = '<% File.read('data/a.data') %>'
var b = '<% File.read('data/b.data') %>'
```

Would produce:

```js
var a = "A";
var b = "B";
```

You can also see [Index files are proxies for folders](#index-files-are-proxies-for-folders) for another method of organizing folders that will give you more control.

### stub

`stub` *path* excludes that asset and its dependencies from the asset bundle.
Expand Down Expand Up @@ -491,9 +531,9 @@ When you modify the `logo.png` on disk, it will force `application.css` to be
recompiled so that the fingerprint will be correct in the generated asset.

You can manually make sprockets depend on any other file that is generated
by sprockets by using the `depend_on` directive. Rails implements the above
feature by auto calling `depend_on` on the original asset when the `asset_url`
is used inside of an asset.
by sprockets by using the `depend_on` or `depend_on_directory` directive. Rails
implements the above feature by auto calling `depend_on` on the original asset
when the `asset_url` is used inside of an asset.

### Styling with Sass and SCSS

Expand Down
20 changes: 19 additions & 1 deletion lib/sprockets/directive_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,24 @@ def process_depend_on_asset_directive(path)
to_load(resolve(path))
end

# Allows you to state a dependency on a relative directory
# without including it.
#
# This is used for caching purposes. Any changes made to
# the dependency directory will invalidate the cache of the
# source file.
#
# This is useful if you are using ERB and File.read to pull
# in contents from multiple files in a directory.
#
# //= depend_on_directory ./data
#
def process_depend_on_directory_directive(path = ".", accept = nil)
path = expand_relative_dirname(:depend_on_directory, path)
accept = expand_accept_shorthand(accept)
resolve_paths(*@environment.stat_directory_with_dependencies(path), accept: accept)
end

# Allows dependency to be excluded from the asset bundle.
#
# The `path` must be a valid asset and may or may not already
Expand Down Expand Up @@ -374,7 +392,7 @@ def resolve_paths(paths, deps, **kargs)
next if subpath == @filename || stat.directory?
uri, deps = @environment.resolve(subpath, **kargs)
@dependencies.merge(deps)
yield uri if uri
yield uri if uri && block_given?
end
end

Expand Down
44 changes: 44 additions & 0 deletions test/test_asset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,50 @@ def self.test(name, &block)
end
end
end

test "modify asset's dependency file in directory" do
main = fixture_path('asset/test-main.js.erb')
dep = fixture_path('asset/data/foo.txt')
begin
::FileUtils.mkdir File.dirname(dep)
sandbox main, dep do
write(main, "//= depend_on_directory ./data\n<%= File.read('#{dep}') %>")
write(dep, "a;")
asset = asset('test-main.js')
old_digest = asset.hexdigest
old_uri = asset.uri
assert_equal "a;\n", asset.to_s

write(dep, "b;")
asset = asset('test-main.js')
refute_equal old_digest, asset.hexdigest
refute_equal old_uri, asset.uri
assert_equal "b;\n", asset.to_s
end
ensure
::FileUtils.rmtree File.dirname(dep)
end
end

test "asset's dependency on directory exists" do
main = fixture_path('asset/test-missing-directory.js.erb')
dep = fixture_path('asset/data/foo.txt')

begin
sandbox main, dep do
::FileUtils.rmtree File.dirname(dep)
write(main, "//= depend_on_directory ./data")
assert_raises(Sprockets::ArgumentError) do
asset('test-missing-directory.js')
end

::FileUtils.mkdir File.dirname(dep)
assert asset('test-missing-directory.js')
end
ensure
::FileUtils.rmtree File.dirname(dep)
end
end
end

class TextStaticAssetTest < Sprockets::TestCase
Expand Down

0 comments on commit 7b492a6

Please sign in to comment.