lib/hanami/assets/compiler.rb in hanami-assets-0.2.1 vs lib/hanami/assets/compiler.rb in hanami-assets-0.3.0
- old
+ new
@@ -1,19 +1,23 @@
+require 'set'
require 'find'
+require 'hanami/utils/class_attribute'
module Hanami
module Assets
+ # Missing Asset error
class MissingAsset < Error
def initialize(name, sources)
sources = sources.map(&:to_s).join(', ')
- super("Missing asset: `#{ name }' (sources: #{ sources })")
+ super("Missing asset: `#{name}' (sources: #{sources})")
end
end
+ # Unknown Asset Engine error
class UnknownAssetEngine < Error
def initialize(source)
- super("No asset engine registered for `#{ ::File.basename(source) }'")
+ super("No asset engine registered for `#{::File.basename(source)}'")
end
end
# Assets compiler
#
@@ -22,28 +26,37 @@
#
# Vanilla javascripts or stylesheets are just copied over.
#
# @since 0.1.0
# @api private
- class Compiler
+ class Compiler # rubocop:disable Metrics/ClassLength
# @since 0.1.0
# @api private
- DEFAULT_PERMISSIONS = 0644
+ DEFAULT_PERMISSIONS = 0o644
# @since 0.1.0
# @api private
COMPILE_PATTERN = '*.*.*'.freeze # Example hello.js.es6
# @since 0.1.0
# @api private
- EXTENSIONS = {'.js' => true, '.css' => true, '.map' => true}.freeze
+ EXTENSIONS = { '.js' => true, '.css' => true, '.map' => true }.freeze
- # @since 0.1.0
+ include Utils::ClassAttribute
+
+ # @since 0.3.0
# @api private
- SASS_CACHE_LOCATION = Pathname(Hanami.respond_to?(:root) ?
- Hanami.root : Dir.pwd).join('tmp', 'sass-cache')
+ class_attribute :subclasses
+ self.subclasses = Set.new
+ # @since 0.3.0
+ # @api private
+ def self.inherited(subclass)
+ super
+ subclasses.add(subclass)
+ end
+
# Compile the given asset
#
# @param configuration [Hanami::Assets::Configuration] the application
# configuration associated with the given asset
#
@@ -54,21 +67,40 @@
def self.compile(configuration, name)
return unless configuration.compile
require 'tilt'
require 'hanami/assets/cache'
- new(configuration, name).compile
+ require 'hanami/assets/compilers/sass'
+ require 'hanami/assets/compilers/less'
+ fabricate(configuration, name).compile
end
+ # @since 0.3.0
+ # @api private
+ def self.fabricate(configuration, name)
+ source = configuration.source(name)
+ engine = (subclasses + [self]).find do |klass|
+ klass.eligible?(source)
+ end
+
+ engine.new(configuration, name)
+ end
+
+ # @since 0.3.0
+ # @api private
+ def self.eligible?(_name)
+ true
+ end
+
# Assets cache
#
# @since 0.1.0
# @api private
#
# @see Hanami::Assets::Cache
def self.cache
- @@cache ||= Assets::Cache.new
+ @@cache ||= Assets::Cache.new # rubocop:disable Style/ClassVars
end
# Return a new instance
#
# @param configuration [Hanami::Assets::Configuration] the application
@@ -92,11 +124,11 @@
#
# @since 0.1.0
# @api private
def compile
raise MissingAsset.new(@name, @configuration.sources) unless exist?
- return unless fresh?
+ return unless modified?
if compile?
compile!
else
copy!
@@ -108,14 +140,11 @@
private
# @since 0.1.0
# @api private
def source
- @source ||= begin
- @name.absolute? ? @name :
- @configuration.find(@name)
- end
+ @source ||= @configuration.source(@name)
end
# @since 0.1.0
# @api private
def destination
@@ -139,42 +168,28 @@
def exist?
!source.nil? &&
source.exist?
end
- # @since 0.1.0
+ # @since 0.3.0
# @api private
- def fresh?
+ def modified?
!destination.exist? ||
- cache.fresh?(source)
+ cache.modified?(source)
end
# @since 0.1.0
# @api private
def compile?
- @compile ||= ::File.fnmatch(COMPILE_PATTERN, source.to_s) &&
- !EXTENSIONS[::File.extname(source.to_s)]
+ @compile ||= ::File.fnmatch(COMPILE_PATTERN, ::File.basename(source.to_s)) &&
+ !EXTENSIONS[::File.extname(source.to_s)]
end
# @since 0.1.0
# @api private
def compile!
- # NOTE `:load_paths' is useful only for Sass engine, to make `@include' directive to work.
- # For now we don't want to maintan a specialized Compiler version for Sass.
- #
- # If in the future other precompilers will need special treatment,
- # we can consider to maintain several specialized versions in order to
- # don't add a perf tax to all the other preprocessors who "just work".
- #
- # Example: if Less "just works", we can keep it in the general `Compiler',
- # but have a `SassCompiler` if it requires more than `:load_paths'.
- #
- # NOTE: We need another option to pass for Sass: `:cache_location'.
- #
- # This is needed to don't create a `.sass-cache' directory at the root of the project,
- # but to have it under `tmp/sass-cache'.
- write { Tilt.new(source, nil, load_paths: sass_load_paths, cache_location: sass_cache_location).render }
+ write { renderer.render }
rescue RuntimeError
raise UnknownAssetEngine.new(source)
end
# @since 0.1.0
@@ -184,45 +199,51 @@
end
# @since 0.1.0
# @api private
def cache!
- cache.store(source)
+ cache.store(source, dependencies)
end
# @since 0.1.0
# @api private
def write
destination.dirname.mkpath
- destination.open(File::WRONLY|File::TRUNC|File::CREAT, DEFAULT_PERMISSIONS) do |file|
+ destination.open(File::WRONLY | File::TRUNC | File::CREAT, DEFAULT_PERMISSIONS) do |file|
file.write(yield)
end
end
# @since 0.1.0
# @api private
def cache
self.class.cache
end
- # @since x.x.x
+ # @since 0.3.0
# @api private
- def sass_load_paths
+ def renderer
+ Tilt.new(source)
+ end
+
+ # @since 0.3.0
+ # @api private
+ def dependencies
+ nil
+ end
+
+ # @since 0.3.0
+ # @api private
+ def load_paths
result = []
@configuration.sources.each do |source|
Find.find(source) do |path|
result << path if File.directory?(path)
end
end
result
- end
-
- # @since 0.1.0
- # @api private
- def sass_cache_location
- SASS_CACHE_LOCATION
end
end
end
end