Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for propshaft (#2) #114

Merged
merged 2 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing new line at the end again :P
I will add https://editorconfig.org and set insert_final_newline
You should setup your editor(s)/IDE(s) to follow https://editorconfig.org config if present (though I have never seen any project allowing missing new line
I will simply merge and fix it later

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops sorry!

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
Loading