premailer-rails-revived 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.github/workflows/build.yml +43 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +165 -0
- data/Gemfile +28 -0
- data/LICENSE +14 -0
- data/README.md +180 -0
- data/Rakefile +7 -0
- data/VERSION +1 -0
- data/example/.gitignore +16 -0
- data/example/Gemfile +10 -0
- data/example/README.md +10 -0
- data/example/Rakefile +6 -0
- data/example/app/assets/stylesheets/email.css +32 -0
- data/example/app/mailers/example_mailer.rb +7 -0
- data/example/app/views/example_mailer/test_message.html.erb +18 -0
- data/example/bin/rails +4 -0
- data/example/config/application.rb +12 -0
- data/example/config/boot.rb +3 -0
- data/example/config/environment.rb +5 -0
- data/example/config/environments/development.rb +9 -0
- data/example/config/environments/production.rb +9 -0
- data/example/config/initializers/assets.rb +2 -0
- data/example/config/routes.rb +3 -0
- data/example/config/secrets.yml +2 -0
- data/example/config.ru +4 -0
- data/example/test/mailers/previews/example_mailer_preview.rb +5 -0
- data/lib/premailer/rails/css_helper.rb +70 -0
- data/lib/premailer/rails/css_loaders/asset_pipeline_loader.rb +36 -0
- data/lib/premailer/rails/css_loaders/file_system_loader.rb +37 -0
- data/lib/premailer/rails/css_loaders/network_loader.rb +39 -0
- data/lib/premailer/rails/css_loaders/propshaft_loader.rb +33 -0
- data/lib/premailer/rails/css_loaders.rb +6 -0
- data/lib/premailer/rails/customized_premailer.rb +20 -0
- data/lib/premailer/rails/hook.rb +140 -0
- data/lib/premailer/rails/railtie.rb +9 -0
- data/lib/premailer/rails/version.rb +7 -0
- data/lib/premailer/rails.rb +31 -0
- data/premailer-rails.gemspec +32 -0
- data/spec/integration/css_helper_spec.rb +192 -0
- data/spec/integration/delivery_spec.rb +13 -0
- data/spec/integration/hook_spec.rb +163 -0
- data/spec/rails_app/app/assets/config/manifest.js +3 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +3 -0
- data/spec/rails_app/app/mailers/application_mailer.rb +4 -0
- data/spec/rails_app/app/mailers/welcome_mailer.rb +6 -0
- data/spec/rails_app/app/views/layouts/mailer.html.erb +11 -0
- data/spec/rails_app/app/views/welcome_mailer/welcome_email.html.erb +1 -0
- data/spec/rails_app/config/application.rb +16 -0
- data/spec/rails_app/config/boot.rb +5 -0
- data/spec/rails_app/config/environment.rb +2 -0
- data/spec/rails_app/config/environments/test.rb +10 -0
- data/spec/rails_app/config/routes.rb +3 -0
- data/spec/rails_app/config.ru +5 -0
- data/spec/rails_app/log/.keep +0 -0
- data/spec/rails_app/tmp/.keep +0 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/fixtures/html.rb +38 -0
- data/spec/support/fixtures/message.rb +182 -0
- data/spec/unit/css_loaders/asset_pipeline_loader_spec.rb +57 -0
- data/spec/unit/css_loaders/file_system_loader_spec.rb +37 -0
- data/spec/unit/css_loaders/network_loader_spec.rb +58 -0
- data/spec/unit/css_loaders/propshaft_loader_spec.rb +57 -0
- data/spec/unit/customized_premailer_spec.rb +64 -0
- data/spec/unit/premailer_rails_spec.rb +19 -0
- metadata +233 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
module CSSHelper
|
4
|
+
extend self
|
5
|
+
|
6
|
+
FileNotFound = Class.new(StandardError)
|
7
|
+
|
8
|
+
attr_accessor :cache
|
9
|
+
self.cache = {}
|
10
|
+
|
11
|
+
# Returns all linked CSS files concatenated as string.
|
12
|
+
def css_for_doc(doc)
|
13
|
+
css_urls_in_doc(doc).map { |url| css_for_url(url) }.join("\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
def css_for_url(url)
|
17
|
+
if cache_enabled?
|
18
|
+
load_css_with_cache(url)
|
19
|
+
else
|
20
|
+
load_css(url)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def css_urls_in_doc(doc)
|
27
|
+
doc.search('link[@rel="stylesheet"]:not([@data-premailer="ignore"])').map do |link|
|
28
|
+
if link.respond_to?(:remove)
|
29
|
+
link.remove
|
30
|
+
else
|
31
|
+
link.parent.children.delete(link)
|
32
|
+
end
|
33
|
+
link.attributes['href'].to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_css_with_cache(url)
|
38
|
+
self.cache[url] ||= load_css(url)
|
39
|
+
end
|
40
|
+
|
41
|
+
def cache_enabled?
|
42
|
+
defined?(::Rails.env) && ::Rails.env.production?
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_css(url)
|
46
|
+
Premailer::Rails.config.fetch(:strategies).each do |strategy|
|
47
|
+
css = find_strategy(strategy).load(url)
|
48
|
+
return css if css
|
49
|
+
end
|
50
|
+
|
51
|
+
raise FileNotFound, %{File with URL "#{url}" could not be loaded by any strategy.}
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_strategy(key)
|
55
|
+
case key
|
56
|
+
when :filesystem
|
57
|
+
CSSLoaders::FileSystemLoader
|
58
|
+
when :asset_pipeline
|
59
|
+
CSSLoaders::AssetPipelineLoader
|
60
|
+
when :propshaft
|
61
|
+
CSSLoaders::PropshaftLoader
|
62
|
+
when :network
|
63
|
+
CSSLoaders::NetworkLoader
|
64
|
+
else
|
65
|
+
key
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
module CSSLoaders
|
4
|
+
module AssetPipelineLoader
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def load(url)
|
8
|
+
return unless asset_pipeline_present?
|
9
|
+
|
10
|
+
file = file_name(url)
|
11
|
+
::Rails.application.assets_manifest.find_sources(file).first
|
12
|
+
rescue Errno::ENOENT, TypeError => _error
|
13
|
+
end
|
14
|
+
|
15
|
+
def file_name(url)
|
16
|
+
prefix = File.join(
|
17
|
+
::Rails.configuration.relative_url_root.to_s,
|
18
|
+
::Rails.configuration.assets.prefix.to_s,
|
19
|
+
'/'
|
20
|
+
)
|
21
|
+
URI(url).path
|
22
|
+
.sub(/\A#{prefix}/, '')
|
23
|
+
.sub(/-(\h{32}|\h{64})\.css\z/, '.css')
|
24
|
+
end
|
25
|
+
|
26
|
+
def asset_pipeline_present?
|
27
|
+
defined?(::Rails) &&
|
28
|
+
::Rails.respond_to?(:application) &&
|
29
|
+
::Rails.application &&
|
30
|
+
::Rails.application.respond_to?(:assets_manifest) &&
|
31
|
+
::Rails.application.assets_manifest
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
module CSSLoaders
|
4
|
+
module FileSystemLoader
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def load(url)
|
8
|
+
file = file_name(url)
|
9
|
+
File.read(file) if File.file?(file)
|
10
|
+
end
|
11
|
+
|
12
|
+
def file_name(url)
|
13
|
+
path = URI(url).path
|
14
|
+
if relative_url_root
|
15
|
+
path = path.sub(/\A#{relative_url_root.chomp('/')}/, '')
|
16
|
+
end
|
17
|
+
asset_filename(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def asset_filename(filename)
|
21
|
+
if defined?(::Rails) && ::Rails.respond_to?(:root)
|
22
|
+
File.join(::Rails.root, 'public', filename)
|
23
|
+
else
|
24
|
+
File.join('public', filename)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def relative_url_root
|
29
|
+
defined?(::Rails) &&
|
30
|
+
::Rails.respond_to?(:configuration) &&
|
31
|
+
::Rails.configuration.respond_to?(:relative_url_root) &&
|
32
|
+
::Rails.configuration.relative_url_root
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
module CSSLoaders
|
4
|
+
module NetworkLoader
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def load(url)
|
8
|
+
uri = uri_for_url(url)
|
9
|
+
Net::HTTP.get(uri) if uri
|
10
|
+
end
|
11
|
+
|
12
|
+
def uri_for_url(url)
|
13
|
+
uri = URI(url)
|
14
|
+
|
15
|
+
if uri.host.present?
|
16
|
+
return uri if uri.scheme.present?
|
17
|
+
URI("http:#{uri}")
|
18
|
+
elsif asset_host_present?
|
19
|
+
scheme, host = asset_host(url).split(%r{:?//})
|
20
|
+
scheme, host = host, scheme if host.nil?
|
21
|
+
scheme = 'http' if scheme.blank?
|
22
|
+
path = url
|
23
|
+
URI(File.join("#{scheme}://#{host}", path))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def asset_host_present?
|
28
|
+
::Rails.respond_to?(:configuration) &&
|
29
|
+
::Rails.configuration.action_controller.asset_host.present?
|
30
|
+
end
|
31
|
+
|
32
|
+
def asset_host(url)
|
33
|
+
config = ::Rails.configuration.action_controller.asset_host
|
34
|
+
config.respond_to?(:call) ? config.call(url) : config
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
module CSSLoaders
|
4
|
+
module PropshaftLoader
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def load(url)
|
8
|
+
return unless propshaft_present?
|
9
|
+
|
10
|
+
file = file_name(url)
|
11
|
+
asset = ::Rails.application.assets.load_path.find(file)
|
12
|
+
::Rails.application.assets.compilers.compile(asset) if asset
|
13
|
+
end
|
14
|
+
|
15
|
+
def file_name(url)
|
16
|
+
prefix = File.join(
|
17
|
+
::Rails.configuration.relative_url_root.to_s,
|
18
|
+
::Rails.configuration.assets.prefix.to_s,
|
19
|
+
'/'
|
20
|
+
)
|
21
|
+
URI(url).path
|
22
|
+
.sub(/\A#{prefix}/, '')
|
23
|
+
.sub(/-(\h{40})\.css\z/, '.css')
|
24
|
+
end
|
25
|
+
|
26
|
+
def propshaft_present?
|
27
|
+
defined?(::Rails) &&
|
28
|
+
::Rails.try(:application).try(:assets).class.name == "Propshaft::Assembly"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
class CustomizedPremailer < ::Premailer
|
4
|
+
def initialize(html)
|
5
|
+
# In order to pass the CSS as string to super it is necessary to access
|
6
|
+
# the parsed HTML beforehand. To do so, the adapter needs to be
|
7
|
+
# initialized. The ::Premailer::Adapter handles the discovery of
|
8
|
+
# a suitable adapter. To make load_html work, an adapter needs to be
|
9
|
+
# included and @options[:with_html_string] needs to be set. For further
|
10
|
+
# information, refer to ::Premailer#initialize.
|
11
|
+
@options = Rails.config.merge(with_html_string: true)
|
12
|
+
Premailer.send(:include, Adapter.find(Adapter.use))
|
13
|
+
doc = load_html(html)
|
14
|
+
options = @options.merge(css_string: CSSHelper.css_for_doc(doc))
|
15
|
+
|
16
|
+
super(doc.to_s, options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
class Premailer
|
2
|
+
module Rails
|
3
|
+
class Hook
|
4
|
+
attr_reader :message
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def perform(message)
|
8
|
+
new(message).perform
|
9
|
+
message
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_method :delivering_email, :perform
|
13
|
+
alias_method :previewing_email, :perform
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(message)
|
17
|
+
@message = message
|
18
|
+
end
|
19
|
+
|
20
|
+
def perform
|
21
|
+
if skip_premailer_header_present?
|
22
|
+
remove_skip_premailer_header
|
23
|
+
elsif message_contains_html?
|
24
|
+
replace_html_part(generate_html_part_replacement)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def skip_premailer_header_present?
|
31
|
+
message.header[:skip_premailer]
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove_skip_premailer_header
|
35
|
+
message.header[:skip_premailer] = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def message_contains_html?
|
39
|
+
html_part.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if the message itself has a content type of text/html, thus
|
43
|
+
# it does not contain other parts such as alternatives and attachments.
|
44
|
+
def pure_html_message?
|
45
|
+
message.content_type && message.content_type.include?('text/html')
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_html_part_replacement
|
49
|
+
if generate_text_part?
|
50
|
+
generate_alternative_part
|
51
|
+
else
|
52
|
+
generate_html_part
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def generate_text_part?
|
57
|
+
Rails.config[:generate_text_part] && !message.text_part
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate_alternative_part
|
61
|
+
part = Mail::Part.new(content_type: 'multipart/alternative')
|
62
|
+
part.add_part(generate_text_part)
|
63
|
+
part.add_part(generate_html_part)
|
64
|
+
|
65
|
+
part
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_html_part
|
69
|
+
# Make sure that the text part is generated first. Otherwise the text
|
70
|
+
# can end up containing CSS rules.
|
71
|
+
generate_text_part if generate_text_part?
|
72
|
+
|
73
|
+
part = html_part
|
74
|
+
html = premailer.to_inline_css
|
75
|
+
Mail::Part.new do
|
76
|
+
content_type "text/html; charset=#{html.encoding}"
|
77
|
+
body html
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_text_part
|
82
|
+
@text_part ||= begin
|
83
|
+
part = html_part
|
84
|
+
text = premailer.to_plain_text
|
85
|
+
Mail::Part.new do
|
86
|
+
content_type "text/plain; charset=#{text.encoding}"
|
87
|
+
body text
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def premailer
|
93
|
+
@premailer ||= CustomizedPremailer.new(html_part.decoded)
|
94
|
+
end
|
95
|
+
|
96
|
+
def html_part
|
97
|
+
if pure_html_message?
|
98
|
+
message
|
99
|
+
else
|
100
|
+
message.html_part
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def replace_html_part(new_part)
|
105
|
+
if pure_html_message?
|
106
|
+
replace_in_pure_html_message(new_part)
|
107
|
+
else
|
108
|
+
replace_part_in_list(message.parts, html_part, new_part)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# If the new part is a pure text/html part, the body and its content type
|
113
|
+
# are used for the message. If the new part is
|
114
|
+
def replace_in_pure_html_message(new_part)
|
115
|
+
if new_part.content_type.include?('text/html')
|
116
|
+
message.body = new_part.decoded
|
117
|
+
message.content_type = new_part.content_type
|
118
|
+
else
|
119
|
+
message.body = nil
|
120
|
+
message.content_type = new_part.content_type
|
121
|
+
new_part.parts.each do |part|
|
122
|
+
message.add_part(part)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def replace_part_in_list(parts_list, old_part, new_part)
|
128
|
+
if (index = parts_list.index(old_part))
|
129
|
+
parts_list[index] = new_part
|
130
|
+
else
|
131
|
+
parts_list.any? do |part|
|
132
|
+
if part.respond_to?(:parts)
|
133
|
+
replace_part_in_list(part.parts, old_part, new_part)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'premailer'
|
2
|
+
require 'action_mailer'
|
3
|
+
|
4
|
+
require 'premailer/rails/version'
|
5
|
+
require 'premailer/rails/css_loaders'
|
6
|
+
require 'premailer/rails/css_helper'
|
7
|
+
require 'premailer/rails/customized_premailer'
|
8
|
+
require 'premailer/rails/hook'
|
9
|
+
|
10
|
+
class Premailer
|
11
|
+
module Rails
|
12
|
+
@config = {
|
13
|
+
input_encoding: 'UTF-8',
|
14
|
+
generate_text_part: true,
|
15
|
+
strategies: [:filesystem, :asset_pipeline, :propshaft, :network]
|
16
|
+
}
|
17
|
+
class << self
|
18
|
+
attr_accessor :config
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.register_interceptors
|
22
|
+
ActionMailer::Base.register_interceptor(Premailer::Rails::Hook)
|
23
|
+
|
24
|
+
if ActionMailer::Base.respond_to?(:register_preview_interceptor)
|
25
|
+
ActionMailer::Base.register_preview_interceptor(Premailer::Rails::Hook)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
require 'premailer/rails/railtie' if defined?(Rails)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "premailer/rails/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "premailer-rails-revived"
|
7
|
+
s.version = Premailer::Rails::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.license = 'MIT'
|
10
|
+
s.authors = ["Adrien Siami"]
|
11
|
+
s.email = ["adrien@siami.fr"]
|
12
|
+
s.homepage = "https://github.com/Intrepidd/premailer-rails"
|
13
|
+
s.summary = %q{Forked and revived: Easily create styled HTML emails in Rails.}
|
14
|
+
s.description = %q{This gem brings you the power of the premailer gem to Rails
|
15
|
+
without any configuration needs. Create HTML emails,
|
16
|
+
include a CSS file as you do in a normal HTML document and
|
17
|
+
premailer will inline the included CSS. Forked and revived from the work of fphilipe.}
|
18
|
+
|
19
|
+
s.metadata["changelog_uri"] = "https://github.com/Intrepidd/premailer-rails/blob/v#{Premailer::Rails::VERSION}/CHANGELOG.md"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- {example,spec}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
|
26
|
+
s.add_dependency 'premailer', '~> 1.7', '>= 1.7.9'
|
27
|
+
s.add_dependency 'actionmailer', '>= 3'
|
28
|
+
|
29
|
+
s.add_development_dependency 'rspec', '~> 3.3'
|
30
|
+
s.add_development_dependency 'nokogiri'
|
31
|
+
s.add_development_dependency 'coveralls' if RUBY_ENGINE == 'ruby'
|
32
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Premailer::Rails::CSSHelper do
|
4
|
+
# Reset the CSS cache:
|
5
|
+
after do
|
6
|
+
Premailer::Rails::CSSHelper.cache = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def css_for_url(path)
|
10
|
+
Premailer::Rails::CSSHelper.css_for_url(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def css_for_doc(doc)
|
14
|
+
Premailer::Rails::CSSHelper.css_for_doc(doc)
|
15
|
+
end
|
16
|
+
|
17
|
+
def file_loader
|
18
|
+
if Premailer::Rails::CSSLoaders::PropshaftLoader.propshaft_present?
|
19
|
+
Premailer::Rails::CSSLoaders::PropshaftLoader
|
20
|
+
else
|
21
|
+
Premailer::Rails::CSSLoaders::AssetPipelineLoader
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def expect_file(path, content='file content')
|
26
|
+
path = "#{Rails.root}/#{path}"
|
27
|
+
allow(File).to receive(:file?).with(path).and_return(true)
|
28
|
+
expect(File).to receive(:read).with(path).and_return(content)
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#css_for_doc' do
|
32
|
+
let(:html) { Fixtures::HTML.with_css_links(*files) }
|
33
|
+
let(:doc) { Nokogiri(html) }
|
34
|
+
|
35
|
+
context 'when HTML contains linked CSS files' do
|
36
|
+
let(:files) { %w[ stylesheets/base.css stylesheets/font.css ] }
|
37
|
+
|
38
|
+
it 'returns the content of both files concatenated' do
|
39
|
+
allow(Premailer::Rails::CSSHelper).to \
|
40
|
+
receive(:css_for_url)
|
41
|
+
.with('http://example.com/stylesheets/base.css')
|
42
|
+
.and_return('content of base.css')
|
43
|
+
allow(Premailer::Rails::CSSHelper).to \
|
44
|
+
receive(:css_for_url)
|
45
|
+
.with('http://example.com/stylesheets/font.css')
|
46
|
+
.and_return('content of font.css')
|
47
|
+
|
48
|
+
expect(css_for_doc(doc)).to eq("content of base.css\ncontent of font.css")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when HTML contains ignored links' do
|
53
|
+
let(:files) { ['ignore.css', 'data-premailer' => 'ignore'] }
|
54
|
+
|
55
|
+
it 'ignores links' do
|
56
|
+
expect(Premailer::Rails::CSSHelper).to_not receive(:css_for_url)
|
57
|
+
css_for_doc(doc)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#css_for_url' do
|
63
|
+
context 'when path is a url' do
|
64
|
+
it 'loads the CSS at the local path' do
|
65
|
+
expect_file('public/stylesheets/base.css')
|
66
|
+
|
67
|
+
css_for_url('http://example.com/stylesheets/base.css?test')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when path is a relative url' do
|
72
|
+
it 'loads the CSS at the local path' do
|
73
|
+
expect_file('public/stylesheets/base.css')
|
74
|
+
css_for_url('/stylesheets/base.css?test')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when cache is enabled' do
|
79
|
+
before do
|
80
|
+
allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when file is cached' do
|
84
|
+
it 'returns the cached value' do
|
85
|
+
Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'content of base.css'
|
86
|
+
|
87
|
+
expect(css_for_url('http://example.com/stylesheets/base.css')).to \
|
88
|
+
eq('content of base.css')
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when cache is disabled' do
|
94
|
+
before do
|
95
|
+
allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(false)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not return cached values' do
|
99
|
+
Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'cached content'
|
100
|
+
content = 'new content of base.css'
|
101
|
+
expect_file('public/stylesheets/base.css', content)
|
102
|
+
|
103
|
+
expect(css_for_url('http://example.com/stylesheets/base.css')).to eq(content)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when Rails asset pipeline is used' do
|
108
|
+
before do
|
109
|
+
allow(Rails.configuration)
|
110
|
+
.to receive(:assets).and_return(double(prefix: '/assets'))
|
111
|
+
allow(Rails.configuration)
|
112
|
+
.to receive(:relative_url_root).and_return(nil)
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'and a precompiled file exists' do
|
116
|
+
it 'returns that file' do
|
117
|
+
path = '/assets/email-digest.css'
|
118
|
+
content = 'read from file'
|
119
|
+
expect_file("public#{path}", content)
|
120
|
+
expect(css_for_url(path)).to eq(content)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns the content of the file compiled by Rails' do
|
125
|
+
expect(file_loader).to \
|
126
|
+
receive(:load)
|
127
|
+
.with('http://example.com/assets/base.css')
|
128
|
+
.and_return('content of base.css')
|
129
|
+
|
130
|
+
expect(css_for_url('http://example.com/assets/base.css')).to \
|
131
|
+
eq('content of base.css')
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'returns same file when path contains file fingerprint' do
|
135
|
+
expect(file_loader).to \
|
136
|
+
receive(:load)
|
137
|
+
.with('http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css')
|
138
|
+
.and_return('content of base.css')
|
139
|
+
|
140
|
+
expect(css_for_url(
|
141
|
+
'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css'
|
142
|
+
)).to eq('content of base.css')
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when asset can not be found' do
|
146
|
+
let(:response) { 'content of base.css' }
|
147
|
+
let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' }
|
148
|
+
let(:url) { "http://assets.example.com#{path}" }
|
149
|
+
let(:asset_host) { 'http://assets.example.com' }
|
150
|
+
|
151
|
+
before do
|
152
|
+
allow(file_loader).to \
|
153
|
+
receive(:load).and_return(nil)
|
154
|
+
|
155
|
+
config = double(asset_host: asset_host)
|
156
|
+
allow(Rails.configuration).to \
|
157
|
+
receive(:action_controller).and_return(config)
|
158
|
+
|
159
|
+
allow(Net::HTTP).to \
|
160
|
+
receive(:get).with(URI(url)).and_return(response)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'requests the file' do
|
164
|
+
expect(css_for_url(url)).to eq('content of base.css')
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'when file url does not include the host' do
|
168
|
+
it 'requests the file using the asset host as host' do
|
169
|
+
expect(css_for_url(path)).to eq('content of base.css')
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'and the asset host uses protocol relative scheme' do
|
173
|
+
let(:asset_host) { '//assets.example.com' }
|
174
|
+
|
175
|
+
it 'requests the file using http as the scheme' do
|
176
|
+
expect(css_for_url(path)).to eq('content of base.css')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'when static stylesheets are used' do
|
184
|
+
it 'returns the content of the static file' do
|
185
|
+
content = 'content of base.css'
|
186
|
+
expect_file('public/stylesheets/base.css', content)
|
187
|
+
loaded_content = css_for_url('http://example.com/stylesheets/base.css')
|
188
|
+
expect(loaded_content).to eq(content)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'ActionMailer::Base delivery' do
|
4
|
+
it 'delivers email with inlined CSS' do
|
5
|
+
WelcomeMailer.welcome_email("world").deliver_now
|
6
|
+
|
7
|
+
mail = ActionMailer::Base.deliveries.last
|
8
|
+
expect(mail).to be_present
|
9
|
+
body = mail.html_part.body.to_s
|
10
|
+
expect(body).to be_present
|
11
|
+
expect(body).to include(%{<p style="font-size: 12px;">Hello world</p>})
|
12
|
+
end
|
13
|
+
end
|