airmail 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/airmail/airmail.rb +30 -0
- data/lib/airmail/airmail_default_controller.rb +4 -0
- data/lib/airmail/mail_controller.rb +43 -0
- data/lib/airmail/mail_processor.rb +42 -0
- data/lib/airmail/mail_router.rb +74 -0
- data/lib/airmail.rb +7 -0
- data/spec/airmail_spec.rb +105 -0
- data/spec/spec_helper.rb +17 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c9710202c8bf57ba445332b14803f9f87149ffb4
|
4
|
+
data.tar.gz: 59baa6d1b4bc05dc2742c815c943b2a5ab161ce4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a780b078507aa04f219dde6e1a28fc76bfbbd6ca1a6d2478c6826e5a400dc1b49336c0d7d7d74f6cf1f4f009f61bc10071324a44888da5e66b31a74a86ab75e0
|
7
|
+
data.tar.gz: 75f69ff448caa3d4117d2c6bcd747847f4c9976f31b757defc9d74af57cb0dafa53b08ef9112d55193166cdecdea6621d080c2d835a06e1b73191596f764ff4e
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
module Airmail
|
4
|
+
class << self
|
5
|
+
def receive( original )
|
6
|
+
raise RoutesNotDefined unless defined? @@route
|
7
|
+
|
8
|
+
mail = Mail.new( original )
|
9
|
+
MailProcessor.new(mail, @@route).receive
|
10
|
+
|
11
|
+
mail
|
12
|
+
end
|
13
|
+
|
14
|
+
def route(&route)
|
15
|
+
@@route = route
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger= logr
|
19
|
+
@@logger = logr
|
20
|
+
end
|
21
|
+
|
22
|
+
def logger
|
23
|
+
defined?(@@logger) ? @@logger : Logger.new("log/airmail.log")
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class RoutesNotDefined < StandardError
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Airmail
|
2
|
+
class Controller
|
3
|
+
def initialize(mail)
|
4
|
+
@mail = mail
|
5
|
+
end
|
6
|
+
|
7
|
+
def from
|
8
|
+
return @from if @from
|
9
|
+
|
10
|
+
@from = if @mail.from.is_a? Array
|
11
|
+
@mail.from.first
|
12
|
+
else
|
13
|
+
@mail.from
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to
|
18
|
+
@to ||= Array.wrap(@mail.to) + Array.wrap(@mail.cc)
|
19
|
+
end
|
20
|
+
|
21
|
+
def subject
|
22
|
+
@mail.subject
|
23
|
+
end
|
24
|
+
|
25
|
+
def headers
|
26
|
+
@mail.header
|
27
|
+
end
|
28
|
+
|
29
|
+
def reference_id
|
30
|
+
reference_id = headers['Reference-ID'].is_a?(Array) && headers['Reference-ID'].last.value
|
31
|
+
reference_id ||= headers['Reference-ID'] && headers['Reference-ID'].value
|
32
|
+
reference_id ||= headers['References'] && headers['References'].value
|
33
|
+
|
34
|
+
reference_id = headers['Message-ID'] if reference_id.blank?
|
35
|
+
reference_id
|
36
|
+
end
|
37
|
+
|
38
|
+
def attachments
|
39
|
+
@mail.attachments
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class MailProcessor
|
2
|
+
|
3
|
+
def initialize mail, route
|
4
|
+
@mail = mail
|
5
|
+
@route = route
|
6
|
+
default_controller
|
7
|
+
end
|
8
|
+
|
9
|
+
def receive
|
10
|
+
default_controller
|
11
|
+
self.instance_eval(&@route)
|
12
|
+
|
13
|
+
@delivering_controller ||= AirmailDefaultController
|
14
|
+
@delivering_controller.new(@mail).receive
|
15
|
+
end
|
16
|
+
|
17
|
+
def from? *args
|
18
|
+
from
|
19
|
+
end
|
20
|
+
|
21
|
+
def has_attachment?
|
22
|
+
@mail.attachments.size > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
def deliver controller
|
26
|
+
@delivering_controller ||= controller
|
27
|
+
end
|
28
|
+
|
29
|
+
def deliver_to controller
|
30
|
+
controller = controller.gsub(/(^|_)([a-z])/){ $2.upcase }
|
31
|
+
@delivering_controller ||= "#{controller}Controller".constantize
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_controller
|
35
|
+
@delivering_controller = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger
|
39
|
+
Airmail.logger
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'mail'
|
2
|
+
class MailRouter
|
3
|
+
|
4
|
+
def initialize(mail)
|
5
|
+
@mail = mail
|
6
|
+
end
|
7
|
+
|
8
|
+
def route #this is the app specific controller logic
|
9
|
+
MailRouter.info("Receiving email '#{@mail.subject}' from #{@mail.from}, #{@mail.to} with #{@mail.attachments.count} attachments")
|
10
|
+
|
11
|
+
@mail.attachments.each do |attach|
|
12
|
+
MailRouter.info(">> #{attach.filename}")#(#{a.content_type.to_s})")
|
13
|
+
end
|
14
|
+
|
15
|
+
#TODO catch mailerdaemon stuff
|
16
|
+
#TODO circular loops? throttle speed
|
17
|
+
#if from? :mailerdaemon, :root
|
18
|
+
|
19
|
+
(deliver MailDemoController and return) if @mail.to.join(" ") =~ /demo.*@amendment.io/
|
20
|
+
|
21
|
+
if has_attachment?
|
22
|
+
#if not on the contact list, don't grant access to services
|
23
|
+
(deliver ThanksForPlayingController and return) unless Contact.find_by_email(@mail.from.collect{|f| f.downcase})
|
24
|
+
|
25
|
+
(deliver AttachmentController and return)
|
26
|
+
end
|
27
|
+
|
28
|
+
deliver DefaultController and return
|
29
|
+
end
|
30
|
+
|
31
|
+
def from? *args
|
32
|
+
from
|
33
|
+
end
|
34
|
+
|
35
|
+
def has_attachment?
|
36
|
+
@mail.attachments.size > 0
|
37
|
+
end
|
38
|
+
|
39
|
+
def deliver controller
|
40
|
+
controller.new(@mail).receive
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
def receive( original )
|
46
|
+
mail = Mail.new( original )
|
47
|
+
|
48
|
+
record(mail, original)
|
49
|
+
self.new(mail).route
|
50
|
+
|
51
|
+
mail
|
52
|
+
end
|
53
|
+
|
54
|
+
def record(mail, original)
|
55
|
+
Message.create!(
|
56
|
+
from: mail.from.first,
|
57
|
+
#reference_id: mail.
|
58
|
+
to: mail.to,
|
59
|
+
subject: mail.subject,
|
60
|
+
original: original,
|
61
|
+
taxonomy: 'log'
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def info msg
|
66
|
+
@logger && @logger.info(msg)
|
67
|
+
end
|
68
|
+
|
69
|
+
def logger= loggr
|
70
|
+
@logger = loggr
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/airmail.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Airmail do
|
4
|
+
|
5
|
+
let(:mail) { :garbage }
|
6
|
+
|
7
|
+
it 'sets route' do
|
8
|
+
Airmail.route do
|
9
|
+
Counter.inc
|
10
|
+
end
|
11
|
+
|
12
|
+
expect(Airmail.class_variable_get(:@@route)).to be_a(Proc)
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'received email' do
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
Counter.reset
|
19
|
+
|
20
|
+
Airmail.route do
|
21
|
+
Counter.inc
|
22
|
+
end
|
23
|
+
|
24
|
+
Airmail.receive(mail)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'runs route' do
|
28
|
+
expect(Counter.count).to eq(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'runs route for each mail' do
|
32
|
+
Airmail.receive(mail)
|
33
|
+
expect(Counter.count).to eq(2)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'multiple controllers' do
|
39
|
+
|
40
|
+
it 'delivers once' do
|
41
|
+
Counter.reset
|
42
|
+
|
43
|
+
Airmail.route do
|
44
|
+
deliver CountController
|
45
|
+
deliver CountController
|
46
|
+
end
|
47
|
+
|
48
|
+
Airmail.receive(mail)
|
49
|
+
expect(Counter.count).to eq(1)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'delivers to the first controller' do
|
53
|
+
Counter.reset
|
54
|
+
|
55
|
+
Airmail.route do
|
56
|
+
deliver BlankController
|
57
|
+
deliver CountController
|
58
|
+
end
|
59
|
+
|
60
|
+
Airmail.receive(mail)
|
61
|
+
expect(Counter.count).to eq(0)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'delivers to the first controller' do
|
65
|
+
Counter.reset
|
66
|
+
|
67
|
+
Airmail.route do
|
68
|
+
deliver CountController
|
69
|
+
deliver BlankController
|
70
|
+
end
|
71
|
+
|
72
|
+
Airmail.receive(mail)
|
73
|
+
expect(Counter.count).to eq(1)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class CountController < Airmail::Controller
|
81
|
+
def receive
|
82
|
+
Counter.inc
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class BlankController < Airmail::Controller
|
87
|
+
def receive
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Counter
|
92
|
+
@@val = 0
|
93
|
+
|
94
|
+
def self.reset
|
95
|
+
@@val = 0
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.inc
|
99
|
+
@@val += 1
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.count
|
103
|
+
@@val
|
104
|
+
end
|
105
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path('../../lib/airmail', __FILE__)
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.expect_with :rspec do |expectations|
|
5
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
6
|
+
end
|
7
|
+
|
8
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
9
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
10
|
+
|
11
|
+
config.mock_with :rspec do |mocks|
|
12
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
13
|
+
# a real object. This is generally recommended, and will default to
|
14
|
+
# `true` in RSpec 4.
|
15
|
+
mocks.verify_partial_doubles = true
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: airmail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Trevor Grayson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mail
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.0.0
|
41
|
+
description: Ruby on Wings!
|
42
|
+
email: trevor@trevorgrayson.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- lib/airmail.rb
|
48
|
+
- lib/airmail/airmail.rb
|
49
|
+
- lib/airmail/airmail_default_controller.rb
|
50
|
+
- lib/airmail/mail_controller.rb
|
51
|
+
- lib/airmail/mail_processor.rb
|
52
|
+
- lib/airmail/mail_router.rb
|
53
|
+
- spec/airmail_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
homepage: https://rubygems.org/gems/airmail
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.4.5.1
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: Incoming Email Router
|
79
|
+
test_files: []
|