roadie 2.4.3 → 3.0.0.pre1
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.
- 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
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Roadie
|
4
|
+
# @api private
|
5
|
+
# Class that handles URL generation
|
6
|
+
#
|
7
|
+
# URL generation is all about converting relative URLs into absolute URLS
|
8
|
+
# according to the given options. It is written such as absolute URLs will
|
9
|
+
# get passed right through, so all URLs could be passed through here.
|
10
|
+
class UrlGenerator
|
11
|
+
attr_reader :url_options
|
12
|
+
|
13
|
+
# Create a new instance with the given URL options.
|
14
|
+
#
|
15
|
+
# Initializing without a host setting raises an error, as do unknown keys.
|
16
|
+
#
|
17
|
+
# @param [Hash] url_options
|
18
|
+
# @option url_options [String] :host (required)
|
19
|
+
# @option url_options [String, Integer] :port
|
20
|
+
# @option url_options [String] :path root path
|
21
|
+
# @option url_options [String] :scheme URL scheme ("http" is default)
|
22
|
+
# @option url_options [String] :protocol alias for :scheme
|
23
|
+
def initialize(url_options)
|
24
|
+
raise ArgumentError, "No URL options were specified" unless url_options
|
25
|
+
raise ArgumentError, "No :host was specified; options are: #{url_options.inspect}" unless url_options[:host]
|
26
|
+
validate_options url_options
|
27
|
+
|
28
|
+
@url_options = url_options
|
29
|
+
@root_uri = build_root_uri
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generate an absolute URL from a relative URL.
|
33
|
+
#
|
34
|
+
# If the passed path is already an absolute URL, it will be returned as-is.
|
35
|
+
# If passed a blank path, the "root URL" will be returned. The root URL is
|
36
|
+
# the URL that the {#url_options} would generate by themselves.
|
37
|
+
#
|
38
|
+
# An optional base can be specified. The base is another relative path from
|
39
|
+
# the root that specifies an "offset" from which the path was found in. A
|
40
|
+
# common use-case is to convert a relative path found in a stylesheet which
|
41
|
+
# resides in a subdirectory.
|
42
|
+
#
|
43
|
+
# @example Normal conversions
|
44
|
+
# generator = Roadie::UrlGenerator.new host: "foo.com", scheme: "https"
|
45
|
+
# generator.generate_url("bar.html") # => "https://foo.com/bar.html"
|
46
|
+
# generator.generate_url("/bar.html") # => "https://foo.com/bar.html"
|
47
|
+
# generator.generate_url("") # => "https://foo.com"
|
48
|
+
#
|
49
|
+
# @example Conversions with a base
|
50
|
+
# generator = Roadie::UrlGenerator.new host: "foo.com", scheme: "https"
|
51
|
+
# generator.generate_url("../images/logo.png", "/css") # => "https://foo.com/images/logo.png"
|
52
|
+
# generator.generate_url("../images/logo.png", "/assets/css") # => "https://foo.com/assets/images/logo.png"
|
53
|
+
#
|
54
|
+
# @param [String] base The base which the relative path comes from
|
55
|
+
# @return [String] an absolute URL
|
56
|
+
def generate_url(path, base = "/")
|
57
|
+
return root_uri.to_s if path.nil? or path.empty?
|
58
|
+
return path if path_is_absolute?(path)
|
59
|
+
|
60
|
+
combine_segments(root_uri, base, path).to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
attr_reader :root_uri
|
65
|
+
|
66
|
+
def build_root_uri
|
67
|
+
path = make_absolute url_options[:path]
|
68
|
+
port = parse_port url_options[:port]
|
69
|
+
scheme = normalize_scheme(url_options[:scheme] || url_options[:protocol])
|
70
|
+
URI::Generic.build(scheme: scheme, host: url_options[:host], port: port, path: path)
|
71
|
+
end
|
72
|
+
|
73
|
+
def combine_segments(root, base, path)
|
74
|
+
new_path = apply_base(base, path)
|
75
|
+
if root.path
|
76
|
+
new_path = File.join(root.path, new_path)
|
77
|
+
end
|
78
|
+
root.merge(new_path)
|
79
|
+
end
|
80
|
+
|
81
|
+
def apply_base(base, path)
|
82
|
+
if path[0] == "/"
|
83
|
+
path
|
84
|
+
else
|
85
|
+
File.join(base, path)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Strip :// from any scheme, if present
|
90
|
+
def normalize_scheme(scheme)
|
91
|
+
return 'http' unless scheme
|
92
|
+
scheme.to_s[/^\w+/]
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_port(port)
|
96
|
+
(port ? port.to_i : port)
|
97
|
+
end
|
98
|
+
|
99
|
+
def make_absolute(path)
|
100
|
+
if path.nil? || path[0] == "/"
|
101
|
+
path
|
102
|
+
else
|
103
|
+
"/#{path}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def path_is_absolute?(path)
|
108
|
+
not parse_path(path).relative?
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse_path(path)
|
112
|
+
URI.parse(path)
|
113
|
+
rescue URI::InvalidURIError => error
|
114
|
+
raise InvalidUrlPath.new(path, error)
|
115
|
+
end
|
116
|
+
|
117
|
+
VALID_OPTIONS = Set[:host, :port, :path, :protocol, :scheme].freeze
|
118
|
+
|
119
|
+
def validate_options(options)
|
120
|
+
keys = Set.new(options.keys)
|
121
|
+
unless keys.subset? VALID_OPTIONS
|
122
|
+
raise ArgumentError, "Passed invalid options: #{(keys - VALID_OPTIONS).to_a}, valid options are: #{VALID_OPTIONS.to_a}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Roadie
|
2
|
+
# @api private
|
3
|
+
#
|
4
|
+
# Class that rewrites URLs in the DOM.
|
5
|
+
class UrlRewriter
|
6
|
+
# @param [UrlGenerator] generator
|
7
|
+
def initialize(generator)
|
8
|
+
@generator = generator
|
9
|
+
end
|
10
|
+
|
11
|
+
# Mutates the passed DOM tree, rewriting certain element's attributes.
|
12
|
+
#
|
13
|
+
# This will make all a[href] and img[src] into absolute URLs, as well as
|
14
|
+
# all "url()" directives inside style-attributes.
|
15
|
+
#
|
16
|
+
# [nil] is returned so no one can misunderstand that this method mutates
|
17
|
+
# the passed instance.
|
18
|
+
#
|
19
|
+
# @param [Nokogiri::HTML::Document] dom
|
20
|
+
# @return [nil] DOM tree is mutated
|
21
|
+
def transform_dom(dom)
|
22
|
+
# Use only a single loop to do this
|
23
|
+
dom.css("a[href], img[src], *[style]").each do |element|
|
24
|
+
transform_element_style element if element.has_attribute?('style')
|
25
|
+
transform_element element
|
26
|
+
end
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Mutates passed CSS, rewriting url() directives.
|
31
|
+
#
|
32
|
+
# This will make all URLs inside url() absolute.
|
33
|
+
#
|
34
|
+
# [nil] is returned so no one can misunderstand that this method mutates
|
35
|
+
# the passed string.
|
36
|
+
#
|
37
|
+
# @param [String] css the css to mutate
|
38
|
+
# @return [nil] css is mutated
|
39
|
+
def transform_css(css)
|
40
|
+
css.gsub!(CSS_URL_REGEXP) do
|
41
|
+
matches = Regexp.last_match
|
42
|
+
"url(#{matches[:quote]}#{generate_url(matches[:url])}#{matches[:quote]})"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def generate_url(*args) @generator.generate_url(*args) end
|
48
|
+
|
49
|
+
# Regexp matching all the url() declarations in CSS
|
50
|
+
#
|
51
|
+
# It matches without any quotes and with both single and double quotes
|
52
|
+
# inside the parenthesis. There's much room for improvement, of course.
|
53
|
+
CSS_URL_REGEXP = %r{
|
54
|
+
url\(
|
55
|
+
(?<quote>
|
56
|
+
(?:["']|%22)? # Optional opening quote
|
57
|
+
)
|
58
|
+
(?<url> # The URL.
|
59
|
+
# We match URLs with parenthesis inside it here,
|
60
|
+
# so url(foo(bar)baz) will match correctly.
|
61
|
+
[^(]* # Text leading up to before opening parens
|
62
|
+
(?:\([^)]*\))* # Texts containing parens pairs
|
63
|
+
[^(]+ # Texts without parens - required
|
64
|
+
)
|
65
|
+
\k'quote' # Closing quote
|
66
|
+
\)
|
67
|
+
}x
|
68
|
+
|
69
|
+
def transform_element(element)
|
70
|
+
case element.name
|
71
|
+
when "a" then element["href"] = generate_url element["href"]
|
72
|
+
when "img" then element["src"] = generate_url element["src"]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def transform_element_style(element)
|
77
|
+
# We need to use a setter for Nokogiri to detect the string mutation.
|
78
|
+
# If nokogiri used "dumber" data structures, this would all be redundant.
|
79
|
+
css = element["style"]
|
80
|
+
transform_css css
|
81
|
+
element["style"] = css
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/roadie/version.rb
CHANGED
data/roadie.gemspec
CHANGED
@@ -11,20 +11,16 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.authors = ['Magnus Bergmark']
|
12
12
|
s.email = ['magnus.bergmark@gmail.com']
|
13
13
|
s.homepage = 'http://github.com/Mange/roadie'
|
14
|
-
s.summary = %q{Making HTML emails comfortable for the
|
15
|
-
s.description = %q{Roadie tries to make sending HTML emails a little less painful
|
14
|
+
s.summary = %q{Making HTML emails comfortable for the Ruby rockstars}
|
15
|
+
s.description = %q{Roadie tries to make sending HTML emails a little less painful by inlining stylesheets and rewriting relative URLs for you.}
|
16
|
+
s.license = "MIT"
|
16
17
|
|
17
|
-
s.
|
18
|
+
s.required_ruby_version = ">= 1.9"
|
19
|
+
|
20
|
+
s.add_dependency 'nokogiri', '~> 1.6.0'
|
18
21
|
s.add_dependency 'css_parser', '~> 1.3.4'
|
19
|
-
s.add_dependency 'actionmailer', '> 3.0.0', '< 5.0.0'
|
20
|
-
s.add_dependency 'sprockets'
|
21
22
|
|
22
|
-
s.add_development_dependency 'rake'
|
23
|
-
s.add_development_dependency 'rails'
|
24
23
|
s.add_development_dependency 'rspec'
|
25
|
-
s.add_development_dependency 'rspec-rails'
|
26
|
-
|
27
|
-
s.add_development_dependency 'appraisal'
|
28
24
|
|
29
25
|
s.extra_rdoc_files = %w[README.md Changelog.md]
|
30
26
|
s.require_paths = %w[lib]
|
@@ -0,0 +1 @@
|
|
1
|
+
em { font-size: 200%; }
|
@@ -0,0 +1 @@
|
|
1
|
+
body { color: green; }
|
data/spec/integration_spec.rb
CHANGED
@@ -1,110 +1,140 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
append_view_path FIXTURES_PATH.join('views')
|
8
|
-
|
9
|
-
# Needed for correct path lookup
|
10
|
-
self.mailer_name = "integration_mailer"
|
11
|
-
|
12
|
-
def notification(to, reason)
|
13
|
-
@reason = reason
|
14
|
-
mail(:subject => 'Notification for you', :to => to) { |format| format.html; format.text }
|
15
|
-
end
|
16
|
-
|
17
|
-
def marketing(to)
|
18
|
-
headers('X-Spam' => 'No way! Trust us!')
|
19
|
-
mail(:subject => 'Buy cheap v1agra', :to => to)
|
20
|
-
end
|
21
|
-
|
22
|
-
def url_options
|
23
|
-
# This allows apps to calculate any options on a per-email basis
|
24
|
-
super.merge(:protocol => 'https')
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def parse_html_in_email(mail)
|
29
|
-
Nokogiri::HTML.parse mail.html_part.body.decoded
|
30
|
-
end
|
31
|
-
|
32
|
-
before(:each) do
|
33
|
-
change_default_url_options(:host => 'example.app.org')
|
34
|
-
mailer.delivery_method = :test
|
35
|
-
end
|
36
|
-
|
37
|
-
it "inlines styles for an email" do
|
38
|
-
email = mailer.notification('doe@example.com', 'your quota limit has been reached')
|
39
|
-
|
40
|
-
email.to.should == ['doe@example.com']
|
41
|
-
email.from.should == ['john@example.com']
|
42
|
-
email.should have(2).parts
|
43
|
-
|
44
|
-
email.text_part.body.decoded.should_not match(/<.*>/)
|
45
|
-
|
46
|
-
html = email.html_part.body.decoded
|
47
|
-
html.should include '<!DOCTYPE'
|
48
|
-
html.should include '<head'
|
49
|
-
|
50
|
-
document = parse_html_in_email(email)
|
51
|
-
document.should have_selector('body #message h1')
|
52
|
-
document.should have_styling('background' => 'url(https://example.app.org/images/dots.png) repeat-x').at_selector('body')
|
53
|
-
document.should have_selector('strong[contains("quota")]')
|
54
|
-
|
55
|
-
# If we deliver mails we can catch weird problems with headers being invalid
|
56
|
-
email.deliver
|
57
|
-
end
|
58
|
-
|
59
|
-
it "does not add headers for the roadie options" do
|
60
|
-
email = mailer.notification('doe@example.com', 'no berries left in chest')
|
61
|
-
email.header.fields.map(&:name).should_not include('css')
|
62
|
-
end
|
63
|
-
|
64
|
-
it "keeps custom headers in place" do
|
65
|
-
email = mailer.marketing('everyone@inter.net')
|
66
|
-
email.header['X-Spam'].should be_present
|
67
|
-
end
|
3
|
+
describe "Roadie functionality" do
|
4
|
+
def parse_html(html)
|
5
|
+
Nokogiri::HTML.parse(html)
|
6
|
+
end
|
68
7
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
8
|
+
it "adds missing structure" do
|
9
|
+
html = "<h1>Hello world!</h1>".encode("Shift_JIS")
|
10
|
+
document = Roadie::Document.new(html)
|
11
|
+
result = document.transform
|
12
|
+
|
13
|
+
unless defined?(JRuby)
|
14
|
+
# JRuby has a bug that makes DTD manipulation impossible
|
15
|
+
# See Nokogiri bugs #984 and #985
|
16
|
+
# https://github.com/sparklemotion/nokogiri/issues/984
|
17
|
+
# https://github.com/sparklemotion/nokogiri/issues/985
|
18
|
+
result.should include("<!DOCTYPE html>")
|
76
19
|
end
|
77
20
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
email = mailer.notification('doe@example.com', 'your quota limit has been reached')
|
21
|
+
result.should include("<html>")
|
22
|
+
result.should include("<head>")
|
23
|
+
result.should include("<body>")
|
82
24
|
|
83
|
-
|
25
|
+
result.should include("<meta")
|
26
|
+
result.should include("text/html; charset=Shift_JIS")
|
27
|
+
end
|
84
28
|
|
85
|
-
|
86
|
-
|
87
|
-
|
29
|
+
it "inlines given css" do
|
30
|
+
document = Roadie::Document.new <<-HTML
|
31
|
+
<html>
|
32
|
+
<head>
|
33
|
+
<title>Hello world!</title>
|
34
|
+
</head>
|
35
|
+
<body>
|
36
|
+
<h1>Hello world!</h1>
|
37
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
38
|
+
</body>
|
39
|
+
</html>
|
40
|
+
HTML
|
41
|
+
document.add_css <<-CSS
|
42
|
+
em { color: red; }
|
43
|
+
h1 { text-align: center; }
|
44
|
+
CSS
|
45
|
+
|
46
|
+
result = parse_html document.transform
|
47
|
+
result.should have_styling('text-align' => 'center').at_selector('h1')
|
48
|
+
result.should have_styling('color' => 'red').at_selector('p > em')
|
49
|
+
end
|
88
50
|
|
89
|
-
|
90
|
-
|
91
|
-
html
|
51
|
+
it "inlines css from disk" do
|
52
|
+
document = Roadie::Document.new <<-HTML
|
53
|
+
<!DOCTYPE html>
|
54
|
+
<html>
|
55
|
+
<head>
|
56
|
+
<title>Hello world!</title>
|
57
|
+
<link rel="stylesheet" href="/spec/fixtures/big_em.css">
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
<h1>Hello world!</h1>
|
61
|
+
<p>Check out these <em>awesome</em> prices!</p>
|
62
|
+
</body>
|
63
|
+
</html>
|
64
|
+
HTML
|
65
|
+
|
66
|
+
result = parse_html document.transform
|
67
|
+
result.should have_styling('font-size' => '200%').at_selector('p > em')
|
68
|
+
end
|
92
69
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
70
|
+
it "crashes when stylesheets cannot be found, unless using NullProvider" do
|
71
|
+
document = Roadie::Document.new <<-HTML
|
72
|
+
<!DOCTYPE html>
|
73
|
+
<html>
|
74
|
+
<head>
|
75
|
+
<link rel="stylesheet" href="/spec/fixtures/does_not_exist.css">
|
76
|
+
</head>
|
77
|
+
<body>
|
78
|
+
</body>
|
79
|
+
</html>
|
80
|
+
HTML
|
81
|
+
|
82
|
+
expect { document.transform }.to raise_error(Roadie::CssNotFound, /does_not_exist\.css/)
|
83
|
+
|
84
|
+
document.asset_providers << Roadie::NullProvider.new
|
85
|
+
expect { document.transform }.to_not raise_error
|
97
86
|
end
|
98
87
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
88
|
+
it "makes URLs absolute" do
|
89
|
+
document = Roadie::Document.new <<-HTML
|
90
|
+
<!DOCTYPE html>
|
91
|
+
<html>
|
92
|
+
<head>
|
93
|
+
<style>
|
94
|
+
body { background: url("/assets/bg-abcdef1234567890.png"); }
|
95
|
+
</style>
|
96
|
+
<link rel="stylesheet" href="/style.css">
|
97
|
+
</head>
|
98
|
+
<body>
|
99
|
+
<a href="/about_us"><img src="/assets/about_us-abcdef1234567890.png" alt="About us"></a>
|
100
|
+
</body>
|
101
|
+
</html>
|
102
|
+
HTML
|
103
|
+
|
104
|
+
document.asset_providers = TestProvider.new(
|
105
|
+
"/style.css" => "a { background: url(/assets/link-abcdef1234567890.png); }"
|
106
|
+
)
|
107
|
+
document.url_options = {host: "myapp.com", scheme: "https", path: "rails/app/"}
|
108
|
+
result = parse_html document.transform
|
109
|
+
|
110
|
+
result.at_css("a")["href"].should == "https://myapp.com/rails/app/about_us"
|
111
|
+
|
112
|
+
result.at_css("img")["src"].should == "https://myapp.com/rails/app/assets/about_us-abcdef1234567890.png"
|
113
|
+
|
114
|
+
result.should have_styling(
|
115
|
+
"background" => 'url("https://myapp.com/rails/app/assets/bg-abcdef1234567890.png")'
|
116
|
+
).at_selector("body")
|
117
|
+
|
118
|
+
result.should have_styling(
|
119
|
+
"background" => 'url(https://myapp.com/rails/app/assets/link-abcdef1234567890.png)'
|
120
|
+
).at_selector("a")
|
103
121
|
end
|
104
122
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
123
|
+
it "allows custom callbacks during inlining" do
|
124
|
+
document = Roadie::Document.new <<-HTML
|
125
|
+
<!DOCTYPE html>
|
126
|
+
<html>
|
127
|
+
<body>
|
128
|
+
<span>Hello world</span>
|
129
|
+
</body>
|
130
|
+
</html>
|
131
|
+
HTML
|
132
|
+
|
133
|
+
document.before_transformation = proc { |dom| dom.at_css("body")["class"] = "roadie" }
|
134
|
+
document.after_transformation = proc { |dom| dom.at_css("span").remove }
|
135
|
+
|
136
|
+
result = parse_html document.transform
|
137
|
+
result.at_css("body")["class"].should == "roadie"
|
138
|
+
result.at_css("span").should be_nil
|
109
139
|
end
|
110
140
|
end
|