Skip to content

Commit

Permalink
Merge pull request #114 from jvillarejo/master
Browse files Browse the repository at this point in the history
  • Loading branch information
PikachuEXE authored May 10, 2024
2 parents 4980148 + b6d8de1 commit 610fe6e
Show file tree
Hide file tree
Showing 34 changed files with 583 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .standard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ignore:
- 'lib/roadie/rails/mailer.rb':
- Style/ArgumentsForwarding
- 'lib/roadie/rails/options.rb':
- Performance/StringIdentifierArgument
1 change: 1 addition & 0 deletions lib/roadie/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module Rails
require "roadie/rails/mail_inliner"

require "roadie/rails/asset_pipeline_provider"
require "roadie/rails/asset_propshaft_provider"

require "roadie/rails/mailer"

Expand Down
41 changes: 41 additions & 0 deletions lib/roadie/rails/asset_propshaft_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true

module Roadie
module Rails
class AssetPropshaftProvider
include Roadie::AssetProvider

def initialize(assembly)
@assembly = assembly
@stylesheets = {}
end

def find_stylesheet(stylesheet_asset_path)
stylesheet = @stylesheets[stylesheet_asset_path]
return stylesheet if stylesheet.present?

path, digest = extract_path_and_digest(stylesheet_asset_path)

asset = @assembly.load_path.find(path)
if asset.present? && asset.fresh?(digest)
compiled_content = @assembly.compilers.compile(asset)

stylesheet = Stylesheet.new(stylesheet_asset_path, compiled_content.force_encoding("UTF-8"))
@stylesheets[stylesheet_asset_path] = stylesheet
end

stylesheet
end

private

def extract_path_and_digest(stylesheet_asset_path)
full_path = stylesheet_asset_path.sub(%r{#{::Rails.configuration.assets.prefix}/}, "")
digest = full_path[/-([0-9a-zA-Z]{7,128})\.(?!digested)[^.]+\z/, 1]
path = digest ? full_path.sub("-#{digest}", "") : full_path

[path, digest]
end
end
end
end
7 changes: 5 additions & 2 deletions lib/roadie/rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ class Railtie < ::Rails::Railtie

if app.config.respond_to?(:assets) && app.config.assets
if app.assets
config.roadie.asset_providers <<
AssetPipelineProvider.new(app.assets)
config.roadie.asset_providers << AssetPipelineProvider.new(app.assets)
elsif defined?(Propshaft)
config.after_initialize do |app|
config.roadie.asset_providers << AssetPropshaftProvider.new(app.assets)
end
else
app.config.assets.configure do |env|
config.roadie.asset_providers <<
Expand Down
86 changes: 84 additions & 2 deletions spec/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def parse_html_in_email(mail)
RailsApp.new("Rails 6.0", "rails_60", max_ruby_version: "3.0"),
RailsApp.new("Rails 6.1", "rails_61", max_ruby_version: "3.0"),
RailsApp.new("Rails 7.0", "rails_70", min_ruby_version: "2.7"),
RailsApp.new("Rails 7.1", "rails_71", min_ruby_version: "2.7")
RailsApp.new("Rails 7.1 with sprockets", "rails_71", min_ruby_version: "2.7"),
RailsApp.new("Rails 7.1 with propshaft", "rails_71_with_propshaft", min_ruby_version: "2.7", asset_pipeline: :propshaft)
]

shared_examples "generates valid email" do |message|
Expand Down Expand Up @@ -47,7 +48,7 @@ def parse_html_in_email(mail)
end
end

rails_apps.select(&:supported?).each do |app|
rails_apps.select(&:supported?).select(&:with_sprockets?).each do |app|
describe "with #{app}" do
before { app.reset }

Expand Down Expand Up @@ -94,4 +95,85 @@ def parse_html_in_email(mail)
end
end
end

rails_apps.select(&:supported?).select(&:with_propshaft?).each do |app|
describe "with #{app}" do
before { app.reset }

it "inlines styles for multipart emails" do
email = app.read_email(:normal_email)

expect(email.to).to eq(["[email protected]"])
expect(email.from).to eq(["[email protected]"])
expect(email).to have(2).parts

expect(email.text_part.body.decoded).not_to match(/<.*>/)

html = email.html_part.body.decoded
expect(html).to include "<!DOCTYPE"
expect(html).to include "<head"

document = parse_html_in_email(email)
expect(document).to have_selector("body h1")

expect(document).to have_styling(
"background-color" => "green"
).at_selector("body")

expected_image_url =
"https://example.app.org/assets/rails-fbe4356d4aa42b95f211236439f3e675a5f9a7e6.png"

expect(document).to have_styling(
"background" => "url(\"#{expected_image_url}\")"
).at_selector(".image")

# If we deliver mails we can catch weird problems with headers being
# invalid
email.delivery_method :test
email.deliver
end

it "inlines styles with automatic mailer" do
email = app.read_delivered_email(:normal_email)

expect(email.to).to eq(["[email protected]"])
expect(email.from).to eq(["[email protected]"])
expect(email).to have(2).parts

expect(email.text_part.body.decoded).not_to match(/<.*>/)

html = email.html_part.body.decoded
expect(html).to include "<!DOCTYPE"
expect(html).to include "<head"

document = parse_html_in_email(email)
expect(document).to have_selector("body h1")

expect(document).to have_styling(
"background-color" => "green"
).at_selector("body")

expected_image_url =
"https://example.app.org/assets/rails-fbe4356d4aa42b95f211236439f3e675a5f9a7e6.png"

expect(document).to have_styling(
"background" => "url(\"#{expected_image_url}\")"
).at_selector(".image")

# If we deliver mails we can catch weird problems with headers being
# invalid
email.delivery_method :test
email.deliver
end

it "has a AssetPropshaftProvider together with a FilesystemProvider" do
expect(app.read_providers).to eq(
%w[
Roadie::FilesystemProvider
Roadie::Rails::AssetPropshaftProvider
]
)
end
end
end
end
11 changes: 11 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

source "https://rubygems.org"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.1.0"

# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", ">= 1.4.4", require: false
gem "propshaft"
gem "roadie-rails", path: "../../.."
6 changes: 6 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 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_relative "config/application"

Rails.application.load_tasks
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
body { background-color: green; }
.image { background: url("/rails.png"); }
30 changes: 30 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/app/mailers/auto_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

class AutoMailer < ActionMailer::Base
include Roadie::Rails::Automatic

default from: "[email protected]"

def normal_email
generate_email
end

def disabled_email
generate_email
end

private

def roadie_options
unless /disabled/.match?(action_name)
super.combine(url_options: {protocol: "https"})
end
end

def generate_email
mail(to: "[email protected]", subject: "Notification for you") do |format|
format.html { render :normal_email }
format.text { render :normal_email }
end
end
end
20 changes: 20 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/app/mailers/mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

class Mailer < ActionMailer::Base
include Roadie::Rails::Mailer

default from: "[email protected]"

def normal_email
roadie_mail(to: "[email protected]", subject: "Notification for you") do |format|
format.html
format.text
end
end

private

def roadie_options
super.combine(url_options: {protocol: "https"})
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<%= stylesheet_link_tag "email" %>
</head>
<body>
<h1>Normal email</h1>
<div class="image"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Normal email
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<%= stylesheet_link_tag "email" %>
</head>
<body>
<h1>Normal email</h1>
<div class="image"></div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Normal email
114 changes: 114 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/bin/bundle
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'bundle' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require "rubygems"

m = Module.new do
module_function

def invoked_as_script?
File.expand_path($0) == File.expand_path(__FILE__)
end

def env_var_version
ENV["BUNDLER_VERSION"]
end

def cli_arg_version
return unless invoked_as_script? # don't want to hijack other binstubs
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
bundler_version = nil
update_index = nil
ARGV.each_with_index do |a, i|
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
bundler_version = a
end
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o
bundler_version = $1
update_index = i
end
bundler_version
end

def gemfile
gemfile = ENV["BUNDLE_GEMFILE"]
return gemfile if gemfile && !gemfile.empty?

File.expand_path("../../Gemfile", __FILE__)
end

def lockfile
lockfile =
case File.basename(gemfile)
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
else "#{gemfile}.lock"
end
File.expand_path(lockfile)
end

def lockfile_version
return unless File.file?(lockfile)
lockfile_contents = File.read(lockfile)
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o
Regexp.last_match(1)
end

def bundler_requirement
@bundler_requirement ||=
env_var_version || cli_arg_version ||
bundler_requirement_for(lockfile_version)
end

def bundler_requirement_for(version)
return "#{Gem::Requirement.default}.a" unless version

bundler_gem_version = Gem::Version.new(version)

requirement = bundler_gem_version.approximate_recommendation

return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")

requirement += ".a" if bundler_gem_version.prerelease?

requirement
end

def load_bundler!
ENV["BUNDLE_GEMFILE"] ||= gemfile

activate_bundler
end

def activate_bundler
gem_error = activation_error_handling do
gem "bundler", bundler_requirement
end
return if gem_error.nil?
require_error = activation_error_handling do
require "bundler/version"
end
return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
exit 42
end

def activation_error_handling
yield
nil
rescue StandardError, LoadError => e
e
end
end

m.load_bundler!

if m.invoked_as_script?
load Gem.bin_path("bundler", "bundle")
end
4 changes: 4 additions & 0 deletions spec/railsapps/rails_71_with_propshaft/bin/rails
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby
APP_PATH = File.expand_path("../config/application", __dir__)
require_relative "../config/boot"
require "rails/commands"
Loading

0 comments on commit 610fe6e

Please sign in to comment.