premailer-rails 1.5.1 → 1.6.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 +4 -4
- data/.travis.yml +11 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile +10 -0
- data/README.md +82 -40
- data/VERSION +1 -1
- data/lib/premailer/rails.rb +1 -2
- data/lib/premailer/rails/css_helper.rb +8 -20
- data/lib/premailer/rails/css_loaders.rb +4 -63
- data/lib/premailer/rails/css_loaders/asset_pipeline_loader.rb +27 -0
- data/lib/premailer/rails/css_loaders/cache_loader.rb +19 -0
- data/lib/premailer/rails/css_loaders/file_system_loader.rb +15 -0
- data/lib/premailer/rails/css_loaders/network_loader.rb +32 -0
- data/lib/premailer/rails/hook.rb +12 -3
- data/premailer-rails.gemspec +4 -6
- data/spec/integration/css_helper_spec.rb +168 -0
- data/spec/{lib → integration}/hook_registration_spec.rb +2 -3
- data/spec/{lib → integration}/hook_spec.rb +26 -10
- data/spec/spec_helper.rb +17 -5
- data/spec/{fixtures → support/fixtures}/html.rb +0 -8
- data/spec/{fixtures → support/fixtures}/message.rb +0 -0
- data/spec/{stubs → support/stubs}/action_mailer.rb +0 -0
- data/spec/{stubs → support/stubs}/rails.rb +0 -26
- data/spec/unit/css_loaders/asset_pipeline_loader_spec.rb +30 -0
- data/spec/{lib → unit}/customized_premailer_spec.rb +12 -13
- data/spec/{lib/premailer_rails_3_spec.rb → unit/premailer_rails_spec.rb} +0 -0
- metadata +44 -56
- data/spec/lib/css_helper_spec.rb +0 -164
- data/spec/stubs/dummy.rb +0 -5
data/lib/premailer/rails/hook.rb
CHANGED
@@ -5,6 +5,7 @@ class Premailer
|
|
5
5
|
|
6
6
|
def self.delivering_email(message)
|
7
7
|
self.new(message).perform
|
8
|
+
message
|
8
9
|
end
|
9
10
|
|
10
11
|
def initialize(message)
|
@@ -12,15 +13,23 @@ class Premailer
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def perform
|
15
|
-
if
|
16
|
+
if skip_premailer_header_present?
|
17
|
+
remove_skip_premailer_header
|
18
|
+
elsif message_contains_html?
|
16
19
|
replace_html_part(generate_html_part_replacement)
|
17
20
|
end
|
18
|
-
|
19
|
-
message
|
20
21
|
end
|
21
22
|
|
22
23
|
private
|
23
24
|
|
25
|
+
def skip_premailer_header_present?
|
26
|
+
message.header[:skip_premailer]
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_skip_premailer_header
|
30
|
+
message.header[:skip_premailer] = nil
|
31
|
+
end
|
32
|
+
|
24
33
|
def message_contains_html?
|
25
34
|
html_part.present?
|
26
35
|
end
|
data/premailer-rails.gemspec
CHANGED
@@ -20,13 +20,11 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
|
23
|
-
s.add_dependency 'premailer', '~> 1.7'
|
24
|
-
s.add_dependency '
|
23
|
+
s.add_dependency 'premailer', '~> 1.7', '>= 1.7.9'
|
24
|
+
s.add_dependency 'actionmailer', '>= 3', '< 5'
|
25
25
|
|
26
|
-
s.add_development_dependency 'rspec
|
27
|
-
s.add_development_dependency 'rspec-expectations'
|
28
|
-
s.add_development_dependency 'mocha'
|
29
|
-
s.add_development_dependency 'mail'
|
26
|
+
s.add_development_dependency 'rspec', '>= 3.0.0.beta1'
|
30
27
|
s.add_development_dependency 'nokogiri'
|
31
28
|
s.add_development_dependency 'hpricot' unless RUBY_PLATFORM == 'java'
|
29
|
+
s.add_development_dependency 'coveralls'
|
32
30
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Premailer::Rails::CSSHelper do
|
4
|
+
# Reset the CSS cache:
|
5
|
+
after do
|
6
|
+
Premailer::Rails::CSSHelper.send(:instance_variable_set, '@cache', {})
|
7
|
+
end
|
8
|
+
|
9
|
+
def load_css(path)
|
10
|
+
Premailer::Rails::CSSHelper.send(:load_css, path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def css_for_doc(doc)
|
14
|
+
Premailer::Rails::CSSHelper.css_for_doc(doc)
|
15
|
+
end
|
16
|
+
|
17
|
+
def expect_file(path, content='file content')
|
18
|
+
allow(File).to receive(:exist?).with(path).and_return(true)
|
19
|
+
expect(File).to receive(:read).with(path).and_return(content)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#css_for_doc' do
|
23
|
+
let(:html) { Fixtures::HTML.with_css_links(*files) }
|
24
|
+
let(:doc) { Nokogiri(html) }
|
25
|
+
|
26
|
+
context 'when HTML contains linked CSS files' do
|
27
|
+
let(:files) { %w[ stylesheets/base.css stylesheets/font.css ] }
|
28
|
+
|
29
|
+
it 'should return the content of both files concatenated' do
|
30
|
+
allow(Premailer::Rails::CSSHelper).to \
|
31
|
+
receive(:load_css)
|
32
|
+
.with('http://example.com/stylesheets/base.css')
|
33
|
+
.and_return('content of base.css')
|
34
|
+
allow(Premailer::Rails::CSSHelper).to \
|
35
|
+
receive(:load_css)
|
36
|
+
.with('http://example.com/stylesheets/font.css')
|
37
|
+
.and_return('content of font.css')
|
38
|
+
|
39
|
+
expect(css_for_doc(doc)).to eq("content of base.css\ncontent of font.css")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#load_css' do
|
45
|
+
context 'when path is a url' do
|
46
|
+
it 'should load the CSS at the local path' do
|
47
|
+
expect_file('public/stylesheets/base.css')
|
48
|
+
|
49
|
+
load_css('http://example.com/stylesheets/base.css?test')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when path is a relative url' do
|
54
|
+
it 'should load the CSS at the local path' do
|
55
|
+
expect_file('public/stylesheets/base.css')
|
56
|
+
load_css('/stylesheets/base.css?test')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when file is cached' do
|
61
|
+
it 'should return the cached value' do
|
62
|
+
cache =
|
63
|
+
Premailer::Rails::CSSHelper.send(:instance_variable_get, '@cache')
|
64
|
+
cache['http://example.com/stylesheets/base.css'] = 'content of base.css'
|
65
|
+
|
66
|
+
expect(load_css('http://example.com/stylesheets/base.css')).to \
|
67
|
+
eq('content of base.css')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when in development mode' do
|
72
|
+
it 'should not return cached values' do
|
73
|
+
cache =
|
74
|
+
Premailer::Rails::CSSHelper.send(:instance_variable_get, '@cache')
|
75
|
+
cache['http://example.com/stylesheets/base.css'] =
|
76
|
+
'cached content of base.css'
|
77
|
+
content = 'new content of base.css'
|
78
|
+
expect_file('public/stylesheets/base.css', content)
|
79
|
+
allow(Rails.env).to receive(:development?).and_return(true)
|
80
|
+
|
81
|
+
expect(load_css('http://example.com/stylesheets/base.css')).to eq(content)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when Rails asset pipeline is used' do
|
86
|
+
before do
|
87
|
+
allow(Rails.configuration).to receive(:assets).and_return(double(prefix: '/assets'))
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'and a precompiled file exists' do
|
91
|
+
it 'should return that file' do
|
92
|
+
path = '/assets/email-digest.css'
|
93
|
+
content = 'read from file'
|
94
|
+
expect_file("public#{path}", content)
|
95
|
+
expect(load_css(path)).to eq(content)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should return the content of the file compiled by Rails' do
|
100
|
+
expect(Rails.application.assets).to \
|
101
|
+
receive(:find_asset)
|
102
|
+
.with('base.css')
|
103
|
+
.and_return(double(to_s: 'content of base.css'))
|
104
|
+
|
105
|
+
expect(load_css('http://example.com/assets/base.css')).to \
|
106
|
+
eq('content of base.css')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should return same file when path contains file fingerprint' do
|
110
|
+
expect(Rails.application.assets).to \
|
111
|
+
receive(:find_asset)
|
112
|
+
.with('base.css')
|
113
|
+
.and_return(double(to_s: 'content of base.css'))
|
114
|
+
|
115
|
+
expect(load_css(
|
116
|
+
'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css'
|
117
|
+
)).to eq('content of base.css')
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'when asset can not be found' do
|
121
|
+
let(:response) { 'content of base.css' }
|
122
|
+
let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' }
|
123
|
+
let(:url) { "http://assets.example.com#{path}" }
|
124
|
+
let(:asset_host) { 'http://assets.example.com' }
|
125
|
+
|
126
|
+
before do
|
127
|
+
allow(Rails.application.assets).to \
|
128
|
+
receive(:find_asset).and_return(nil)
|
129
|
+
|
130
|
+
config = double(asset_host: asset_host)
|
131
|
+
allow(Rails.configuration).to \
|
132
|
+
receive(:action_controller).and_return(config)
|
133
|
+
|
134
|
+
uri_satisfaction = satisfy { |uri| uri.to_s == url }
|
135
|
+
allow(Net::HTTP).to \
|
136
|
+
receive(:get).with(uri_satisfaction).and_return(response)
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should request the file' do
|
140
|
+
expect(load_css(url)).to eq('content of base.css')
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'when file url does not include the host' do
|
144
|
+
it 'should request the file using the asset host as host' do
|
145
|
+
expect(load_css(path)).to eq('content of base.css')
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'and the asset host uses protocol relative scheme' do
|
149
|
+
let(:asset_host) { '//assets.example.com' }
|
150
|
+
|
151
|
+
it 'should request the file using http as the scheme' do
|
152
|
+
expect(load_css(path)).to eq('content of base.css')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'when static stylesheets are used' do
|
160
|
+
it 'should return the content of the static file' do
|
161
|
+
content = 'content of base.css'
|
162
|
+
expect_file('public/stylesheets/base.css', content)
|
163
|
+
loaded_content = load_css('http://example.com/stylesheets/base.css')
|
164
|
+
expect(loaded_content).to eq(content)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -2,9 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe 'ActionMailer::Base.register_interceptor' do
|
4
4
|
it 'should register interceptor Premailer::Rails::Hook' do
|
5
|
-
ActionMailer::Base
|
6
|
-
|
7
|
-
.with(Premailer::Rails::Hook)
|
5
|
+
expect(ActionMailer::Base).to \
|
6
|
+
receive(:register_interceptor).with(Premailer::Rails::Hook)
|
8
7
|
load 'premailer/rails.rb'
|
9
8
|
end
|
10
9
|
end
|
@@ -11,7 +11,7 @@ describe Premailer::Rails::Hook do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
let(:message) { Fixtures::Message.
|
14
|
+
let(:message) { Fixtures::Message.with_parts(:html) }
|
15
15
|
let(:processed_message) { run_hook(message) }
|
16
16
|
|
17
17
|
it 'inlines the CSS' do
|
@@ -20,8 +20,9 @@ describe Premailer::Rails::Hook do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'replaces the html part with an alternative part containing text and html parts' do
|
23
|
-
processed_message.content_type.
|
24
|
-
|
23
|
+
expect(processed_message.content_type).to include('multipart/alternative')
|
24
|
+
expected_parts = [message.html_part, message.text_part]
|
25
|
+
expect(processed_message.parts).to match_array(expected_parts)
|
25
26
|
end
|
26
27
|
|
27
28
|
it 'generates a text part from the html' do
|
@@ -29,7 +30,7 @@ describe Premailer::Rails::Hook do
|
|
29
30
|
end
|
30
31
|
|
31
32
|
context 'when message contains no html' do
|
32
|
-
let(:message) { Fixtures::Message.
|
33
|
+
let(:message) { Fixtures::Message.with_parts(:text) }
|
33
34
|
|
34
35
|
it 'does not modify the message' do
|
35
36
|
expect { run_hook(message) }.to_not change(message, :html_string)
|
@@ -64,14 +65,29 @@ describe Premailer::Rails::Hook do
|
|
64
65
|
context 'when message also contains an attachment' do
|
65
66
|
let(:message) { Fixtures::Message.with_parts(:html, :attachment) }
|
66
67
|
it 'does not mess with it' do
|
67
|
-
message.content_type.
|
68
|
-
message.parts.first.content_type.
|
69
|
-
message.parts.last.content_type.
|
68
|
+
expect(message.content_type).to include 'multipart/mixed'
|
69
|
+
expect(message.parts.first.content_type).to include 'text/html'
|
70
|
+
expect(message.parts.last.content_type).to include 'image/png'
|
70
71
|
|
71
|
-
processed_message.content_type.
|
72
|
-
processed_message.parts.first.content_type.
|
72
|
+
expect(processed_message.content_type).to include 'multipart/mixed'
|
73
|
+
expect(processed_message.parts.first.content_type).to \
|
73
74
|
include 'multipart/alternative'
|
74
|
-
processed_message.parts.last.content_type.
|
75
|
+
expect(processed_message.parts.last.content_type).to include 'image/png'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when message has a skip premailer header' do
|
80
|
+
before do
|
81
|
+
message.header[:skip_premailer] = true
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'does not change the message body' do
|
85
|
+
expect { run_hook(message) }.to_not change(message, :body)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'removes that header' do
|
89
|
+
expect { run_hook(message) }.to \
|
90
|
+
change { message.header[:skip_premailer].nil? }.to(true)
|
75
91
|
end
|
76
92
|
end
|
77
93
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
|
+
if ENV['CI']
|
2
|
+
require 'coveralls'
|
3
|
+
Coveralls::Output.silent = true
|
4
|
+
Coveralls.wear! do
|
5
|
+
add_filter 'spec/'
|
6
|
+
end
|
7
|
+
else
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start
|
10
|
+
end
|
11
|
+
|
1
12
|
require 'premailer/rails'
|
2
13
|
|
3
|
-
require 'stubs/action_mailer'
|
4
|
-
require 'stubs/rails'
|
5
|
-
require 'fixtures/message'
|
6
|
-
require 'fixtures/html'
|
14
|
+
require 'support/stubs/action_mailer'
|
15
|
+
require 'support/stubs/rails'
|
16
|
+
require 'support/fixtures/message'
|
17
|
+
require 'support/fixtures/html'
|
7
18
|
|
8
19
|
require 'hpricot' unless RUBY_PLATFORM == 'java'
|
9
20
|
require 'nokogiri'
|
10
21
|
|
11
22
|
RSpec.configure do |config|
|
12
|
-
config.
|
23
|
+
config.raise_errors_for_deprecations!
|
24
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
13
25
|
end
|
File without changes
|
File without changes
|
@@ -1,26 +1,8 @@
|
|
1
|
-
require 'stubs/dummy'
|
2
|
-
|
3
|
-
class Logger
|
4
|
-
def self.try(*args); end
|
5
|
-
end
|
6
|
-
|
7
1
|
module Rails
|
8
2
|
extend self
|
9
3
|
|
10
4
|
module Configuration
|
11
5
|
extend self
|
12
|
-
|
13
|
-
module Middleware
|
14
|
-
extend self
|
15
|
-
|
16
|
-
def include?(what)
|
17
|
-
false
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def middleware
|
22
|
-
Middleware
|
23
|
-
end
|
24
6
|
end
|
25
7
|
|
26
8
|
module Env
|
@@ -51,14 +33,6 @@ module Rails
|
|
51
33
|
Configuration
|
52
34
|
end
|
53
35
|
|
54
|
-
def logger
|
55
|
-
Logger
|
56
|
-
end
|
57
|
-
|
58
|
-
def root
|
59
|
-
'RAILS_ROOT'
|
60
|
-
end
|
61
|
-
|
62
36
|
def application
|
63
37
|
Application
|
64
38
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Premailer::Rails::CSSLoaders::AssetPipelineLoader do
|
4
|
+
before do
|
5
|
+
assets = double(prefix: '/assets')
|
6
|
+
config = double(assets: assets)
|
7
|
+
allow(Rails).to receive(:configuration).and_return(config)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe ".file_name" do
|
11
|
+
subject do
|
12
|
+
Premailer::Rails::CSSLoaders::AssetPipelineLoader.file_name(asset)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when asset file path contains prefix" do
|
16
|
+
let(:asset) { '/assets/application.css' }
|
17
|
+
it { should == 'application.css' }
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when asset file path contains fingerprint" do
|
21
|
+
let(:asset) { 'application-6776f581a4329e299531e1d52aa59832.css' }
|
22
|
+
it { should == 'application.css' }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when asset file page contains numbers, but not a fingerprint" do
|
26
|
+
let(:asset) { 'test/20130708152545-foo-bar.css' }
|
27
|
+
it { should == "test/20130708152545-foo-bar.css" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# coding: UTF-8
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
|
5
3
|
describe Premailer::Rails::CustomizedPremailer do
|
@@ -7,27 +5,28 @@ describe Premailer::Rails::CustomizedPremailer do
|
|
7
5
|
next if adapter == :hpricot and RUBY_PLATFORM == 'java'
|
8
6
|
|
9
7
|
context "when adapter is #{adapter}" do
|
10
|
-
before { Premailer::Adapter.
|
8
|
+
before { allow(Premailer::Adapter).to receive(:use).and_return(adapter) }
|
11
9
|
|
12
10
|
describe '#to_plain_text' do
|
13
11
|
it 'should include the text from the HTML part' do
|
14
12
|
premailer =
|
15
13
|
Premailer::Rails::CustomizedPremailer
|
16
14
|
.new(Fixtures::Message::HTML_PART)
|
17
|
-
premailer.to_plain_text.gsub(/\s/, ' ').strip
|
18
|
-
|
15
|
+
expect(premailer.to_plain_text.gsub(/\s/, ' ').strip).to \
|
16
|
+
eq(Fixtures::Message::TEXT_PART.gsub(/\s/, ' ').strip)
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
20
|
describe '#to_inline_css' do
|
21
|
+
let(:regex) { %r{<p style=("|')color: ?red;?\1>} }
|
22
|
+
|
23
23
|
context 'when inline CSS block present' do
|
24
24
|
it 'should return the HTML with the CSS inlined' do
|
25
|
-
Premailer::Rails::CSSHelper
|
26
|
-
|
27
|
-
.returns('p { color: red; }')
|
25
|
+
allow(Premailer::Rails::CSSHelper).to \
|
26
|
+
receive(:css_for_doc).and_return('p { color: red; }')
|
28
27
|
html = Fixtures::Message::HTML_PART
|
29
28
|
premailer = Premailer::Rails::CustomizedPremailer.new(html)
|
30
|
-
premailer.to_inline_css.
|
29
|
+
expect(premailer.to_inline_css).to match(regex)
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
@@ -35,7 +34,7 @@ describe Premailer::Rails::CustomizedPremailer do
|
|
35
34
|
it 'should return the HTML with the CSS inlined' do
|
36
35
|
html = Fixtures::Message::HTML_PART_WITH_CSS
|
37
36
|
premailer = Premailer::Rails::CustomizedPremailer.new(html)
|
38
|
-
premailer.to_inline_css.
|
37
|
+
expect(premailer.to_inline_css).to match(regex)
|
39
38
|
end
|
40
39
|
end
|
41
40
|
end
|
@@ -44,21 +43,21 @@ describe Premailer::Rails::CustomizedPremailer do
|
|
44
43
|
|
45
44
|
describe '.new' do
|
46
45
|
it 'should extract the CSS' do
|
47
|
-
Premailer::Rails::CSSHelper.
|
46
|
+
expect(Premailer::Rails::CSSHelper).to receive(:css_for_doc)
|
48
47
|
Premailer::Rails::CustomizedPremailer.new('some html')
|
49
48
|
end
|
50
49
|
|
51
50
|
it 'should pass on the configs' do
|
52
51
|
Premailer::Rails.config = { foo: :bar }
|
53
52
|
premailer = Premailer::Rails::CustomizedPremailer.new('some html')
|
54
|
-
premailer.instance_variable_get(:'@options')[:foo].
|
53
|
+
expect(premailer.instance_variable_get(:'@options')[:foo]).to eq(:bar)
|
55
54
|
end
|
56
55
|
|
57
56
|
it 'should not allow to override with_html_string' do
|
58
57
|
Premailer::Rails.config = { with_html_string: false }
|
59
58
|
premailer = Premailer::Rails::CustomizedPremailer.new('some html')
|
60
59
|
options = premailer.instance_variable_get(:'@options')
|
61
|
-
options[:with_html_string].
|
60
|
+
expect(options[:with_html_string]).to eq(true)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
end
|