lotus-assets 0.0.0 → 0.1.0
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/CHANGELOG.md +19 -0
- data/{LICENSE.txt → LICENSE.md} +1 -1
- data/README.md +429 -7
- data/bin/lotus-assets +22 -0
- data/lib/lotus/assets.rb +153 -2
- data/lib/lotus/assets/bundler.rb +173 -0
- data/lib/lotus/assets/cache.rb +58 -0
- data/lib/lotus/assets/compiler.rb +212 -0
- data/lib/lotus/assets/compressors/abstract.rb +119 -0
- data/lib/lotus/assets/compressors/builtin_javascript.rb +36 -0
- data/lib/lotus/assets/compressors/builtin_stylesheet.rb +57 -0
- data/lib/lotus/assets/compressors/closure_javascript.rb +25 -0
- data/lib/lotus/assets/compressors/javascript.rb +77 -0
- data/lib/lotus/assets/compressors/jsmin.rb +283 -0
- data/lib/lotus/assets/compressors/null_compressor.rb +19 -0
- data/lib/lotus/assets/compressors/sass_stylesheet.rb +38 -0
- data/lib/lotus/assets/compressors/stylesheet.rb +77 -0
- data/lib/lotus/assets/compressors/uglifier_javascript.rb +25 -0
- data/lib/lotus/assets/compressors/yui_javascript.rb +25 -0
- data/lib/lotus/assets/compressors/yui_stylesheet.rb +25 -0
- data/lib/lotus/assets/config/global_sources.rb +50 -0
- data/lib/lotus/assets/config/manifest.rb +112 -0
- data/lib/lotus/assets/config/sources.rb +77 -0
- data/lib/lotus/assets/configuration.rb +539 -0
- data/lib/lotus/assets/helpers.rb +733 -0
- data/lib/lotus/assets/precompiler.rb +67 -0
- data/lib/lotus/assets/version.rb +4 -1
- data/lotus-assets.gemspec +25 -11
- metadata +192 -15
- data/.gitignore +0 -22
- data/Gemfile +0 -4
- data/Rakefile +0 -2
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'lotus/assets/compressors/abstract'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
module Assets
|
5
|
+
module Compressors
|
6
|
+
# No-op, it returns the asset contents without to compress them.
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
class NullCompressor < Abstract
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
def compress(filename)
|
14
|
+
read(filename)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'lotus/assets/compressors/stylesheet'
|
2
|
+
require 'sass'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Assets
|
6
|
+
module Compressors
|
7
|
+
# Sass compressor for stylesheet
|
8
|
+
#
|
9
|
+
# It depends on <tt>sass</tt> gem.
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see http://sass-lang.com
|
15
|
+
# @see https://rubygems.org/gems/sass
|
16
|
+
class SassStylesheet < Stylesheet
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
# FIXME This is the same logic that we have for Lotus::Assets::Compiler
|
21
|
+
SASS_CACHE_LOCATION = Pathname(Lotus.respond_to?(:root) ?
|
22
|
+
Lotus.root : Dir.pwd).join('tmp', 'sass-cache')
|
23
|
+
# @since 0.1.0
|
24
|
+
# @api private
|
25
|
+
def initialize
|
26
|
+
@compressor = Sass::Engine
|
27
|
+
end
|
28
|
+
|
29
|
+
# @since 0.1.0
|
30
|
+
# @api private
|
31
|
+
def compress(filename)
|
32
|
+
compressor.new(read(filename), filename: filename, syntax: :scss,
|
33
|
+
style: :compressed, cache_location: SASS_CACHE_LOCATION).render
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'lotus/assets/compressors/abstract'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
module Assets
|
5
|
+
module Compressors
|
6
|
+
# Base class for stylesheet compressors
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
class Stylesheet < Abstract
|
11
|
+
# Factory for Stylesheet compressors.
|
12
|
+
#
|
13
|
+
# It loads a compressor for the given name.
|
14
|
+
#
|
15
|
+
# @param engine_name [Symbol,String,NilClass,#compress] the name of the
|
16
|
+
# engine to load or an instance of an engine
|
17
|
+
#
|
18
|
+
# @return [Lotus::Assets::Compressors::Abstract] returns a concrete
|
19
|
+
# implementation of a compressor
|
20
|
+
#
|
21
|
+
# @raise [Lotus::Assets::Compressors::UnknownCompressorError] when the
|
22
|
+
# given name refers to an unknown compressor engine
|
23
|
+
#
|
24
|
+
# @since 0.1.0
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
# @see Lotus::Assets::Compressors::Abstract#for
|
28
|
+
# @see Lotus::Assets::Configuration#stylesheet_compressor
|
29
|
+
#
|
30
|
+
# @example Basic Usage
|
31
|
+
# require 'lotus/assets'
|
32
|
+
# require 'lotus/assets/compressors/stylesheet'
|
33
|
+
#
|
34
|
+
# Lotus::Assets::Compressors::Stylesheet.for(:sass)
|
35
|
+
# # => #<Lotus::Assets::Compressors::SassStylesheet:0x007f8674cc4a50 ...>
|
36
|
+
#
|
37
|
+
# @example Null Compressor
|
38
|
+
# require 'lotus/assets'
|
39
|
+
# require 'lotus/assets/compressors/stylesheet'
|
40
|
+
#
|
41
|
+
# Lotus::Assets::Compressors::Stylesheet.for(nil)
|
42
|
+
# # => #<Lotus::Assets::Compressors::NullCompressor:0x007fa32a314258>
|
43
|
+
#
|
44
|
+
# @example Custom Compressor
|
45
|
+
# require 'lotus/assets'
|
46
|
+
# require 'lotus/assets/compressors/stylesheet'
|
47
|
+
#
|
48
|
+
# class CustomStylesheetCompressor
|
49
|
+
# def compress(filename)
|
50
|
+
# # ...
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# Lotus::Assets::Compressors::Stylesheet.for(CustomStylesheetCompressor.new)
|
55
|
+
# # => #<CustomStylesheetCompressor:0x007fa32a2cdf10>
|
56
|
+
#
|
57
|
+
# @example Third Party Compressor
|
58
|
+
# require 'lotus/assets'
|
59
|
+
# require 'lotus/assets/compressors/stylesheet'
|
60
|
+
# require 'lotus/foo/compressor' # third party gem
|
61
|
+
#
|
62
|
+
# Lotus::Assets::Compressors::Stylesheet.for(:foo)
|
63
|
+
# # => #<Lotus::Assets::Compressors::FooStylesheet:0x007fa3dd9ed968>
|
64
|
+
#
|
65
|
+
# @example Unknown Engine
|
66
|
+
# require 'lotus/assets'
|
67
|
+
# require 'lotus/assets/compressors/stylesheet'
|
68
|
+
#
|
69
|
+
# Lotus::Assets::Compressors::Stylesheet.for(:wat)
|
70
|
+
# # => Lotus::Assets::Compressors::UnknownCompressorError: Unknown Stylesheet compressor: :wat
|
71
|
+
def self.for(engine_name)
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lotus/assets/compressors/javascript'
|
2
|
+
require 'uglifier'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Assets
|
6
|
+
module Compressors
|
7
|
+
# Uglifier compressor for JavaScript
|
8
|
+
#
|
9
|
+
# It depends on <tt>uglifier</tt> gem
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see http://lisperator.net/uglifyjs
|
15
|
+
# @see https://rubygems.org/gems/uglifier
|
16
|
+
class UglifierJavascript < Javascript
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
def initialize
|
20
|
+
@compressor = Uglifier.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lotus/assets/compressors/javascript'
|
2
|
+
require 'yui/compressor'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Assets
|
6
|
+
module Compressors
|
7
|
+
# YUI Compressor for JavaScript
|
8
|
+
#
|
9
|
+
# It depends on <tt>yui-compressor</tt> gem
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see http://yui.github.io/yuicompressor
|
15
|
+
# @see https://rubygems.org/gems/yui-compressor
|
16
|
+
class YuiJavascript < Javascript
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
def initialize
|
20
|
+
@compressor = YUI::JavaScriptCompressor.new(munge: true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lotus/assets/compressors/stylesheet'
|
2
|
+
require 'yui/compressor'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Assets
|
6
|
+
module Compressors
|
7
|
+
# YUI Compressor for stylesheet
|
8
|
+
#
|
9
|
+
# It depends on <tt>yui-compressor</tt> gem
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see http://yui.github.io/yuicompressor
|
15
|
+
# @see https://rubygems.org/gems/yui-compressor
|
16
|
+
class YuiStylesheet < Stylesheet
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
def initialize
|
20
|
+
@compressor = YUI::CssCompressor.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'lotus/utils/load_paths'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
module Assets
|
5
|
+
# Configuration settings
|
6
|
+
#
|
7
|
+
# @since 0.1.0
|
8
|
+
# @api private
|
9
|
+
module Config
|
10
|
+
# Global asset sources across all the duplicated <tt>Lotus::Assets</tt>
|
11
|
+
# instances.
|
12
|
+
#
|
13
|
+
# @since 0.1.0
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# @see Lotus::Assets.duplicate
|
17
|
+
# @see http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/LoadPaths
|
18
|
+
class GlobalSources < Utils::LoadPaths
|
19
|
+
# @since 0.1.0
|
20
|
+
# @api private
|
21
|
+
def push(*paths)
|
22
|
+
super
|
23
|
+
|
24
|
+
sync_configuration
|
25
|
+
sync_duplicated_frameworks
|
26
|
+
end
|
27
|
+
|
28
|
+
# @since 0.1.0
|
29
|
+
# @api private
|
30
|
+
alias_method :<<, :push
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# @since 0.1.0
|
35
|
+
# @api private
|
36
|
+
def sync_configuration
|
37
|
+
Lotus::Assets.configuration.sources << @paths
|
38
|
+
end
|
39
|
+
|
40
|
+
# @since 0.1.0
|
41
|
+
# @api private
|
42
|
+
def sync_duplicated_frameworks
|
43
|
+
Lotus::Assets.duplicates.each do |duplicate|
|
44
|
+
duplicate.configuration.sources << @paths
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Assets
|
3
|
+
# This error is raised when the application starts but can't be load the
|
4
|
+
# digest manifest.
|
5
|
+
#
|
6
|
+
# @since 0.1.0
|
7
|
+
# @api private
|
8
|
+
class MissingDigestManifestError < Error
|
9
|
+
def initialize(path)
|
10
|
+
super("Can't read manifest: #{ path }")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# This error is raised when an asset is referenced from the DOM, but it's
|
15
|
+
# not present in the digest manifest
|
16
|
+
#
|
17
|
+
# @since 0.1.0
|
18
|
+
# @api private
|
19
|
+
class MissingDigestAssetError < Error
|
20
|
+
def initialize(asset, manifest_path)
|
21
|
+
super("Can't find asset `#{ asset }' in manifest (#{ manifest_path })")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Configuration settings
|
26
|
+
#
|
27
|
+
# @since 0.1.0
|
28
|
+
# @api private
|
29
|
+
module Config
|
30
|
+
# Default value for configuration's digest manifest.
|
31
|
+
#
|
32
|
+
# It indicates that the digest manifest wasn't loaded yet.
|
33
|
+
#
|
34
|
+
# At the load time, this should be replaced by an instance of
|
35
|
+
# <tt>Lotus::Assets::Config::Manifest</tt>.
|
36
|
+
#
|
37
|
+
# If for some reason that won't happen, the instance of this class is
|
38
|
+
# still referenced by the configuration and all the method invocations
|
39
|
+
# will raise a <tt>Lotus::Assets::MissingDigestManifestError</tt>.
|
40
|
+
#
|
41
|
+
# @since 0.1.0
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
# @see Lotus::Assets::Configuration#manifest
|
45
|
+
# @see Lotus::Assets::Configuration#manifest_path
|
46
|
+
# @see Lotus::Assets::Configuration#digest
|
47
|
+
class NullDigestManifest < Utils::BasicObject
|
48
|
+
# Return a new instance
|
49
|
+
#
|
50
|
+
# @param configuration [Lotus::Assets::Configuration]
|
51
|
+
#
|
52
|
+
# @return [Lotus::Assets::Config::NullDigestManifest] a new instance
|
53
|
+
#
|
54
|
+
# @since 0.1.0
|
55
|
+
# @api private
|
56
|
+
def initialize(configuration)
|
57
|
+
@configuration = configuration
|
58
|
+
end
|
59
|
+
|
60
|
+
# @raise [Lotus::Assets::MissingDigestManifestError]
|
61
|
+
#
|
62
|
+
# @since 0.1.0
|
63
|
+
# @api private
|
64
|
+
def method_missing(*)
|
65
|
+
::Kernel.raise(
|
66
|
+
::Lotus::Assets::MissingDigestManifestError.new(@configuration.manifest_path)
|
67
|
+
)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Digest manifest
|
72
|
+
#
|
73
|
+
# @since 0.1.0
|
74
|
+
# @api private
|
75
|
+
class DigestManifest
|
76
|
+
# Return a new instance
|
77
|
+
#
|
78
|
+
# @param assets [Hash] the content of the digest manifest
|
79
|
+
# @param manifest_path [Pathname] the path to the digest manifest
|
80
|
+
#
|
81
|
+
# @return [Lotus::Assets::Config::Manifest] a new instance
|
82
|
+
#
|
83
|
+
# @since 0.1.0
|
84
|
+
# @api private
|
85
|
+
#
|
86
|
+
# @see Lotus::Assets::Configuration#manifest
|
87
|
+
# @see Lotus::Assets::Configuration#manifest_path
|
88
|
+
def initialize(assets, manifest_path)
|
89
|
+
@assets = assets
|
90
|
+
@manifest_path = manifest_path
|
91
|
+
end
|
92
|
+
|
93
|
+
# Resolve the given asset into a digest path
|
94
|
+
#
|
95
|
+
# For a given path <tt>/assets/application.js</tt> it will return
|
96
|
+
# <tt>/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js</tt>
|
97
|
+
#
|
98
|
+
# @param asset [#to_s] the relateive asset path
|
99
|
+
#
|
100
|
+
# @return [String] the digest path
|
101
|
+
#
|
102
|
+
# @raise [Lotus::Assets::MissingDigestAssetError] when the asset can't be
|
103
|
+
# found in manifest
|
104
|
+
def resolve(asset)
|
105
|
+
@assets.fetch(asset.to_s) do
|
106
|
+
raise Lotus::Assets::MissingDigestAssetError.new(asset, @manifest_path)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'lotus/utils/load_paths'
|
2
|
+
|
3
|
+
module Lotus
|
4
|
+
module Assets
|
5
|
+
# Configuration settings
|
6
|
+
#
|
7
|
+
# @since 0.1.0
|
8
|
+
# @api private
|
9
|
+
module Config
|
10
|
+
# Source directories for a specific application
|
11
|
+
#
|
12
|
+
# @since 0.1.0
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
# @see Lotus::Assets.duplicate
|
16
|
+
# @see http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/LoadPaths
|
17
|
+
#
|
18
|
+
# TODO The perf of this class is poor, consider to improve it.
|
19
|
+
class Sources < Utils::LoadPaths
|
20
|
+
# @since 0.1.0
|
21
|
+
# @api private
|
22
|
+
attr_writer :root
|
23
|
+
|
24
|
+
# @since 0.1.0
|
25
|
+
# @api private
|
26
|
+
def initialize(root)
|
27
|
+
super()
|
28
|
+
@root = root
|
29
|
+
end
|
30
|
+
|
31
|
+
# @since 0.1.0
|
32
|
+
# @api private
|
33
|
+
def map
|
34
|
+
Array.new.tap do |result|
|
35
|
+
each do |source|
|
36
|
+
result << yield(source)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @since 0.1.0
|
42
|
+
# @api private
|
43
|
+
def find(filename)
|
44
|
+
result = files(filename).first
|
45
|
+
result = Pathname.new(result) unless result.nil?
|
46
|
+
result
|
47
|
+
end
|
48
|
+
|
49
|
+
# @since 0.1.0
|
50
|
+
# @api private
|
51
|
+
def files(name = nil)
|
52
|
+
result = []
|
53
|
+
|
54
|
+
Dir.glob(map {|source| "#{ source }#{ ::File::SEPARATOR }**#{ ::File::SEPARATOR }#{ name }*"}).each do |file|
|
55
|
+
next if ::File.directory?(file) || ::File.basename(file).match(/\A\_/)
|
56
|
+
result << file
|
57
|
+
end
|
58
|
+
|
59
|
+
result
|
60
|
+
end
|
61
|
+
|
62
|
+
# @since 0.1.0
|
63
|
+
# @api private
|
64
|
+
def to_a
|
65
|
+
map {|s| s }
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
# @since 0.1.0
|
70
|
+
# @api private
|
71
|
+
def realpath(path)
|
72
|
+
@root.join(path).realpath
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|