griddler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Caleb Thompson, Joel Oliveira and thoughtbot, inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ Griddler
2
+ ========
3
+
4
+ Griddler is a Rails engine (full plugin) that provides an endpoint for the
5
+ [Sendgrid parse
6
+ api](http://sendgrid.com/docs/API%20Reference/Webhooks/parse.html) that hands
7
+ off a built email object to a class implemented by you.
8
+
9
+ Installation
10
+ ------------
11
+
12
+ Add griddler to your application's Gemfile and run `bundle install`:
13
+
14
+ ```ruby
15
+ gem 'griddler'
16
+ ```
17
+
18
+ Griddler comes with a default endpoint that will be displayed at the bottom of
19
+ the output of `rake routes`. If there is a previously defined route that matches
20
+ `/email_processor`–or you would like to rename the matched path–you may
21
+ add the route to the desired position in routes.rb with the following:
22
+
23
+ ```ruby
24
+ match '/email_processor' => 'griddler/emails#create', via: :post
25
+ ```
26
+
27
+ Defaults
28
+ --------
29
+
30
+ By default Griddler will look for a class to be created in your application
31
+ called EmailProcessor with a class method implemented named process, taking in
32
+ one argument (presumably `email`). For example, in `./lib/email_processor.rb`:
33
+
34
+ ```ruby
35
+ class EmailProcessor
36
+ def self.process(email)
37
+ # all of your application-specific code here - creating models, processing
38
+ # reports, etc
39
+ end
40
+ end
41
+ ```
42
+
43
+ The contents of the `email` object passed into your process method is a hash
44
+ containing:
45
+
46
+ * `:to`
47
+ * `:from`
48
+ * `:subject`
49
+ * `:body`
50
+
51
+ Each of those has some sensible defaults.
52
+
53
+ `:from` and `:subject` will contain the obvious values found in the email, the
54
+ raw from and subject values.
55
+
56
+ `:body` will contain the full contents of the email body **unless** there is a
57
+ line in the email containing the string `-- Reply ABOVE THIS LINE --`. In that
58
+ case `:body` will contain everything before that line.
59
+
60
+ `:to` will contain all of the text before the email's "@" character. We've found
61
+ that this is the most often use portion of the email address and consider it to
62
+ be the token we'll key off of for interaction with our application.
63
+
64
+ Configuration Options
65
+ ---------------------
66
+
67
+ An initializer can be created to control some of the options in Griddler.
68
+ Defaults are shown below with sample overrides following. In
69
+ `config/initializer/griddler.rb`:
70
+
71
+ ```ruby
72
+ Griddler.configure do |config|
73
+ config.handler_class = EmailProcessor # MyEmailProcessor
74
+ config.handler_method = :process # :go
75
+ config.raw_body = false # true
76
+ config.to = :token # :raw, :email, :hash
77
+ # :raw => 'AppName <s13.6b2d13dc6a1d33db7644@mail.myapp.com>'
78
+ # :email => 's13.6b2d13dc6a1d33db7644@mail.myapp.com'
79
+ # :token => 's13.6b2d13dc6a1d33db7644'
80
+ # :hash => { raw: '', email: '', token: '', host: '' }
81
+ config.reply_delimeter = '-- REPLY ABOVE THIS LINE --'
82
+ end
83
+ ```
84
+
85
+ * `config.handler_class` change the class Griddler will use to handle your
86
+ incoming emails.
87
+ * `config.handler_method` change the class method called on
88
+ `config.handler_class`.
89
+ * `config.reply_delimeter` change the string searched for that will split your
90
+ body.
91
+ * `config.raw_body` use the full email body whether or not
92
+ `config.reply_delimeter` is set.
93
+ * `config.to` change the format of the returned value for the `:to` key in the
94
+ email object. `:hash` will return all options within a (surprise!) - hash.
95
+
96
+ More Information
97
+ ----------------
98
+
99
+ * [Sendgrid](http://www.sendgrid.com)
100
+ * [Sendgrid Parse API](www.sendgrid.com/docs/API Reference/Webhooks/parse.html)
101
+
102
+ Credits
103
+ -------
104
+
105
+ Griddler was written by Caleb Thompson and Joel Oliveira.
106
+
107
+ Large portions of the codebase were extracted from thoughtbot's
108
+ [Trajectory](http://www.apptrajectory.com).
109
+
110
+ ![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
111
+
112
+ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
113
+
114
+ License
115
+ -------
116
+
117
+ Griddler is Copyright © 2012 Caleb Thompson, Joel Oliveira and thoughtbot. It is
118
+ free software, and may be redistributed under the terms specified in the LICENSE
119
+ file.
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ Bundler::GemHelper.install_tasks
9
+
10
+ require 'rspec/core/rake_task'
11
+ RSpec::Core::RakeTask.new(:spec)
12
+ task default: :spec
@@ -0,0 +1,6 @@
1
+ class Griddler::EmailsController < ActionController::Base
2
+ def create
3
+ Griddler::Email.new(params)
4
+ head :ok
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ require 'griddler'
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ match '/email_processor' => 'griddler/emails#create', via: :post, as: :email_processor
3
+ end
@@ -0,0 +1,43 @@
1
+ module Griddler
2
+ class << self
3
+ attr_accessor :configuration
4
+ end
5
+
6
+ def self.configure
7
+ self.configuration = Configuration.new
8
+
9
+ if block_given?
10
+ yield configuration
11
+ end
12
+
13
+ self.configuration
14
+ end
15
+
16
+ def self.configuration
17
+ @configuration || self.configure
18
+ end
19
+
20
+ class Configuration
21
+ attr_accessor :handler_class, :handler_method, :raw_body, :reply_delimiter, :to
22
+
23
+ def to
24
+ @to ||= :token
25
+ end
26
+
27
+ def handler_class
28
+ @handler_class ||= EmailProcessor
29
+ end
30
+
31
+ def handler_method
32
+ @handler_method ||= :process
33
+ end
34
+
35
+ def raw_body
36
+ @raw_body ||= false
37
+ end
38
+
39
+ def reply_delimiter
40
+ @reply_delimiter ||= 'Reply ABOVE THIS LINE'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ require 'iconv'
2
+
3
+ class Griddler::Email
4
+ attr_accessor :to, :from, :body, :subject
5
+
6
+ def initialize(params)
7
+ @to = extract_address(params[:to], config.to)
8
+ @from = extract_address(params[:from], :email)
9
+ @subject = params[:subject]
10
+
11
+ if config.raw_body
12
+ @body = params[:text]
13
+ else
14
+ @body = extract_body(params[:text], params[:charsets])
15
+ end
16
+
17
+ handler_class = config.handler_class
18
+ handler_method = config.handler_method
19
+ handler_class.send(handler_method, self)
20
+ end
21
+
22
+ private
23
+
24
+ def extract_address(address, type)
25
+ parsed = EmailParser.parse_address(address)
26
+ if type == :hash
27
+ parsed
28
+ else
29
+ parsed[type]
30
+ end
31
+ end
32
+
33
+ def extract_body(body_text, charsets)
34
+ if charsets.present?
35
+ charsets = ActiveSupport::JSON.decode(charsets)
36
+ body_text = Iconv.new('utf-8', charsets['text']).iconv(body_text)
37
+ end
38
+
39
+ EmailParser.extract_reply_body(body_text)
40
+ end
41
+
42
+ def config
43
+ Griddler.configuration
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ module Griddler::EmailFormat
2
+ LocalPartSpecialChars = Regexp.escape('!#$%&\'*-/=?+-^_`{|}~')
3
+ LocalPartUnquoted = '(([[:alnum:]' + LocalPartSpecialChars + ']+[\.\+]+))*[[:alnum:]' + LocalPartSpecialChars + '+]+'
4
+ LocalPartQuoted = '\"(([[:alnum:]' + LocalPartSpecialChars + '\.\+]*|(\\\\[\u0001-\uFFFF]))*)\"'
5
+ Regex = Regexp.new('((' + LocalPartUnquoted + ')|(' + LocalPartQuoted + ')+)@(((\w+\-+)|(\w+\.))*\w{1,63}\.[a-z]{2,6})', Regexp::EXTENDED | Regexp::IGNORECASE)
6
+ end
@@ -0,0 +1,49 @@
1
+ # Parse emails from their full format into a hash containing full email, host,
2
+ # local token, and the raw argument.
3
+ #
4
+ # Some Body <somebody@example.com>
5
+ # # => {
6
+ # token: 'somebody',
7
+ # host: 'example.com',
8
+ # email: 'somebody@example.com',
9
+ # full: 'Some Body <somebody@example.com>',
10
+ # }
11
+ module EmailParser
12
+ def self.parse_address(full_address)
13
+ email_address = extract_email_address(full_address)
14
+ token, host = split_address(email_address)
15
+ {
16
+ token: token,
17
+ host: host,
18
+ email: email_address,
19
+ full: full_address,
20
+ }
21
+ end
22
+
23
+ def self.extract_reply_body(body)
24
+ if body
25
+ delimeter = Griddler.configuration.reply_delimiter
26
+ body.split(delimeter).first.
27
+ split(/^\s*[-]+\s*Original Message\s*[-]+\s*$/).first.
28
+ split(/^\s*--\s*$/).first.
29
+ split(/[\r]*\n/).reject do |line|
30
+ line =~ /^\s*>/ ||
31
+ line =~ /^\s*On.*wrote:$/ ||
32
+ line =~ /^\s*Sent from my /
33
+ end.
34
+ join("\n").
35
+ gsub(/^\s*On.*\r?\n?\s*.*\s*wrote:$/,'').
36
+ strip
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def self.extract_email_address(full_address)
43
+ full_address.split('<').last.delete('>').strip
44
+ end
45
+
46
+ def self.split_address(email_address)
47
+ email_address.try :split, '@'
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module Griddler
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module Griddler
2
+ VERSION = "0.1.0"
3
+ end
data/lib/griddler.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'griddler/engine'
2
+ require 'griddler/email'
3
+ require 'griddler/email_format'
4
+ require 'griddler/email_parser'
5
+ require 'griddler/configuration'
6
+
7
+ module Griddler
8
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :griddler do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: griddler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Caleb Thompson
9
+ - Joel Oliveira
10
+ - thoughtbot
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-11-06 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 3.2.0
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: 3.2.0
32
+ - !ruby/object:Gem::Dependency
33
+ name: sqlite3
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec-rails
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ description:
65
+ email:
66
+ - cjaysson@gmail.com
67
+ - joel@thoughtbot.com
68
+ executables: []
69
+ extensions: []
70
+ extra_rdoc_files: []
71
+ files:
72
+ - app/controllers/griddler/emails_controller.rb
73
+ - config/initializers/griddler.rb
74
+ - config/routes.rb
75
+ - lib/griddler/configuration.rb
76
+ - lib/griddler/email.rb
77
+ - lib/griddler/email_format.rb
78
+ - lib/griddler/email_parser.rb
79
+ - lib/griddler/engine.rb
80
+ - lib/griddler/version.rb
81
+ - lib/griddler.rb
82
+ - lib/tasks/griddler_tasks.rake
83
+ - LICENSE
84
+ - Rakefile
85
+ - README.md
86
+ homepage: http://thoughtbot.com
87
+ licenses: []
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - app
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ segments:
100
+ - 0
101
+ hash: -1174056135577180289
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ segments:
109
+ - 0
110
+ hash: -1174056135577180289
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 1.8.24
114
+ signing_key:
115
+ specification_version: 3
116
+ summary: SendGrid Parse API client Rails Engine
117
+ test_files: []