premailer-rails 1.9.7 → 1.10.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.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +6 -0
  4. data/Gemfile +8 -4
  5. data/README.md +20 -28
  6. data/VERSION +1 -1
  7. data/lib/premailer/rails.rb +2 -1
  8. data/lib/premailer/rails/css_helper.rb +29 -10
  9. data/lib/premailer/rails/css_loaders.rb +0 -1
  10. data/lib/premailer/rails/css_loaders/asset_pipeline_loader.rb +8 -11
  11. data/lib/premailer/rails/css_loaders/network_loader.rb +1 -1
  12. data/lib/premailer/rails/customized_premailer.rb +4 -4
  13. data/premailer-rails.gemspec +0 -1
  14. data/spec/integration/css_helper_spec.rb +155 -141
  15. data/spec/integration/delivery_spec.rb +13 -0
  16. data/spec/integration/hook_spec.rb +1 -1
  17. data/spec/rails_app/app/assets/config/manifest.js +3 -0
  18. data/spec/rails_app/app/assets/stylesheets/application.css +3 -0
  19. data/spec/rails_app/app/mailers/application_mailer.rb +4 -0
  20. data/spec/rails_app/app/mailers/welcome_mailer.rb +6 -0
  21. data/spec/rails_app/app/views/layouts/mailer.html.erb +11 -0
  22. data/spec/rails_app/app/views/welcome_mailer/welcome_email.html.erb +1 -0
  23. data/spec/rails_app/config.ru +5 -0
  24. data/spec/rails_app/config/application.rb +13 -0
  25. data/spec/rails_app/config/boot.rb +5 -0
  26. data/spec/rails_app/config/environment.rb +2 -0
  27. data/spec/rails_app/config/environments/test.rb +10 -0
  28. data/spec/rails_app/config/initializers/assets.rb +1 -0
  29. data/spec/rails_app/config/routes.rb +3 -0
  30. data/spec/spec_helper.rb +3 -8
  31. data/spec/unit/css_loaders/network_loader_spec.rb +1 -1
  32. data/spec/unit/customized_premailer_spec.rb +32 -40
  33. metadata +31 -24
  34. data/lib/premailer/rails/css_loaders/cache_loader.rb +0 -29
  35. data/spec/integration/hook_registration_spec.rb +0 -11
  36. data/spec/support/stubs/action_mailer.rb +0 -5
  37. data/spec/support/stubs/rails.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 34449fbe39f5a20061a3f6b389a95c4286b6a2cc
4
- data.tar.gz: 551a7262f0926f02554a6bea9c47eb4446d04d82
2
+ SHA256:
3
+ metadata.gz: 77899a5ba8ec23d6cf40d3b3d630f7872e92cbc959ba64ac528b31a658ae6f13
4
+ data.tar.gz: 4d13e28457b824306a517a7cff731fd83713654367562c9a7192e8a632d62aa2
5
5
  SHA512:
6
- metadata.gz: 507c28b394a9ec1fdf24e6a51b45f0cad7ac6b7ad3012f7c5993db164eeb1f100d9a410a10d4d5876f68cf9e8f9a3a2febf8a5cad5cc116823abaa84714ba8e7
7
- data.tar.gz: f3fb70d57c1acf9e196dadc98c53061ed45b2604c9a721e8eff194dd9e42b45243c5c07b4bcaa6b555ae97e5d163ba27c65cb6dcbfab61ed9b4f2168f813f853
6
+ metadata.gz: fc27f934b5772cc632cb7424013d31176afcdf47e60696479eb712c4ee85abe24a5d2524848e862e5a1489aea0778a789b1f2e96dc308df5caeb91a06f763e96
7
+ data.tar.gz: c3de5ed3a8625350eff2c07b30ed01cf418f6bb0a820e2244ad1e5c1018f495d8270e699539e21fbcac2e31c43d5e9fc36202b867b4d784c8955f8ea6338d13d
data/.gitignore CHANGED
@@ -2,3 +2,7 @@
2
2
  doc/
3
3
  Gemfile.lock
4
4
  coverage/
5
+ spec/rails_app/tmp/
6
+ spec/rails_app/log/
7
+ .ruby-version
8
+ /.bundle
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.10.0
4
+
5
+ - Drop support for hpricot now that premailer-rails also doesn't support it
6
+ - Use `Rails.application.assets_manifest` instead of `Rails.application.assets` in Asset Pipeline loader (@kirs, #201)
7
+ - Introduce `:strategies` config option that allows to control CSS fetching stragies
8
+
3
9
  ## v1.9.7
4
10
 
5
11
  - Use `Rails.root` in `FileSystemLoader` (@stanhu, #195)
data/Gemfile CHANGED
@@ -2,16 +2,20 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- action_mailer_version = ENV.fetch('ACTION_MAILER_VERSION', '5')
5
+ rails_version = ENV.fetch('ACTION_MAILER_VERSION', '5')
6
6
 
7
- if action_mailer_version == 'master'
7
+ if rails_version == 'master'
8
8
  git 'git://github.com/rails/rails.git' do
9
- gem 'actionmailer'
9
+ gem 'rails'
10
10
  end
11
+ gem 'sprockets-rails', github: 'rails/sprockets-rails'
12
+ gem 'arel', github: 'rails/arel'
11
13
  else
12
- gem 'actionmailer', "~> #{action_mailer_version}"
14
+ gem 'rails', "~> #{rails_version}"
13
15
  end
14
16
 
17
+ gem 'byebug'
18
+
15
19
  platforms :rbx do
16
20
  gem 'rubysl'
17
21
  gem 'racc'
data/README.md CHANGED
@@ -39,34 +39,34 @@ styled emails without having to set anything up.
39
39
  Whenever premailer-rails processes an email, it collects the URLs of all linked
40
40
  stylesheets (`<link rel="stylesheet" href="css_url">`). Then, for each of these
41
41
  URLs, it tries to get the content through a couple of strategies. As long as
42
- a strategy does not return anything, the next one is used. The strategies and
43
- their order are as follows:
42
+ a strategy does not return anything, the next one is used. The strategies
43
+ available are:
44
44
 
45
- 1. **Cache:** If there's a file in cache matching that URL, the cache content
46
- is returned. The cache right now is rather rudimentary. Whenever a CSS file
47
- is retrieved, it is stored in memory such that subsequent requests to the
48
- same file are faster. The caching is disabled inside Rails in the
49
- development environment.
50
-
51
- 2. **File System:** If there's a file inside `public/` with the same path as in
45
+ - `:filesystem`: If there's a file inside `public/` with the same path as in
52
46
  the URL, it is read from disk. E.g. if the URL is
53
47
  `http://cdn.example.com/assets/email.css` the contents of the file located
54
48
  at `public/assets/email.css` gets returned if it exists.
55
49
 
56
- 3. **Asset Pipeline:** If Rails is available and the asset pipeline is enabled,
50
+ - `:asset_pipeline`: If Rails is available and the asset pipeline is enabled,
57
51
  the file is retrieved through the asset pipeline. E.g. if the URL is
58
52
  `http://cdn.example.com/assets/email-fingerprint123.css`, the file
59
53
  `email.css` is requested from the asset pipeline. That is, the fingerprint
60
54
  and the prefix (in this case `assets` is the prefix) are stripped before
61
55
  requesting it from the asset pipeline.
62
56
 
63
- 4. **Network:** As a last resort, the URL is simply requested and the response
64
- body is used. This is usefull when the assets are not bundled in the
57
+ - `:network`: As a last resort, the URL is simply requested and the response
58
+ body is used. This is useful when the assets are not bundled in the
65
59
  application and only available on a CDN. On Heroku e.g. you can add assets
66
60
  to your `.slugignore` causing your assets to not be available to the app
67
61
  (and thus resulting in a smaller app) and deploy the assets to a CDN such
68
62
  as S3/CloudFront.
69
63
 
64
+ You can configure which strategies you want to use as well as specify their
65
+ order. Refer to the *Configuration* section for more on this.
66
+
67
+ Note that the retrieved CSS is cached when the gem is running with Rails in
68
+ production.
69
+
70
70
  ## Installation
71
71
 
72
72
  Simply add the gem to your `Gemfile`:
@@ -75,20 +75,12 @@ Simply add the gem to your `Gemfile`:
75
75
  gem 'premailer-rails'
76
76
  ```
77
77
 
78
- premailer-rails requires either [nokogiri] or [hpricot]. It doesn't list them as
79
- a dependency so you can choose which one to use. Since hpricot is no longer
80
- maintained, I suggest you to go with nokogiri. Add either one to your `Gemfile`:
81
-
82
- ```ruby
83
- gem 'nokogiri'
84
- # or
85
- gem 'hpricot'
86
- ```
87
-
88
- If both gems are loaded for some reason, premailer chooses hpricot.
89
-
90
- You can also explicitly configure the apapter as documented
91
- [here](https://github.com/premailer/premailer#adapters).
78
+ premailer-rails and premailer require a gem that is used to parse the email's
79
+ HTML. For a list of supported gems and how to select which one to use, please
80
+ refer to the [*Adapter*
81
+ section](https://github.com/premailer/premailer#adapters) of premailer. Note
82
+ that there is no hard dependency from either gem so you should add one yourself.
83
+ Also note that this gem is only tested with [nokogiri].
92
84
 
93
85
  ## Configuration
94
86
 
@@ -107,7 +99,8 @@ configs are:
107
99
  ```ruby
108
100
  {
109
101
  input_encoding: 'UTF-8',
110
- generate_text_part: true
102
+ generate_text_part: true,
103
+ strategies: [:filesystem, :asset_pipeline, :network]
111
104
  }
112
105
  ```
113
106
 
@@ -185,7 +178,6 @@ premailer-rails is released under the MIT license. See the [license file].
185
178
  [premailer]: https://github.com/premailer/premailer
186
179
  [actionmailer]: https://github.com/rails/rails/tree/master/actionmailer
187
180
  [nokogiri]: https://github.com/sparklemotion/nokogiri
188
- [hpricot]: https://github.com/hpricot/hpricot
189
181
 
190
182
  [premailer documentation]: http://rubydoc.info/gems/premailer/1.7.3/Premailer:initialize
191
183
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.9.7
1
+ 1.10.0
@@ -11,7 +11,8 @@ class Premailer
11
11
  module Rails
12
12
  @config = {
13
13
  input_encoding: 'UTF-8',
14
- generate_text_part: true
14
+ generate_text_part: true,
15
+ strategies: [:filesystem, :asset_pipeline, :network]
15
16
  }
16
17
  class << self
17
18
  attr_accessor :config
@@ -5,12 +5,8 @@ class Premailer
5
5
 
6
6
  FileNotFound = Class.new(StandardError)
7
7
 
8
- STRATEGIES = [
9
- CSSLoaders::CacheLoader,
10
- CSSLoaders::FileSystemLoader,
11
- CSSLoaders::AssetPipelineLoader,
12
- CSSLoaders::NetworkLoader
13
- ]
8
+ attr_accessor :cache
9
+ self.cache = {}
14
10
 
15
11
  # Returns all linked CSS files concatenated as string.
16
12
  def css_for_doc(doc)
@@ -18,8 +14,10 @@ class Premailer
18
14
  end
19
15
 
20
16
  def css_for_url(url)
21
- load_css(url).tap do |content|
22
- CSSLoaders::CacheLoader.store(url, content)
17
+ if cache_enabled?
18
+ load_css_with_cache(url)
19
+ else
20
+ load_css(url)
23
21
  end
24
22
  end
25
23
 
@@ -36,14 +34,35 @@ class Premailer
36
34
  end
37
35
  end
38
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) && ::Rails.env.production?
43
+ end
44
+
39
45
  def load_css(url)
40
- STRATEGIES.each do |strategy|
41
- css = strategy.load(url)
46
+ Premailer::Rails.config.fetch(:strategies).each do |strategy|
47
+ css = find_strategy(strategy).load(url)
42
48
  return css.force_encoding('UTF-8') if css
43
49
  end
44
50
 
45
51
  raise FileNotFound, %{File with URL "#{url}" could not be loaded by any strategy.}
46
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 :network
61
+ CSSLoaders::NetworkLoader
62
+ else
63
+ key
64
+ end
65
+ end
47
66
  end
48
67
  end
49
68
  end
@@ -1,6 +1,5 @@
1
1
  require 'uri'
2
2
 
3
- require 'premailer/rails/css_loaders/cache_loader'
4
3
  require 'premailer/rails/css_loaders/file_system_loader'
5
4
  require 'premailer/rails/css_loaders/asset_pipeline_loader'
6
5
  require 'premailer/rails/css_loaders/network_loader'
@@ -5,18 +5,11 @@ class Premailer
5
5
  extend self
6
6
 
7
7
  def load(url)
8
- if asset_pipeline_present?
9
- file = file_name(url)
10
- asset = ::Rails.application.assets.find_asset(file)
11
- asset.to_s if asset
12
- end
13
- end
8
+ return unless asset_pipeline_present?
14
9
 
15
- def asset_pipeline_present?
16
- defined?(::Rails) &&
17
- ::Rails.respond_to?(:application) &&
18
- ::Rails.application.respond_to?(:assets) &&
19
- ::Rails.application.assets
10
+ file = file_name(url)
11
+ ::Rails.application.assets_manifest.find_sources(file).first
12
+ rescue Errno::ENOENT => _error
20
13
  end
21
14
 
22
15
  def file_name(url)
@@ -29,6 +22,10 @@ class Premailer
29
22
  .sub(/\A#{prefix}/, '')
30
23
  .sub(/-(\h{32}|\h{64})\.css\z/, '.css')
31
24
  end
25
+
26
+ def asset_pipeline_present?
27
+ defined?(::Rails) && ::Rails.application && ::Rails.application.assets_manifest
28
+ end
32
29
  end
33
30
  end
34
31
  end
@@ -14,7 +14,7 @@ class Premailer
14
14
 
15
15
  if uri.host.present?
16
16
  return uri if uri.scheme.present?
17
- URI("http://#{uri.to_s}")
17
+ URI("http:#{uri}")
18
18
  elsif asset_host_present?
19
19
  scheme, host = asset_host(url).split(%r{:?//})
20
20
  scheme, host = host, scheme if host.nil?
@@ -4,10 +4,10 @@ class Premailer
4
4
  def initialize(html)
5
5
  # In order to pass the CSS as string to super it is necessary to access
6
6
  # the parsed HTML beforehand. To do so, the adapter needs to be
7
- # initialized. The ::Premailer::Adaptor handles the discovery of a
8
- # suitable adaptor (Nokogiri or Hpricot). To make load_html work, an
9
- # adaptor needs to be included and @options[:with_html_string] needs to
10
- # be set. For further information, refer to ::Premailer#initialize.
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
11
  @options = Rails.config.merge(with_html_string: true)
12
12
  Premailer.send(:include, Adapter.find(Adapter.use))
13
13
  doc = load_html(html)
@@ -26,6 +26,5 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_development_dependency 'rspec', '~> 3.3'
28
28
  s.add_development_dependency 'nokogiri'
29
- s.add_development_dependency 'hpricot' unless RUBY_PLATFORM == 'java'
30
29
  s.add_development_dependency 'coveralls' if RUBY_ENGINE == 'ruby'
31
30
  end
@@ -1,186 +1,200 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Premailer::Rails::CSSHelper do
4
- [ :Nokogiri, :Hpricot ].each do |adapter|
5
- next if adapter == :Hpricot and RUBY_PLATFORM == 'java'
4
+ # Reset the CSS cache:
5
+ after do
6
+ Premailer::Rails::CSSHelper.cache = {}
7
+ end
6
8
 
7
- context "when adapter is #{adapter}" do
8
- # Reset the CSS cache:
9
- after do
10
- Premailer::Rails::CSSLoaders::CacheLoader.clear!
11
- end
9
+ def css_for_url(path)
10
+ Premailer::Rails::CSSHelper.css_for_url(path)
11
+ end
12
12
 
13
- def css_for_url(path)
14
- Premailer::Rails::CSSHelper.css_for_url(path)
15
- end
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
+ path = "#{Rails.root}/#{path}"
19
+ allow(File).to receive(:file?).with(path).and_return(true)
20
+ expect(File).to receive(:read).with(path).and_return(content)
21
+ end
16
22
 
17
- def css_for_doc(doc)
18
- Premailer::Rails::CSSHelper.css_for_doc(doc)
23
+ describe '#css_for_doc' do
24
+ let(:html) { Fixtures::HTML.with_css_links(*files) }
25
+ let(:doc) { Nokogiri(html) }
26
+
27
+ context 'when HTML contains linked CSS files' do
28
+ let(:files) { %w[ stylesheets/base.css stylesheets/font.css ] }
29
+
30
+ it 'returns the content of both files concatenated' do
31
+ allow(Premailer::Rails::CSSHelper).to \
32
+ receive(:css_for_url)
33
+ .with('http://example.com/stylesheets/base.css')
34
+ .and_return('content of base.css')
35
+ allow(Premailer::Rails::CSSHelper).to \
36
+ receive(:css_for_url)
37
+ .with('http://example.com/stylesheets/font.css')
38
+ .and_return('content of font.css')
39
+
40
+ expect(css_for_doc(doc)).to eq("content of base.css\ncontent of font.css")
19
41
  end
42
+ end
20
43
 
21
- def expect_file(path, content='file content')
22
- allow(File).to receive(:file?).with(path).and_return(true)
23
- expect(File).to receive(:read).with(path).and_return(content)
44
+ context 'when HTML contains ignored links' do
45
+ let(:files) { ['ignore.css', 'data-premailer' => 'ignore'] }
46
+
47
+ it 'ignores links' do
48
+ expect(Premailer::Rails::CSSHelper).to_not receive(:css_for_url)
49
+ css_for_doc(doc)
24
50
  end
51
+ end
52
+ end
25
53
 
26
- describe '#css_for_doc' do
27
- let(:html) { Fixtures::HTML.with_css_links(*files) }
28
- let(:doc) { send(adapter, html) }
54
+ describe '#css_for_url' do
55
+ context 'when path is a url' do
56
+ it 'loads the CSS at the local path' do
57
+ expect_file('public/stylesheets/base.css')
29
58
 
30
- context 'when HTML contains linked CSS files' do
31
- let(:files) { %w[ stylesheets/base.css stylesheets/font.css ] }
59
+ css_for_url('http://example.com/stylesheets/base.css?test')
60
+ end
61
+ end
32
62
 
33
- it 'returns the content of both files concatenated' do
34
- allow(Premailer::Rails::CSSHelper).to \
35
- receive(:css_for_url)
36
- .with('http://example.com/stylesheets/base.css')
37
- .and_return('content of base.css')
38
- allow(Premailer::Rails::CSSHelper).to \
39
- receive(:css_for_url)
40
- .with('http://example.com/stylesheets/font.css')
41
- .and_return('content of font.css')
63
+ context 'when path is a relative url' do
64
+ it 'loads the CSS at the local path' do
65
+ expect_file('public/stylesheets/base.css')
66
+ css_for_url('/stylesheets/base.css?test')
67
+ end
68
+ end
42
69
 
43
- expect(css_for_doc(doc)).to eq("content of base.css\ncontent of font.css")
44
- end
45
- end
70
+ context 'when cache is enabled' do
71
+ before do
72
+ allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(true)
73
+ end
46
74
 
47
- context 'when HTML contains ignored links' do
48
- let(:files) { ['ignore.css', 'data-premailer' => 'ignore'] }
75
+ context 'when file is cached' do
76
+ it 'returns the cached value' do
77
+ Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'content of base.css'
49
78
 
50
- it 'ignores links' do
51
- expect(Premailer::Rails::CSSHelper).to_not receive(:css_for_url)
52
- css_for_doc(doc)
53
- end
79
+ expect(css_for_url('http://example.com/stylesheets/base.css')).to \
80
+ eq('content of base.css')
54
81
  end
55
82
  end
83
+ end
56
84
 
57
- describe '#css_for_url' do
58
- context 'when path is a url' do
59
- it 'loads the CSS at the local path' do
60
- expect_file('public/stylesheets/base.css')
85
+ context 'when cache is disabled' do
86
+ before do
87
+ allow(Premailer::Rails::CSSHelper).to receive(:cache_enabled?).and_return(false)
88
+ end
61
89
 
62
- css_for_url('http://example.com/stylesheets/base.css?test')
63
- end
64
- end
90
+ it 'does not return cached values' do
91
+ Premailer::Rails::CSSHelper.cache['http://example.com/stylesheets/base.css'] = 'cached content'
92
+ content = 'new content of base.css'
93
+ expect_file('public/stylesheets/base.css', content)
65
94
 
66
- context 'when path is a relative url' do
67
- it 'loads the CSS at the local path' do
68
- expect_file('public/stylesheets/base.css')
69
- css_for_url('/stylesheets/base.css?test')
70
- end
71
- end
95
+ expect(css_for_url('http://example.com/stylesheets/base.css')).to eq(content)
96
+ end
97
+ end
72
98
 
73
- context 'when file is cached' do
74
- it 'returns the cached value' do
75
- Premailer::Rails::CSSLoaders::CacheLoader.store(
76
- 'http://example.com/stylesheets/base.css',
77
- 'content of base.css'
78
- )
99
+ context 'when Rails asset pipeline is used' do
100
+ before do
101
+ allow(Rails.configuration)
102
+ .to receive(:assets).and_return(double(prefix: '/assets'))
103
+ allow(Rails.configuration)
104
+ .to receive(:relative_url_root).and_return(nil)
105
+ end
79
106
 
80
- expect(css_for_url('http://example.com/stylesheets/base.css')).to \
81
- eq('content of base.css')
82
- end
107
+ context 'and a precompiled file exists' do
108
+ it 'returns that file' do
109
+ path = '/assets/email-digest.css'
110
+ content = 'read from file'
111
+ expect_file("public#{path}", content)
112
+ expect(css_for_url(path)).to eq(content)
83
113
  end
114
+ end
84
115
 
85
- context 'when in development mode' do
86
- it 'does not return cached values' do
87
- Premailer::Rails::CSSLoaders::CacheLoader.store(
88
- 'http://example.com/stylesheets/base.css',
89
- 'cached content of base.css'
90
- )
91
- content = 'new content of base.css'
92
- expect_file('public/stylesheets/base.css', content)
93
- allow(Rails.env).to receive(:development?).and_return(true)
94
-
95
- expect(css_for_url('http://example.com/stylesheets/base.css')).to eq(content)
96
- end
97
- end
116
+ context "when find_sources raises Errno::ENOENT" do
117
+ let(:response) { 'content of base.css' }
118
+ let(:uri) { URI('http://example.com/assets/base.css') }
98
119
 
99
- context 'when Rails asset pipeline is used' do
100
- before do
101
- allow(Rails.configuration)
102
- .to receive(:assets).and_return(double(prefix: '/assets'))
103
- allow(Rails.configuration)
104
- .to receive(:relative_url_root).and_return(nil)
105
- end
120
+ it "falls back to Net::HTTP" do
121
+ expect(Rails.application.assets_manifest).to \
122
+ receive(:find_sources)
123
+ .with('base.css')
124
+ .and_raise(Errno::ENOENT)
106
125
 
107
- context 'and a precompiled file exists' do
108
- it 'returns that file' do
109
- path = '/assets/email-digest.css'
110
- content = 'read from file'
111
- expect_file("public#{path}", content)
112
- expect(css_for_url(path)).to eq(content)
113
- end
114
- end
126
+ allow(Net::HTTP).to \
127
+ receive(:get).with(uri).and_return(response)
128
+ expect(css_for_url('http://example.com/assets/base.css')).to \
129
+ eq(response)
130
+ end
131
+ end
115
132
 
116
- it 'returns the content of the file compiled by Rails' do
117
- expect(Rails.application.assets).to \
118
- receive(:find_asset)
119
- .with('base.css')
120
- .and_return(double(to_s: 'content of base.css'))
133
+ it 'returns the content of the file compiled by Rails' do
134
+ expect(Rails.application.assets_manifest).to \
135
+ receive(:find_sources)
136
+ .with('base.css')
137
+ .and_return(['content of base.css'])
121
138
 
122
- expect(css_for_url('http://example.com/assets/base.css')).to \
123
- eq('content of base.css')
124
- end
139
+ expect(css_for_url('http://example.com/assets/base.css')).to \
140
+ eq('content of base.css')
141
+ end
125
142
 
126
- it 'returns same file when path contains file fingerprint' do
127
- expect(Rails.application.assets).to \
128
- receive(:find_asset)
129
- .with('base.css')
130
- .and_return(double(to_s: 'content of base.css'))
143
+ it 'returns same file when path contains file fingerprint' do
144
+ expect(Rails.application.assets_manifest).to \
145
+ receive(:find_sources)
146
+ .with('base.css')
147
+ .and_return(['content of base.css'])
131
148
 
132
- expect(css_for_url(
133
- 'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css'
134
- )).to eq('content of base.css')
135
- end
149
+ expect(css_for_url(
150
+ 'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css'
151
+ )).to eq('content of base.css')
152
+ end
136
153
 
137
- context 'when asset can not be found' do
138
- let(:response) { 'content of base.css' }
139
- let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' }
140
- let(:url) { "http://assets.example.com#{path}" }
141
- let(:asset_host) { 'http://assets.example.com' }
154
+ context 'when asset can not be found' do
155
+ let(:response) { 'content of base.css' }
156
+ let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' }
157
+ let(:url) { "http://assets.example.com#{path}" }
158
+ let(:asset_host) { 'http://assets.example.com' }
142
159
 
143
- before do
144
- allow(Rails.application.assets).to \
145
- receive(:find_asset).and_return(nil)
160
+ before do
161
+ allow(Rails.application.assets_manifest).to \
162
+ receive(:find_sources).and_return([])
146
163
 
147
- config = double(asset_host: asset_host)
148
- allow(Rails.configuration).to \
149
- receive(:action_controller).and_return(config)
164
+ config = double(asset_host: asset_host)
165
+ allow(Rails.configuration).to \
166
+ receive(:action_controller).and_return(config)
150
167
 
151
- uri_satisfaction = satisfy { |uri| uri.to_s == url }
152
- allow(Net::HTTP).to \
153
- receive(:get).with(uri_satisfaction).and_return(response)
154
- end
168
+ allow(Net::HTTP).to \
169
+ receive(:get).with(URI(url)).and_return(response)
170
+ end
155
171
 
156
- it 'requests the file' do
157
- expect(css_for_url(url)).to eq('content of base.css')
158
- end
172
+ it 'requests the file' do
173
+ expect(css_for_url(url)).to eq('content of base.css')
174
+ end
159
175
 
160
- context 'when file url does not include the host' do
161
- it 'requests the file using the asset host as host' do
162
- expect(css_for_url(path)).to eq('content of base.css')
163
- end
176
+ context 'when file url does not include the host' do
177
+ it 'requests the file using the asset host as host' do
178
+ expect(css_for_url(path)).to eq('content of base.css')
179
+ end
164
180
 
165
- context 'and the asset host uses protocol relative scheme' do
166
- let(:asset_host) { '//assets.example.com' }
181
+ context 'and the asset host uses protocol relative scheme' do
182
+ let(:asset_host) { '//assets.example.com' }
167
183
 
168
- it 'requests the file using http as the scheme' do
169
- expect(css_for_url(path)).to eq('content of base.css')
170
- end
171
- end
184
+ it 'requests the file using http as the scheme' do
185
+ expect(css_for_url(path)).to eq('content of base.css')
172
186
  end
173
187
  end
174
188
  end
189
+ end
190
+ end
175
191
 
176
- context 'when static stylesheets are used' do
177
- it 'returns the content of the static file' do
178
- content = 'content of base.css'
179
- expect_file('public/stylesheets/base.css', content)
180
- loaded_content = css_for_url('http://example.com/stylesheets/base.css')
181
- expect(loaded_content).to eq(content)
182
- end
183
- end
192
+ context 'when static stylesheets are used' do
193
+ it 'returns the content of the static file' do
194
+ content = 'content of base.css'
195
+ expect_file('public/stylesheets/base.css', content)
196
+ loaded_content = css_for_url('http://example.com/stylesheets/base.css')
197
+ expect(loaded_content).to eq(content)
184
198
  end
185
199
  end
186
200
  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
@@ -91,7 +91,7 @@ describe Premailer::Rails::Hook do
91
91
 
92
92
  it 'does not replace any message part' do
93
93
  expect { run_hook(message) }.to_not \
94
- change { message.all_parts.map(&:content_type) }
94
+ change { message.all_parts.map(&:content_type).sort }
95
95
  end
96
96
  end
97
97
 
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../javascripts .js
3
+ //= link_directory ../stylesheets .css
@@ -0,0 +1,3 @@
1
+ p {
2
+ font-size: 12px;
3
+ }
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
@@ -0,0 +1,6 @@
1
+ class WelcomeMailer < ApplicationMailer
2
+ def welcome_email(greeting)
3
+ @greeting = greeting
4
+ mail to: "example@example.com"
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <link rel="stylesheet" href="/assets/application.css"/>
6
+ </head>
7
+
8
+ <body>
9
+ <%= yield %>
10
+ </body>
11
+ </html>
@@ -0,0 +1 @@
1
+ <p>Hello <%= @greeting %></p>
@@ -0,0 +1,5 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require_relative 'config/environment'
4
+
5
+ run Rails.application
@@ -0,0 +1,13 @@
1
+ require_relative 'boot'
2
+
3
+ require "action_mailer/railtie"
4
+ require "action_view/railtie"
5
+ require "sprockets/railtie"
6
+ require "rails/test_unit/railtie"
7
+
8
+ Bundler.require(*Rails.groups)
9
+
10
+ module Dummy
11
+ class Application < Rails::Application
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3
+
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
@@ -0,0 +1,2 @@
1
+ require_relative 'application'
2
+ Rails.application.initialize!
@@ -0,0 +1,10 @@
1
+ Rails.application.configure do
2
+ config.cache_classes = true
3
+ config.eager_load = false
4
+ config.consider_all_requests_local = true
5
+ config.action_controller.perform_caching = false
6
+ config.action_dispatch.show_exceptions = false
7
+ config.action_controller.allow_forgery_protection = false
8
+ config.action_mailer.delivery_method = :test
9
+ config.active_support.deprecation = :stderr
10
+ end
@@ -0,0 +1 @@
1
+ Rails.application.config.assets.version = '1.0'
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ root 'application#main'
3
+ end
data/spec/spec_helper.rb CHANGED
@@ -11,16 +11,11 @@ if RUBY_ENGINE == 'ruby'
11
11
  end
12
12
  end
13
13
 
14
- # Temporary fix for missing require. See
15
- # https://github.com/rails/rails/pull/28835
16
- require 'active_support/rescuable'
14
+ # Configure Rails Environment
15
+ ENV["RAILS_ENV"] = "test"
16
+ require File.expand_path("../../spec/rails_app/config/environment.rb", __FILE__)
17
17
 
18
- require 'premailer/rails'
19
-
20
- require 'support/stubs/action_mailer'
21
- require 'support/stubs/rails'
22
18
  require 'support/fixtures/message'
23
19
  require 'support/fixtures/html'
24
20
 
25
- require 'hpricot' unless RUBY_PLATFORM == 'java'
26
21
  require 'nokogiri'
@@ -18,7 +18,7 @@ describe Premailer::Rails::CSSLoaders::NetworkLoader do
18
18
 
19
19
  context 'with a protocol relative URL' do
20
20
  let(:url) { '//example.com/test.css' }
21
- it { is_expected.to eq(URI("http://#{url}")) }
21
+ it { is_expected.to eq(URI("http:#{url}")) }
22
22
  end
23
23
 
24
24
  context 'with a file path' do
@@ -1,51 +1,43 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Premailer::Rails::CustomizedPremailer do
4
- [ :nokogiri, :hpricot ].each do |adapter|
5
- next if adapter == :hpricot and RUBY_PLATFORM == 'java'
4
+ describe '#to_plain_text' do
5
+ it 'includes the text from the HTML part' do
6
+ premailer =
7
+ Premailer::Rails::CustomizedPremailer
8
+ .new(Fixtures::Message::HTML_PART)
9
+ expect(premailer.to_plain_text.gsub(/\s/, ' ').strip).to \
10
+ eq(Fixtures::Message::TEXT_PART.gsub(/\s/, ' ').strip)
11
+ end
12
+ end
6
13
 
7
- context "when adapter is #{adapter}" do
8
- before { allow(Premailer::Adapter).to receive(:use).and_return(adapter) }
14
+ describe '#to_inline_css' do
15
+ let(:regex) { %r{<p style=("|')color: ?red;?\1>} }
9
16
 
10
- describe '#to_plain_text' do
11
- it 'includes the text from the HTML part' do
12
- premailer =
13
- Premailer::Rails::CustomizedPremailer
14
- .new(Fixtures::Message::HTML_PART)
15
- expect(premailer.to_plain_text.gsub(/\s/, ' ').strip).to \
16
- eq(Fixtures::Message::TEXT_PART.gsub(/\s/, ' ').strip)
17
- end
17
+ context 'when inline CSS block present' do
18
+ it 'returns the HTML with the CSS inlined' do
19
+ allow(Premailer::Rails::CSSHelper).to \
20
+ receive(:css_for_doc).and_return('p { color: red; }')
21
+ html = Fixtures::Message::HTML_PART
22
+ premailer = Premailer::Rails::CustomizedPremailer.new(html)
23
+ expect(premailer.to_inline_css).to match(regex)
18
24
  end
25
+ end
19
26
 
20
- describe '#to_inline_css' do
21
- let(:regex) { %r{<p style=("|')color: ?red;?\1>} }
22
-
23
- context 'when inline CSS block present' do
24
- it 'returns the HTML with the CSS inlined' do
25
- allow(Premailer::Rails::CSSHelper).to \
26
- receive(:css_for_doc).and_return('p { color: red; }')
27
- html = Fixtures::Message::HTML_PART
28
- premailer = Premailer::Rails::CustomizedPremailer.new(html)
29
- expect(premailer.to_inline_css).to match(regex)
30
- end
31
- end
32
-
33
- context 'when CSS is loaded externally' do
34
- it 'returns the HTML with the CSS inlined' do
35
- html = Fixtures::Message::HTML_PART_WITH_CSS
36
- premailer = Premailer::Rails::CustomizedPremailer.new(html)
37
- expect(premailer.to_inline_css).to match(regex)
38
- end
39
- end
27
+ context 'when CSS is loaded externally' do
28
+ it 'returns the HTML with the CSS inlined' do
29
+ html = Fixtures::Message::HTML_PART_WITH_CSS
30
+ premailer = Premailer::Rails::CustomizedPremailer.new(html)
31
+ expect(premailer.to_inline_css).to match(regex)
32
+ end
33
+ end
40
34
 
41
- context 'when HTML contains unicode' do
42
- it 'does not mess those up' do
43
- html = Fixtures::Message::HTML_PART_WITH_UNICODE
44
- premailer = Premailer::Rails::CustomizedPremailer.new(html)
45
- expect(premailer.to_inline_css).to \
46
- include(Fixtures::Message::UNICODE_STRING)
47
- end
48
- end
35
+ context 'when HTML contains unicode' do
36
+ it 'does not mess those up' do
37
+ html = Fixtures::Message::HTML_PART_WITH_UNICODE
38
+ premailer = Premailer::Rails::CustomizedPremailer.new(html)
39
+ expect(premailer.to_inline_css).to \
40
+ include(Fixtures::Message::UNICODE_STRING)
49
41
  end
50
42
  end
51
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: premailer-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.7
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philipe Fatio
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-26 00:00:00.000000000 Z
11
+ date: 2017-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: premailer
@@ -78,20 +78,6 @@ dependencies:
78
78
  - - ">="
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
- - !ruby/object:Gem::Dependency
82
- name: hpricot
83
- requirement: !ruby/object:Gem::Requirement
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- version: '0'
88
- type: :development
89
- prerelease: false
90
- version_requirements: !ruby/object:Gem::Requirement
91
- requirements:
92
- - - ">="
93
- - !ruby/object:Gem::Version
94
- version: '0'
95
81
  - !ruby/object:Gem::Dependency
96
82
  name: coveralls
97
83
  requirement: !ruby/object:Gem::Requirement
@@ -149,7 +135,6 @@ files:
149
135
  - lib/premailer/rails/css_helper.rb
150
136
  - lib/premailer/rails/css_loaders.rb
151
137
  - lib/premailer/rails/css_loaders/asset_pipeline_loader.rb
152
- - lib/premailer/rails/css_loaders/cache_loader.rb
153
138
  - lib/premailer/rails/css_loaders/file_system_loader.rb
154
139
  - lib/premailer/rails/css_loaders/network_loader.rb
155
140
  - lib/premailer/rails/customized_premailer.rb
@@ -158,13 +143,24 @@ files:
158
143
  - lib/premailer/rails/version.rb
159
144
  - premailer-rails.gemspec
160
145
  - spec/integration/css_helper_spec.rb
161
- - spec/integration/hook_registration_spec.rb
146
+ - spec/integration/delivery_spec.rb
162
147
  - spec/integration/hook_spec.rb
148
+ - spec/rails_app/app/assets/config/manifest.js
149
+ - spec/rails_app/app/assets/stylesheets/application.css
150
+ - spec/rails_app/app/mailers/application_mailer.rb
151
+ - spec/rails_app/app/mailers/welcome_mailer.rb
152
+ - spec/rails_app/app/views/layouts/mailer.html.erb
153
+ - spec/rails_app/app/views/welcome_mailer/welcome_email.html.erb
154
+ - spec/rails_app/config.ru
155
+ - spec/rails_app/config/application.rb
156
+ - spec/rails_app/config/boot.rb
157
+ - spec/rails_app/config/environment.rb
158
+ - spec/rails_app/config/environments/test.rb
159
+ - spec/rails_app/config/initializers/assets.rb
160
+ - spec/rails_app/config/routes.rb
163
161
  - spec/spec_helper.rb
164
162
  - spec/support/fixtures/html.rb
165
163
  - spec/support/fixtures/message.rb
166
- - spec/support/stubs/action_mailer.rb
167
- - spec/support/stubs/rails.rb
168
164
  - spec/unit/css_loaders/asset_pipeline_loader_spec.rb
169
165
  - spec/unit/css_loaders/file_system_loader_spec.rb
170
166
  - spec/unit/css_loaders/network_loader_spec.rb
@@ -190,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
190
186
  version: '0'
191
187
  requirements: []
192
188
  rubyforge_project:
193
- rubygems_version: 2.6.11
189
+ rubygems_version: 2.7.2
194
190
  signing_key:
195
191
  specification_version: 4
196
192
  summary: Easily create styled HTML emails in Rails.
@@ -213,13 +209,24 @@ test_files:
213
209
  - example/config/secrets.yml
214
210
  - example/test/mailers/previews/example_mailer_preview.rb
215
211
  - spec/integration/css_helper_spec.rb
216
- - spec/integration/hook_registration_spec.rb
212
+ - spec/integration/delivery_spec.rb
217
213
  - spec/integration/hook_spec.rb
214
+ - spec/rails_app/app/assets/config/manifest.js
215
+ - spec/rails_app/app/assets/stylesheets/application.css
216
+ - spec/rails_app/app/mailers/application_mailer.rb
217
+ - spec/rails_app/app/mailers/welcome_mailer.rb
218
+ - spec/rails_app/app/views/layouts/mailer.html.erb
219
+ - spec/rails_app/app/views/welcome_mailer/welcome_email.html.erb
220
+ - spec/rails_app/config.ru
221
+ - spec/rails_app/config/application.rb
222
+ - spec/rails_app/config/boot.rb
223
+ - spec/rails_app/config/environment.rb
224
+ - spec/rails_app/config/environments/test.rb
225
+ - spec/rails_app/config/initializers/assets.rb
226
+ - spec/rails_app/config/routes.rb
218
227
  - spec/spec_helper.rb
219
228
  - spec/support/fixtures/html.rb
220
229
  - spec/support/fixtures/message.rb
221
- - spec/support/stubs/action_mailer.rb
222
- - spec/support/stubs/rails.rb
223
230
  - spec/unit/css_loaders/asset_pipeline_loader_spec.rb
224
231
  - spec/unit/css_loaders/file_system_loader_spec.rb
225
232
  - spec/unit/css_loaders/network_loader_spec.rb
@@ -1,29 +0,0 @@
1
- class Premailer
2
- module Rails
3
- module CSSLoaders
4
- module CacheLoader
5
- extend self
6
-
7
- @cache = {}
8
-
9
- def load(url)
10
- @cache[url] unless development_env?
11
- end
12
-
13
- def store(url, content)
14
- @cache[url] ||= content unless development_env?
15
- end
16
-
17
- def clear!
18
- @cache = {}
19
- end
20
-
21
- def development_env?
22
- defined?(::Rails) &&
23
- ::Rails.respond_to?(:env) &&
24
- ::Rails.env.development?
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,11 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe 'ActionMailer::Base.register_interceptor' do
4
- it 'registers interceptors' do
5
- expect(ActionMailer::Base).to \
6
- receive(:register_interceptor).with(Premailer::Rails::Hook)
7
- expect(ActionMailer::Base).to \
8
- receive(:register_preview_interceptor).with(Premailer::Rails::Hook)
9
- load 'premailer/rails.rb'
10
- end
11
- end
@@ -1,5 +0,0 @@
1
- module ActionMailer
2
- class Base
3
- def self.register_interceptor(x); end
4
- end
5
- end
@@ -1,51 +0,0 @@
1
- module Rails
2
- extend self
3
-
4
- module Configuration
5
- extend self
6
- end
7
-
8
- module Env
9
- extend self
10
-
11
- def development?
12
- false
13
- end
14
- end
15
-
16
- module Application
17
- extend self
18
-
19
- module Assets
20
- extend self
21
- end
22
-
23
- def assets
24
- Assets
25
- end
26
- end
27
-
28
- class Railtie
29
- class Configuration
30
- def after_initialize
31
- yield
32
- end
33
- end
34
-
35
- def self.config
36
- Configuration.new
37
- end
38
- end
39
-
40
- def env
41
- Env
42
- end
43
-
44
- def configuration
45
- Configuration
46
- end
47
-
48
- def application
49
- Application
50
- end
51
- end