griddler-acd 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0bbff6edeef08baf0c4cfc81727aef71984e8064
4
+ data.tar.gz: e622fa41d89c7ca9513c0a21752b51d05a5f2b30
5
+ SHA512:
6
+ metadata.gz: 20ee0b0ce115d5a9bb41e0f4e6084d0a187ca9249e4edf4f1713ccc1f5f2377479da328367249ce7cef6f1879e6ca8501f8e4e40e61944225ae06e766b007237
7
+ data.tar.gz: 3cf2ede21b125530f4bd26d95b953842d02d2dc3428e21122d04b650a60399f050f149be7793a302d46b3b23d76d026a65f87e4f3b86b5744cc5237cdbd5d2ef
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 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,212 @@
1
+ Griddler
2
+ ========
3
+
4
+ [![Build Status](https://travis-ci.org/thoughtbot/griddler.png?branch=master)](https://travis-ci.org/thoughtbot/griddler)
5
+ [![Code Climate](https://codeclimate.com/github/thoughtbot/griddler.png)](https://codeclimate.com/github/thoughtbot/griddler)
6
+
7
+ ### Receive emails in your Rails app
8
+
9
+ Griddler is a Rails engine that provides an endpoint for services that convert
10
+ incoming emails to HTTP POST requests. It parses these POSTs and hands off a
11
+ built email object to a class implemented by you.
12
+
13
+ Tutorials
14
+ ---------
15
+
16
+ * SendGrid wrote a
17
+ [great tutorial](http://blog.sendgrid.com/receiving-email-in-your-rails-app-with-griddler/)
18
+ on integrating Griddler with your application.
19
+ * We have our own blog post on the subject over at
20
+ [Giant Robots](http://robots.thoughtbot.com/handle-incoming-email-with-griddler).
21
+
22
+ Installation
23
+ ------------
24
+
25
+ 1. Add `griddler` and an [adapter] gem to your application's Gemfile
26
+ and run `bundle install`.
27
+
28
+ [adapter]: #adapters
29
+
30
+ 2. A route is needed for the endpoint which receives `POST` messages. To add the
31
+ route, in `config/routes.rb` you may either use the provided routing method
32
+ `mount_griddler` or set the route explicitly. Examples:
33
+
34
+ ```ruby
35
+ # config/routes.rb
36
+
37
+ # mount using default path: /email_processor
38
+ mount_griddler
39
+
40
+ # mount using a custom path
41
+ mount_griddler('/email/incoming')
42
+
43
+ # the DIY approach:
44
+ post '/email_processor' => 'griddler/emails#create'
45
+ ```
46
+
47
+ ### Configuration Options
48
+
49
+ An initializer can be created to control some of the options in Griddler.
50
+ Defaults are shown below with sample overrides following. In
51
+ `config/initializers/griddler.rb`:
52
+
53
+ ```ruby
54
+ Griddler.configure do |config|
55
+ config.processor_class = EmailProcessor # CommentViaEmail
56
+ config.processor_method = :process # :create_comment (A method on CommentViaEmail)
57
+ config.reply_delimiter = '-- REPLY ABOVE THIS LINE --'
58
+ config.email_service = :sendgrid # :cloudmailin, :postmark, :mandrill, :mailgun
59
+ end
60
+ ```
61
+
62
+ | Option | Meaning
63
+ | ------ | -------
64
+ | `processor_class` | The class Griddler will use to handle your incoming emails.
65
+ | `processor_method` | The method Griddler will call on the processor class when handling your incoming emails.
66
+ | `reply_delimiter` | The string searched for that will split your body.
67
+ | `email_service` | Tells Griddler which email service you are using. The supported email service options are `:sendgrid` (the default), `:cloudmailin` (expects multipart format), `:postmark`, `:mandrill` and `:mailgun`. You will also need to have an appropriate [adapter] gem included in your Gemfile.
68
+
69
+ By default Griddler will look for a class named `EmailProcessor` with a method
70
+ named `process`, taking in one argument, a `Griddler::Email` instance
71
+ representing the incoming email. For example, in `./lib/email_processor.rb`:
72
+
73
+ ```ruby
74
+ class EmailProcessor
75
+ def initialize(email)
76
+ @email = email
77
+ end
78
+
79
+ def process
80
+ # all of your application-specific code here - creating models,
81
+ # processing reports, etc
82
+
83
+ # here's an example of model creation
84
+ user = User.find_by_email(@email.from[:email])
85
+ user.posts.create!(
86
+ subject: @email.subject,
87
+ body: @email.body
88
+ )
89
+ end
90
+ end
91
+ ```
92
+
93
+ Griddler::Email attributes
94
+ --------------------------
95
+
96
+ | Attribute | Description
97
+ | -------------- | -----------
98
+ | `#to` | An array of hashes containing recipient address information. See [Email Addresses](#email-addresses) for more information.
99
+ | `#from` | A hash containing the sender address information.
100
+ | `#cc` | An array of hashes containing cc email address information.
101
+ | `#subject` | The subject of the email message.
102
+ | `#body` | The full contents of the email body **unless** there is a line in the email containing the string `-- Reply ABOVE THIS LINE --`. In that case `.body` will contain everything before that line.
103
+ | `#raw_text` | The raw text part of the body.
104
+ | `#raw_html` | The raw html part of the body.
105
+ | `#raw_body` | The raw body information provided by the email service.
106
+ | `#attachments` | An array of `File` objects containing any attachments.
107
+ | `#headers` | A hash of headers parsed by `Mail::Header`.
108
+ | `#raw_headers` | The raw headers included in the message.
109
+
110
+ ### Email Addresses
111
+
112
+ Gridder::Email provides email addresses as hashes. Each hash will have the following
113
+ information of each recipient:
114
+
115
+ | Key | Value
116
+ | --- | -----
117
+ | `:token` | All the text before the email's "@". We've found that this is the most often used portion of the email address and consider it to be the token we'll key off of for interaction with our application.
118
+ | `:host` | All the text after the email's "@". This is important to filter the recipients sent to the application vs emails to other domains. More info below on the Upgrading to 0.5.0 section.
119
+ | `:email` | The email address of the recipient.
120
+ | `:full` | The whole recipient field (e.g., `Some User <hello@example.com>`).
121
+ | `:name` | The name of the recipient (e.g., `Some User`).
122
+
123
+ Testing In Your App
124
+ -------------------
125
+
126
+ You may want to create a factory for when testing the integration of Griddler
127
+ into your application. If you're using factory\_girl this can be accomplished
128
+ with the following sample factory:
129
+
130
+ ```ruby
131
+ factory :email, class: OpenStruct do
132
+ # Assumes Griddler.configure.to is :hash (default)
133
+ to [{ full: 'to_user@email.com', email: 'to_user@email.com', token: 'to_user', host: 'email.com', name: nil }]
134
+ from 'user@email.com'
135
+ subject 'email subject'
136
+ body 'Hello!'
137
+ attachments {[]}
138
+
139
+ trait :with_attachment do
140
+ attachments {[
141
+ ActionDispatch::Http::UploadedFile.new({
142
+ filename: 'img.png',
143
+ type: 'image/png',
144
+ tempfile: File.new("#{File.expand_path(File.dirname(__FILE__))}/fixtures/img.png")
145
+ })
146
+ ]}
147
+ end
148
+ end
149
+ ```
150
+
151
+ Bear in mind, if you plan on using the `:with_attachment` trait, that this
152
+ example assumes your factories are in `spec/factories.rb` and you have
153
+ an image file in `spec/fixtures/`.
154
+
155
+ To use it in your tests, build with `email = build(:email)`
156
+ or `email = build(:email, :with_attachment)`.
157
+
158
+ Adapters
159
+ --------
160
+
161
+ Depending on the service you want to use Griddler with, you'll need to add an
162
+ adapter gem in addition to `griddler`.
163
+
164
+ | Service | Adapter
165
+ | ------- | -------
166
+ | sendgrid | [griddler-sendgrid]
167
+ | mandrill | [griddler-mandrill]
168
+ | mailgun | [griddler-mailgun]
169
+ | postmark | [griddler-postmark]
170
+
171
+ [griddler-sendgrid]: https://github.com/thoughtbot/griddler-sendgrid
172
+ [griddler-mandrill]: https://github.com/wingrunr21/griddler-mandrill
173
+ [griddler-mailgun]: https://github.com/bradpauly/griddler-mailgun
174
+ [griddler-postmark]: https://github.com/r38y/griddler-postmark
175
+
176
+ Writing an Adapter
177
+ ------------------
178
+
179
+ Griddler can theoretically work with any email => POST service. In order to work
180
+ correctly, adapters need to have their POST parameters restructured.
181
+
182
+ `Griddler::Email` expects certain parameters to be in place for proper parsing
183
+ to occur. When writing an adapter, ensure that the `normalized_params` method of
184
+ your adapter returns a hash with these keys:
185
+
186
+ | Parameter | Contents
187
+ | --------- | --------
188
+ | `:to` | The recipient field
189
+ | `:from` | The sender field
190
+ | `:subject` | Email subject
191
+ | `:text` | The text body of the email
192
+ | `:html` | The html body of the email, nil or empty string if not present
193
+ | `:attachments` | Array of attachments to the email. Can be an empty array.
194
+ | `:headers` | The raw headers of the email. **Optional**.
195
+ | `:charsets` | A JSON string containing the character sets of the fields extracted from the message. **Optional**.
196
+
197
+ All keys are required unless otherwise stated.
198
+
199
+ Adapters should be provided as gems. If you write an adapter, let us know and we
200
+ will add it to this README. See [griddler-sendgrid] for an example
201
+ implementation.
202
+
203
+ Credits
204
+ -------
205
+
206
+ Griddler was written by Caleb Thompson and Joel Oliveira.
207
+
208
+ Thanks to our [contributors](https://github.com/thoughtbot/griddler/contributors)!
209
+
210
+ ![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
211
+
212
+ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ exit 1
8
+ end
9
+
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ require 'rspec/core/rake_task'
13
+ RSpec::Core::RakeTask.new(:spec)
14
+ task default: :spec
@@ -0,0 +1,25 @@
1
+ class Griddler::EmailsController < ActionController::Base
2
+ def create
3
+ normalized_params.each do |p|
4
+ process_email Griddler::Email.new(p)
5
+ end
6
+
7
+ head :ok
8
+ end
9
+
10
+ private
11
+
12
+ delegate :processor_class, :processor_method, :email_service, to: :griddler_configuration
13
+
14
+ def normalized_params
15
+ Array.wrap(email_service.normalize_params(params))
16
+ end
17
+
18
+ def process_email(email)
19
+ processor_class.new(email).public_send(processor_method)
20
+ end
21
+
22
+ def griddler_configuration
23
+ Griddler.configuration
24
+ end
25
+ end
@@ -0,0 +1 @@
1
+ require 'griddler'
data/lib/griddler.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'rails/engine' if defined?(::Rails::Engine)
2
+ require 'action_view'
3
+ require 'griddler/errors'
4
+ require 'griddler/engine'
5
+ require 'griddler/email'
6
+ require 'griddler/email_parser'
7
+ require 'griddler/configuration'
8
+ require 'griddler/route_extensions'
9
+ require 'griddler/adapter_registry'
@@ -0,0 +1,28 @@
1
+ module Griddler
2
+ class AdapterRegistry
3
+ DEFAULT_ADAPTER = :sendgrid
4
+
5
+ def initialize
6
+ @registry = {}
7
+ end
8
+
9
+ def register(adapter_name, adapter_class)
10
+ if adapter_name == DEFAULT_ADAPTER
11
+ @registry[:default] = adapter_class
12
+ end
13
+ @registry[adapter_name] = adapter_class
14
+ end
15
+
16
+ def [](adapter_name)
17
+ @registry[adapter_name]
18
+ end
19
+
20
+ def fetch(key, &block)
21
+ @registry.fetch(key, &block)
22
+ end
23
+ end
24
+
25
+ def self.adapter_registry
26
+ @adapter_registry ||= AdapterRegistry.new
27
+ end
28
+ end
@@ -0,0 +1,52 @@
1
+ module Griddler
2
+ @@configuration = nil
3
+
4
+ def self.configure
5
+ @@configuration = Configuration.new
6
+
7
+ if block_given?
8
+ yield configuration
9
+ end
10
+
11
+ configuration
12
+ end
13
+
14
+ def self.configuration
15
+ @@configuration || configure
16
+ end
17
+
18
+ class Configuration
19
+ attr_accessor :processor_class, :processor_method, :reply_delimiter
20
+
21
+ def processor_class
22
+ @processor_class ||=
23
+ begin
24
+ if Kernel.const_defined?(:EmailProcessor)
25
+ EmailProcessor
26
+ else
27
+ raise NameError.new(<<-ERROR.strip_heredoc, 'EmailProcessor')
28
+ To use Griddler, you must either define `EmailProcessor` or configure a
29
+ different processor. See https://github.com/thoughtbot/griddler#defaults for
30
+ more information.
31
+ ERROR
32
+ end
33
+ end
34
+ end
35
+
36
+ def processor_method
37
+ @processor_method ||= :process
38
+ end
39
+
40
+ def reply_delimiter
41
+ @reply_delimiter ||= 'Reply ABOVE THIS LINE'
42
+ end
43
+
44
+ def email_service
45
+ @email_service_adapter ||= Griddler.adapter_registry[:default]
46
+ end
47
+
48
+ def email_service=(new_email_service)
49
+ @email_service_adapter = Griddler.adapter_registry.fetch(new_email_service) { raise Griddler::Errors::EmailServiceAdapterNotFound }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,84 @@
1
+ require 'htmlentities'
2
+
3
+ module Griddler
4
+ class Email
5
+ include ActionView::Helpers::SanitizeHelper
6
+ attr_reader :to, :from, :cc, :subject, :body, :raw_body, :raw_text, :raw_html,
7
+ :headers, :raw_headers, :attachments
8
+
9
+ def initialize(params)
10
+ @params = params
11
+
12
+ @to = recipients(:to)
13
+ @from = extract_address(params[:from])
14
+ @subject = params[:subject]
15
+
16
+ @body = extract_body
17
+ @raw_text = params[:text]
18
+ @raw_html = params[:html]
19
+ @raw_body = @raw_text.presence || @raw_html
20
+
21
+ @headers = extract_headers
22
+
23
+ @cc = recipients(:cc)
24
+
25
+ @raw_headers = params[:headers]
26
+
27
+ @attachments = params[:attachments]
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :params
33
+
34
+ def config
35
+ @config ||= Griddler.configuration
36
+ end
37
+
38
+ def recipients(type)
39
+ params[type].to_a.map { |recipient| extract_address(recipient) }
40
+ end
41
+
42
+ def extract_address(address)
43
+ EmailParser.parse_address(address)
44
+ end
45
+
46
+ def extract_body
47
+ EmailParser.extract_reply_body(text_or_sanitized_html)
48
+ end
49
+
50
+ def extract_headers
51
+ EmailParser.extract_headers(params[:headers])
52
+ end
53
+
54
+ def extract_cc_from_headers(headers)
55
+ EmailParser.extract_cc(headers)
56
+ end
57
+
58
+ def text_or_sanitized_html
59
+ text = clean_text(params.fetch(:text, ''))
60
+ text.presence || clean_html(params.fetch(:html, '')).presence
61
+ end
62
+
63
+ def clean_text(text)
64
+ clean_invalid_utf8_bytes(text)
65
+ end
66
+
67
+ def clean_html(html)
68
+ cleaned_html = clean_invalid_utf8_bytes(html)
69
+ cleaned_html = strip_tags(cleaned_html)
70
+ cleaned_html = HTMLEntities.new.decode(cleaned_html)
71
+ cleaned_html
72
+ end
73
+
74
+ def clean_invalid_utf8_bytes(text)
75
+ if !text.valid_encoding?
76
+ text = text
77
+ .force_encoding('ISO-8859-1')
78
+ .encode('UTF-8')
79
+ end
80
+
81
+ text
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,91 @@
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
+ require 'mail'
12
+
13
+ module Griddler::EmailParser
14
+ def self.parse_address(full_address)
15
+ email_address = extract_email_address(full_address)
16
+ name = extract_name(full_address)
17
+ token, host = split_address(email_address)
18
+ {
19
+ token: token,
20
+ host: host,
21
+ email: email_address,
22
+ full: full_address,
23
+ name: name,
24
+ }
25
+ end
26
+
27
+ def self.extract_reply_body(body)
28
+ if body.blank?
29
+ ""
30
+ else
31
+ remove_reply_portion(body)
32
+ .split(/[\r]*\n/)
33
+ .reject do |line|
34
+ line =~ /^\s+>/ ||
35
+ line =~ /^\s*Sent from my /
36
+ end.
37
+ join("\n").
38
+ strip
39
+ end
40
+ end
41
+
42
+ def self.extract_headers(raw_headers)
43
+ header_fields = Mail::Header.new(raw_headers).fields
44
+
45
+ header_fields.inject({}) do |header_hash, header_field|
46
+ header_hash[header_field.name.to_s] = header_field.value.to_s
47
+ header_hash
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def self.reply_delimeter_regex
54
+ delimiter = Array(Griddler.configuration.reply_delimiter).join('|')
55
+ %r{#{delimiter}}
56
+ end
57
+
58
+ def self.extract_email_address(full_address)
59
+ full_address.split('<').last.delete('>').strip
60
+ end
61
+
62
+ def self.extract_name(full_address)
63
+ full_address = full_address.strip
64
+ name = full_address.split('<').first.strip
65
+ if name.present? && name != full_address
66
+ name
67
+ end
68
+ end
69
+
70
+ def self.split_address(email_address)
71
+ email_address.try :split, '@'
72
+ end
73
+
74
+ def self.regex_split_points
75
+ [
76
+ reply_delimeter_regex,
77
+ /^\s*[-]+\s*Original Message\s*[-]+\s*$/i,
78
+ /^\s*--\s*$/,
79
+ /^\s*\>?\s*On.*\r?\n?\s*.*\s*wrote:\r?\n?$/,
80
+ /On.*wrote:/,
81
+ /From:.*$/i,
82
+ /^\s*\d{4}\/\d{1,2}\/\d{1,2}\s.*\s<.*>?$/i
83
+ ]
84
+ end
85
+
86
+ def self.remove_reply_portion(body)
87
+ regex_split_points.inject(body) do |result, split_point|
88
+ result.split(split_point).first || ""
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,9 @@
1
+ module Griddler
2
+ class Engine < ::Rails::Engine
3
+ initializer 'griddler.routes',
4
+ after: 'action_dispatch.prepare_dispatcher' do |app|
5
+
6
+ ActionDispatch::Routing::Mapper.send :include, Griddler::RouteExtensions
7
+ end
8
+ end if defined?(::Rails::Engine)
9
+ end
@@ -0,0 +1,9 @@
1
+ module Griddler
2
+ class Error < StandardError
3
+ end
4
+
5
+ module Errors
6
+ class EmailServiceAdapterNotFound < Griddler::Error
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Griddler
2
+ module RouteExtensions
3
+ def mount_griddler(path='/email_processor')
4
+ post path => 'griddler/emails#create', as: :email_processor
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,89 @@
1
+ require 'action_dispatch'
2
+
3
+ module Griddler::Testing
4
+ def upload_1
5
+ @upload_1 ||= UploadedImage.new('photo1.jpg').file
6
+ end
7
+
8
+ def upload_2
9
+ @upload_2 ||= UploadedImage.new('photo2.jpg').file
10
+ end
11
+
12
+ def normalize_params(params)
13
+ Griddler::Sendgrid::Adapter.normalize_params(params)
14
+ end
15
+
16
+ class UploadedImage
17
+ def initialize(name)
18
+ @name = name
19
+ end
20
+
21
+ def file
22
+ ActionDispatch::Http::UploadedFile.new({
23
+ filename: @name,
24
+ type: 'image/jpeg',
25
+ tempfile: fixture_file
26
+ })
27
+ end
28
+
29
+ private
30
+
31
+ def fixture_file
32
+ cwd = File.expand_path File.dirname(__FILE__)
33
+ File.new(File.join(cwd, '..', '..', 'spec', 'fixtures', @name))
34
+ end
35
+ end
36
+ end
37
+
38
+ shared_examples_for 'Griddler adapter' do |adapter, service_params|
39
+ it 'adapts params to expected values' do
40
+ Griddler.configuration.email_service = adapter
41
+
42
+ normalized_params = Griddler.configuration.email_service.normalize_params(service_params)
43
+
44
+ Array.wrap(normalized_params).each do |params|
45
+ email = Griddler::Email.new(params)
46
+
47
+ expect(email.to).to eq([{
48
+ token: 'hi',
49
+ host: 'example.com',
50
+ full: 'Hello World <hi@example.com>',
51
+ email: 'hi@example.com',
52
+ name: 'Hello World',
53
+ }])
54
+ expect(email.cc).to eq [{
55
+ token: 'emily',
56
+ host: 'example.com',
57
+ email: 'emily@example.com',
58
+ full: 'emily@example.com',
59
+ name: nil
60
+ }]
61
+ end
62
+ end
63
+ end
64
+
65
+ RSpec::Matchers.define :be_normalized_to do |expected|
66
+ failure_message do |actual|
67
+ message = ""
68
+ expected.each do |k, v|
69
+ if actual[k] != expected[k]
70
+ message << "expected :#{k} to be normalized to #{expected[k].inspect}, "\
71
+ "but received #{actual[k].inspect}\n"
72
+ end
73
+ end
74
+ message
75
+ end
76
+
77
+ description do
78
+ "be normalized to #{expected}"
79
+ end
80
+
81
+ match do |actual|
82
+ expected.each do |k, v|
83
+ case v
84
+ when Regexp then expect(actual[k]).to =~ v
85
+ else expect(actual[k]).to === v
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,3 @@
1
+ module Griddler
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: griddler-acd
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Caleb Thompson
8
+ - Joel Oliveira
9
+ - thoughtbot
10
+ - Swift
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2014-08-20 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: htmlentities
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: sqlite3
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: pry
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: jquery-rails
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ description:
87
+ email:
88
+ - cjaysson@gmail.com
89
+ - joel@thoughtbot.com
90
+ - theycallmeswift@gmail.com
91
+ executables: []
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - LICENSE
96
+ - README.md
97
+ - Rakefile
98
+ - app/controllers/griddler/emails_controller.rb
99
+ - config/initializers/griddler.rb
100
+ - lib/griddler.rb
101
+ - lib/griddler/adapter_registry.rb
102
+ - lib/griddler/configuration.rb
103
+ - lib/griddler/email.rb
104
+ - lib/griddler/email_parser.rb
105
+ - lib/griddler/engine.rb
106
+ - lib/griddler/errors.rb
107
+ - lib/griddler/route_extensions.rb
108
+ - lib/griddler/testing.rb
109
+ - lib/griddler/version.rb
110
+ homepage: http://thoughtbot.com
111
+ licenses: []
112
+ metadata: {}
113
+ post_install_message: |
114
+ When upgrading from a Griddler version previous to 0.5.0, it is important that
115
+ you view https://github.com/thoughtbot/griddler/#upgrading-to-griddler-050 for
116
+ upgrade information.
117
+ rdoc_options: []
118
+ require_paths:
119
+ - app
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: 1.9.2
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.3.0
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: SendGrid Parse API client Rails Engine
137
+ test_files: []
138
+ has_rdoc: