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 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,4 @@
1
+ class AirmailDefaultController < Airmail::Controller
2
+ def receive
3
+ end
4
+ 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,7 @@
1
+ require 'airmail/mail_router'
2
+ require 'airmail/mail_processor'
3
+
4
+ require 'airmail/mail_controller'
5
+ require 'airmail/airmail_default_controller'
6
+ require 'airmail/airmail' #tmp
7
+
@@ -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
@@ -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: []