premailer-rails-revived 1.12.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.
- 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
|