griddler-acd 1.0.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: 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: