aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib/action_mailer/preview.rb
blob: a763b9776cc9121877142daa1e380aba68967b0b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# frozen_string_literal: true

require "active_support/descendants_tracker"

module ActionMailer
  module Previews #:nodoc:
    extend ActiveSupport::Concern

    included do
      # Set the location of mailer previews through app configuration:
      #
      #     config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
      #
      mattr_accessor :preview_path, instance_writer: false

      # Enable or disable mailer previews through app configuration:
      #
      #     config.action_mailer.show_previews = true
      #
      # Defaults to +true+ for development environment
      #
      mattr_accessor :show_previews, instance_writer: false

      # :nodoc:
      mattr_accessor :preview_interceptors, instance_writer: false, default: [ActionMailer::InlinePreviewInterceptor]
    end

    module ClassMethods
      # Register one or more Interceptors which will be called before mail is previewed.
      def register_preview_interceptors(*interceptors)
        interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) }
      end

      # Unregister one or more previously registered Interceptors.
      def unregister_preview_interceptors(*interceptors)
        interceptors.flatten.compact.each { |interceptor| unregister_preview_interceptor(interceptor) }
      end

      # Register an Interceptor which will be called before mail is previewed.
      # Either a class or a string can be passed in as the Interceptor. If a
      # string is passed in it will be constantized.
      def register_preview_interceptor(interceptor)
        preview_interceptor = interceptor_class_for(interceptor)

        unless preview_interceptors.include?(preview_interceptor)
          preview_interceptors << preview_interceptor
        end
      end

      # Unregister a previously registered Interceptor.
      # Either a class or a string can be passed in as the Interceptor. If a
      # string is passed in it will be constantized.
      def unregister_preview_interceptor(interceptor)
        preview_interceptors.delete(interceptor_class_for(interceptor))
      end

      private
        def interceptor_class_for(interceptor)
          case interceptor
          when String, Symbol
            interceptor.to_s.camelize.constantize
          else
            interceptor
          end
        end
    end
  end

  class Preview
    extend ActiveSupport::DescendantsTracker

    attr_reader :params

    def initialize(params = {})
      @params = params
    end

    class << self
      # Returns all mailer preview classes.
      def all
        load_previews if descendants.empty?
        descendants
      end

      # Returns the mail object for the given email name. The registered preview
      # interceptors will be informed so that they can transform the message
      # as they would if the mail was actually being delivered.
      def call(email, params = {})
        preview = new(params)
        message = preview.public_send(email)
        inform_preview_interceptors(message)
        message
      end

      # Returns all of the available email previews.
      def emails
        public_instance_methods(false).map(&:to_s).sort
      end

      # Returns +true+ if the email exists.
      def email_exists?(email)
        emails.include?(email)
      end

      # Returns +true+ if the preview exists.
      def exists?(preview)
        all.any? { |p| p.preview_name == preview }
      end

      # Find a mailer preview by its underscored class name.
      def find(preview)
        all.find { |p| p.preview_name == preview }
      end

      # Returns the underscored name of the mailer preview without the suffix.
      def preview_name
        name.sub(/Preview$/, "").underscore
      end

      private
        def load_previews
          if preview_path
            Dir["#{preview_path}/**/*_preview.rb"].sort.each { |file| require_dependency file }
          end
        end

        def preview_path
          Base.preview_path
        end

        def show_previews
          Base.show_previews
        end

        def inform_preview_interceptors(message)
          Base.preview_interceptors.each do |interceptor|
            interceptor.previewing_email(message)
          end
        end
    end
  end
end