diff options
Diffstat (limited to 'lib/action_mailbox')
-rw-r--r-- | lib/action_mailbox/base.rb | 45 | ||||
-rw-r--r-- | lib/action_mailbox/callbacks.rb | 26 | ||||
-rw-r--r-- | lib/action_mailbox/engine.rb | 14 | ||||
-rw-r--r-- | lib/action_mailbox/router.rb | 36 | ||||
-rw-r--r-- | lib/action_mailbox/router/route.rb | 31 | ||||
-rw-r--r-- | lib/action_mailbox/routing.rb | 17 | ||||
-rw-r--r-- | lib/action_mailbox/test_helper.rb | 22 | ||||
-rw-r--r-- | lib/action_mailbox/version.rb | 3 |
8 files changed, 194 insertions, 0 deletions
diff --git a/lib/action_mailbox/base.rb b/lib/action_mailbox/base.rb new file mode 100644 index 0000000000..476c343415 --- /dev/null +++ b/lib/action_mailbox/base.rb @@ -0,0 +1,45 @@ +require "active_support/rescuable" + +require "action_mailbox/callbacks" +require "action_mailbox/routing" + +class ActionMailbox::Base + include ActiveSupport::Rescuable + include ActionMailbox::Callbacks, ActionMailbox::Routing + + attr_reader :inbound_email + delegate :mail, :bounced!, to: :inbound_email + + def self.receive(inbound_email) + new(inbound_email).perform_processing + end + + def initialize(inbound_email) + @inbound_email = inbound_email + end + + def perform_processing + run_callbacks :process do + track_status_of_inbound_email do + process + end + end + rescue => exception + # TODO: Include a reference to the inbound_email in the exception raised so error handling becomes easier + rescue_with_handler(exception) || raise + end + + def process + # Overwrite in subclasses + end + + private + def track_status_of_inbound_email + inbound_email.processing! + yield + inbound_email.delivered! unless inbound_email.bounced? + rescue => exception + inbound_email.failed! + raise + end +end diff --git a/lib/action_mailbox/callbacks.rb b/lib/action_mailbox/callbacks.rb new file mode 100644 index 0000000000..33caaafd2a --- /dev/null +++ b/lib/action_mailbox/callbacks.rb @@ -0,0 +1,26 @@ +require "active_support/callbacks" + +module ActionMailbox + module Callbacks + extend ActiveSupport::Concern + include ActiveSupport::Callbacks + + included do + define_callbacks :process + end + + module ClassMethods + def before_processing(*methods, &block) + set_callback(:process, :before, *methods, &block) + end + + def after_processing(*methods, &block) + set_callback(:process, :after, *methods, &block) + end + + def around_processing(*methods, &block) + set_callback(:process, :around, *methods, &block) + end + end + end +end diff --git a/lib/action_mailbox/engine.rb b/lib/action_mailbox/engine.rb new file mode 100644 index 0000000000..92852a0fa3 --- /dev/null +++ b/lib/action_mailbox/engine.rb @@ -0,0 +1,14 @@ +require "rails/engine" + +module ActionMailbox + class Engine < Rails::Engine + isolate_namespace ActionMailbox + config.eager_load_namespaces << ActionMailbox + + initializer "action_mailbox.config" do + config.after_initialize do |app| + # Configure + end + end + end +end diff --git a/lib/action_mailbox/router.rb b/lib/action_mailbox/router.rb new file mode 100644 index 0000000000..8ba3ad0bae --- /dev/null +++ b/lib/action_mailbox/router.rb @@ -0,0 +1,36 @@ +class ActionMailbox::Router + class RoutingError < StandardError; end + + def initialize + @routes = [] + end + + def add_routes(routes) + routes.each do |(address, mailbox_name)| + add_route address, to: mailbox_name + end + end + + def add_route(address, to:) + routes.append Route.new(address, to: to) + end + + def route(inbound_email) + if mailbox = match_to_mailbox(inbound_email) + mailbox.receive(inbound_email) + else + inbound_email.bounced! + + raise RoutingError + end + end + + private + attr_reader :routes + + def match_to_mailbox(inbound_email) + routes.detect { |route| route.match?(inbound_email) }.try(:mailbox_class) + end +end + +require "action_mailbox/router/route" diff --git a/lib/action_mailbox/router/route.rb b/lib/action_mailbox/router/route.rb new file mode 100644 index 0000000000..7be4407339 --- /dev/null +++ b/lib/action_mailbox/router/route.rb @@ -0,0 +1,31 @@ +class ActionMailbox::Router::Route + class InvalidAddressError < StandardError; end + + attr_reader :address, :mailbox_name + + def initialize(address, to:) + @address, @mailbox_name = address, to + end + + def match?(inbound_email) + case address + when String + recipients_from(inbound_email.mail).include?(address) + when Regexp + recipients_from(inbound_email.mail).detect { |recipient| address.match?(recipient) } + when Proc + address.call(inbound_email) + else + address.try(:match?, inbound_email) || raise(InvalidAddressError) + end + end + + def mailbox_class + "#{mailbox_name.to_s.capitalize}Mailbox".constantize + end + + private + def recipients_from(mail) + Array(mail.to) + Array(mail.cc) + Array(mail.bcc) + end +end diff --git a/lib/action_mailbox/routing.rb b/lib/action_mailbox/routing.rb new file mode 100644 index 0000000000..b40e2774e4 --- /dev/null +++ b/lib/action_mailbox/routing.rb @@ -0,0 +1,17 @@ +module ActionMailbox + module Routing + extend ActiveSupport::Concern + + class_methods do + attr_reader :router + + def routing(routes) + (@router ||= ActionMailbox::Router.new).add_routes(routes) + end + + def route(inbound_email) + @router.route(inbound_email) + end + end + end +end diff --git a/lib/action_mailbox/test_helper.rb b/lib/action_mailbox/test_helper.rb new file mode 100644 index 0000000000..65a15a1281 --- /dev/null +++ b/lib/action_mailbox/test_helper.rb @@ -0,0 +1,22 @@ +require "mail" + +module ActionMailbox + module TestHelper + # Create an InboundEmail record using an eml fixture in the format of message/rfc822 + # referenced with +fixture_name+ located in +test/fixtures/files/fixture_name+. + def create_inbound_email_from_fixture(fixture_name, status: :processing) + create_inbound_email file_fixture(fixture_name), filename: fixture_name, status: status + end + + def create_inbound_email_from_mail(status: :processing, **mail_options) + raw_email = Tempfile.new.tap { |raw_email| raw_email.write Mail.new(mail_options).to_s } + create_inbound_email(raw_email, status: status) + end + + def create_inbound_email(io, filename: 'mail.eml', status: :processing) + ActionMailbox::InboundEmail.create_and_extract_message_id! \ + ActionDispatch::Http::UploadedFile.new(tempfile: io, filename: filename, type: 'message/rfc822'), + status: status + end + end +end diff --git a/lib/action_mailbox/version.rb b/lib/action_mailbox/version.rb new file mode 100644 index 0000000000..23c615dbbd --- /dev/null +++ b/lib/action_mailbox/version.rb @@ -0,0 +1,3 @@ +module ActionMailbox + VERSION = '0.1.0' +end |