mailcannon 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36ee9ad3e66d2643ea8c836320032d2c4bbcf9e1
4
- data.tar.gz: ab5103437e1bfa9d43eec27014dfcc43590af0c4
3
+ metadata.gz: 3959f5796552901e917b0be3bb6fb9aa24764385
4
+ data.tar.gz: d7e17ded5d4953acb0f5f1120d4f0ed3b5f70a07
5
5
  SHA512:
6
- metadata.gz: 05c4f51be1809c2ce8c0960216c4392a3214d4dc0c8de368db77ec36482ca13f8ce91ac4779e6233d15eaf3181792a4bd8a77e659353a3584b177f1e5ac09b3b
7
- data.tar.gz: ee3bc50462bcf0e27a8fabe9ee2c63f500f30b4063dcf997ac3551d5883a43140027945386e8a225b11531c0c342935460099d027ec1582fb4eb9cd39564df72
6
+ metadata.gz: 677148b0354d35fa556dd58890ea96b7991b28057fcc7dadf1df893805fb1db242d02b2eca74552dcb29fc40b9cb7005e8433479dbf58496214c7a0461d3204c
7
+ data.tar.gz: d6308cd543f6f44779d90d5ec62c4a799189ceebc4e4f653ffd4b368e62db775bd0b656c7192b7ecf7a5373bdf0f75aa2caad1edc4580bc98616d9b267a794e7
data/.gitignore CHANGED
@@ -5,3 +5,6 @@ doc/
5
5
  .yardoc/
6
6
  coverage/
7
7
  .coveralls.yml
8
+ pkg/
9
+ Gemfile.lock
10
+ config/mailcannon.yml
data/.travis.yml CHANGED
@@ -2,7 +2,7 @@ rvm:
2
2
  - rbx-2.1.1
3
3
  - 2.0.0-p353
4
4
  # - 1.9.3-p448
5
- - jruby-19mode
5
+ - jruby-20mode
6
6
  before_install:
7
7
  - gem install bundler
8
8
  services:
data/Gemfile CHANGED
@@ -1,16 +1,15 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'redis'
4
- gem 'mongoid'
4
+ gem 'activemodel'
5
+ gem 'mongoid', '3.1.6'
5
6
  gem 'sidekiq'
6
7
  gem 'sendgrid_webapi'
7
- #gem 'sendgrid_toolkit'
8
- gem 'librato-metrics'
8
+ #gem 'librato-metrics'
9
9
  gem 'rubysl', platform: :rbx
10
10
  gem 'jruby-openssl', platform: :jruby
11
- #for SendgridWeb adapter
12
- gem "yajl-ruby", :platforms=>[:rbx,:ruby]
13
- gem "json-schema"
11
+ gem 'yajl-ruby', :platforms=>[:rbx,:ruby]
12
+ gem 'json-schema'
14
13
 
15
14
  group :development do
16
15
  gem 'rake'
@@ -24,7 +23,7 @@ group :test do
24
23
  gem 'rspec-mocks'
25
24
  gem 'rspec-expectations'
26
25
  gem 'database_cleaner'
27
- gem 'factory_girl'
26
+ gem 'factory_girl', '4.2.0'
28
27
  gem 'vcr'
29
28
  gem 'webmock', '>= 1.8.0', '< 1.16'
30
29
  gem 'coveralls', platform: :ruby, require: false
data/Rakefile CHANGED
@@ -13,6 +13,7 @@ end
13
13
  desc 'Opens a Pry/IRB session with the environment loaded'
14
14
  task :console => :environment do
15
15
  #gotcha!
16
+ Mongoid.load!("spec/support/mongoid.yml", 'development')
16
17
  binding.pry
17
18
  end
18
19
 
data/Readme.md CHANGED
@@ -29,11 +29,28 @@ Create a `MailCannon::Envelope`:
29
29
  ```ruby
30
30
  envelope = MailCannon::Envelope.create(
31
31
  from: 'test@mailcannon.com',
32
- to: 'lucasmartins@railsnapraia.com',
32
+ to: [{email: 'lucasmartins@railsnapraia.com', name: 'Lucas Martins'}],
33
33
  subject: 'Test',
34
- mail: MailCannon::Mail.create(text: 'you will see this when no HTML reader is available', html: 'this should be an HTML'))
35
- envelope.send!
34
+ mail: MailCannon::Mail.new(text: 'you will see this when no HTML reader is available', html: 'this should be an HTML'))
35
+
36
+ envelope.post!
37
+ ```
38
+
39
+ ### Campaign abstraction
40
+
41
+ Create a `MailCannon::Envelope`:
42
+ ```ruby
43
+ envelope_bag = MailCannon::EnvelopeBag.new(integration_code: 'my-cool-campaign')
44
+ envelope = MailCannon::Envelope.create(
45
+ from: 'test@mailcannon.com',
46
+ to: [{email: 'lucasmartins@railsnapraia.com', name: 'Lucas Martins'}],
47
+ subject: 'Test',
48
+ mail: MailCannon::Mail.new(text: 'you will see this when no HTML reader is available', html: 'this should be an HTML'))
49
+ envelope_bag.push envelope
50
+ # envelope_bag.push ...
51
+ envelope_bag.post!
36
52
  ```
53
+
37
54
  ### Configuration file
38
55
  If you are on Rails, run the following command to generate a config file:
39
56
 
@@ -11,7 +11,7 @@ module MailCannon::Adapter::SendgridWeb
11
11
  response = send_single_email
12
12
  end
13
13
  self.after_sent(successfully_sent?(response))
14
- return response
14
+ return successfully_sent?(response)
15
15
  end
16
16
 
17
17
  def send_bulk!
@@ -34,14 +34,18 @@ module MailCannon::Adapter::SendgridWeb
34
34
 
35
35
  def prepare_xsmtpapi!
36
36
  validate_envelope!
37
- self.xsmtpapi = build_xsmtpapi({'to'=>self.to},{'sub'=>self.substitutions})
37
+ self.xsmtpapi = {} if self.xsmtpapi.nil?
38
+ self.xsmtpapi = self.xsmtpapi.merge(build_xsmtpapi({'to'=>self.to},{'sub'=>self.substitutions}))
38
39
  validate_xsmtpapi!
40
+ self.save!
39
41
  end
40
42
 
41
43
  def build_xsmtpapi(recipients,subs)
42
44
  xsmtpapi = {}
43
45
  to = []
44
- recipients['to'].each do |h|
46
+ recipients.symbolize_keys!
47
+ recipients[:to].each do |h|
48
+ h.symbolize_keys!
45
49
  to.push h[:email]
46
50
  end
47
51
  xsmtpapi.merge!({'to' => to})
@@ -71,9 +75,12 @@ module MailCannon::Adapter::SendgridWeb
71
75
  end
72
76
 
73
77
  def send_single_email
78
+ if self.xsmtpapi!=nil
79
+ logger.warn "Single emails do not support X-SMTPAPI! #{self.id}"
80
+ end
74
81
  api_client.mail.send(
75
- :to => self.to.first[:email],
76
- :toname => self.to.first[:name],
82
+ :to => self.to.first['email'],
83
+ :toname => self.to.first['name'],
77
84
  :subject => self.subject,
78
85
  :text => self.mail.text,
79
86
  :html => self.mail.html,
@@ -86,10 +93,8 @@ module MailCannon::Adapter::SendgridWeb
86
93
 
87
94
  def send_multiple_emails
88
95
  prepare_xsmtpapi!
89
-
90
96
  api_client.mail.send(
91
97
  :to => self.from,
92
- #:toname => self.to.first[:name],
93
98
  :subject => self.subject,
94
99
  :text => self.mail.text,
95
100
  :html => self.mail.html,
@@ -6,12 +6,11 @@ class MailCannon::Envelope
6
6
 
7
7
  embeds_one :mail
8
8
  embeds_many :stamps
9
-
10
- field :group_id, type: Bignum # create sparse Index for this field, put this in RDoc
9
+ belongs_to :envelope_bag
10
+
11
11
  field :from, type: String
12
12
  field :from_name, type: String
13
- field :to, type: Array # of hashes
14
- field :to_name, type: Array # strings
13
+ field :to, type: Array # of hashes. [{email: '', name: ''},...]
15
14
  field :substitutions, type: Hash # of hashes
16
15
  field :subject, type: String
17
16
  field :bcc, type: String
@@ -21,24 +20,31 @@ class MailCannon::Envelope
21
20
 
22
21
  validates :from, :to, :subject, :mail, presence: true
23
22
  validates_associated :mail
24
-
25
- after_create :post_envelope!
26
23
 
27
24
  # Post this Envelope!
28
25
  def post_envelope!
29
26
  self.save if self.changed?
30
- self.stamp! MailCannon::Event::New.stamp
27
+ self.stamp! MailCannon::Event::Posted.stamp
31
28
  if validate_xsmtpapi(self.xsmtpapi)
32
- MailCannon::Barrel.perform_async(self.id)
29
+ if MailCannon.config['waiting_time'] && MailCannon.config['waiting_time'].to_i>0
30
+ MailCannon::Barrel.perform_in(MailCannon.config['waiting_time'].seconds,self.id)
31
+ else
32
+ MailCannon::Barrel.perform_async(self.id)
33
+ end
33
34
  else
34
35
  raise 'Invalid xsmtpapi hash!'
35
36
  end
36
37
  end
38
+ alias_method :"post!",:"post_envelope!"
37
39
 
38
40
  # Stamp this Envelope with code.
39
- def stamp!(code)
41
+ def stamp!(code,recipient=nil)
40
42
  self.class.valid_code_kind?(code)
41
- self.stamps << MailCannon::Stamp.from_code(code)
43
+ unless self.persisted?
44
+ logger.warn "You're trying to save the Stamp with an unsaved Envelope! Auto-saving Envelope."
45
+ self.save
46
+ end
47
+ self.stamps.create(code: MailCannon::Stamp.from_code(code).code, recipient: recipient)
42
48
  end
43
49
 
44
50
  # Callback to be run after the Envelope has been processed.
@@ -49,6 +55,14 @@ class MailCannon::Envelope
49
55
  self.mail=nil # to avoid reload
50
56
  end
51
57
  end
58
+
59
+ def posted?
60
+ if self.stamps.where(code: 0).count > 0
61
+ true
62
+ else
63
+ false
64
+ end
65
+ end
52
66
 
53
67
  private
54
68
  def self.valid_code_kind?(code)
@@ -0,0 +1,27 @@
1
+ # Where the magic happens, the Envelope is responsible for keeping the information necessary to send the email(s) and holding the Stamps related to mailing Events.
2
+ class MailCannon::EnvelopeBag
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+
6
+ has_many :envelopes, autosave: true
7
+ field :integration_code, type: String # Used to link your own app models to the Bag.
8
+
9
+ def push(envelope)
10
+ self.envelopes.push envelope
11
+ end
12
+ alias_method :"add",:"push"
13
+
14
+ # Post this Envelope!
15
+ def post_envelopes!
16
+ return false if envelopes.size==0
17
+ self.save if self.changed?
18
+ envelopes.each do |e|
19
+ unless e.posted?
20
+ e.post!
21
+ end
22
+ end
23
+ true
24
+ end
25
+ alias_method :"post!",:"post_envelopes!"
26
+
27
+ end
@@ -2,7 +2,7 @@
2
2
  class MailCannon::Event
3
3
 
4
4
  EVENTS = [
5
- 'new',
5
+ 'posted',
6
6
  'processed',
7
7
  'delivered',
8
8
  'open',
@@ -0,0 +1,10 @@
1
+ module MailCannon
2
+ refine Hash do
3
+ def symbolize_keys!
4
+ keys.each do |key|
5
+ self[(key.to_sym rescue key) || key] = delete(key)
6
+ end
7
+ self
8
+ end
9
+ end
10
+ end
@@ -3,7 +3,7 @@ class MailCannon::Mail
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
5
 
6
- embedded_in :envelope#, index: true
6
+ embedded_in :envelope
7
7
 
8
8
  field :text, type: String
9
9
  field :html, type: String
@@ -3,11 +3,11 @@ class MailCannon::Stamp
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
5
 
6
- belongs_to :envelope, index: true
6
+ embedded_in :envelope
7
7
 
8
8
  field :code, type: Integer, default: 0
9
9
  field :recipient # email address for this "notification"
10
- validates :code, :envelope, presence: true
10
+ validates :code, presence: true
11
11
 
12
12
  # Returns the Event for this Stamp.
13
13
  def event
@@ -3,7 +3,7 @@ module MailCannon
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 0
6
- PATCH = 4
6
+ PATCH = 5
7
7
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
8
  end
9
9
  end
@@ -3,24 +3,20 @@ class MailCannon::Barrel
3
3
  include Sidekiq::Worker
4
4
 
5
5
  def perform(envelope_id)
6
- aggregator = Librato::Metrics::Aggregator.new
7
- aggregator.time 'mailcannon.barrel.perform' do
8
- envelope_id = envelope_id['$oid'] if envelope_id['$oid']
9
- puts "sending MailCannon::Envelope.find('#{envelope_id}')"
10
-
11
- begin
12
- envelope = MailCannon::Envelope.find(envelope_id)
13
- if envelope.valid?
14
- response = envelope.send!
15
- unless response==true
16
- raise response
17
- end
18
- end
19
- rescue Exception => e
20
- puts "unable to send MailCannon::Envelope.find(#{envelope_id})"
21
- puts e.backtrace
6
+ envelope_id = envelope_id['$oid'] if envelope_id['$oid']
7
+ logger.info "sending MailCannon::Envelope.find('#{envelope_id}')"
8
+ begin
9
+ envelope = MailCannon::Envelope.find(envelope_id.to_s)
10
+ if envelope.valid?
11
+ response = envelope.send!
12
+ unless response==true
13
+ raise response
14
+ end
22
15
  end
16
+ rescue Mongoid::Errors::DocumentNotFound
17
+ logger.error "unable to find the document MailCannon::Envelope.find('#{envelope_id}')"
18
+ rescue Exception => e
19
+ logger.error "unable to send MailCannon::Envelope.find('#{envelope_id}')\n#{e.backtrace}"
23
20
  end
24
- aggregator.submit
25
21
  end
26
22
  end
data/lib/mailcannon.rb CHANGED
@@ -2,14 +2,13 @@ require 'yaml'
2
2
  require 'openssl'
3
3
  require 'bundler'
4
4
  require 'json'
5
+ #should use Bundler
5
6
  require 'mongoid'
6
-
7
- case ENV['RACK_ENV']
8
- when 'production'
9
- Bundler.require(:default)
10
- else
11
- Bundler.require(:default,:development)
12
- end
7
+ require 'sidekiq'
8
+ require 'sendgrid_webapi'
9
+ require 'yajl-ruby' if RUBY_PLATFORM=='ruby'
10
+ require 'jruby-openssl' if RUBY_PLATFORM=='jruby'
11
+ #require 'librato-metrics'
13
12
 
14
13
  Encoding.default_internal = "utf-8"
15
14
  Encoding.default_external = "utf-8"
@@ -17,6 +16,7 @@ Encoding.default_external = "utf-8"
17
16
  module MailCannon
18
17
  require_relative 'mailcannon/adapter'
19
18
  require_relative 'mailcannon/adapters/sendgrid_web'
19
+ require_relative 'mailcannon/envelope_bag'
20
20
  require_relative 'mailcannon/envelope'
21
21
  require_relative 'mailcannon/mail'
22
22
  require_relative 'mailcannon/stamp'
@@ -24,16 +24,16 @@ module MailCannon
24
24
  require_relative 'mailcannon/workers/barrel'
25
25
  require_relative 'mailcannon/version'
26
26
 
27
- self.warmode if ENV['MAILCANNON_MODE']=='war'
27
+ #Librato::Metrics.authenticate(ENV['LIBRATO_USER'], ENV['LIBRATO_TOKEN']) if ENV['LIBRATO_TOKEN'] && ENV['LIBRATO_USER'] # change to initializer
28
28
 
29
29
  # To be used with caution
30
30
  def self.warmode
31
- Bundler.require(:default)
32
- Mongoid.load!("config/mongoid.yml", ENV['RACK_ENV']) # change to env URL
33
- Librato::Metrics.authenticate(ENV['LIBRATO_USER'], ENV['LIBRATO_TOKEN']) if ENV['LIBRATO_TOKEN'] && ENV['LIBRATO_USER'] # change to initializer
34
- redis_uri = URI.parse(ENV['REDIS_URL'])
35
- $redis = Redis.new(host: redis_uri.host, port: redis_uri.port)
31
+ #Mongoid.load!("config/mongoid.yml", ENV['RACK_ENV']||'development') # change to env URL
32
+ Sidekiq.configure_client do |config|
33
+ config.redis = { :namespace => 'mailcannon', :size => 1, :url => ENV['REDIS_URL'] }
34
+ end
36
35
  end
36
+ self.warmode if ENV['MAILCANNON_MODE']=='war'
37
37
 
38
38
  # Returns the config Hash
39
39
  def self.config(root_dir=nil)
@@ -52,4 +52,24 @@ module MailCannon
52
52
  YAML.load(erb)
53
53
  end
54
54
 
55
+ # alias method
56
+ def logger
57
+ MailCannon.logger
58
+ end
59
+
60
+ # Returns the lib logger object
61
+ def self.logger
62
+ @logger || initialize_logger
63
+ end
64
+
65
+ # Initializes logger with mailcannon setup
66
+ def self.initialize_logger(log_target = STDOUT)
67
+ oldlogger = @logger
68
+ @logger = Logger.new(log_target)
69
+ @logger.level = Logger::INFO
70
+ @logger.progname = 'mailcannon'
71
+ oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
72
+ @logger
73
+ end
74
+
55
75
  end
data/mailcannon.gemspec CHANGED
@@ -18,6 +18,25 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
+
22
+ if RUBY_ENGINE=='rbx'
23
+ s.add_dependency 'rubysl'
24
+ end
25
+ if RUBY_PLATFORM=='ruby'
26
+ s.add_dependency 'yajl-ruby'
27
+ end
28
+ if RUBY_PLATFORM=='jruby'
29
+ s.add_dependency 'jruby-openssl'
30
+ end
31
+
32
+ s.add_runtime_dependency 'activemodel', '>= 3.0.0'
33
+
34
+ #s.add_dependency 'librato-metrics'
35
+ s.add_dependency 'redis'
36
+ s.add_dependency 'mongoid'
37
+ s.add_dependency 'sidekiq'
38
+ s.add_dependency 'sendgrid_webapi'
39
+ s.add_dependency 'json-schema'
21
40
 
22
41
  s.add_development_dependency "vcr"
23
42
  s.add_development_dependency "rspec"
@@ -0,0 +1,13 @@
1
+ FactoryGirl.define do
2
+ factory :empty_envelope_bag, class: MailCannon::EnvelopeBag do
3
+ envelopes []
4
+
5
+ factory :filled_envelope_bag, class: MailCannon::EnvelopeBag do
6
+ # This factories will be inconsistent on tests running post! at the same spec
7
+ envelope_a = FactoryGirl.build(:envelope)
8
+ envelope_b = FactoryGirl.build(:envelope_multi)
9
+ envelopes [envelope_a,envelope_b]
10
+ end
11
+
12
+ end
13
+ end
@@ -4,7 +4,7 @@ describe "shoot 1k emails!" do
4
4
  let(:envelope) { build(:envelope_multi_1k) }
5
5
  it "sends http request for Sendgrid web API" do
6
6
  VCR.use_cassette('mailcannon_integration_1k') do
7
- expect(envelope.send_bulk!).to eq({"message"=>"success"})
7
+ expect(envelope.send_bulk!).to be_true
8
8
  end
9
9
  end
10
10
  end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ describe 'X-SMTPAPI compatibility' do
4
+ describe "xsmtpapi for bulk" do
5
+ context "generates expected xsmtpapi for #post!" do
6
+ let(:envelope) { build(:envelope_multi, xsmtpapi: { "unique_args" => { "userid" => "1123", "template" => "welcome" }}) }
7
+
8
+ it "returns true" do
9
+ VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
10
+ Sidekiq::Testing.inline! do
11
+ envelope.post!
12
+ end
13
+ end
14
+ envelope.reload # content is changed inside the Adapter module
15
+ #binding.pry
16
+ expect(envelope.xsmtpapi['to']).to match_array ['mailcannon@railsnapraia.com', 'contact@railsonthebeach.com', 'lucasmartins@railsnapraia.com']
17
+ end
18
+ end
19
+ end
20
+ end
@@ -5,7 +5,7 @@ describe MailCannon::Adapter::SendgridWeb do
5
5
  let(:envelope) { build(:envelope) }
6
6
  it "sends http request for Sendgrid web API" do
7
7
  VCR.use_cassette('mailcannon_adapter_sendgrid_send') do
8
- expect(envelope.send!).to eq({"message"=>"success"})
8
+ expect(envelope.send!).to be_true
9
9
  end
10
10
  end
11
11
  it "calls after_sent callback" do
@@ -20,7 +20,7 @@ describe MailCannon::Adapter::SendgridWeb do
20
20
  let(:envelope) { build(:envelope_multi) }
21
21
  it "sends http request for Sendgrid web API" do
22
22
  VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
23
- expect(envelope.send_bulk!).to eq({"message"=>"success"})
23
+ expect(envelope.send_bulk!).to be_true
24
24
  end
25
25
  end
26
26
  end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ describe MailCannon::EnvelopeBag do
4
+ describe "#post!" do
5
+ context "when it has no Envelopes" do
6
+ let(:envelope_bag) { build(:empty_envelope_bag) }
7
+ it "does not raise error" do
8
+ expect{envelope_bag.post!}.not_to raise_error
9
+ end
10
+ it "returns false" do
11
+ expect(envelope_bag.post!).to be_false
12
+ end
13
+ end
14
+ context "when it has Envelopes" do
15
+ let(:envelope_bag) { build(:filled_envelope_bag) }
16
+ it "does not raise error" do
17
+ expect{envelope_bag.post!}.not_to raise_error
18
+ end
19
+ it "returns true" do
20
+ expect(envelope_bag.post!).to be_true
21
+ end
22
+ end
23
+ context "when it has Envelopes (isolating Bag)" do
24
+ let(:envelope_bag) { MailCannon::EnvelopeBag.new }
25
+ it "posts Envelopes in the bag" do
26
+ envelope_bag.envelopes.push build(:envelope)
27
+ envelope_bag.envelopes.push build(:envelope_multi)
28
+ expect(envelope_bag.envelopes.size).to eq(2)
29
+ # Rspec can't do 'any_instance.should_receive' twice.
30
+ envelope_bag.envelopes.each do |envelope|
31
+ expect(envelope).to receive(:post!)
32
+ end
33
+ envelope_bag.post!
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,10 +1,11 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe MailCannon::Envelope do
4
- describe "initialize" do
4
+ describe "initialization" do
5
5
  let(:envelope) { build(:envelope) }
6
- it "creates a new Stamp" do
7
- expect{ envelope.save }.to change{envelope.stamps.size}.by(1)
6
+ it "has no Stamps" do
7
+ envelope.save
8
+ expect(envelope.stamps.size).to eq(0)
8
9
  end
9
10
  context "check for expected adapter behavior" do
10
11
  it "implements send! behavior" do
@@ -52,6 +53,42 @@ describe MailCannon::Envelope do
52
53
  end
53
54
  end
54
55
 
56
+ describe "#posted?" do
57
+ context "when already posted" do
58
+ let(:envelope) { build(:envelope) }
59
+ it "returns true" do
60
+ VCR.use_cassette('mailcannon_adapter_sendgrid_send') do
61
+ envelope.post!
62
+ end
63
+ expect(envelope.posted?).to be_true
64
+ end
65
+ end
66
+ context "when not yet posted" do
67
+ let(:envelope) { build(:envelope) }
68
+ it "returns false" do
69
+ expect(envelope.posted?).to be_false
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "xsmtpapi" do
75
+ context "keep xsmtpapi arguments after #post!" do
76
+ let(:envelope) { build(:envelope_multi, xsmtpapi: { "unique_args" => { "userid" => "1123", "template" => "welcome" }}) }
77
+
78
+ it "returns true" do
79
+ VCR.use_cassette('mailcannon_adapter_sendgrid_send_bulk') do
80
+ Sidekiq::Testing.inline! do
81
+ envelope.post!
82
+ end
83
+ end
84
+ envelope.reload # content is changed inside the Adapter module
85
+ expect(envelope.xsmtpapi).to have_key("unique_args")
86
+ expect(envelope.xsmtpapi).to have_key("to")
87
+ expect(envelope.xsmtpapi).to have_key("sub")
88
+ end
89
+ end
90
+ end
91
+
55
92
  describe "#after_sent" do
56
93
  let(:envelope) { create(:envelope) }
57
94
  it "creates a Processed Stamp" do
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ describe MailCannon::Barrel do
4
+ describe "perform" do
5
+ let(:envelope) { create(:envelope) }
6
+ it "creates a new Stamp" do
7
+ VCR.use_cassette('mailcannon_barrel_envelope_post') do
8
+ expect{ envelope.post! }.to change{envelope.stamps.size}.by(1)
9
+ end
10
+ expect(envelope.stamps.first.code).to eq(0) # 0=posted
11
+ end
12
+ it "looks for an existing MongoDB document" do
13
+ VCR.use_cassette('mailcannon_barrel_envelope_post') do
14
+ Sidekiq::Testing.inline! do
15
+ expect{envelope.post!}.not_to raise_error(Mongoid::Errors::DocumentNotFound)
16
+ end
17
+ end
18
+ end
19
+ it "runs the job without errors" do
20
+ VCR.use_cassette('mailcannon_barrel_envelope_post') do
21
+ Sidekiq::Testing.inline! do
22
+ expect{envelope.post!}.not_to raise_error
23
+ end
24
+ end
25
+ end
26
+ pending "calls Envelope#send!" do
27
+ Sidekiq::Testing.inline! do
28
+ MailCannon::Adapter::SendgridWeb.any_instance.should_receive('send!')
29
+ VCR.use_cassette('mailcannon_barrel_envelope_post') do
30
+ envelope.post!
31
+ end
32
+ end
33
+ end
34
+ context "check for expected adapter behavior" do
35
+ it "implements send! behavior" do
36
+ expect(envelope.respond_to?(:send!)).to be_true
37
+ end
38
+ end
39
+ end
40
+ end
@@ -4,6 +4,6 @@ describe MailCannon, "::config" do
4
4
  it "returns a valid configuration Hash" do
5
5
  config = MailCannon.config('templates')
6
6
  config.should be_a_kind_of(Hash)
7
- config.should have_key('folder')
7
+ config.should have_key('auto_post')
8
8
  end
9
9
  end
data/spec/spec_helper.rb CHANGED
@@ -10,6 +10,7 @@ require 'factory_girl'
10
10
  require 'vcr'
11
11
  require 'webmock'
12
12
  require 'webmock/rspec'
13
+ require 'sidekiq/testing'
13
14
 
14
15
  if ENV['TRAVIS']==true
15
16
  begin
@@ -54,4 +55,5 @@ VCR.configure do |c|
54
55
  c.allow_http_connections_when_no_cassette = true
55
56
  end
56
57
 
57
- Mongoid.load!("spec/support/mongoid.yml", 'test')
58
+ Mongoid.load!("spec/support/mongoid.yml", 'test')
59
+ MailCannon.config('templates')
@@ -1,3 +1,7 @@
1
1
  # This file will hold any hard configurations MailCannon may need. Customizations included.
2
- folder:
3
- key: value
2
+
3
+ # When creating a MailCannon::Envelope the default behavior is to auto-post the Job to send it.
4
+ auto_post: true
5
+
6
+ # This is will schedule a delay for the Job execution, useful if you want to be able to Cancel the Job.
7
+ waiting_time: 0
metadata CHANGED
@@ -1,15 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mailcannon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Martins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-07 00:00:00.000000000 Z
11
+ date: 2014-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mongoid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sidekiq
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sendgrid_webapi
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: json-schema
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
13
97
  - !ruby/object:Gem::Dependency
14
98
  name: vcr
15
99
  requirement: !ruby/object:Gem::Requirement
@@ -183,35 +267,40 @@ files:
183
267
  - .travis.yml
184
268
  - .yardopts
185
269
  - Gemfile
186
- - Gemfile.lock
187
270
  - Rakefile
188
271
  - Readme.md
189
272
  - ReadmeContributors.md
190
273
  - config/locales/en.yml
191
274
  - config/locales/pt-BR.yml
192
275
  - env.sample.sh
193
- - lib/generators/highrise_mapper/USAGE
194
- - lib/generators/highrise_mapper/config_generator.rb
276
+ - lib/generators/mailcannon/USAGE
277
+ - lib/generators/mailcannon/config_generator.rb
195
278
  - lib/mailcannon.rb
196
279
  - lib/mailcannon/adapter.rb
197
280
  - lib/mailcannon/adapters/sendgrid_web.rb
198
281
  - lib/mailcannon/envelope.rb
282
+ - lib/mailcannon/envelope_bag.rb
199
283
  - lib/mailcannon/event.rb
284
+ - lib/mailcannon/hash.rb
200
285
  - lib/mailcannon/mail.rb
201
286
  - lib/mailcannon/stamp.rb
202
287
  - lib/mailcannon/version.rb
203
288
  - lib/mailcannon/workers/barrel.rb
204
289
  - mailcannon.gemspec
205
290
  - spec/factories/envelope.rb
291
+ - spec/factories/envelope_bag.rb
206
292
  - spec/factories/stamp.rb
207
293
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid.send.yml
208
294
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send.yml
209
295
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send_bulk.yml
210
296
  - spec/fixtures/cassettes/mailcannon_integration_1k.yml
211
297
  - spec/integration/1k_spec.rb
298
+ - spec/integration/xsmtpapi_spec.rb
212
299
  - spec/mailcannon/adapters/sendgrid_spec.rb
300
+ - spec/mailcannon/envelope_bag_spec.rb
213
301
  - spec/mailcannon/envelope_spec.rb
214
302
  - spec/mailcannon/stamp_spec.rb
303
+ - spec/mailcannon/workers/barrel_spec.rb
215
304
  - spec/mailcannon_spec.rb
216
305
  - spec/spec_helper.rb
217
306
  - spec/support/mongoid.yml
@@ -243,15 +332,19 @@ specification_version: 4
243
332
  summary: A mass mailing tool for real threads aficionados
244
333
  test_files:
245
334
  - spec/factories/envelope.rb
335
+ - spec/factories/envelope_bag.rb
246
336
  - spec/factories/stamp.rb
247
337
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid.send.yml
248
338
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send.yml
249
339
  - spec/fixtures/cassettes/mailcannon_adapter_sendgrid_send_bulk.yml
250
340
  - spec/fixtures/cassettes/mailcannon_integration_1k.yml
251
341
  - spec/integration/1k_spec.rb
342
+ - spec/integration/xsmtpapi_spec.rb
252
343
  - spec/mailcannon/adapters/sendgrid_spec.rb
344
+ - spec/mailcannon/envelope_bag_spec.rb
253
345
  - spec/mailcannon/envelope_spec.rb
254
346
  - spec/mailcannon/stamp_spec.rb
347
+ - spec/mailcannon/workers/barrel_spec.rb
255
348
  - spec/mailcannon_spec.rb
256
349
  - spec/spec_helper.rb
257
350
  - spec/support/mongoid.yml
data/Gemfile.lock DELETED
@@ -1,322 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- activemodel (3.2.16)
5
- activesupport (= 3.2.16)
6
- builder (~> 3.0.0)
7
- activesupport (3.2.16)
8
- i18n (~> 0.6, >= 0.6.4)
9
- multi_json (~> 1.0)
10
- addressable (2.3.5)
11
- aggregate (0.2.2)
12
- builder (3.0.4)
13
- celluloid (0.15.2)
14
- timers (~> 1.1.0)
15
- coderay (1.0.9)
16
- colorize (0.5.8)
17
- connection_pool (1.2.0)
18
- coveralls (0.6.7)
19
- colorize
20
- multi_json (~> 1.3)
21
- rest-client
22
- simplecov (>= 0.7)
23
- thor
24
- crack (0.4.1)
25
- safe_yaml (~> 0.9.0)
26
- database_cleaner (1.2.0)
27
- diff-lcs (1.2.4)
28
- factory_girl (4.3.0)
29
- activesupport (>= 3.0.0)
30
- faraday (0.8.8)
31
- multipart-post (~> 1.2.0)
32
- ffi2-generators (0.1.1)
33
- i18n (0.6.9)
34
- json (1.8.1)
35
- json-schema (2.1.8)
36
- librato-metrics (1.3.0)
37
- aggregate (~> 0.2.2)
38
- faraday (~> 0.7)
39
- multi_json
40
- method_source (0.8.1)
41
- mime-types (1.23)
42
- mongoid (3.1.6)
43
- activemodel (~> 3.2)
44
- moped (~> 1.4)
45
- origin (~> 1.0)
46
- tzinfo (~> 0.3.29)
47
- moped (1.5.1)
48
- multi_json (1.8.2)
49
- multipart-post (1.2.0)
50
- origin (1.1.0)
51
- pry (0.9.12.2)
52
- coderay (~> 1.0.5)
53
- method_source (~> 0.8)
54
- slop (~> 3.4)
55
- pry-nav (0.2.3)
56
- pry (~> 0.9.10)
57
- rake (10.0.4)
58
- redis (3.0.6)
59
- redis-namespace (1.4.1)
60
- redis (~> 3.0.4)
61
- rest-client (1.6.7)
62
- mime-types (>= 1.16)
63
- rspec (2.13.0)
64
- rspec-core (~> 2.13.0)
65
- rspec-expectations (~> 2.13.0)
66
- rspec-mocks (~> 2.13.0)
67
- rspec-core (2.13.1)
68
- rspec-expectations (2.13.0)
69
- diff-lcs (>= 1.1.3, < 2.0)
70
- rspec-mocks (2.13.1)
71
- rubysl (2.0.15)
72
- rubysl-abbrev (~> 2.0)
73
- rubysl-base64 (~> 2.0)
74
- rubysl-benchmark (~> 2.0)
75
- rubysl-bigdecimal (~> 2.0)
76
- rubysl-cgi (~> 2.0)
77
- rubysl-cgi-session (~> 2.0)
78
- rubysl-cmath (~> 2.0)
79
- rubysl-complex (~> 2.0)
80
- rubysl-continuation (~> 2.0)
81
- rubysl-coverage (~> 2.0)
82
- rubysl-csv (~> 2.0)
83
- rubysl-curses (~> 2.0)
84
- rubysl-date (~> 2.0)
85
- rubysl-delegate (~> 2.0)
86
- rubysl-digest (~> 2.0)
87
- rubysl-drb (~> 2.0)
88
- rubysl-e2mmap (~> 2.0)
89
- rubysl-english (~> 2.0)
90
- rubysl-enumerator (~> 2.0)
91
- rubysl-erb (~> 2.0)
92
- rubysl-etc (~> 2.0)
93
- rubysl-expect (~> 2.0)
94
- rubysl-fcntl (~> 2.0)
95
- rubysl-fiber (~> 2.0)
96
- rubysl-fileutils (~> 2.0)
97
- rubysl-find (~> 2.0)
98
- rubysl-forwardable (~> 2.0)
99
- rubysl-getoptlong (~> 2.0)
100
- rubysl-gserver (~> 2.0)
101
- rubysl-io-console (~> 2.0)
102
- rubysl-io-nonblock (~> 2.0)
103
- rubysl-io-wait (~> 2.0)
104
- rubysl-ipaddr (~> 2.0)
105
- rubysl-irb (~> 2.0)
106
- rubysl-logger (~> 2.0)
107
- rubysl-mathn (~> 2.0)
108
- rubysl-matrix (~> 2.0)
109
- rubysl-mkmf (~> 2.0)
110
- rubysl-monitor (~> 2.0)
111
- rubysl-mutex_m (~> 2.0)
112
- rubysl-net-ftp (~> 2.0)
113
- rubysl-net-http (~> 2.0)
114
- rubysl-net-imap (~> 2.0)
115
- rubysl-net-pop (~> 2.0)
116
- rubysl-net-protocol (~> 2.0)
117
- rubysl-net-smtp (~> 2.0)
118
- rubysl-net-telnet (~> 2.0)
119
- rubysl-nkf (~> 2.0)
120
- rubysl-observer (~> 2.0)
121
- rubysl-open-uri (~> 2.0)
122
- rubysl-open3 (~> 2.0)
123
- rubysl-openssl (~> 2.0)
124
- rubysl-optparse (~> 2.0)
125
- rubysl-ostruct (~> 2.0)
126
- rubysl-pathname (~> 2.0)
127
- rubysl-prettyprint (~> 2.0)
128
- rubysl-prime (~> 2.0)
129
- rubysl-profile (~> 2.0)
130
- rubysl-profiler (~> 2.0)
131
- rubysl-pstore (~> 2.0)
132
- rubysl-pty (~> 2.0)
133
- rubysl-rational (~> 2.0)
134
- rubysl-readline (~> 2.0)
135
- rubysl-resolv (~> 2.0)
136
- rubysl-rexml (~> 2.0)
137
- rubysl-rinda (~> 2.0)
138
- rubysl-rss (~> 2.0)
139
- rubysl-scanf (~> 2.0)
140
- rubysl-securerandom (~> 2.0)
141
- rubysl-set (~> 2.0)
142
- rubysl-shellwords (~> 2.0)
143
- rubysl-singleton (~> 2.0)
144
- rubysl-socket (~> 2.0)
145
- rubysl-stringio (~> 2.0)
146
- rubysl-strscan (~> 2.0)
147
- rubysl-sync (~> 2.0)
148
- rubysl-syslog (~> 2.0)
149
- rubysl-tempfile (~> 2.0)
150
- rubysl-thread (~> 2.0)
151
- rubysl-thwait (~> 2.0)
152
- rubysl-time (~> 2.0)
153
- rubysl-timeout (~> 2.0)
154
- rubysl-tmpdir (~> 2.0)
155
- rubysl-tsort (~> 2.0)
156
- rubysl-un (~> 2.0)
157
- rubysl-uri (~> 2.0)
158
- rubysl-weakref (~> 2.0)
159
- rubysl-webrick (~> 2.0)
160
- rubysl-xmlrpc (~> 2.0)
161
- rubysl-yaml (~> 2.0)
162
- rubysl-zlib (~> 2.0)
163
- rubysl-abbrev (2.0.4)
164
- rubysl-base64 (2.0.0)
165
- rubysl-benchmark (2.0.1)
166
- rubysl-bigdecimal (2.0.2)
167
- rubysl-cgi (2.0.1)
168
- rubysl-cgi-session (2.0.1)
169
- rubysl-cmath (2.0.0)
170
- rubysl-complex (2.0.0)
171
- rubysl-continuation (2.0.0)
172
- rubysl-coverage (2.0.3)
173
- rubysl-csv (2.0.2)
174
- rubysl-english (~> 2.0)
175
- rubysl-curses (2.0.0)
176
- rubysl-date (2.0.6)
177
- rubysl-delegate (2.0.1)
178
- rubysl-digest (2.0.3)
179
- rubysl-drb (2.0.1)
180
- rubysl-e2mmap (2.0.0)
181
- rubysl-english (2.0.0)
182
- rubysl-enumerator (2.0.0)
183
- rubysl-erb (2.0.1)
184
- rubysl-etc (2.0.3)
185
- ffi2-generators (~> 0.1)
186
- rubysl-expect (2.0.0)
187
- rubysl-fcntl (2.0.4)
188
- ffi2-generators (~> 0.1)
189
- rubysl-fiber (2.0.0)
190
- rubysl-fileutils (2.0.3)
191
- rubysl-find (2.0.1)
192
- rubysl-forwardable (2.0.1)
193
- rubysl-getoptlong (2.0.0)
194
- rubysl-gserver (2.0.0)
195
- rubysl-socket (~> 2.0)
196
- rubysl-thread (~> 2.0)
197
- rubysl-io-console (2.0.0)
198
- rubysl-io-nonblock (2.0.0)
199
- rubysl-io-wait (2.0.0)
200
- rubysl-ipaddr (2.0.0)
201
- rubysl-irb (2.0.4)
202
- rubysl-e2mmap (~> 2.0)
203
- rubysl-mathn (~> 2.0)
204
- rubysl-readline (~> 2.0)
205
- rubysl-thread (~> 2.0)
206
- rubysl-logger (2.0.0)
207
- rubysl-mathn (2.0.0)
208
- rubysl-matrix (2.1.0)
209
- rubysl-e2mmap (~> 2.0)
210
- rubysl-mkmf (2.0.1)
211
- rubysl-fileutils (~> 2.0)
212
- rubysl-shellwords (~> 2.0)
213
- rubysl-monitor (2.0.0)
214
- rubysl-mutex_m (2.0.0)
215
- rubysl-net-ftp (2.0.1)
216
- rubysl-net-http (2.0.4)
217
- rubysl-cgi (~> 2.0)
218
- rubysl-erb (~> 2.0)
219
- rubysl-singleton (~> 2.0)
220
- rubysl-net-imap (2.0.1)
221
- rubysl-net-pop (2.0.1)
222
- rubysl-net-protocol (2.0.1)
223
- rubysl-net-smtp (2.0.1)
224
- rubysl-net-telnet (2.0.0)
225
- rubysl-nkf (2.0.1)
226
- rubysl-observer (2.0.0)
227
- rubysl-open-uri (2.0.0)
228
- rubysl-open3 (2.0.0)
229
- rubysl-openssl (2.0.5)
230
- rubysl-optparse (2.0.1)
231
- rubysl-shellwords (~> 2.0)
232
- rubysl-ostruct (2.0.4)
233
- rubysl-pathname (2.0.0)
234
- rubysl-prettyprint (2.0.2)
235
- rubysl-prime (2.0.1)
236
- rubysl-profile (2.0.0)
237
- rubysl-profiler (2.0.1)
238
- rubysl-pstore (2.0.0)
239
- rubysl-pty (2.0.2)
240
- rubysl-rational (2.0.1)
241
- rubysl-readline (2.0.2)
242
- rubysl-resolv (2.0.0)
243
- rubysl-rexml (2.0.2)
244
- rubysl-rinda (2.0.0)
245
- rubysl-rss (2.0.0)
246
- rubysl-scanf (2.0.0)
247
- rubysl-securerandom (2.0.0)
248
- rubysl-set (2.0.1)
249
- rubysl-shellwords (2.0.0)
250
- rubysl-singleton (2.0.0)
251
- rubysl-socket (2.0.1)
252
- rubysl-stringio (2.0.0)
253
- rubysl-strscan (2.0.0)
254
- rubysl-sync (2.0.0)
255
- rubysl-syslog (2.0.1)
256
- ffi2-generators (~> 0.1)
257
- rubysl-tempfile (2.0.1)
258
- rubysl-thread (2.0.2)
259
- rubysl-thwait (2.0.0)
260
- rubysl-time (2.0.3)
261
- rubysl-timeout (2.0.0)
262
- rubysl-tmpdir (2.0.0)
263
- rubysl-tsort (2.0.1)
264
- rubysl-un (2.0.0)
265
- rubysl-fileutils (~> 2.0)
266
- rubysl-optparse (~> 2.0)
267
- rubysl-uri (2.0.0)
268
- rubysl-weakref (2.0.0)
269
- rubysl-webrick (2.0.0)
270
- rubysl-xmlrpc (2.0.0)
271
- rubysl-yaml (2.0.4)
272
- rubysl-zlib (2.0.1)
273
- safe_yaml (0.9.7)
274
- sendgrid_webapi (0.0.2)
275
- faraday (~> 0.8.0)
276
- json (~> 1.8.0)
277
- sidekiq (2.17.0)
278
- celluloid (>= 0.15.2)
279
- connection_pool (>= 1.0.0)
280
- json
281
- redis (>= 3.0.4)
282
- redis-namespace (>= 1.3.1)
283
- simplecov (0.7.1)
284
- multi_json (~> 1.0)
285
- simplecov-html (~> 0.7.1)
286
- simplecov-html (0.7.1)
287
- slop (3.4.5)
288
- thor (0.18.1)
289
- timers (1.1.0)
290
- tzinfo (0.3.38)
291
- vcr (2.8.0)
292
- webmock (1.15.2)
293
- addressable (>= 2.2.7)
294
- crack (>= 0.3.2)
295
- yajl-ruby (1.2.0)
296
- yard (0.8.6.2)
297
-
298
- PLATFORMS
299
- ruby
300
-
301
- DEPENDENCIES
302
- coveralls
303
- database_cleaner
304
- factory_girl
305
- jruby-openssl
306
- json-schema
307
- librato-metrics
308
- mongoid
309
- pry
310
- pry-nav
311
- rake
312
- redis
313
- rspec
314
- rspec-expectations
315
- rspec-mocks
316
- rubysl
317
- sendgrid_webapi
318
- sidekiq
319
- vcr
320
- webmock (>= 1.8.0, < 1.16)
321
- yajl-ruby
322
- yard