premailer-rails-1.9.7/0000755000004100000410000000000013126443374014657 5ustar www-datawww-datapremailer-rails-1.9.7/Rakefile0000644000004100000410000000021113126443374016316 0ustar www-datawww-datarequire 'bundler' Bundler::GemHelper.install_tasks require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task default: :spec premailer-rails-1.9.7/Gemfile0000644000004100000410000000060613126443374016154 0ustar www-datawww-datasource 'https://rubygems.org' gemspec action_mailer_version = ENV.fetch('ACTION_MAILER_VERSION', '5') if action_mailer_version == 'master' git 'git://github.com/rails/rails.git' do gem 'actionmailer' end else gem 'actionmailer', "~> #{action_mailer_version}" end platforms :rbx do gem 'rubysl' gem 'racc' end gem 'tins', '< 1.7' if RUBY_VERSION.split('.').first.to_i < 2 premailer-rails-1.9.7/.coveralls.yml0000644000004100000410000000003013126443374017443 0ustar www-datawww-dataservice_name: travis-ci premailer-rails-1.9.7/.rspec0000644000004100000410000000002513126443374015771 0ustar www-datawww-data--format doc --color premailer-rails-1.9.7/premailer-rails.gemspec0000644000004100000410000000251013126443374021312 0ustar www-datawww-data# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) require "premailer/rails/version" Gem::Specification.new do |s| s.name = "premailer-rails" s.version = Premailer::Rails::VERSION s.platform = Gem::Platform::RUBY s.license = 'MIT' s.authors = ["Philipe Fatio"] s.email = ["philipe.fatio@gmail.com"] s.homepage = "https://github.com/fphilipe/premailer-rails" s.summary = %q{Easily create styled HTML emails in Rails.} s.description = %q{This gem brings you the power of the premailer gem to Rails without any configuration needs. Create HTML emails, include a CSS file as you do in a normal HTML document and premailer will inline the included CSS.} s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {example,spec}/*`.split("\n") s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } s.require_paths = ["lib"] s.add_dependency 'premailer', '~> 1.7', '>= 1.7.9' s.add_dependency 'actionmailer', '>= 3', '< 6' s.add_development_dependency 'rspec', '~> 3.3' s.add_development_dependency 'nokogiri' s.add_development_dependency 'hpricot' unless RUBY_PLATFORM == 'java' s.add_development_dependency 'coveralls' if RUBY_ENGINE == 'ruby' end premailer-rails-1.9.7/spec/0000755000004100000410000000000013126443374015611 5ustar www-datawww-datapremailer-rails-1.9.7/spec/spec_helper.rb0000644000004100000410000000107213126443374020427 0ustar www-datawww-dataif RUBY_ENGINE == 'ruby' if ENV['CI'] require 'coveralls' Coveralls::Output.silent = true Coveralls.wear! do add_filter 'spec/' end else require 'simplecov' SimpleCov.start end end # Temporary fix for missing require. See # https://github.com/rails/rails/pull/28835 require 'active_support/rescuable' require 'premailer/rails' require 'support/stubs/action_mailer' require 'support/stubs/rails' require 'support/fixtures/message' require 'support/fixtures/html' require 'hpricot' unless RUBY_PLATFORM == 'java' require 'nokogiri' premailer-rails-1.9.7/spec/integration/0000755000004100000410000000000013126443374020134 5ustar www-datawww-datapremailer-rails-1.9.7/spec/integration/hook_spec.rb0000644000004100000410000001036113126443374022434 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails::Hook do def run_hook(message) Premailer::Rails::Hook.perform(message) end def body_content(message) Nokogiri::HTML(message.html_string).at('body').content end class Mail::Message def html_string (html_part || self).body.to_s end end let(:message) { Fixtures::Message.with_parts(:html) } let(:processed_message) { run_hook(message) } describe '.delivering_email' do it 'is an alias to .perform' do method = described_class.method(:delivering_email) expected_method = described_class.method(:perform) expect(method).to eq expected_method end end describe '.previewing_email' do it 'is an alias to .perform' do method = described_class.method(:previewing_email) expected_method = described_class.method(:perform) expect(method).to eq expected_method end end it 'inlines the CSS' do expect { run_hook(message) }.to \ change { message.html_string.include?("

'ignore'] } it 'ignores links' do expect(Premailer::Rails::CSSHelper).to_not receive(:css_for_url) css_for_doc(doc) end end end describe '#css_for_url' do context 'when path is a url' do it 'loads the CSS at the local path' do expect_file('public/stylesheets/base.css') css_for_url('http://example.com/stylesheets/base.css?test') end end context 'when path is a relative url' do it 'loads the CSS at the local path' do expect_file('public/stylesheets/base.css') css_for_url('/stylesheets/base.css?test') end end context 'when file is cached' do it 'returns the cached value' do Premailer::Rails::CSSLoaders::CacheLoader.store( 'http://example.com/stylesheets/base.css', 'content of base.css' ) expect(css_for_url('http://example.com/stylesheets/base.css')).to \ eq('content of base.css') end end context 'when in development mode' do it 'does not return cached values' do Premailer::Rails::CSSLoaders::CacheLoader.store( 'http://example.com/stylesheets/base.css', 'cached content of base.css' ) content = 'new content of base.css' expect_file('public/stylesheets/base.css', content) allow(Rails.env).to receive(:development?).and_return(true) expect(css_for_url('http://example.com/stylesheets/base.css')).to eq(content) end end context 'when Rails asset pipeline is used' do before do allow(Rails.configuration) .to receive(:assets).and_return(double(prefix: '/assets')) allow(Rails.configuration) .to receive(:relative_url_root).and_return(nil) end context 'and a precompiled file exists' do it 'returns that file' do path = '/assets/email-digest.css' content = 'read from file' expect_file("public#{path}", content) expect(css_for_url(path)).to eq(content) end end it 'returns the content of the file compiled by Rails' do expect(Rails.application.assets).to \ receive(:find_asset) .with('base.css') .and_return(double(to_s: 'content of base.css')) expect(css_for_url('http://example.com/assets/base.css')).to \ eq('content of base.css') end it 'returns same file when path contains file fingerprint' do expect(Rails.application.assets).to \ receive(:find_asset) .with('base.css') .and_return(double(to_s: 'content of base.css')) expect(css_for_url( 'http://example.com/assets/base-089e35bd5d84297b8d31ad552e433275.css' )).to eq('content of base.css') end context 'when asset can not be found' do let(:response) { 'content of base.css' } let(:path) { '/assets/base-089e35bd5d84297b8d31ad552e433275.css' } let(:url) { "http://assets.example.com#{path}" } let(:asset_host) { 'http://assets.example.com' } before do allow(Rails.application.assets).to \ receive(:find_asset).and_return(nil) config = double(asset_host: asset_host) allow(Rails.configuration).to \ receive(:action_controller).and_return(config) uri_satisfaction = satisfy { |uri| uri.to_s == url } allow(Net::HTTP).to \ receive(:get).with(uri_satisfaction).and_return(response) end it 'requests the file' do expect(css_for_url(url)).to eq('content of base.css') end context 'when file url does not include the host' do it 'requests the file using the asset host as host' do expect(css_for_url(path)).to eq('content of base.css') end context 'and the asset host uses protocol relative scheme' do let(:asset_host) { '//assets.example.com' } it 'requests the file using http as the scheme' do expect(css_for_url(path)).to eq('content of base.css') end end end end end context 'when static stylesheets are used' do it 'returns the content of the static file' do content = 'content of base.css' expect_file('public/stylesheets/base.css', content) loaded_content = css_for_url('http://example.com/stylesheets/base.css') expect(loaded_content).to eq(content) end end end end end end premailer-rails-1.9.7/spec/unit/0000755000004100000410000000000013126443374016570 5ustar www-datawww-datapremailer-rails-1.9.7/spec/unit/css_loaders/0000755000004100000410000000000013126443374021071 5ustar www-datawww-datapremailer-rails-1.9.7/spec/unit/css_loaders/file_system_loader_spec.rb0000644000004100000410000000231713126443374026304 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails::CSSLoaders::FileSystemLoader do before do allow(Rails.configuration) .to receive(:assets).and_return(double(prefix: '/assets')) allow(Rails) .to receive(:root).and_return(Pathname.new('/rails_root')) end describe '#file_name' do subject { described_class.file_name(asset) } let(:relative_url_root) { nil } before do config = double(relative_url_root: relative_url_root) allow(Rails).to receive(:configuration).and_return(config) end context 'when relative_url_root is not set' do let(:asset) { '/assets/application.css' } it { is_expected.to eq(File.join(Rails.root, 'public/assets/application.css')) } end context 'when relative_url_root is set' do let(:relative_url_root) { '/foo' } let(:asset) { '/foo/assets/application.css' } it { is_expected.to eq(File.join(Rails.root, 'public/assets/application.css')) } end context 'when relative_url_root has a trailing slash' do let(:relative_url_root) { '/foo/' } let(:asset) { '/foo/assets/application.css' } it { is_expected.to eq(File.join(Rails.root, 'public/assets/application.css')) } end end end premailer-rails-1.9.7/spec/unit/css_loaders/asset_pipeline_loader_spec.rb0000644000004100000410000000277213126443374026772 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails::CSSLoaders::AssetPipelineLoader do before do allow(Rails.configuration) .to receive(:assets).and_return(double(prefix: '/assets')) allow(Rails.configuration).to receive(:relative_url_root).and_return(nil) end describe ".file_name" do subject do Premailer::Rails::CSSLoaders::AssetPipelineLoader.file_name(asset) end context "when asset file path contains prefix" do let(:asset) { '/assets/application.css' } it { is_expected.to eq('application.css') } end context "when asset file path contains prefix and relative_url_root is set" do before do allow(Rails.configuration) .to receive(:relative_url_root).and_return('/foo') end let(:asset) { '/foo/assets/application.css' } it { is_expected.to eq('application.css') } end context "when asset file path contains 32 chars fingerprint" do let(:asset) { 'application-6776f581a4329e299531e1d52aa59832.css' } it { is_expected.to eq('application.css') } end context "when asset file path contains 64 chars fingerprint" do let(:asset) { 'application-02275ccb3fd0c11615bbfb11c99ea123ca2287e75045fe7b72cefafb880dad2b.css' } it { is_expected.to eq('application.css') } end context "when asset file page contains numbers, but not a fingerprint" do let(:asset) { 'test/20130708152545-foo-bar.css' } it { is_expected.to eq("test/20130708152545-foo-bar.css") } end end end premailer-rails-1.9.7/spec/unit/css_loaders/network_loader_spec.rb0000644000004100000410000000336613126443374025457 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails::CSSLoaders::NetworkLoader do describe '#uri_for_url' do subject { described_class.uri_for_url(url) } let(:asset_host) { nil } before do action_controller = double(asset_host: asset_host) config = double(action_controller: action_controller) allow(Rails).to receive(:configuration).and_return(config) end context 'with a valid URL' do let(:url) { 'http://example.com/test.css' } it { is_expected.to eq(URI(url)) } end context 'with a protocol relative URL' do let(:url) { '//example.com/test.css' } it { is_expected.to eq(URI("http://#{url}")) } end context 'with a file path' do let(:url) { '/assets/foo.css' } context 'and a domain as asset host' do let(:asset_host) { 'example.com' } it { is_expected.to eq(URI("http://example.com#{url}")) } end context 'and a URL as asset host' do let(:asset_host) { 'https://example.com' } it { is_expected.to eq(URI("https://example.com/assets/foo.css")) } end context 'and a protocol relative URL as asset host' do let(:asset_host) { '//example.com' } it { is_expected.to eq(URI("http://example.com/assets/foo.css")) } end context 'and a callable object as asset host' do let(:asset_host) { double } it 'calls #call with the asset path as argument' do expect(asset_host).to receive(:call).with(url).and_return( 'http://example.com') expect(subject).to eq(URI('http://example.com/assets/foo.css')) end end context 'without an asset host' do let(:asset_host) { nil } it { is_expected.not_to be } end end end end premailer-rails-1.9.7/spec/unit/customized_premailer_spec.rb0000644000004100000410000000513513126443374024361 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails::CustomizedPremailer do [ :nokogiri, :hpricot ].each do |adapter| next if adapter == :hpricot and RUBY_PLATFORM == 'java' context "when adapter is #{adapter}" do before { allow(Premailer::Adapter).to receive(:use).and_return(adapter) } describe '#to_plain_text' do it 'includes the text from the HTML part' do premailer = Premailer::Rails::CustomizedPremailer .new(Fixtures::Message::HTML_PART) expect(premailer.to_plain_text.gsub(/\s/, ' ').strip).to \ eq(Fixtures::Message::TEXT_PART.gsub(/\s/, ' ').strip) end end describe '#to_inline_css' do let(:regex) { %r{

} } context 'when inline CSS block present' do it 'returns the HTML with the CSS inlined' do allow(Premailer::Rails::CSSHelper).to \ receive(:css_for_doc).and_return('p { color: red; }') html = Fixtures::Message::HTML_PART premailer = Premailer::Rails::CustomizedPremailer.new(html) expect(premailer.to_inline_css).to match(regex) end end context 'when CSS is loaded externally' do it 'returns the HTML with the CSS inlined' do html = Fixtures::Message::HTML_PART_WITH_CSS premailer = Premailer::Rails::CustomizedPremailer.new(html) expect(premailer.to_inline_css).to match(regex) end end context 'when HTML contains unicode' do it 'does not mess those up' do html = Fixtures::Message::HTML_PART_WITH_UNICODE premailer = Premailer::Rails::CustomizedPremailer.new(html) expect(premailer.to_inline_css).to \ include(Fixtures::Message::UNICODE_STRING) end end end end end describe '.new' do it 'extracts the CSS' do expect(Premailer::Rails::CSSHelper).to receive(:css_for_doc) Premailer::Rails::CustomizedPremailer.new('some html') end it 'passes on the configs' do Premailer::Rails.config.merge!(foo: :bar) premailer = Premailer::Rails::CustomizedPremailer.new('some html') expect(premailer.instance_variable_get(:'@options')[:foo]).to eq(:bar) end it 'does not allow to override with_html_string' do Premailer::Rails.config.merge!(with_html_string: false) premailer = Premailer::Rails::CustomizedPremailer.new('some html') options = premailer.instance_variable_get(:'@options') expect(options[:with_html_string]).to eq(true) end end end premailer-rails-1.9.7/spec/unit/premailer_rails_spec.rb0000644000004100000410000000070213126443374023300 0ustar www-datawww-datarequire 'spec_helper' describe Premailer::Rails do describe '#config' do subject { Premailer::Rails.config } context 'when set' do around do |example| begin default_config = described_class.config described_class.config = { foo: :bar } example.run ensure described_class.config = default_config end end it { is_expected.to eq(foo: :bar) } end end end premailer-rails-1.9.7/spec/support/0000755000004100000410000000000013126443374017325 5ustar www-datawww-datapremailer-rails-1.9.7/spec/support/stubs/0000755000004100000410000000000013126443374020465 5ustar www-datawww-datapremailer-rails-1.9.7/spec/support/stubs/action_mailer.rb0000644000004100000410000000012513126443374023616 0ustar www-datawww-datamodule ActionMailer class Base def self.register_interceptor(x); end end end premailer-rails-1.9.7/spec/support/stubs/rails.rb0000644000004100000410000000104713126443374022126 0ustar www-datawww-datamodule Rails extend self module Configuration extend self end module Env extend self def development? false end end module Application extend self module Assets extend self end def assets Assets end end class Railtie class Configuration def after_initialize yield end end def self.config Configuration.new end end def env Env end def configuration Configuration end def application Application end end premailer-rails-1.9.7/spec/support/fixtures/0000755000004100000410000000000013126443374021176 5ustar www-datawww-datapremailer-rails-1.9.7/spec/support/fixtures/message.rb0000644000004100000410000000641613126443374023156 0ustar www-datawww-data# coding: utf-8 require 'mail' module Fixtures module Message extend self HTML_PART = <<-HTML

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

HTML UNICODE_STRING = '٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃).' HTML_PART_WITH_UNICODE = <<-HTML

#{UNICODE_STRING}

HTML HTML_PART_WITH_CSS = <<-HTML

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

HTML TEXT_PART = <<-TEXT Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. TEXT def with_parts(*part_types) if part_types.count == 1 and [:html, :text].include?(part_types.first) return with_body(part_types.first) end message = base_message content_part = message if part_types.include?(:html) and part_types.include?(:text) content_part = Mail::Part.new(content_type: 'multipart/alternative') message.add_part(content_part) end if part_types.include? :html html_part = Mail::Part.new do body HTML_PART_WITH_CSS content_type 'text/html; charset=UTF-8' end content_part.html_part = html_part end if part_types.include? :text text_part = Mail::Part.new do body TEXT_PART content_type 'text/plain; charset=UTF-8' end content_part.text_part = text_part end if part_types.include? :attachment message.add_file(filename: 'foo.png', content: 'foobar') end message.ready_to_send! message end def with_body(body_type) message = base_message case body_type when :html message.body = HTML_PART_WITH_CSS message.content_type 'text/html; charset=UTF-8' when :text message.body = TEXT_PART message.content_type 'text/plain; charset=UTF-8' end message.ready_to_send! message end def latin_message base_message.tap do |message| message.body = HTML_PART message.content_type 'text/html; charset=UTF-8' message.ready_to_send! end end def non_latin_message base_message.tap do |message| message.body = HTML_PART_WITH_UNICODE message.content_type 'text/html; charset=UTF-8' message.ready_to_send! end end private def base_message Mail.new do to 'some@email.com' subject 'testing premailer-rails' end end end end premailer-rails-1.9.7/spec/support/fixtures/html.rb0000644000004100000410000000154113126443374022470 0ustar www-datawww-datamodule Fixtures module HTML extend self TEMPLATE = <<-HTML %s

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

HTML LINK = "\n" def with_css_links(*files) opts = files.last.is_a?(Hash) ? files.pop : {} links = [] files.each do |file| attrs = { href: "http://example.com/#{file}" }.merge(opts) links << LINK % hash_to_attributes(attrs) end TEMPLATE % links.join end def hash_to_attributes(attrs) attrs.map { |attr, value| "#{attr}='#{value}'" }.join(' ') end end end premailer-rails-1.9.7/.travis.yml0000644000004100000410000000042713126443374016773 0ustar www-datawww-datasudo: false language: ruby cache: bundler script: bundle exec rspec rvm: - 2.4.1 env: matrix: - ACTION_MAILER_VERSION=4 - ACTION_MAILER_VERSION=5 - ACTION_MAILER_VERSION=master matrix: fast_finish: true allow_failures: - env: ACTION_MAILER_VERSION=master premailer-rails-1.9.7/lib/0000755000004100000410000000000013126443374015425 5ustar www-datawww-datapremailer-rails-1.9.7/lib/premailer/0000755000004100000410000000000013126443374017405 5ustar www-datawww-datapremailer-rails-1.9.7/lib/premailer/rails/0000755000004100000410000000000013126443374020517 5ustar www-datawww-datapremailer-rails-1.9.7/lib/premailer/rails/css_loaders.rb0000644000004100000410000000035413126443374023347 0ustar www-datawww-datarequire 'uri' require 'premailer/rails/css_loaders/cache_loader' require 'premailer/rails/css_loaders/file_system_loader' require 'premailer/rails/css_loaders/asset_pipeline_loader' require 'premailer/rails/css_loaders/network_loader' premailer-rails-1.9.7/lib/premailer/rails/css_loaders/0000755000004100000410000000000013126443374023020 5ustar www-datawww-datapremailer-rails-1.9.7/lib/premailer/rails/css_loaders/asset_pipeline_loader.rb0000644000004100000410000000156213126443374027703 0ustar www-datawww-dataclass Premailer module Rails module CSSLoaders module AssetPipelineLoader extend self def load(url) if asset_pipeline_present? file = file_name(url) asset = ::Rails.application.assets.find_asset(file) asset.to_s if asset end end def asset_pipeline_present? defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application.respond_to?(:assets) && ::Rails.application.assets end def file_name(url) prefix = [ ::Rails.configuration.relative_url_root, ::Rails.configuration.assets.prefix, '/' ].join URI(url).path .sub(/\A#{prefix}/, '') .sub(/-(\h{32}|\h{64})\.css\z/, '.css') end end end end end premailer-rails-1.9.7/lib/premailer/rails/css_loaders/file_system_loader.rb0000644000004100000410000000166013126443374027221 0ustar www-datawww-dataclass Premailer module Rails module CSSLoaders module FileSystemLoader extend self def load(url) file = file_name(url) File.read(file) if File.file?(file) end def file_name(url) path = URI(url).path if relative_url_root path = path.sub(/\A#{relative_url_root.chomp('/')}/, '') end asset_filename(path) end def asset_filename(filename) if defined?(::Rails) && ::Rails.respond_to?(:root) File.join(::Rails.root, 'public', filename) else File.join('public', filename) end end def relative_url_root defined?(::Rails) && ::Rails.respond_to?(:configuration) && ::Rails.configuration.respond_to?(:relative_url_root) && ::Rails.configuration.relative_url_root end end end end end premailer-rails-1.9.7/lib/premailer/rails/css_loaders/network_loader.rb0000644000004100000410000000202213126443374026360 0ustar www-datawww-dataclass Premailer module Rails module CSSLoaders module NetworkLoader extend self def load(url) uri = uri_for_url(url) Net::HTTP.get(uri) if uri end def uri_for_url(url) uri = URI(url) if uri.host.present? return uri if uri.scheme.present? URI("http://#{uri.to_s}") elsif asset_host_present? scheme, host = asset_host(url).split(%r{:?//}) scheme, host = host, scheme if host.nil? scheme = 'http' if scheme.blank? path = url URI(File.join("#{scheme}://#{host}", path)) end end def asset_host_present? ::Rails.respond_to?(:configuration) && ::Rails.configuration.action_controller.asset_host.present? end def asset_host(url) config = ::Rails.configuration.action_controller.asset_host config.respond_to?(:call) ? config.call(url) : config end end end end end premailer-rails-1.9.7/lib/premailer/rails/css_loaders/cache_loader.rb0000644000004100000410000000103013126443374025730 0ustar www-datawww-dataclass Premailer module Rails module CSSLoaders module CacheLoader extend self @cache = {} def load(url) @cache[url] unless development_env? end def store(url, content) @cache[url] ||= content unless development_env? end def clear! @cache = {} end def development_env? defined?(::Rails) && ::Rails.respond_to?(:env) && ::Rails.env.development? end end end end end premailer-rails-1.9.7/lib/premailer/rails/hook.rb0000644000004100000410000000745313126443374022015 0ustar www-datawww-dataclass Premailer module Rails class Hook attr_reader :message class << self def perform(message) new(message).perform message end alias_method :delivering_email, :perform alias_method :previewing_email, :perform end def initialize(message) @message = message end def perform if skip_premailer_header_present? remove_skip_premailer_header elsif message_contains_html? replace_html_part(generate_html_part_replacement) end end private def skip_premailer_header_present? message.header[:skip_premailer] end def remove_skip_premailer_header message.header[:skip_premailer] = nil end def message_contains_html? html_part.present? end # Returns true if the message itself has a content type of text/html, thus # it does not contain other parts such as alternatives and attachments. def pure_html_message? message.content_type && message.content_type.include?('text/html') end def generate_html_part_replacement if generate_text_part? generate_alternative_part else generate_html_part end end def generate_text_part? Rails.config[:generate_text_part] && !message.text_part end def generate_alternative_part part = Mail::Part.new(content_type: 'multipart/alternative') part.add_part(generate_text_part) part.add_part(generate_html_part) part end def generate_html_part # Make sure that the text part is generated first. Otherwise the text # can end up containing CSS rules. generate_text_part if generate_text_part? part = html_part html = premailer.to_inline_css Mail::Part.new do content_transfer_encoding part.content_transfer_encoding content_type "text/html; charset=#{part.charset}" body html body_encoding part.body.encoding end end def generate_text_part @text_part ||= begin part = html_part text = premailer.to_plain_text Mail::Part.new do content_transfer_encoding part.content_transfer_encoding content_type "text/plain; charset=#{part.charset}" body text body_encoding part.body.encoding end end end def premailer @premailer ||= CustomizedPremailer.new(html_part.decoded) end def html_part if pure_html_message? message else message.html_part end end def replace_html_part(new_part) if pure_html_message? replace_in_pure_html_message(new_part) else replace_part_in_list(message.parts, html_part, new_part) end end # If the new part is a pure text/html part, the body and its content type # are used for the message. If the new part is def replace_in_pure_html_message(new_part) if new_part.content_type.include?('text/html') message.body = new_part.decoded message.content_type = new_part.content_type else message.body = nil message.content_type = new_part.content_type new_part.parts.each do |part| message.add_part(part) end end end def replace_part_in_list(parts_list, old_part, new_part) if (index = parts_list.index(old_part)) parts_list[index] = new_part else parts_list.any? do |part| if part.respond_to?(:parts) replace_part_in_list(part.parts, old_part, new_part) end end end end end end end premailer-rails-1.9.7/lib/premailer/rails/css_helper.rb0000644000004100000410000000231213126443374023171 0ustar www-datawww-dataclass Premailer module Rails module CSSHelper extend self FileNotFound = Class.new(StandardError) STRATEGIES = [ CSSLoaders::CacheLoader, CSSLoaders::FileSystemLoader, CSSLoaders::AssetPipelineLoader, CSSLoaders::NetworkLoader ] # Returns all linked CSS files concatenated as string. def css_for_doc(doc) css_urls_in_doc(doc).map { |url| css_for_url(url) }.join("\n") end def css_for_url(url) load_css(url).tap do |content| CSSLoaders::CacheLoader.store(url, content) end end private def css_urls_in_doc(doc) doc.search('link[@rel="stylesheet"]:not([@data-premailer="ignore"])').map do |link| if link.respond_to?(:remove) link.remove else link.parent.children.delete(link) end link.attributes['href'].to_s end end def load_css(url) STRATEGIES.each do |strategy| css = strategy.load(url) return css.force_encoding('UTF-8') if css end raise FileNotFound, %{File with URL "#{url}" could not be loaded by any strategy.} end end end end premailer-rails-1.9.7/lib/premailer/rails/version.rb0000644000004100000410000000020613126443374022527 0ustar www-datawww-dataclass Premailer module Rails VERSION = File.read( File.expand_path('../../../../VERSION', __FILE__) ).strip end end premailer-rails-1.9.7/lib/premailer/rails/customized_premailer.rb0000644000004100000410000000152113126443374025271 0ustar www-datawww-dataclass Premailer module Rails class CustomizedPremailer < ::Premailer def initialize(html) # In order to pass the CSS as string to super it is necessary to access # the parsed HTML beforehand. To do so, the adapter needs to be # initialized. The ::Premailer::Adaptor handles the discovery of a # suitable adaptor (Nokogiri or Hpricot). To make load_html work, an # adaptor needs to be included and @options[:with_html_string] needs to # be set. For further information, refer to ::Premailer#initialize. @options = Rails.config.merge(with_html_string: true) Premailer.send(:include, Adapter.find(Adapter.use)) doc = load_html(html) options = @options.merge(css_string: CSSHelper.css_for_doc(doc)) super(doc.to_s, options) end end end end premailer-rails-1.9.7/lib/premailer/rails/railtie.rb0000644000004100000410000000026213126443374022475 0ustar www-datawww-dataclass Premailer module Rails class Railtie < ::Rails::Railtie config.after_initialize do ::Premailer::Rails.register_interceptors end end end end premailer-rails-1.9.7/lib/premailer/rails.rb0000644000004100000410000000134613126443374021050 0ustar www-datawww-datarequire 'premailer' require 'action_mailer' require 'premailer/rails/version' require 'premailer/rails/css_loaders' require 'premailer/rails/css_helper' require 'premailer/rails/customized_premailer' require 'premailer/rails/hook' class Premailer module Rails @config = { input_encoding: 'UTF-8', generate_text_part: true } class << self attr_accessor :config end def self.register_interceptors ActionMailer::Base.register_interceptor(Premailer::Rails::Hook) if ActionMailer::Base.respond_to?(:register_preview_interceptor) ActionMailer::Base.register_preview_interceptor(Premailer::Rails::Hook) end end end end require 'premailer/rails/railtie' if defined?(Rails) premailer-rails-1.9.7/.gitignore0000644000004100000410000000004213126443374016643 0ustar www-datawww-data*.gem doc/ Gemfile.lock coverage/ premailer-rails-1.9.7/LICENSE0000644000004100000410000000206013126443374015662 0ustar www-datawww-dataCopyright (C) 2011-2012 Philipe Fatio (fphilipe) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.premailer-rails-1.9.7/example/0000755000004100000410000000000013126443374016312 5ustar www-datawww-datapremailer-rails-1.9.7/example/Rakefile0000644000004100000410000000037113126443374017760 0ustar www-datawww-data# Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. require File.expand_path('../config/application', __FILE__) Rails.application.load_tasks premailer-rails-1.9.7/example/bin/0000755000004100000410000000000013126443374017062 5ustar www-datawww-datapremailer-rails-1.9.7/example/bin/rails0000755000004100000410000000022113126443374020115 0ustar www-datawww-data#!/usr/bin/env ruby APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands' premailer-rails-1.9.7/example/Gemfile0000644000004100000410000000024113126443374017602 0ustar www-datawww-datasource 'https://rubygems.org' gem 'rails', '~> 5.0.0.beta3' gem 'premailer-rails', path: '..' gem 'nokogiri' platforms :rbx do gem 'rubysl' gem 'racc' end premailer-rails-1.9.7/example/test/0000755000004100000410000000000013126443374017271 5ustar www-datawww-datapremailer-rails-1.9.7/example/test/mailers/0000755000004100000410000000000013126443374020725 5ustar www-datawww-datapremailer-rails-1.9.7/example/test/mailers/previews/0000755000004100000410000000000013126443374022571 5ustar www-datawww-datapremailer-rails-1.9.7/example/test/mailers/previews/example_mailer_preview.rb0000644000004100000410000000015713126443374027646 0ustar www-datawww-dataclass ExampleMailerPreview < ActionMailer::Preview def test_message ExampleMailer.test_message end end premailer-rails-1.9.7/example/.gitignore0000644000004100000410000000072213126443374020303 0ustar www-datawww-data# See https://help.github.com/articles/ignoring-files for more about ignoring files. # # If you find yourself ignoring temporary files generated by your text editor # or operating system, you probably want to add a global ignore instead: # git config --global core.excludesfile '~/.gitignore_global' # Ignore bundler config. /.bundle # Ignore the default SQLite database. /db/*.sqlite3 /db/*.sqlite3-journal # Ignore all logfiles and tempfiles. /log/*.log /tmp premailer-rails-1.9.7/example/config/0000755000004100000410000000000013126443374017557 5ustar www-datawww-datapremailer-rails-1.9.7/example/config/application.rb0000644000004100000410000000036213126443374022410 0ustar www-datawww-datarequire File.expand_path('../boot', __FILE__) require 'action_controller/railtie' require 'action_mailer/railtie' require 'sprockets/railtie' Bundler.require(*Rails.groups) module Example class Application < Rails::Application end end premailer-rails-1.9.7/example/config/initializers/0000755000004100000410000000000013126443374022265 5ustar www-datawww-datapremailer-rails-1.9.7/example/config/initializers/assets.rb0000644000004100000410000000015613126443374024116 0ustar www-datawww-dataRails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w( email.css ) premailer-rails-1.9.7/example/config/secrets.yml0000644000004100000410000000024113126443374021747 0ustar www-datawww-datadevelopment: secret_key_base: bc1d05753b1a42a7d983dcb4f998c433532ec8f91ab3842a36ed3d9072d143a2d9c05a6dc43a3d780a2ff3d8e7b75a1011ae2e0d13a022e98dc1f0299da5a5a0 premailer-rails-1.9.7/example/config/boot.rb0000644000004100000410000000020413126443374021043 0ustar www-datawww-dataENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' # Set up gems listed in the Gemfile. premailer-rails-1.9.7/example/config/environments/0000755000004100000410000000000013126443374022306 5ustar www-datawww-datapremailer-rails-1.9.7/example/config/environments/production.rb0000644000004100000410000000042413126443374025021 0ustar www-datawww-dataRails.application.configure do config.cache_classes = true config.eager_load = true config.consider_all_requests_local = false config.action_controller.perform_caching = true config.assets.compile = false config.assets.digest = true config.log_level = :info end premailer-rails-1.9.7/example/config/environments/development.rb0000644000004100000410000000044413126443374025157 0ustar www-datawww-dataRails.application.configure do config.cache_classes = false config.eager_load = false config.consider_all_requests_local = true config.action_controller.perform_caching = false config.assets.debug = true config.assets.digest = true config.assets.raise_runtime_errors = true end premailer-rails-1.9.7/example/config/routes.rb0000644000004100000410000000014613126443374021426 0ustar www-datawww-dataRails.application.routes.draw do root to: redirect('rails/mailers/example_mailer/test_message') end premailer-rails-1.9.7/example/config/environment.rb0000644000004100000410000000022613126443374022450 0ustar www-datawww-data# Load the Rails application. require File.expand_path('../application', __FILE__) # Initialize the Rails application. Rails.application.initialize! premailer-rails-1.9.7/example/app/0000755000004100000410000000000013126443374017072 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/views/0000755000004100000410000000000013126443374020227 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/views/example_mailer/0000755000004100000410000000000013126443374023213 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/views/example_mailer/test_message.html.erb0000644000004100000410000000144013126443374027332 0ustar www-datawww-data <%= stylesheet_link_tag :email %>

Hi, John Doe

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

premailer-rails-1.9.7/example/app/mailers/0000755000004100000410000000000013126443374020526 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/mailers/example_mailer.rb0000644000004100000410000000024113126443374024034 0ustar www-datawww-dataclass ExampleMailer < ActionMailer::Base default from: "from@example.com" def test_message mail to: 'to@example.org', subject: 'Test Message' end end premailer-rails-1.9.7/example/app/assets/0000755000004100000410000000000013126443374020374 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/assets/stylesheets/0000755000004100000410000000000013126443374022750 5ustar www-datawww-datapremailer-rails-1.9.7/example/app/assets/stylesheets/email.css0000644000004100000410000000063413126443374024554 0ustar www-datawww-databody { background: #efefef; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } p { line-height: 1.4; } .wrap { max-width: 40em; margin: 0 auto; padding: 1em; background: white; } .greeting { text-align: center; font-weight: bold; font-size: 110%; } .footer { font-size: 90%; color: #666; } a { color: green; text-decoration: none; border-bottom: 2px solid green; } premailer-rails-1.9.7/example/README.md0000644000004100000410000000024213126443374017567 0ustar www-datawww-data# Example Rails App To run this app, run: ```shell bundle bundle exec rails s ``` Then point your browser at [http://localhost:3000/](http://localhost:3000/). premailer-rails-1.9.7/example/config.ru0000644000004100000410000000023113126443374020123 0ustar www-datawww-data# This file is used by Rack-based servers to start the application. require ::File.expand_path('../config/environment', __FILE__) run Rails.application premailer-rails-1.9.7/VERSION0000644000004100000410000000000613126443374015723 0ustar www-datawww-data1.9.7 premailer-rails-1.9.7/CHANGELOG.md0000644000004100000410000000540613126443374016475 0ustar www-datawww-data# Changelog ## v1.9.7 - Use `Rails.root` in `FileSystemLoader` (@stanhu, #195) ## v1.9.6 - Handle `relative_url_root` in when loading CSS from file system ## v1.9.5 - Mention license in gemspec ## v1.9.4 - Improve check for Rails module - Preserve body encoding to prevent garbled mails ## v1.9.3 - Add support for rails' `relative_url_root` config - Fix link tag removal under Hpricot - Pass url to `asset_host` if it responds to `call` - Fixed issue where urls may conflict with folder names. ## v1.9.2 - Update rails dependency to allow rails 5 ## v1.9.1 - Respect data-premailer="ignore" on link tags - Ensure content-transfer-encoding is maintained ## v1.9.0 - Improved CSS loading and caching. - Fixed incompatibility with newer rails and sprockets versions. ## v1.8.2 - `Premailer::Rails::CSSLoaders::NetworkLoader` is more resilient and works even if the Rails asset host is set without a URI scheme. (panthomakos) - Remove stylesheet links from the HTML that have been processed. ## v1.8.1 - Add support for longer fingerprint generated by sprocket 3. ## v1.8.0 - `ActionMailer` interceptors are registered after Rails initialization and no longer when loading this gem. If you were using this gem outside Rails, you'll need to call `Premailer::Rails.register_interceptors` manually. ## v1.7.0 - Register preview hook for the new previewing functionality introduced in rails 4.1.0 - Add example rails application ## v1.6.1 - Remove Nokogiri unicode fix since it's working properly without it by now - Make sure html part comes before text part ## v1.6.0 - Only use asset pipeline if Rails is defined and if compile is true - Depend on actionmailer instead of rails - Check whether `::Rails` is defined before using it - Add ability to skip premailer - Test against multiple action mailer versions on travis - Ensure CSS strings are always UTF-8 encoded - Require premailer version >= 1.7.9 ## v1.5.1 - Prefer precompiled assets over asset pipeline - Improve construction of file URL when requesting from CDN - No longer use open-uri - Remove gzip unzipping after requesting file ## v1.5.0 - No longer support ruby 1.8 - Find linked stylesheets by `rel='stylesheet'` attribute instead of `type='text/css'` - Don't test hpricot on JRuby due to incompatibility ## v1.4.0 - Fix attachments ## v1.3.2 - Rename gem to premailer-rails (drop the 3) - Add support for rails 4 - Refactor code - Add support for precompiled assets - No longer include default `email.css` ## v1.1.0 - Fixed several bugs - Strip asset digest from CSS path - Improve nokogiri support - Request CSS file if asset is not found locally This allows you to host all your assets on a CDN and deploy the app without the `app/assets` folder. Thanks to everyone who contributed! premailer-rails-1.9.7/README.md0000644000004100000410000001702113126443374016137 0ustar www-datawww-data# premailer-rails CSS styled emails without the hassle. [![Build Status][build-image]][build-link] [![Gem Version][gem-image]][gem-link] [![Dependency Status][deps-image]][deps-link] [![Code Climate][gpa-image]][gpa-link] [![Coverage Status][cov-image]][cov-link] ## Introduction This gem is a drop in solution for styling HTML emails with CSS without having to do the hard work yourself. Styling emails is not just a matter of linking to a stylesheet. Most clients, especially web clients, ignore linked stylesheets or `