assets-publisher-for-hanami 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,47 @@
1
+ require 'fileutils'
2
+ require 'tilt'
3
+ require_relative 'compiler'
4
+
5
+ module CabezaDeTermo
6
+ module AssetsPublisher
7
+ class TiltCompiler < Compiler
8
+ protected
9
+
10
+ def source_asset
11
+ compilation_job.assets.first
12
+ end
13
+
14
+ def compile_assets()
15
+ validate_assets_collection
16
+
17
+ compile_single_asset
18
+ end
19
+
20
+ def compile_single_asset()
21
+ just_copy? ? copy! : compile!
22
+ end
23
+
24
+ def validate_assets_collection()
25
+ raise_compilation_job_not_supported_error unless source_assets.size == 1
26
+ end
27
+
28
+ def just_copy?()
29
+ ['.css', '.js'].include?(source_asset.real_path.extname)
30
+ end
31
+
32
+ def copy!()
33
+ ::FileUtils.copy_file(source_asset.real_path, compilation_job.destination_filename)
34
+ end
35
+
36
+ def compile!()
37
+ ::File.open(compilation_job.destination_filename, 'w') do |file|
38
+ file.write( render_source )
39
+ end
40
+ end
41
+
42
+ def render_source()
43
+ Tilt.new(source_asset.real_path).render
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,66 @@
1
+ module CabezaDeTermo
2
+ module AssetsPublisher
3
+ # The Publisher configuration.
4
+ class Configuration
5
+ # Initialize the instance
6
+ def initialize
7
+ @source_folders = []
8
+
9
+ destination_folder nil
10
+ add_timestamps_to_published_assets true
11
+ stylesheets_compiler {}
12
+ javascripts_compiler {}
13
+ use_rendering_scope_assets_collector nil
14
+ end
15
+
16
+ # Answer if the Publisher will add a timestamp to the assets with relative paths
17
+ def add_timestamps_to_published_assets?
18
+ @add_timestamps_to_published_assets
19
+ end
20
+
21
+ # Set if the Publisher will add a timestamp to the assets with relative paths
22
+ def add_timestamps_to_published_assets(boolean)
23
+ @add_timestamps_to_published_assets = boolean
24
+ end
25
+
26
+ # Answer the asset sources collection
27
+ def source_folders
28
+ @source_folders
29
+ end
30
+
31
+ def destination_folder(folder)
32
+ @destination_folder = folder
33
+ end
34
+
35
+ def destination_path
36
+ ::Pathname.new(@destination_folder)
37
+ end
38
+
39
+ def published_assets_subfolder
40
+ ::Pathname.new('assets')
41
+ end
42
+
43
+ def stylesheets_compiler(&block)
44
+ return @stylesheets_compiler_block.call if block.nil?
45
+ @stylesheets_compiler_block = block
46
+ end
47
+
48
+ def javascripts_compiler(&block)
49
+ return @javascripts_compiler_block.call if block.nil?
50
+ @javascripts_compiler_block = block
51
+ end
52
+
53
+ def command_line_compiler(&block)
54
+ CommandLineCompiler.new(&block)
55
+ end
56
+
57
+ def use_rendering_scope_assets_collector(rendering_scope_assets_collector)
58
+ @rendering_scope_assets_collector = rendering_scope_assets_collector
59
+ end
60
+
61
+ def rendering_scope_assets_collector()
62
+ @rendering_scope_assets_collector
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'error'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class AssetNotFoundError < Error
6
+ def initialize(asset_uri)
7
+ super("The asset '#{asset_uri}' was not found.")
8
+ @asset_uri = asset_uri
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'error'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class CompilationJobFailedError < Error
6
+ def initialize(compiler, compilation_job, message)
7
+ super(message)
8
+
9
+ @compiler = compiler
10
+ @compilation_job = compilation_job
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'error'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class CompilationJobNotSupportedError < Error
6
+ def initialize(compiler, compilation_job, message)
7
+ super(message)
8
+
9
+ @compiler = compiler
10
+ @compilation_job = compilation_job
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ module CabezaDeTermo
2
+ module AssetsPublisher
3
+ class Error < ::StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'error'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class UnknownAssetLocationError < Error
6
+ def initialize(asset_uri)
7
+ super("Unkown location for '#{asset_uri}'.")
8
+ @asset_uri = asset_uri
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require 'cabeza-de-termo/assets-publisher/publisher'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ # The View helper.
6
+ # To use it, add this to your view:
7
+ #
8
+ # module Web
9
+ # module Views
10
+ # class YourView
11
+ # include CabezaDeTermo::AssetsPublisher::Helper
12
+ # ...
13
+ # end
14
+ # end
15
+ # end
16
+ #
17
+ # This will publish only one method to your views: #assets_publisher. From that method
18
+ # you can call the Publisher protocol.
19
+ module Helper
20
+ # Answer the Publisher to collect and publish stylesheets and javascripts from
21
+ # your current view.
22
+ def assets_publisher
23
+ Publisher.new
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'location'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class AbsoluteLocation < Location
6
+ # Answer the path of the asset uri.
7
+ def real_path_of(uri)
8
+ Pathname.new(destination_path.to_s + uri.to_s).expand_path
9
+ end
10
+
11
+ # Asking
12
+
13
+ def is_absolute?()
14
+ true
15
+ end
16
+
17
+ protected
18
+
19
+ # Answer the path of the folder where compiled assets are published
20
+ def destination_path
21
+ configuration.destination_path
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ require 'cabeza-de-termo/assets-publisher/errors/unknown-asset-location-error'
2
+
3
+ module CabezaDeTermo
4
+ module AssetsPublisher
5
+ class Location
6
+ # Answer a new location for the uri
7
+ def self.from(uri)
8
+ class_for_uri(uri).new(uri)
9
+ end
10
+
11
+ # Answer the class to use for the uri
12
+ def self.class_for_uri(uri)
13
+ uri = Pathname.new(uri)
14
+
15
+ return class_named("CabezaDeTermo::AssetsPublisher::AbsoluteLocation") if uri.absolute?
16
+ return class_named("CabezaDeTermo::AssetsPublisher::SourceLocation") if uri.relative?
17
+
18
+ raise UnknownAssetLocationError.new(uri)
19
+ end
20
+
21
+ # Answer a class from its fully quilified name
22
+ def self.class_named(fully_qualified)
23
+ fully_qualified.split('::').inject(Object) do |mod, class_name|
24
+ mod.const_get(class_name)
25
+ end
26
+ end
27
+
28
+ # Instance methods
29
+
30
+ def initialize(uri)
31
+ @uri = uri
32
+ end
33
+
34
+ # Answer the Publisher configuration.
35
+ def configuration
36
+ Publisher.configuration
37
+ end
38
+
39
+ # Asking
40
+
41
+ def is_absolute?()
42
+ false
43
+ end
44
+
45
+ # Answer the path of the asset uri
46
+ def real_path_of(uri)
47
+ CdT.subclass_responsibility
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ require_relative 'source-location'
54
+ require_relative 'absolute-location'
@@ -0,0 +1,13 @@
1
+ require_relative 'location'
2
+ require 'cabeza-de-termo/assets-publisher/source-finders/assets-finder'
3
+
4
+ module CabezaDeTermo
5
+ module AssetsPublisher
6
+ class SourceLocation < Location
7
+ # Answer the path of the asset uri
8
+ def real_path_of(uri)
9
+ (AssetsFinder.asset_source_path_of uri).expand_path
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,142 @@
1
+ require 'cdt/utilities/bind'
2
+ require 'cdt/utilities/subclass-responsibility'
3
+ require 'cdt/utilities/if-not-nil'
4
+ require_relative 'version'
5
+ require_relative 'configuration/configuration'
6
+ require_relative 'assets/asset'
7
+ require_relative 'compilation-jobs/one-file-per-asset-builder'
8
+ require_relative 'compilers/tilt-compiler'
9
+ require_relative 'compilers/command-line-compiler'
10
+ require_relative 'clock-cards/clock-card-machine'
11
+
12
+ module CabezaDeTermo
13
+ module AssetsPublisher
14
+ # The collector and publisher of the stylesheets and javascripts from the current view.
15
+ class Publisher
16
+
17
+ @configuration_prototype = nil
18
+
19
+ @clock_card_machine = ClockCardMachine.new
20
+
21
+ @configurations_per_thread = {}
22
+
23
+ # Class methods
24
+ class << self
25
+ # Answer the Publihser configuration
26
+ def configuration()
27
+ configurations_per_thread[::Thread.current]
28
+ end
29
+
30
+ # Pass the config to the block to allow the app to configure the Publisher
31
+ def configure(&block)
32
+ CdT.bind_block_evaluation_to configuration_prototype, &block
33
+ end
34
+
35
+ def clock_card_machine()
36
+ @clock_card_machine
37
+ end
38
+
39
+ def configurations_per_thread()
40
+ @configurations_per_thread
41
+ end
42
+
43
+ # Answer the Publihser configuration
44
+ def configuration_prototype()
45
+ @configuration_prototype ||= default_configuration
46
+ end
47
+
48
+ def reset_configuration_prototype()
49
+ @configuration_prototype = nil
50
+ end
51
+
52
+ def default_configuration()
53
+ CdT.bind_block_evaluation_to(Configuration.new) do
54
+ add_timestamps_to_published_assets true
55
+
56
+ stylesheets_compiler { TiltCompiler.new }
57
+ javascripts_compiler { TiltCompiler.new }
58
+
59
+ use_rendering_scope_assets_collector CabezaDeTermo::Assets::HanamiRenderingScope
60
+
61
+ self
62
+ end
63
+ end
64
+ end
65
+
66
+ # Instance methods
67
+
68
+ # Collect and publish the stylesheets from the rendering_scope. Answer the html to include
69
+ # in your template.
70
+ def stylesheets_for(rendering_scope)
71
+ with_configuration_copy do
72
+ compile_and_build_html_for(StylesheetType.new, rendering_scope)
73
+ end
74
+ end
75
+
76
+ # Collect and publish the javascripts from the rendering_scope. Answer the html to include
77
+ # in your template.
78
+ def javascripts_for(rendering_scope)
79
+ with_configuration_copy do
80
+ compile_and_build_html_for(JavascriptType.new, rendering_scope)
81
+ end
82
+ end
83
+
84
+ protected
85
+
86
+ def compile_and_build_html_for(asset_type, rendering_scope)
87
+ compile_assets_and_build_html_for asset_type, assets_for(asset_type, rendering_scope)
88
+ end
89
+
90
+ def compile_assets_and_build_html_for(asset_type, assets)
91
+ jobs = compilation_jobs_for(asset_type, assets)
92
+
93
+ compile_jobs(asset_type, jobs)
94
+ jobs_html(jobs)
95
+ end
96
+
97
+ def compile_jobs(asset_type, compilation_jobs)
98
+ compilation_jobs.each do |job|
99
+ compile(job, asset_type)
100
+ end
101
+ end
102
+
103
+ def compile(compilation_job, asset_type)
104
+ return unless clock_card_machine.is_outdated? compilation_job
105
+
106
+ compilation_job.compile_with(asset_type.compiler)
107
+
108
+ clock_card_machine.register_modifications_on compilation_job
109
+ end
110
+
111
+ def jobs_html(compilation_jobs)
112
+ compilation_jobs.inject('') do |html, compilation_job|
113
+ html += compilation_job.html
114
+ end
115
+ end
116
+
117
+ # Create and answer a new comopilation job
118
+ def compilation_jobs_for(asset_type, assets)
119
+ OneFilePerAssetBuilder.jobs_for asset_type, assets
120
+ end
121
+
122
+ def assets_for(asset_type, rendering_scope)
123
+ asset_type.collect_assets_from(rendering_scope)
124
+ end
125
+
126
+ def clock_card_machine()
127
+ self.class.clock_card_machine
128
+ end
129
+
130
+ def with_configuration_copy(&block)
131
+ begin
132
+ self.class.configurations_per_thread[::Thread.current] =
133
+ self.class.configuration_prototype.dup
134
+
135
+ block.call
136
+ ensure
137
+ self.class.configurations_per_thread.delete(::Thread.current)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end