roadie 2.4.3 → 3.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +9 -14
- data/.yardopts +1 -1
- data/Changelog.md +22 -10
- data/Gemfile +3 -0
- data/Guardfile +11 -1
- data/README.md +165 -163
- data/Rakefile +2 -19
- data/lib/roadie.rb +14 -69
- data/lib/roadie/asset_provider.rb +7 -58
- data/lib/roadie/asset_scanner.rb +92 -0
- data/lib/roadie/document.rb +103 -0
- data/lib/roadie/errors.rb +57 -0
- data/lib/roadie/filesystem_provider.rb +21 -62
- data/lib/roadie/inliner.rb +71 -218
- data/lib/roadie/markup_improver.rb +88 -0
- data/lib/roadie/null_provider.rb +13 -0
- data/lib/roadie/null_url_rewriter.rb +12 -0
- data/lib/roadie/provider_list.rb +67 -0
- data/lib/roadie/rspec.rb +1 -0
- data/lib/roadie/rspec/asset_provider.rb +49 -0
- data/lib/roadie/selector.rb +42 -18
- data/lib/roadie/style_block.rb +33 -0
- data/lib/roadie/style_properties.rb +29 -0
- data/lib/roadie/style_property.rb +93 -0
- data/lib/roadie/stylesheet.rb +65 -0
- data/lib/roadie/url_generator.rb +126 -0
- data/lib/roadie/url_rewriter.rb +84 -0
- data/lib/roadie/version.rb +1 -1
- data/roadie.gemspec +6 -10
- data/spec/fixtures/big_em.css +1 -0
- data/spec/fixtures/stylesheets/green.css +1 -0
- data/spec/integration_spec.rb +125 -95
- data/spec/lib/roadie/asset_scanner_spec.rb +153 -0
- data/spec/lib/roadie/css_not_found_spec.rb +16 -0
- data/spec/lib/roadie/document_spec.rb +123 -0
- data/spec/lib/roadie/filesystem_provider_spec.rb +25 -72
- data/spec/lib/roadie/inliner_spec.rb +105 -537
- data/spec/lib/roadie/markup_improver_spec.rb +78 -0
- data/spec/lib/roadie/null_provider_spec.rb +21 -0
- data/spec/lib/roadie/null_url_rewriter_spec.rb +19 -0
- data/spec/lib/roadie/provider_list_spec.rb +81 -0
- data/spec/lib/roadie/selector_spec.rb +7 -5
- data/spec/lib/roadie/style_block_spec.rb +35 -0
- data/spec/lib/roadie/style_properties_spec.rb +61 -0
- data/spec/lib/roadie/style_property_spec.rb +82 -0
- data/spec/lib/roadie/stylesheet_spec.rb +41 -0
- data/spec/lib/roadie/test_provider_spec.rb +29 -0
- data/spec/lib/roadie/url_generator_spec.rb +120 -0
- data/spec/lib/roadie/url_rewriter_spec.rb +79 -0
- data/spec/shared_examples/asset_provider.rb +11 -0
- data/spec/shared_examples/url_rewriter.rb +23 -0
- data/spec/spec_helper.rb +5 -60
- data/spec/support/have_node_matcher.rb +2 -2
- data/spec/support/have_selector_matcher.rb +1 -1
- data/spec/support/have_styling_matcher.rb +48 -14
- data/spec/support/test_provider.rb +13 -0
- metadata +73 -177
- data/Appraisals +0 -15
- data/gemfiles/rails_3.0.gemfile +0 -7
- data/gemfiles/rails_3.0.gemfile.lock +0 -123
- data/gemfiles/rails_3.1.gemfile +0 -7
- data/gemfiles/rails_3.1.gemfile.lock +0 -126
- data/gemfiles/rails_3.2.gemfile +0 -7
- data/gemfiles/rails_3.2.gemfile.lock +0 -124
- data/gemfiles/rails_4.0.gemfile +0 -7
- data/gemfiles/rails_4.0.gemfile.lock +0 -119
- data/lib/roadie/action_mailer_extensions.rb +0 -95
- data/lib/roadie/asset_pipeline_provider.rb +0 -28
- data/lib/roadie/css_file_not_found.rb +0 -22
- data/lib/roadie/railtie.rb +0 -39
- data/lib/roadie/style_declaration.rb +0 -42
- data/spec/fixtures/app/assets/stylesheets/integration.css +0 -10
- data/spec/fixtures/public/stylesheets/integration.css +0 -10
- data/spec/fixtures/views/integration_mailer/marketing.html.erb +0 -2
- data/spec/fixtures/views/integration_mailer/notification.html.erb +0 -8
- data/spec/fixtures/views/integration_mailer/notification.text.erb +0 -6
- data/spec/lib/roadie/action_mailer_extensions_spec.rb +0 -227
- data/spec/lib/roadie/asset_pipeline_provider_spec.rb +0 -65
- data/spec/lib/roadie/css_file_not_found_spec.rb +0 -29
- data/spec/lib/roadie/style_declaration_spec.rb +0 -49
- data/spec/lib/roadie_spec.rb +0 -101
- data/spec/shared_examples/asset_provider_examples.rb +0 -11
- data/spec/support/anonymous_mailer.rb +0 -21
- data/spec/support/change_url_options.rb +0 -5
- data/spec/support/parse_styling.rb +0 -25
data/Rakefile
CHANGED
@@ -1,29 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'bundler/setup'
|
3
|
-
require 'appraisal'
|
4
3
|
|
5
4
|
Bundler::GemHelper.install_tasks
|
6
5
|
|
7
|
-
begin
|
8
|
-
require 'rspec'
|
9
|
-
rescue Bundler::BundlerError => e
|
10
|
-
$stderr.puts e.message
|
11
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
12
|
-
exit e.status_code
|
13
|
-
end
|
14
|
-
|
15
|
-
require 'rspec/core/rake_task'
|
16
|
-
|
17
6
|
desc "Run specs"
|
18
|
-
|
19
|
-
|
20
|
-
t.rspec_opts = ["-c"]
|
7
|
+
task :spec do
|
8
|
+
sh "bundle exec rspec -f progress"
|
21
9
|
end
|
22
10
|
|
23
11
|
desc "Default: Run specs"
|
24
12
|
task :default => :spec
|
25
|
-
|
26
|
-
namespace :spec do
|
27
|
-
desc 'Run specs against all supported versions of Rails'
|
28
|
-
task :all => ["appraisal:install", "appraisal", "spec"]
|
29
|
-
end
|
data/lib/roadie.rb
CHANGED
@@ -1,79 +1,24 @@
|
|
1
1
|
module Roadie
|
2
|
-
class << self
|
3
|
-
# Shortcut for inlining CSS using {Inliner}
|
4
|
-
# @see Inliner
|
5
|
-
def inline_css(*args)
|
6
|
-
Roadie::Inliner.new(*args).execute
|
7
|
-
end
|
8
|
-
|
9
|
-
# Shortcut to Rails.application
|
10
|
-
def app
|
11
|
-
Rails.application
|
12
|
-
end
|
13
|
-
|
14
|
-
# Returns all available providers
|
15
|
-
def providers
|
16
|
-
[AssetPipelineProvider, FilesystemProvider]
|
17
|
-
end
|
18
|
-
|
19
|
-
# Returns the value of +config.roadie.enabled+.
|
20
|
-
#
|
21
|
-
# Roadie will disable all processing if this config is set to +false+. If
|
22
|
-
# you just want to disable CSS inlining without disabling the rest of
|
23
|
-
# Roadie, pass +css: nil+ to the +defaults+ method inside your mailers.
|
24
|
-
def enabled?
|
25
|
-
config.roadie.enabled
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the active provider
|
29
|
-
#
|
30
|
-
# If no provider has been configured a new provider will be instantiated
|
31
|
-
# depending on if the asset pipeline is enabled or not.
|
32
|
-
#
|
33
|
-
# If +config.assets.enabled+ is +true+, the {AssetPipelineProvider} will be used
|
34
|
-
# while {FilesystemProvider} will be used if it is set to +false+.
|
35
|
-
#
|
36
|
-
# @see AssetPipelineProvider
|
37
|
-
# @see FilesystemProvider
|
38
|
-
def current_provider
|
39
|
-
return config.roadie.provider if config.roadie.provider
|
40
|
-
|
41
|
-
if assets_enabled?
|
42
|
-
AssetPipelineProvider.new
|
43
|
-
else
|
44
|
-
FilesystemProvider.new
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the value of +config.roadie.after_inlining+
|
49
|
-
#
|
50
|
-
def after_inlining_handler
|
51
|
-
config.roadie.after_inlining
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
def config
|
56
|
-
Roadie.app.config
|
57
|
-
end
|
58
|
-
|
59
|
-
def assets_enabled?
|
60
|
-
# In Rails 4.0, config.assets.enabled is nil by default, so we need to
|
61
|
-
# explicitly make sure it's not false rather than checking for a
|
62
|
-
# truthy value.
|
63
|
-
config.respond_to?(:assets) and config.assets and config.assets.enabled != false
|
64
|
-
end
|
65
|
-
end
|
66
2
|
end
|
67
3
|
|
68
4
|
require 'roadie/version'
|
69
|
-
require 'roadie/
|
5
|
+
require 'roadie/errors'
|
6
|
+
|
7
|
+
require 'roadie/stylesheet'
|
70
8
|
require 'roadie/selector'
|
71
|
-
require 'roadie/
|
9
|
+
require 'roadie/style_property'
|
10
|
+
require 'roadie/style_properties'
|
11
|
+
require 'roadie/style_block'
|
72
12
|
|
73
13
|
require 'roadie/asset_provider'
|
74
|
-
require 'roadie/
|
14
|
+
require 'roadie/provider_list'
|
75
15
|
require 'roadie/filesystem_provider'
|
16
|
+
require 'roadie/null_provider'
|
76
17
|
|
18
|
+
require 'roadie/asset_scanner'
|
19
|
+
require 'roadie/markup_improver'
|
20
|
+
require 'roadie/url_generator'
|
21
|
+
require 'roadie/url_rewriter'
|
22
|
+
require 'roadie/null_url_rewriter'
|
77
23
|
require 'roadie/inliner'
|
78
|
-
|
79
|
-
require 'roadie/railtie' if defined?(Rails)
|
24
|
+
require 'roadie/document'
|
@@ -1,62 +1,11 @@
|
|
1
1
|
module Roadie
|
2
|
-
#
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# find("posts/comment.css")
|
10
|
-
attr_reader :prefix
|
11
|
-
|
12
|
-
# @param [String] prefix Prefix of assets as seen from the browser
|
13
|
-
# @see #prefix
|
14
|
-
def initialize(prefix = "/assets")
|
15
|
-
@prefix = prefix
|
16
|
-
@quoted_prefix = prepare_prefix(prefix)
|
2
|
+
# This module can be included in your own code to help you implement the
|
3
|
+
# standard behavior for asset providers.
|
4
|
+
#
|
5
|
+
# It helps you by declaring {#find_stylesheet!} in the terms of #find_stylesheet in your own class.
|
6
|
+
module AssetProvider
|
7
|
+
def find_stylesheet!(name)
|
8
|
+
find_stylesheet(name) or raise CssNotFound, name
|
17
9
|
end
|
18
|
-
|
19
|
-
# Iterates all the passed elements and calls {#find} on them, joining the results with a newline.
|
20
|
-
#
|
21
|
-
# @example
|
22
|
-
# MyProvider.all("first", "second.css", :third)
|
23
|
-
#
|
24
|
-
# @param [Array] files The target files to be loaded together
|
25
|
-
# @raise [CSSFileNotFound] In case any of the elements is not found
|
26
|
-
# @see #find
|
27
|
-
def all(files)
|
28
|
-
files.map { |file| find(file) }.join("\n")
|
29
|
-
end
|
30
|
-
|
31
|
-
# @abstract Implement in your own subclass
|
32
|
-
#
|
33
|
-
# Return the CSS contents of the file specified. A provider should not care about
|
34
|
-
# the +.css+ extension; it can, however, behave differently if it's passed or not.
|
35
|
-
#
|
36
|
-
# If the asset cannot be found, the method should raise {CSSFileNotFound}.
|
37
|
-
#
|
38
|
-
# @example
|
39
|
-
# MyProvider.find("mystyle")
|
40
|
-
# MyProvider.find("mystyle.css")
|
41
|
-
# MyProvider.find(:mystyle)
|
42
|
-
#
|
43
|
-
# @param [String] name Name of the file requested
|
44
|
-
# @raise [CSSFileNotFound] In case any of the elements is not found
|
45
|
-
def find(name)
|
46
|
-
raise "Not implemented"
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
def prepare_prefix(prefix)
|
51
|
-
if prefix =~ /^\//
|
52
|
-
"/?#{Regexp.quote(prefix[1, prefix.size])}"
|
53
|
-
else
|
54
|
-
Regexp.quote(prefix)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def remove_prefix(name)
|
59
|
-
name.sub(/^#{@quoted_prefix}\/?/, '').sub(%r{^/}, '').gsub(%r{//+}, '/')
|
60
|
-
end
|
61
10
|
end
|
62
11
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Roadie
|
2
|
+
# @api private
|
3
|
+
#
|
4
|
+
# The asset scanner's main usage is finding and/or extracting styles from a
|
5
|
+
# DOM tree. Referenced styles will be found using the provided asset
|
6
|
+
# provider.
|
7
|
+
#
|
8
|
+
# Any style declaration tagged with +data-roadie-ignore+ will be ignored.
|
9
|
+
class AssetScanner
|
10
|
+
attr_reader :dom, :asset_provider
|
11
|
+
|
12
|
+
# @param [Nokogiri::HTML::Document] dom
|
13
|
+
# @param [#find_stylesheet!] asset_provider
|
14
|
+
def initialize(dom, asset_provider)
|
15
|
+
@dom = dom
|
16
|
+
@asset_provider = asset_provider
|
17
|
+
end
|
18
|
+
|
19
|
+
# Looks for all non-ignored stylesheets and returns them.
|
20
|
+
#
|
21
|
+
# This method will *not* mutate the DOM and is safe to call multiple times.
|
22
|
+
#
|
23
|
+
# The order of the array corresponds with the document order in the DOM.
|
24
|
+
#
|
25
|
+
# @see #extract_css
|
26
|
+
# @return [Enumerable<Stylesheet>] every found stylesheet
|
27
|
+
def find_css
|
28
|
+
@dom.css(STYLE_ELEMENT_QUERY).map { |element| read_stylesheet(element) }.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
# Looks for all non-ignored stylesheets, removes their references from the
|
32
|
+
# DOM and then returns them.
|
33
|
+
#
|
34
|
+
# This will mutate the DOM tree.
|
35
|
+
#
|
36
|
+
# The order of the array corresponds with the document order in the DOM.
|
37
|
+
#
|
38
|
+
# @see #find_css
|
39
|
+
# @return [Enumerable<Stylesheet>] every extracted stylesheet
|
40
|
+
def extract_css
|
41
|
+
@dom.css(STYLE_ELEMENT_QUERY).map { |element|
|
42
|
+
stylesheet = read_stylesheet(element)
|
43
|
+
element.remove if stylesheet
|
44
|
+
stylesheet
|
45
|
+
}.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
STYLE_ELEMENT_QUERY = (
|
51
|
+
"style:not([data-roadie-ignore]), " +
|
52
|
+
# TODO: When using Nokogiri 1.6.1 and later; we may use a double :not here
|
53
|
+
# instead of the extra code inside #read_stylesheet, and the #compact
|
54
|
+
# call in #find_css.
|
55
|
+
"link[rel=stylesheet][href]:not([data-roadie-ignore])"
|
56
|
+
).freeze
|
57
|
+
|
58
|
+
# Cleans out stupid CDATA and/or HTML comments from the style text
|
59
|
+
# TinyMCE causes this, allegedly
|
60
|
+
CLEANING_MATCHER = /
|
61
|
+
(^\s* # Beginning-of-lines matches
|
62
|
+
(<!\[CDATA\[)|
|
63
|
+
(<!--+)
|
64
|
+
)|( # End-of-line matches
|
65
|
+
(--+>)|
|
66
|
+
(\]\]>)
|
67
|
+
$)
|
68
|
+
/x.freeze
|
69
|
+
|
70
|
+
def read_stylesheet(element)
|
71
|
+
if element.name == "style"
|
72
|
+
read_style_element element
|
73
|
+
else
|
74
|
+
read_link_element element
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def read_style_element(element)
|
79
|
+
Stylesheet.new "(inline)", clean_css(element.text.strip)
|
80
|
+
end
|
81
|
+
|
82
|
+
def read_link_element(element)
|
83
|
+
if element['media'] != "print" && element["href"]
|
84
|
+
asset_provider.find_stylesheet! element['href']
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def clean_css(css)
|
89
|
+
css.gsub(CLEANING_MATCHER, '')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Roadie
|
2
|
+
# The main entry point for Roadie. A document represents a working unit and
|
3
|
+
# is built with the input HTML and the configuration options you need.
|
4
|
+
#
|
5
|
+
# A Document must never be used from two threads at the same time. Reusing
|
6
|
+
# Documents is discouraged.
|
7
|
+
#
|
8
|
+
# Stylesheets are added to the HTML from three different sources:
|
9
|
+
# 1. Stylesheets inside the document ( +<style>+ elements)
|
10
|
+
# 2. Stylesheets referenced by the DOM ( +<link>+ elements)
|
11
|
+
# 3. The internal stylesheet (see {#add_css})
|
12
|
+
#
|
13
|
+
# The internal stylesheet is used last and gets the highest priority. The
|
14
|
+
# rest is used in the same order as browsers are supposed to use them.
|
15
|
+
#
|
16
|
+
# @attr [#call] before_transformation Callback to call just before {#transform}ation is begun. Will be called with the parsed DOM tree.
|
17
|
+
# @attr [#call] after_transformation Callback to call just before {#transform}ation is completed. Will be called with the current DOM tree.
|
18
|
+
class Document
|
19
|
+
attr_reader :html, :asset_providers
|
20
|
+
|
21
|
+
# URL options. If none are given no URL rewriting will take place.
|
22
|
+
# @see UrlGenerator#initialize
|
23
|
+
attr_accessor :url_options
|
24
|
+
|
25
|
+
attr_accessor :before_transformation, :after_transformation
|
26
|
+
|
27
|
+
# @param [String] html the input HTML
|
28
|
+
def initialize(html)
|
29
|
+
@html = html
|
30
|
+
@asset_providers = ProviderList.wrap(FilesystemProvider.new)
|
31
|
+
@css = ""
|
32
|
+
end
|
33
|
+
|
34
|
+
# Append additional CSS to the document's internal stylesheet.
|
35
|
+
# @param [String] new_css
|
36
|
+
def add_css(new_css)
|
37
|
+
@css << "\n\n" << new_css
|
38
|
+
end
|
39
|
+
|
40
|
+
# Transform the input HTML and returns the processed HTML.
|
41
|
+
#
|
42
|
+
# Before the transformation begins, the {#before_transformation} callback will be
|
43
|
+
# called with the parsed HTML tree, and after all work is complete the
|
44
|
+
# {#after_transformation} callback will be invoked.
|
45
|
+
#
|
46
|
+
# Most of the work is delegated to other classes. A list of them can be seen below.
|
47
|
+
#
|
48
|
+
# @see MarkupImprover MarkupImprover (improves the markup of the DOM)
|
49
|
+
# @see Inliner Inliner (inlines the stylesheets)
|
50
|
+
# @see UrlRewriter UrlRewriter (rewrites URLs and makes them absolute)
|
51
|
+
#
|
52
|
+
# @return [String] the transformed HTML
|
53
|
+
def transform
|
54
|
+
dom = Nokogiri::HTML.parse html
|
55
|
+
|
56
|
+
callback before_transformation, dom
|
57
|
+
|
58
|
+
improve dom
|
59
|
+
inline dom
|
60
|
+
rewrite_urls dom
|
61
|
+
|
62
|
+
callback after_transformation, dom
|
63
|
+
|
64
|
+
# #dup is called since it fixed a few segfaults in certain versions of Nokogiri
|
65
|
+
dom.dup.to_html
|
66
|
+
end
|
67
|
+
|
68
|
+
# Assign new asset providers. The supplied list will be wrapped in a {ProviderList} using {ProviderList.wrap}.
|
69
|
+
def asset_providers=(list)
|
70
|
+
@asset_providers = ProviderList.wrap(list)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def stylesheet
|
75
|
+
Stylesheet.new "(Document styles)", @css
|
76
|
+
end
|
77
|
+
|
78
|
+
def improve(dom)
|
79
|
+
MarkupImprover.new(dom, html).improve
|
80
|
+
end
|
81
|
+
|
82
|
+
def inline(dom)
|
83
|
+
dom_stylesheets = AssetScanner.new(dom, asset_providers).extract_css
|
84
|
+
Inliner.new(dom_stylesheets + [stylesheet]).inline(dom)
|
85
|
+
end
|
86
|
+
|
87
|
+
def rewrite_urls(dom)
|
88
|
+
make_url_rewriter.transform_dom(dom)
|
89
|
+
end
|
90
|
+
|
91
|
+
def make_url_rewriter
|
92
|
+
if url_options
|
93
|
+
UrlRewriter.new(UrlGenerator.new(url_options))
|
94
|
+
else
|
95
|
+
NullUrlRewriter.new
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def callback(callable, dom)
|
100
|
+
callable.(dom) if callable.respond_to?(:call)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Roadie
|
2
|
+
# Base class for all Roadie errors. Rescue this if you want to catch errors
|
3
|
+
# from Roadie.
|
4
|
+
#
|
5
|
+
# If Roadie raises an error that does not inherit this class, please report
|
6
|
+
# it as a bug.
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
|
9
|
+
# Raised when a declaration which cannot be parsed is encountered.
|
10
|
+
#
|
11
|
+
# A declaration is something like "font-size: 12pt;".
|
12
|
+
class UnparseableDeclaration < Error; end
|
13
|
+
|
14
|
+
# Raised when Roadie encounters an invalid URL which cannot be parsed by
|
15
|
+
# Ruby's +URI+ class.
|
16
|
+
#
|
17
|
+
# This could be a hint that something in your HTML or CSS is broken.
|
18
|
+
class InvalidUrlPath < Error
|
19
|
+
# The original error, raised from +URI+.
|
20
|
+
attr_reader :cause
|
21
|
+
|
22
|
+
def initialize(given_path, cause = nil)
|
23
|
+
@cause = cause
|
24
|
+
if cause
|
25
|
+
cause_message = " Caused by: #{cause}"
|
26
|
+
else
|
27
|
+
cause_message = ""
|
28
|
+
end
|
29
|
+
super "Cannot use path \"#{given_path}\" in URL generation.#{cause_message}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Raised when an asset provider cannot find a stylesheet.
|
34
|
+
#
|
35
|
+
# If you are writing your own asset provider, make sure to raise this in the
|
36
|
+
# +#find_stylesheet!+ method.
|
37
|
+
#
|
38
|
+
# @see AssetProvider
|
39
|
+
class CssNotFound < Error
|
40
|
+
# The name of the stylesheet that cannot be found
|
41
|
+
attr_reader :css_name
|
42
|
+
|
43
|
+
def initialize(css_name, extra_message = nil)
|
44
|
+
@css_name = css_name
|
45
|
+
super build_message(extra_message)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def build_message(extra_message)
|
50
|
+
if extra_message
|
51
|
+
%(Could not find stylesheet "#{css_name}": #{extra_message})
|
52
|
+
else
|
53
|
+
%(Could not find stylesheet "#{css_name}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|