cat_herder 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +13 -0
- data/lib/cat_herder.rb +15 -0
- data/lib/cat_herder/asset_not_found.rb +9 -0
- data/lib/cat_herder/asset_not_public.rb +9 -0
- data/lib/cat_herder/assets.rb +84 -0
- data/lib/cat_herder/assets/asset.rb +89 -0
- data/lib/cat_herder/assets/erb_asset.rb +68 -0
- data/lib/cat_herder/assets/verbatim_asset.rb +23 -0
- data/lib/cat_herder/current.rb +19 -0
- data/lib/cat_herder/helper.rb +9 -0
- data/lib/cat_herder/railtie.rb +38 -0
- data/lib/cat_herder/version.rb +3 -0
- data/lib/tasks/cat_herder_tasks.rake +8 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 479a2393cf3c34ee5fca8210d9e642232ab012b6475a5204811e913cfbb9a4df
|
4
|
+
data.tar.gz: 461262ce3786120921eadfb1b082a4e494a6aacf7bb5806e0b569c3dbfd0c47b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d44bbb630714a52bbed16ed50f5c107e50212ddadcc28d830acd50864ae68c64cf320140fc1103c5ccd7c90ece4acf083c786f5279ac0a478a17377ce15e0d89
|
7
|
+
data.tar.gz: cda8936e2835e4e8635317da66c5611ff745799c02e91ac6c150e7dc85d835c3425b1ecfb3b5e8c23228b815fcc358cf00169a83bd8d55dde230e58b9268c973
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Jonathan Hefner
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# cat_herder
|
2
|
+
|
3
|
+
Minimal Rails asset pipeline experiment:
|
4
|
+
|
5
|
+
* Assets are fingerprinted and copied to `public/assets` in development, just
|
6
|
+
like `rails assets:precompile`. Thus they are served directly, without any
|
7
|
+
special routes or middleware.
|
8
|
+
|
9
|
+
* ERB assets are evaluated; all other assets are copied verbatim.
|
10
|
+
|
11
|
+
* ERB assets can call `asset_path` and all other [`AssetUrlHelper`][] helpers.
|
12
|
+
|
13
|
+
* Currently, `*_url` helpers always return a path instead of a full URL (the
|
14
|
+
same as `*_path` helpers). Assuming these helpers are primarily used for
|
15
|
+
import statements (e.g. `@import url(...)`), this shouldn't pose a problem,
|
16
|
+
because the browser resolves such partial URLs to the asset host rather than
|
17
|
+
the page host. The benefit of the current implementation is that it
|
18
|
+
sidesteps the issue of cache invalidation when `config.asset_host` changes.
|
19
|
+
|
20
|
+
* ERB assets can call `render` to render the content of another asset inline.
|
21
|
+
|
22
|
+
* ERB assets can call `glob` to iterate over other assets. Using a given
|
23
|
+
pattern, `glob` will search all load paths. With a combination of `glob` and
|
24
|
+
`render`, assets can perform their own bundling.
|
25
|
+
|
26
|
+
* ERB assets can call `resolve` to get an absolute path to an asset file. This
|
27
|
+
can be used to pass the asset to an external command, e.g.:
|
28
|
+
|
29
|
+
```erb
|
30
|
+
<%# styles.css.erb %>
|
31
|
+
<%= `sass #{resolve "styles.sass"}` %>
|
32
|
+
```
|
33
|
+
|
34
|
+
* All calls to `asset_path` / `compute_asset_path`, `render`, and `resolve` will
|
35
|
+
add the resulting asset to the current asset's dependencies, so that the
|
36
|
+
current asset will be recompiled when any of its dependencies are.
|
37
|
+
|
38
|
+
* Partial assets are prefixed with an underscore (like view partials), and are
|
39
|
+
not copied to `public/assets` by `rails assets:precompile`. They can be
|
40
|
+
referenced using their logical path without the underscore (like view
|
41
|
+
partials). This allows "private" files, such as raw input files or config
|
42
|
+
files, to be placed in any of the asset load paths and be evaluated like other
|
43
|
+
assets. (Calls to `compute_asset_path` that resolve to a partial asset will
|
44
|
+
raise an error to prevent broken URLs.)
|
45
|
+
|
46
|
+
[`AssetUrlHelper`]: https://api.rubyonrails.org/classes/ActionView/Helpers/AssetUrlHelper.html
|
47
|
+
|
48
|
+
|
49
|
+
## Installation
|
50
|
+
|
51
|
+
Add this line to your application's Gemfile:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
gem "cat_herder"
|
55
|
+
```
|
56
|
+
|
57
|
+
And run:
|
58
|
+
|
59
|
+
```bash
|
60
|
+
$ bundle install
|
61
|
+
```
|
62
|
+
|
63
|
+
Then disable Sprockets, and require *cat_herder* in your `config/application.rb`
|
64
|
+
file:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
require "cat_herder/railtie"
|
68
|
+
```
|
69
|
+
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
Run `bin/test` to run the tests.
|
74
|
+
|
75
|
+
|
76
|
+
## License
|
77
|
+
|
78
|
+
[MIT License](MIT-LICENSE)
|
data/Rakefile
ADDED
data/lib/cat_herder.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cat_herder/version"
|
4
|
+
|
5
|
+
module CatHerder
|
6
|
+
extend ActiveSupport::Autoload
|
7
|
+
|
8
|
+
autoload :AssetNotFound
|
9
|
+
autoload :AssetNotPublic
|
10
|
+
autoload :Assets
|
11
|
+
autoload :Current
|
12
|
+
autoload :Helper
|
13
|
+
|
14
|
+
EMPTY_ARRAY = [].freeze
|
15
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "active_support/core_ext/enumerable"
|
5
|
+
|
6
|
+
module CatHerder
|
7
|
+
module Assets
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
|
10
|
+
autoload :ErbAsset
|
11
|
+
autoload :VerbatimAsset
|
12
|
+
|
13
|
+
mattr_accessor :load_paths, default: []
|
14
|
+
mattr_accessor :public_subpath, default: "assets"
|
15
|
+
mattr_accessor :cache_store
|
16
|
+
mattr_accessor :precompiled, default: false
|
17
|
+
singleton_class.alias_method :precompiled?, :precompiled
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def cache
|
21
|
+
@cache ||= ActiveSupport::Cache.lookup_store(*cache_store)
|
22
|
+
end
|
23
|
+
|
24
|
+
def public_path
|
25
|
+
@public_path ||= Rails.public_path.join(public_subpath)
|
26
|
+
end
|
27
|
+
|
28
|
+
def public_files
|
29
|
+
public_path.glob("**/*").select(&:file?)
|
30
|
+
end
|
31
|
+
|
32
|
+
def public_file_logical_path(public_file)
|
33
|
+
public_file.dirname.relative_path_from(public_path).to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
def resolve_logical_path(logical_path)
|
37
|
+
Current.resolved_paths[logical_path] ||= begin
|
38
|
+
logical_dirname, logical_basename = File.split(logical_path)
|
39
|
+
basename_pattern = /\A_?#{Regexp.escape logical_basename}(?:\.erb)?\z/
|
40
|
+
load_paths.find do |load_path|
|
41
|
+
dirname = File.expand_path(logical_dirname, load_path)
|
42
|
+
basename = Current.dir_children(dirname).find { |name| basename_pattern.match?(name) }
|
43
|
+
break File.join(dirname, basename) if basename
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](logical_path)
|
49
|
+
source_path = resolve_logical_path(logical_path) or raise AssetNotFound, logical_path
|
50
|
+
(@assets ||= {})[source_path] ||= (source_path.end_with?(".erb") ? ErbAsset : VerbatimAsset).new(logical_path, source_path)
|
51
|
+
end
|
52
|
+
|
53
|
+
def glob(*logical_patterns, &block)
|
54
|
+
logical_patterns.map! { |pattern| "#{pattern}{,.erb}" }
|
55
|
+
load_paths.flat_map { |load_path| Dir.glob(*logical_patterns, base: load_path) }.
|
56
|
+
each { |path| path.sub!(%r"_?([^/]+?)(?:\.erb)?\z", '\1') }.uniq.
|
57
|
+
tap { |logical_paths| logical_paths.each(&block) if block }
|
58
|
+
end
|
59
|
+
|
60
|
+
def precompile
|
61
|
+
public_path.rmtree if public_path.exist?
|
62
|
+
glob("**/[^_]*") { |logical_path| self[logical_path].compile }
|
63
|
+
@assets&.each_value { |asset| asset.public_file.dirname.rmtree if asset.partial? && asset.public_file.exist? }
|
64
|
+
cache.clear
|
65
|
+
end
|
66
|
+
|
67
|
+
def precompiled_asset_paths
|
68
|
+
@precompiled_asset_paths ||= public_files.index_by { |file| public_file_logical_path(file) }.
|
69
|
+
transform_values! { |file| "/#{file.relative_path_from(Rails.public_path)}" }
|
70
|
+
end
|
71
|
+
|
72
|
+
def precompiled_asset_path(logical_path)
|
73
|
+
precompiled_asset_paths[logical_path] or raise AssetNotFound, logical_path
|
74
|
+
end
|
75
|
+
|
76
|
+
def clean
|
77
|
+
public_files.each do |file|
|
78
|
+
logical_path = public_file_logical_path(file)
|
79
|
+
file.delete unless resolve_logical_path(logical_path) && file == self[logical_path].public_file
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CatHerder
|
4
|
+
module Assets
|
5
|
+
class Asset
|
6
|
+
attr_reader :logical_path, :source_path, :partial
|
7
|
+
alias :partial? :partial
|
8
|
+
|
9
|
+
def initialize(logical_path, source_path)
|
10
|
+
@logical_path = logical_path
|
11
|
+
@source_path = source_path
|
12
|
+
@partial = File.basename(source_path).start_with?("_")
|
13
|
+
@metadata = Assets.cache.read([self, "metadata"]) || {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def cache_key
|
17
|
+
source_path.delete_prefix(Rails.root.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
def digest_class
|
21
|
+
ActiveSupport::Digest.hash_digest_class
|
22
|
+
end
|
23
|
+
|
24
|
+
def digest
|
25
|
+
@metadata[:digest]
|
26
|
+
end
|
27
|
+
|
28
|
+
def dependencies
|
29
|
+
@metadata[:dependencies]&.map { |logical_path| Assets[logical_path] } || EMPTY_ARRAY
|
30
|
+
end
|
31
|
+
|
32
|
+
def dependency_digests
|
33
|
+
@metadata[:dependency_digests] || EMPTY_ARRAY
|
34
|
+
end
|
35
|
+
|
36
|
+
def mtime
|
37
|
+
@metadata[:mtime] || Float::NAN
|
38
|
+
end
|
39
|
+
|
40
|
+
def source_mtime
|
41
|
+
Current.mtime(source_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def stale?
|
45
|
+
mtime != source_mtime || dependency_digests != dependencies.map(&:digest) || dependencies.any?(&:stale?)
|
46
|
+
end
|
47
|
+
|
48
|
+
def public_subpath
|
49
|
+
File.join(Assets.public_subpath, logical_path, "#{digest}#{File.extname(logical_path)}")
|
50
|
+
end
|
51
|
+
|
52
|
+
def public_file
|
53
|
+
Rails.public_path.join(public_subpath)
|
54
|
+
end
|
55
|
+
|
56
|
+
def written?
|
57
|
+
Current.mtime(public_file.to_s) > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def compile
|
61
|
+
write if !written? || stale?
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_metadata(digest:, dependencies: nil)
|
65
|
+
@metadata = {
|
66
|
+
mtime: source_mtime,
|
67
|
+
digest: digest,
|
68
|
+
dependencies: dependencies&.map(&:logical_path),
|
69
|
+
dependency_digests: dependencies&.map(&:digest),
|
70
|
+
}
|
71
|
+
Assets.cache.write([self, "metadata"], @metadata)
|
72
|
+
end
|
73
|
+
|
74
|
+
def asset_path
|
75
|
+
raise AssetNotPublic, self if partial?
|
76
|
+
compile
|
77
|
+
File.join("/", public_subpath)
|
78
|
+
end
|
79
|
+
|
80
|
+
def render
|
81
|
+
compile
|
82
|
+
read
|
83
|
+
end
|
84
|
+
|
85
|
+
def write; raise NotImplementedError; end
|
86
|
+
def read; raise NotImplementedError; end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_view/helpers/asset_url_helper"
|
4
|
+
require "cat_herder/assets/asset"
|
5
|
+
|
6
|
+
module CatHerder
|
7
|
+
module Assets
|
8
|
+
class ErbAsset < Asset
|
9
|
+
def write
|
10
|
+
result, dependencies = evaluate_erb
|
11
|
+
write_metadata(digest: digest_class.hexdigest(result), dependencies: dependencies)
|
12
|
+
public_file.tap { |file| file.dirname.mkpath }.write(result)
|
13
|
+
end
|
14
|
+
|
15
|
+
def read
|
16
|
+
public_file.read
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def evaluate_erb
|
21
|
+
ruby = Assets.cache.fetch([self, "ruby"], version: source_mtime) do
|
22
|
+
require "erubi"
|
23
|
+
Erubi::Engine.new(File.read(source_path), filename: source_path).src
|
24
|
+
end
|
25
|
+
context = ErbContext.new(logical_path)
|
26
|
+
[context.instance_eval(ruby), context._dependencies]
|
27
|
+
end
|
28
|
+
|
29
|
+
class ErbContext
|
30
|
+
include ActionView::Helpers::AssetUrlHelper
|
31
|
+
|
32
|
+
attr_reader :_dependencies
|
33
|
+
|
34
|
+
def initialize(logical_path)
|
35
|
+
@_logical_path = logical_path
|
36
|
+
@_dependencies = []
|
37
|
+
end
|
38
|
+
|
39
|
+
def compute_asset_path(logical_path, *)
|
40
|
+
_dependency(logical_path).asset_path
|
41
|
+
end
|
42
|
+
|
43
|
+
def resolve(logical_path)
|
44
|
+
_dependency(logical_path).source_path
|
45
|
+
end
|
46
|
+
|
47
|
+
def render(logical_path)
|
48
|
+
_dependency(logical_path).render
|
49
|
+
end
|
50
|
+
|
51
|
+
def glob(*logical_patterns, &block)
|
52
|
+
Assets.glob(*logical_patterns.map { |pattern| _expand_logical_path(pattern) }, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def _dependency(logical_path)
|
57
|
+
dependency = Assets[_expand_logical_path(logical_path)]
|
58
|
+
@_dependencies << dependency unless @_dependencies.include?(dependency)
|
59
|
+
dependency
|
60
|
+
end
|
61
|
+
|
62
|
+
def _expand_logical_path(logical_path)
|
63
|
+
logical_path.start_with?("./", "../") ? Pathname(@_logical_path).dirname.join(logical_path).to_s : logical_path
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require "cat_herder/assets/asset"
|
5
|
+
|
6
|
+
module CatHerder
|
7
|
+
module Assets
|
8
|
+
class VerbatimAsset < Asset
|
9
|
+
def write
|
10
|
+
write_metadata(digest: digest_class.file(source_path).hexdigest)
|
11
|
+
FileUtils.cp(source_path, public_file.tap { |file| file.dirname.mkpath }) unless partial?
|
12
|
+
end
|
13
|
+
|
14
|
+
def written?
|
15
|
+
partial? || super
|
16
|
+
end
|
17
|
+
|
18
|
+
def read
|
19
|
+
File.read(source_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CatHerder
|
4
|
+
class Current < ActiveSupport::CurrentAttributes
|
5
|
+
attribute :resolved_paths, :mtime_cache, :dir_children_cache
|
6
|
+
|
7
|
+
def resolved_paths
|
8
|
+
super || (self.resolved_paths = {})
|
9
|
+
end
|
10
|
+
|
11
|
+
def mtime(path)
|
12
|
+
(self.mtime_cache ||= {})[path] ||= File.file?(path) ? File.mtime(path).to_f : Float::NAN
|
13
|
+
end
|
14
|
+
|
15
|
+
def dir_children(path)
|
16
|
+
(self.dir_children_cache ||= {})[path] ||= File.directory?(path) ? Dir.children(path) : EMPTY_ARRAY
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
require "cat_herder"
|
5
|
+
|
6
|
+
module CatHerder
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
config.assets = ActiveSupport::OrderedOptions.new
|
9
|
+
|
10
|
+
initializer "assets.configure" do |app|
|
11
|
+
Assets.load_paths = [
|
12
|
+
*app.paths["app/assets"].existent_directories,
|
13
|
+
*app.paths["lib/assets"].existent_directories,
|
14
|
+
*app.paths["vendor/assets"].existent_directories,
|
15
|
+
*app.config.assets.paths,
|
16
|
+
]
|
17
|
+
Assets.public_subpath = app.config.assets.prefix.delete_prefix("/") if app.config.assets.prefix
|
18
|
+
Assets.cache_store = app.config.assets.cache_store || [:file_store, app.root.join("tmp/assets.cache")]
|
19
|
+
Assets.precompiled = app.config.assets.compile == false
|
20
|
+
end
|
21
|
+
|
22
|
+
server do
|
23
|
+
if Assets.precompiled?
|
24
|
+
Assets.precompiled_asset_paths # warm up
|
25
|
+
else
|
26
|
+
Assets.clean
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
ActiveSupport.on_load(:action_view) do
|
31
|
+
include Helper
|
32
|
+
end
|
33
|
+
|
34
|
+
rake_tasks do
|
35
|
+
load "tasks/cat_herder_tasks.rake"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cat_herder
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Hefner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-03-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '6.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.1'
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- jonathan@hefner.pro
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- MIT-LICENSE
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- lib/cat_herder.rb
|
38
|
+
- lib/cat_herder/asset_not_found.rb
|
39
|
+
- lib/cat_herder/asset_not_public.rb
|
40
|
+
- lib/cat_herder/assets.rb
|
41
|
+
- lib/cat_herder/assets/asset.rb
|
42
|
+
- lib/cat_herder/assets/erb_asset.rb
|
43
|
+
- lib/cat_herder/assets/verbatim_asset.rb
|
44
|
+
- lib/cat_herder/current.rb
|
45
|
+
- lib/cat_herder/helper.rb
|
46
|
+
- lib/cat_herder/railtie.rb
|
47
|
+
- lib/cat_herder/version.rb
|
48
|
+
- lib/tasks/cat_herder_tasks.rake
|
49
|
+
homepage: https://github.com/jonathanhefner/cat_herder
|
50
|
+
licenses:
|
51
|
+
- MIT
|
52
|
+
metadata:
|
53
|
+
homepage_uri: https://github.com/jonathanhefner/cat_herder
|
54
|
+
source_code_uri: https://github.com/jonathanhefner/cat_herder
|
55
|
+
changelog_uri: https://github.com/jonathanhefner/cat_herder/blob/master/CHANGELOG.md
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubygems_version: 3.1.4
|
72
|
+
signing_key:
|
73
|
+
specification_version: 4
|
74
|
+
summary: Minimal Rails asset pipeline experiment
|
75
|
+
test_files: []
|