assets-publisher-for-hanami 2.0.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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +257 -0
  8. data/Rakefile +2 -0
  9. data/assets-publisher-for-hanami.gemspec +33 -0
  10. data/bin/setup +7 -0
  11. data/lib/cabeza-de-termo/assets-publisher/assets/asset.rb +81 -0
  12. data/lib/cabeza-de-termo/assets-publisher/assets/types/asset-type.rb +41 -0
  13. data/lib/cabeza-de-termo/assets-publisher/assets/types/javascript-type.rb +20 -0
  14. data/lib/cabeza-de-termo/assets-publisher/assets/types/stylesheet-type.rb +20 -0
  15. data/lib/cabeza-de-termo/assets-publisher/clock-cards/clock-card-machine.rb +43 -0
  16. data/lib/cabeza-de-termo/assets-publisher/clock-cards/clock-card.rb +47 -0
  17. data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/compilation-job.rb +80 -0
  18. data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/compilation-jobs-builder.rb +52 -0
  19. data/lib/cabeza-de-termo/assets-publisher/compilation-jobs/one-file-per-asset-builder.rb +66 -0
  20. data/lib/cabeza-de-termo/assets-publisher/compilers/command-line-compiler.rb +26 -0
  21. data/lib/cabeza-de-termo/assets-publisher/compilers/compiler.rb +50 -0
  22. data/lib/cabeza-de-termo/assets-publisher/compilers/tilt-compiler.rb +47 -0
  23. data/lib/cabeza-de-termo/assets-publisher/configuration/configuration.rb +66 -0
  24. data/lib/cabeza-de-termo/assets-publisher/errors/asset-not-found-error.rb +12 -0
  25. data/lib/cabeza-de-termo/assets-publisher/errors/compilation-job-failed-error.rb +14 -0
  26. data/lib/cabeza-de-termo/assets-publisher/errors/compilation-job-not-supported-error.rb +14 -0
  27. data/lib/cabeza-de-termo/assets-publisher/errors/error.rb +6 -0
  28. data/lib/cabeza-de-termo/assets-publisher/errors/unknown-asset-location-error.rb +12 -0
  29. data/lib/cabeza-de-termo/assets-publisher/helpers/helper.rb +27 -0
  30. data/lib/cabeza-de-termo/assets-publisher/locations/absolute-location.rb +25 -0
  31. data/lib/cabeza-de-termo/assets-publisher/locations/location.rb +54 -0
  32. data/lib/cabeza-de-termo/assets-publisher/locations/source-location.rb +13 -0
  33. data/lib/cabeza-de-termo/assets-publisher/publisher.rb +142 -0
  34. data/lib/cabeza-de-termo/assets-publisher/source-finders/assets-finder.rb +44 -0
  35. data/lib/cabeza-de-termo/assets-publisher/source-finders/assets-source.rb +31 -0
  36. data/lib/cabeza-de-termo/assets-publisher/version.rb +7 -0
  37. 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