assets-publisher-for-hanami 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +257 -0
- data/Rakefile +2 -0
- data/assets-publisher-for-hanami.gemspec +33 -0
- data/bin/setup +7 -0
- data/lib/cabeza-de-termo/assets-publisher/assets/asset.rb +81 -0
- data/lib/cabeza-de-termo/assets-publisher/assets/types/asset-type.rb +41 -0
- data/lib/cabeza-de-termo/assets-publisher/assets/types/javascript-type.rb +20 -0
- data/lib/cabeza-de-termo/assets-publisher/assets/types/stylesheet-type.rb +20 -0
- data/lib/cabeza-de-termo/assets-publisher/clock-cards/clock-card-machine.rb +43 -0
- data/lib/cabeza-de-termo/assets-publisher/clock-cards/clock-card.rb +47 -0
- data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/compilation-job.rb +80 -0
- data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/compilation-jobs-builder.rb +52 -0
- data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/one-file-per-asset-builder.rb +66 -0
- data/lib/cabeza-de-termo/assets-publisher/compilers/command-line-compiler.rb +26 -0
- data/lib/cabeza-de-termo/assets-publisher/compilers/compiler.rb +50 -0
- data/lib/cabeza-de-termo/assets-publisher/compilers/tilt-compiler.rb +47 -0
- data/lib/cabeza-de-termo/assets-publisher/configuration/configuration.rb +66 -0
- data/lib/cabeza-de-termo/assets-publisher/errors/asset-not-found-error.rb +12 -0
- data/lib/cabeza-de-termo/assets-publisher/errors/compilation-job-failed-error.rb +14 -0
- data/lib/cabeza-de-termo/assets-publisher/errors/compilation-job-not-supported-error.rb +14 -0
- data/lib/cabeza-de-termo/assets-publisher/errors/error.rb +6 -0
- data/lib/cabeza-de-termo/assets-publisher/errors/unknown-asset-location-error.rb +12 -0
- data/lib/cabeza-de-termo/assets-publisher/helpers/helper.rb +27 -0
- data/lib/cabeza-de-termo/assets-publisher/locations/absolute-location.rb +25 -0
- data/lib/cabeza-de-termo/assets-publisher/locations/location.rb +54 -0
- data/lib/cabeza-de-termo/assets-publisher/locations/source-location.rb +13 -0
- data/lib/cabeza-de-termo/assets-publisher/publisher.rb +142 -0
- data/lib/cabeza-de-termo/assets-publisher/source-finders/assets-finder.rb +44 -0
- data/lib/cabeza-de-termo/assets-publisher/source-finders/assets-source.rb +31 -0
- data/lib/cabeza-de-termo/assets-publisher/version.rb +7 -0
- metadata +191 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'cabeza-de-termo/assets/library'
|
2
|
+
require 'cabeza-de-termo/assets/rendering-scope-adaptors/hanami-rendering-scope'
|
3
|
+
|
4
|
+
module CabezaDeTermo
|
5
|
+
module AssetsPublisher
|
6
|
+
class AssetType
|
7
|
+
def html_for(asset_uri)
|
8
|
+
CdT.subclass_responsibility
|
9
|
+
end
|
10
|
+
|
11
|
+
# Collect the assets to publish from the rendering_scope
|
12
|
+
def collect_assets_from(rendering_scope)
|
13
|
+
collect_uri_from(rendering_scope).collect do |uri|
|
14
|
+
new_asset_from_uri(uri)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Answer a new Asset from the uri and type
|
19
|
+
def new_asset_from_uri(uri)
|
20
|
+
Asset.on_uri self, uri
|
21
|
+
end
|
22
|
+
|
23
|
+
# Collect the javascripts uri from the rendering_scope
|
24
|
+
def collect_uri_from(rendering_scope)
|
25
|
+
CdT.subclass_responsibility
|
26
|
+
end
|
27
|
+
|
28
|
+
def compiler()
|
29
|
+
CdT.subclass_responsibility
|
30
|
+
end
|
31
|
+
|
32
|
+
def rendering_scope_assets_collector()
|
33
|
+
configuration.rendering_scope_assets_collector
|
34
|
+
end
|
35
|
+
|
36
|
+
def configuration()
|
37
|
+
Publisher.configuration
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'asset-type'
|
2
|
+
|
3
|
+
module CabezaDeTermo
|
4
|
+
module AssetsPublisher
|
5
|
+
class JavascriptType < AssetType
|
6
|
+
def html_for(asset_uri)
|
7
|
+
"<script src=\"#{asset_uri}\" type=\"text/javascript\"></script>"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Collect the javascripts uri from the rendering_scope
|
11
|
+
def collect_uri_from(rendering_scope)
|
12
|
+
rendering_scope_assets_collector.javascripts_from rendering_scope
|
13
|
+
end
|
14
|
+
|
15
|
+
def compiler()
|
16
|
+
configuration.javascripts_compiler
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'asset-type'
|
2
|
+
|
3
|
+
module CabezaDeTermo
|
4
|
+
module AssetsPublisher
|
5
|
+
class StylesheetType < AssetType
|
6
|
+
def html_for(asset_uri)
|
7
|
+
"<link href=\"#{asset_uri}\" type=\"text/css\" rel=\"stylesheet\">"
|
8
|
+
end
|
9
|
+
|
10
|
+
# Collect the javascripts uri from the rendering_scope
|
11
|
+
def collect_uri_from(rendering_scope)
|
12
|
+
rendering_scope_assets_collector.stylesheets_from rendering_scope
|
13
|
+
end
|
14
|
+
|
15
|
+
def compiler()
|
16
|
+
configuration.stylesheets_compiler
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module CabezaDeTermo
|
2
|
+
module AssetsPublisher
|
3
|
+
# An object to detect if a compilation job needs to run or not.
|
4
|
+
class ClockCardMachine
|
5
|
+
def initialize()
|
6
|
+
@clock_cards = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def is_outdated?(compilation_job)
|
10
|
+
return true unless has_record_on?(compilation_job.id)
|
11
|
+
|
12
|
+
clock_cards_not_match? clock_cards[compilation_job.id], compilation_job.clock_card
|
13
|
+
end
|
14
|
+
|
15
|
+
def register_modifications_on(compilation_job)
|
16
|
+
clock_cards.delete(compilation_job.id)
|
17
|
+
|
18
|
+
CdT.object compilation_job.clock_card,
|
19
|
+
if_not_nil: proc { |card| clock_cards[compilation_job.id] = card unless card.has_assets_missing? }
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def clock_cards
|
25
|
+
@clock_cards
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_record_on?(id)
|
29
|
+
clock_cards.key?(id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def clock_cards_not_match?(expected_card, card)
|
33
|
+
!clock_cards_match?(expected_card, card)
|
34
|
+
end
|
35
|
+
|
36
|
+
def clock_cards_match?(expected_card, card)
|
37
|
+
return false unless expected_card.size == card.size
|
38
|
+
|
39
|
+
expected_card.all_marks? { |uri, time| time == card.time_for(uri) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module CabezaDeTermo
|
2
|
+
module AssetsPublisher
|
3
|
+
class ClockCard
|
4
|
+
def initialize(&block)
|
5
|
+
@marks = {}
|
6
|
+
|
7
|
+
block.call(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Accesing
|
11
|
+
|
12
|
+
def set_mark_for(asset_uri, time)
|
13
|
+
marks[asset_uri] = time
|
14
|
+
end
|
15
|
+
|
16
|
+
def time_for(asset_uri)
|
17
|
+
marks[asset_uri]
|
18
|
+
end
|
19
|
+
|
20
|
+
# Asking
|
21
|
+
|
22
|
+
def has_time_for?(asset_uri)
|
23
|
+
marks.key?(asset_uri)
|
24
|
+
end
|
25
|
+
|
26
|
+
def all_marks?(&block)
|
27
|
+
marks.all?(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_assets_missing?()
|
31
|
+
marks.any? { |uri, timestamp| timestamp == :not_found }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Querying
|
35
|
+
|
36
|
+
def size()
|
37
|
+
marks.size
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def marks()
|
43
|
+
@marks
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'cabeza-de-termo/assets-publisher/clock-cards/clock-card'
|
2
|
+
|
3
|
+
module CabezaDeTermo
|
4
|
+
module AssetsPublisher
|
5
|
+
class CompilationJob
|
6
|
+
def initialize(assets: nil, destination: nil)
|
7
|
+
@assets = assets
|
8
|
+
@destination = destination
|
9
|
+
end
|
10
|
+
|
11
|
+
# Accessors
|
12
|
+
|
13
|
+
def assets()
|
14
|
+
@assets
|
15
|
+
end
|
16
|
+
|
17
|
+
def destination()
|
18
|
+
@destination
|
19
|
+
end
|
20
|
+
|
21
|
+
def id()
|
22
|
+
@destination.uri.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
# Asking
|
26
|
+
def empty?()
|
27
|
+
assets.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
# File names
|
31
|
+
|
32
|
+
def source_filenames
|
33
|
+
assets.collect { |asset| asset.real_path.to_s }
|
34
|
+
end
|
35
|
+
|
36
|
+
def destination_filename
|
37
|
+
destination.real_path.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
# Compiling
|
41
|
+
|
42
|
+
def compile_with(compiler)
|
43
|
+
validate_source_assets
|
44
|
+
compiler.compile_job self
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Html
|
49
|
+
|
50
|
+
# Answer the asset html to include in a template.
|
51
|
+
def html
|
52
|
+
destination.html
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_source_assets()
|
56
|
+
assets.each { |asset| asset.validate_real_path }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Clock card
|
60
|
+
|
61
|
+
def clock_card()
|
62
|
+
ClockCard.new do |card|
|
63
|
+
assets.each do |asset|
|
64
|
+
card.set_mark_for(asset.uri.to_s, asset.modification_time)
|
65
|
+
end
|
66
|
+
|
67
|
+
card.set_mark_for(destination.uri.to_s, destination.modification_time)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def source_folders()
|
72
|
+
configuration.source_folders.collect { |path| Pathname(path).expand_path.to_s }
|
73
|
+
end
|
74
|
+
|
75
|
+
def configuration()
|
76
|
+
Publisher.configuration
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module CabezaDeTermo
|
2
|
+
module AssetsPublisher
|
3
|
+
class CompilationJobsBuilder
|
4
|
+
# Answer a new collection of compilation jobs for the assets
|
5
|
+
def self.jobs_for(asset_type, assets)
|
6
|
+
self.new(asset_type).jobs_for(assets)
|
7
|
+
end
|
8
|
+
|
9
|
+
def jobs_for(assets)
|
10
|
+
CdT.subclass_responsibility
|
11
|
+
end
|
12
|
+
|
13
|
+
# Initializing
|
14
|
+
|
15
|
+
def initialize(asset_type)
|
16
|
+
@asset_type = asset_type
|
17
|
+
end
|
18
|
+
|
19
|
+
# Accessing
|
20
|
+
|
21
|
+
def asset_type()
|
22
|
+
@asset_type
|
23
|
+
end
|
24
|
+
|
25
|
+
# Configuration
|
26
|
+
|
27
|
+
# Answer the Publisher configuration.
|
28
|
+
def configuration
|
29
|
+
Publisher.configuration
|
30
|
+
end
|
31
|
+
|
32
|
+
def compiled_assets_folder
|
33
|
+
configuration.published_assets_subfolder
|
34
|
+
end
|
35
|
+
|
36
|
+
# Answer the uri of the compiled asset
|
37
|
+
def compiled_uri
|
38
|
+
Pathname.new('/') + compiled_assets_folder + compiled_filename
|
39
|
+
end
|
40
|
+
|
41
|
+
def compiled_filename
|
42
|
+
CdT.subclass_responsibility
|
43
|
+
end
|
44
|
+
|
45
|
+
# Published asset
|
46
|
+
|
47
|
+
def asset_to_publish()
|
48
|
+
Asset.on_uri(asset_type, compiled_uri)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative 'compilation-jobs-builder'
|
2
|
+
require_relative 'compilation-job'
|
3
|
+
|
4
|
+
module CabezaDeTermo
|
5
|
+
module AssetsPublisher
|
6
|
+
class OneFilePerAssetBuilder < CompilationJobsBuilder
|
7
|
+
# Answer a collection of CompilationJob to send to a Compiler.
|
8
|
+
def jobs_for(assets)
|
9
|
+
assets.collect { |asset| new_compilation_job_for asset }
|
10
|
+
end
|
11
|
+
|
12
|
+
# Answer a new CompilationJob for a single asset to send to a Compiler
|
13
|
+
def new_compilation_job_for(asset)
|
14
|
+
@asset = asset
|
15
|
+
CompilationJob.new(assets: assets_to_compile, destination: asset_to_compile_to)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Answer the current asset
|
19
|
+
def asset()
|
20
|
+
@asset
|
21
|
+
end
|
22
|
+
|
23
|
+
# Answer the asset to compile. If the asset has an absolute location
|
24
|
+
# we won't compile it
|
25
|
+
def assets_to_compile()
|
26
|
+
if asset.location.is_absolute?
|
27
|
+
asset.validate_real_path
|
28
|
+
return []
|
29
|
+
end
|
30
|
+
|
31
|
+
[asset]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Answer the asset that will hold the compiled assets.
|
35
|
+
def asset_to_compile_to()
|
36
|
+
return asset if asset.location.is_absolute?
|
37
|
+
|
38
|
+
asset_to_publish
|
39
|
+
.set_uri_parameters(timestamp_string)
|
40
|
+
end
|
41
|
+
|
42
|
+
def compiled_filename
|
43
|
+
return asset.uri if ['.css', '.js'].include?(asset.uri.extname)
|
44
|
+
Pathname.new(remove_last_extension_from_uri)
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_last_extension_from_uri
|
48
|
+
asset.uri.to_s.split('.')[0..-2].join('.')
|
49
|
+
end
|
50
|
+
|
51
|
+
# Uri timestamp
|
52
|
+
|
53
|
+
# Answer if we must append a timestamp to the relative assets or not
|
54
|
+
def add_timestamps?()
|
55
|
+
configuration.add_timestamps_to_published_assets?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Answer the timestamp string to add to this asset.
|
59
|
+
def timestamp_string()
|
60
|
+
return nil unless add_timestamps?
|
61
|
+
|
62
|
+
Time.now.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'open3'
|
3
|
+
require_relative 'compiler'
|
4
|
+
|
5
|
+
module CabezaDeTermo
|
6
|
+
module AssetsPublisher
|
7
|
+
class CommandLineCompiler < Compiler
|
8
|
+
def initialize(&block)
|
9
|
+
super()
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def compile_assets()
|
14
|
+
@block.call(self, compilation_job)
|
15
|
+
end
|
16
|
+
|
17
|
+
def command_line(*args)
|
18
|
+
ouput, status = Open3.capture2e(*args)
|
19
|
+
|
20
|
+
raise_compilation_failed_error(ouput.strip) unless status.success?
|
21
|
+
|
22
|
+
ouput
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tilt'
|
3
|
+
require 'cabeza-de-termo/assets-publisher/errors/compilation-job-not-supported-error'
|
4
|
+
require 'cabeza-de-termo/assets-publisher/errors/compilation-job-failed-error'
|
5
|
+
|
6
|
+
module CabezaDeTermo
|
7
|
+
module AssetsPublisher
|
8
|
+
class Compiler
|
9
|
+
def compile_job(compilation_job)
|
10
|
+
return if compilation_job.empty?
|
11
|
+
|
12
|
+
@compilation_job = compilation_job
|
13
|
+
|
14
|
+
ensure_destination_folder_exists
|
15
|
+
|
16
|
+
compile_assets
|
17
|
+
end
|
18
|
+
|
19
|
+
def compilation_job()
|
20
|
+
@compilation_job
|
21
|
+
end
|
22
|
+
|
23
|
+
def source_assets()
|
24
|
+
@compilation_job.assets
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def compile_assets()
|
30
|
+
CdT.subclass_responsibility
|
31
|
+
end
|
32
|
+
|
33
|
+
def ensure_destination_folder_exists()
|
34
|
+
destination_folder.mkpath
|
35
|
+
end
|
36
|
+
|
37
|
+
def destination_folder()
|
38
|
+
compilation_job.destination.real_path.dirname
|
39
|
+
end
|
40
|
+
|
41
|
+
def raise_compilation_job_not_supported_error()
|
42
|
+
raise CompilationJobNotSupportedError.new(self, compilation_job)
|
43
|
+
end
|
44
|
+
|
45
|
+
def raise_compilation_failed_error(message)
|
46
|
+
raise CompilationJobFailedError.new(self, compilation_job, message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|