diff --git a/lib/prism/mailer/preview_app.rb b/lib/prism/mailer/preview_app.rb new file mode 100644 index 0000000..97be41e --- /dev/null +++ b/lib/prism/mailer/preview_app.rb @@ -0,0 +1,99 @@ +module Prism + module Mailer + # A tiny Rack app that browses Mailer::Preview subclasses in a web + # browser, mirroring Rails' /rails/mailers engine: an index of every + # registered preview, a list of its actions, and a rendered view of one + # email (HTML part if present, raw text part otherwise) - all without + # ever touching Mailer.deliver!. Mount it directly: + # BlogApp.mount('/rails/mailers', Prism::Mailer::PreviewApp.new) + class PreviewApp + def call(env) + request = Request.new(env) + return not_found unless request.request_method == 'GET' + + # Links are built from SCRIPT_NAME (where this app was mounted) + # rather than relative to PATH_INFO, so they work the same whether + # this is mounted at "/rails/mailers" or served standalone at "/" - + # relative hrefs would break the moment the current path lacks a + # trailing slash (the classic Rack::URLMap gotcha). + @base = env['SCRIPT_NAME'].to_s + segments = request.path.split('/').reject(&:empty?) + case segments.length + when 0 then index + when 1 then show_preview(segments[0]) + when 2 then show_email(segments[0], segments[1]) + else not_found + end + rescue ArgumentError + not_found + end + + private + + def index + previews = Preview.registry.sort_by(&:preview_name) + items = previews.map do |klass| + %(
No previews registered.
' if previews.empty? + html(body) + end + + def show_preview(name) + klass = Preview.find(name) + return not_found unless klass + + items = klass.emails.map do |email| + %(« #{escape(klass.preview_name)}
) + body << "#{escape(message.text_body)}"
+ else
+ 'empty body
' + end + end + + def escape(text) + CGI.escapeHTML(text.to_s) + end + + def html(body) + [200, { 'Content-Type' => 'text/html; charset=UTF-8' }, + ["#{body}"]] + end + + def not_found + [404, { 'Content-Type' => 'text/plain' }, ['Not Found']] + end + end + end +end