mailcannon 0.0.4 → 0.0.5

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 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