mailcannon 0.0.8 → 0.1.0.pre.1

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: 16aeff734b9badfd24d471a07fc5a750f6a799a1
4
- data.tar.gz: 05bf51210743215b43333d337f9f3cfd88e0079f
3
+ metadata.gz: 81d99b16a82ec2314d0e8326bc3c2f93eed2fc03
4
+ data.tar.gz: 248dba656dc1d563a28bc4384e8566be3a986fa5
5
5
  SHA512:
6
- metadata.gz: 41bd9d22235bbb8fcbece0a246b3821487f0bf2c4e5fadedf1a03b22567d8fb499443900ebe014b1e66e8ffaae49cad4a309820727dd4737a4ffe0f30c9318d5
7
- data.tar.gz: b08f2360e0901a2d3d9a2c5b73a02fed45182e5ed1b2a0bbe7a9429601bd5bfca017d0bc56354b2c89ff94b7d3f05244e0d74355e731ae66c2517aaa9270b6cf
6
+ metadata.gz: cf38c7f8ad0596607206f3b69d953f0da7736eee72b271f65736138c3fd4958c2e894ab5cbc1292b975100d88ff9343b3bc06083af0476ec4e77e8e9bbe1064c
7
+ data.tar.gz: 15135c7aa6a189aff2ce6243123a1d0b8ff68d35f043790db63547167905609245f2b85ba0df1176807bc8605deb85a5f61894e1b841e2d73c0dcc466964bfac
data/.gitignore CHANGED
@@ -8,3 +8,4 @@ coverage/
8
8
  pkg/
9
9
  Gemfile.lock
10
10
  config/mailcannon.yml
11
+ .bundle/
data/Gemfile CHANGED
@@ -3,10 +3,9 @@ source 'https://rubygems.org'
3
3
  gem 'redis'
4
4
  gem 'activemodel'
5
5
  gem 'mongoid', '>=3.1.6'
6
- gem 'sidekiq'
6
+ gem 'sidekiq', '2.17.7'
7
7
  gem 'sendgrid_webapi', '0.0.3'
8
8
  gem 'librato-metrics'
9
- gem 'airbrake'
10
9
  gem 'rubysl', platform: :rbx
11
10
  gem 'jruby-openssl', platform: :jruby
12
11
  gem 'yajl-ruby', :platforms=>[:rbx,:ruby]
data/Rakefile CHANGED
@@ -23,4 +23,4 @@ task :travis do
23
23
  system("export DISPLAY=:99.0 && bundle exec #{cmd}")
24
24
  raise "#{cmd} failed!" unless $?.exitstatus == 0
25
25
  end
26
- end
26
+ end
data/Readme.md CHANGED
@@ -5,13 +5,13 @@
5
5
  MailCannon
6
6
  ==========
7
7
 
8
- Although this is a **WORK IN PROGRESS**, we're rolling it out to **production** with no issues for now at [Resultados Digitais](http://resultadosdigitais.com.br/).
8
+ Although this is a **WORK IN PROGRESS**, we're getting great results on **production** at [Resultados Digitais](http://resultadosdigitais.com.br/).
9
9
 
10
10
  This Gem relies heavily on both [Sidekiq](https://github.com/mperham/sidekiq) and Celluloid Gems, you are encouraged to use it anywhere with Ruby (a http interface is on the Roadmap ).
11
11
 
12
12
  This Gem provides a worker ready for deploy cooked with [MongoDB](http://www.mongodb.org/) + [Mongoid](https://github.com/mongoid/mongoid) + [Sidekiq](https://github.com/mperham/sidekiq) + [Rubinius](http://rubini.us/) (feel free to use on MRI and jRuby as well).
13
13
 
14
- For production deployment, you should take a loot at both [MailCannon Outpost](https://github.com/lucasmartins/mailcannon-outpost) and [MailCannon Monitor](https://github.com/lucasmartins/mailcannon-monitor) projects.
14
+ For production deployment, you should take a look at both [MailCannon Outpost](https://github.com/lucasmartins/mailcannon-outpost) and [MailCannon Monitor](https://github.com/lucasmartins/mailcannon-monitor) projects.
15
15
 
16
16
  Install
17
17
  =======
@@ -68,7 +68,7 @@ envelope = MailCannon::Envelope.create(
68
68
  subject: 'Test',
69
69
  mail: MailCannon::Mail.new(text: 'you will see this when no HTML reader is available', html: 'this should be an HTML'))
70
70
  envelope_bag.push envelope
71
- envelope_bag.post! # this will sent using the 'hot-account'.
71
+ envelope_bag.post! # this will be sent using the 'hot-account'.
72
72
  ```
73
73
 
74
74
  ### Configuration file
@@ -87,17 +87,52 @@ Edit the file to meet your environemnt needs.
87
87
 
88
88
  Check the [specs](https://github.com/lucasmartins/mailcannon/tree/master/spec) to see the testing example, it will surely make it clearer.
89
89
 
90
+ ### Statistics & MapReduce
91
+
92
+ MailCannon provides statistics calculation/reduce for the events related to an `Envelope`, like `open`,`click`,`spam`, etc. Assuming you have your Outpost running properly (running reduce jobs), you can access the data through the `envelope.stats` method to get the following hash:
93
+
94
+ ```ruby
95
+ {
96
+ "posted"=>{"count"=>0.0, "targets"=>[]},
97
+ "processed"=>{"count"=>0.0, "targets"=>[]},
98
+ "delivered"=>{"count"=>1.0, "targets"=>["1"]},
99
+ "open"=>{"count"=>1.0, "targets"=>["2"]},
100
+ "click"=>{"count"=>0.0, "targets"=>[]},
101
+ "deferred"=>{"count"=>0.0, "targets"=>[]},
102
+ "spam_report"=>{"count"=>0.0, "targets"=>[]},
103
+ "spam"=>{"count"=>0.0, "targets"=>[]},
104
+ "unsubscribe"=>{"count"=>0.0, "targets"=>[]},
105
+ "drop"=>{"count"=>0.0, "targets"=>[]},
106
+ "bounce"=>{"count"=>1.0, "targets"=>["3"]}
107
+ }
108
+ ```
109
+
110
+ You can trigger the reduce operation directly with `envelope.reduce_statistics`.
111
+
112
+ **Targets** are your __glue_id__ to link this data inside your own application, we use it as the "Contact#id" so we can show witch `Contact` has received, read, or clicked the email.
113
+
114
+ Repeating events on the same target will increase the array: `"click"=>{"count"=>3.0, "targets"=>["3","3","3"]}`
115
+
116
+
90
117
  Docs
91
118
  ====
92
119
  You should check the [factories](https://github.com/lucasmartins/mailcannon/tree/master/spec/factories) to learn what you need to build your objects, and the [tests](https://github.com/lucasmartins/mailcannon/tree/master/spec/mailcannon) to learn how to use them. But hey, we have docs [right here](http://rdoc.info/github/lucasmartins/mailcannon/master/frames).
93
120
 
121
+ Roadmap
122
+ =======
123
+
124
+ - Statistics (Map&Reduce awesomeness);
125
+ - Memory optimization (focused on MailCannon Outpost);
126
+ - HTTP (webservice) interface - so you don't need to be coding Ruby to use it!;
127
+ - New service adapter (Mandrill?);
128
+
94
129
  Contribute
95
130
  ==========
96
131
 
97
132
  Just fork [MailCannon](https://github.com/lucasmartins/mailcannon), add your feature+spec, and make a pull request. Do not mess up with the version file though.
98
133
 
99
134
  **NOTICE**: The project is at embrionary stage, breaking changes will apply.
100
-
135
+
101
136
  Support
102
137
  =======
103
138
 
data/env.sample.sh CHANGED
@@ -1,8 +1,9 @@
1
1
  export REDIS_URL='redis://localhost:6379'
2
2
  export SENDGRID_USERNAME=''
3
3
  export SENDGRID_PASSWORD=''
4
- export AIRBRAKE_TOKEN=''
5
- export LIBRATO_SOURCE='staging'
6
- export LIBRATO_USER=''
7
- export LIBRATO_TOKEN=''
4
+ #export AIRBRAKE_TOKEN=''
5
+ #export LIBRATO_SOURCE='staging'
6
+ #export LIBRATO_USER=''
7
+ #export LIBRATO_TOKEN=''
8
8
  export MONGODB_URL=mongodb://localhost:27017:mailcannon_development
9
+ export MONGODB_PORT=27017
@@ -29,7 +29,7 @@ module MailCannon::Adapter::SendgridWeb
29
29
  rescue Exception => e
30
30
  logger.error "Unable to read auth config from Envelope or Bag, using default auth options from ENV"
31
31
  return default_auth
32
- end
32
+ end
33
33
  end
34
34
  end
35
35
 
@@ -55,27 +55,63 @@ module MailCannon::Adapter::SendgridWeb
55
55
  self.save!
56
56
  end
57
57
 
58
- def build_name_subs
59
- name_placeholder = MailCannon.config['default_name_placeholder'] || '%name%'
58
+ def build_to_subs(placeholder, to_key)
60
59
  selected_hash_array = []
61
- self.to.map {|h| selected_hash_array.push h['name']||h[:name]||''}
62
- {'sub'=>{"#{name_placeholder}"=>selected_hash_array}}
60
+ self.to.map {|h| selected_hash_array.push h[to_key]||h[to_key.to_sym]||''}
61
+ {'sub'=>{"#{placeholder}"=>selected_hash_array}}
62
+ end
63
+
64
+ def build_name_subs
65
+ placeholder = MailCannon.config['default_name_placeholder'] || '%name%'
66
+ build_to_subs(placeholder, 'name')
67
+ end
68
+
69
+ def build_email_subs
70
+ placeholder = MailCannon.config['default_email_placeholder'] || '%email%'
71
+ build_to_subs(placeholder, 'email')
72
+ end
73
+
74
+
75
+ def build_unique_args
76
+ unique_args = {}
77
+ if MailCannon.config['add_envelope_id_to_unique_args']
78
+ unique_args.merge!({'envelope_id'=>self.id})
79
+ end
80
+ if MailCannon.config['add_envelope_bag_id_to_unique_args'] && self.envelope_bag
81
+ unique_args.merge!({'envelope_bag_id'=>self.envelope_bag.id})
82
+ end
83
+ unique_args
63
84
  end
64
85
 
65
86
  def build_xsmtpapi(recipients,subs)
66
87
  xsmtpapi = self.xsmtpapi || {}
67
- to = []
68
88
  recipients.symbolize_keys!
69
- recipients[:to].each do |h|
70
- h.symbolize_keys!
71
- to.push h[:email]
72
- end
89
+ to = extract_values(recipients[:to],:email)
73
90
  xsmtpapi.merge!({'to' => to}) if to
74
- xsmtpapi = xsmtpapi.deep_merge(subs) if subs!=nil && subs['sub']!=nil
75
- xsmtpapi = xsmtpapi.deep_merge(build_name_subs) if build_name_subs!=nil && build_name_subs.is_a?(Hash)
91
+ xsmtpapi = merge_subs_hash(xsmtpapi,subs)
92
+ xsmtpapi = merge_subs_hash(xsmtpapi,build_name_subs)
93
+ xsmtpapi = merge_subs_hash(xsmtpapi,build_email_subs)
94
+ xsmtpapi.merge!({'unique_args' => build_unique_args })
76
95
  return xsmtpapi
77
96
  end
78
97
 
98
+ def extract_values(values,key)
99
+ extract=[]
100
+ values.each do |h|
101
+ h.symbolize_keys!
102
+ extract.push h[key]
103
+ end
104
+ extract
105
+ end
106
+
107
+ def merge_subs_hash(xsmtpapi,subs)
108
+ if subs!=nil && subs.is_a?(Hash)
109
+ xsmtpapi.deep_merge(subs)
110
+ else
111
+ xsmtpapi
112
+ end
113
+ end
114
+
79
115
  def validate_xsmtpapi!
80
116
  return true
81
117
  if self.to.size>1
@@ -4,10 +4,12 @@ class MailCannon::Envelope
4
4
  include Mongoid::Timestamps
5
5
  include MailCannon::Adapter::SendgridWeb
6
6
 
7
- embeds_one :mail
8
- embeds_many :stamps
9
7
  belongs_to :envelope_bag
10
8
 
9
+ embeds_one :mail
10
+ embeds_many :stamps
11
+ has_many :sendgrid_events
12
+
11
13
  field :from, type: String
12
14
  field :from_name, type: String
13
15
  field :to, type: Array # of hashes. [{email: '', name: ''},...]
@@ -19,7 +21,6 @@ class MailCannon::Envelope
19
21
  field :xsmtpapi, type: Hash # this will mostly be used by MailCannon itself. http://sendgrid.com/docs/API_Reference/SMTP_API/index.html
20
22
  field :auth, type: Hash # {user: 'foo', password: 'bar'}, some Adapters might need an token:secret pair, which you can translete into user:password pair.
21
23
  field :jid, type: String
22
-
23
24
 
24
25
  validates :from, :to, :subject, presence: true
25
26
  validates_associated :mail
@@ -65,6 +66,14 @@ class MailCannon::Envelope
65
66
  false
66
67
  end
67
68
  end
69
+
70
+ def processed?
71
+ if self.stamps.where(code: 1).count > 0
72
+ true
73
+ else
74
+ false
75
+ end
76
+ end
68
77
 
69
78
  private
70
79
  def schedule_send_job
@@ -2,11 +2,20 @@
2
2
  class MailCannon::EnvelopeBag
3
3
  include Mongoid::Document
4
4
  include Mongoid::Timestamps
5
+ include MailCannon::EnvelopeBagMapReduce
5
6
 
6
7
  has_many :envelopes, autosave: true
7
8
  field :integration_code, type: String # Used to link your own app models to the Bag.
8
9
  field :auth, type: Hash # {user: 'foo', password: 'bar'}, some Adapters might need an token:secret pair, which you can translete into user:password pair. This config will be overriden by the Envelope.auth if present.
9
10
 
11
+ def stats
12
+ begin
13
+ MailCannon::EnvelopeBagStatistic.find(self.id).value
14
+ rescue Mongoid::Errors::DocumentNotFound => e
15
+ raise "You haven't run envelope.reduce_statistics yet, no data available!"
16
+ end
17
+ end
18
+
10
19
  def push(envelope)
11
20
  self.envelopes.push envelope
12
21
  end
@@ -0,0 +1,106 @@
1
+ module MailCannon::EnvelopeBagMapReduce
2
+ module ClassMethods
3
+
4
+ def find_gem_root_path
5
+ path = ""
6
+ begin
7
+ spec = Gem::Specification.find_by_name("mailcannon")
8
+ path = spec.gem_dir
9
+ rescue LoadError => e
10
+ raise unless e.message =~ /mailcannon/
11
+ puts 'mailcannon gem path not found'
12
+ end
13
+ path
14
+ end
15
+
16
+ def reduce_statistics_for_envelope_bag(id)
17
+ events = change_events_status_for_envelope_bag(id, nil, :lock)
18
+ result = events.map_reduce(self.js_map, self.js_reduce).out(merge: "mail_cannon_envelope_bag_statistics")
19
+ set_events_to(events,:processed)
20
+ {raw: result.raw, count: events.count}
21
+ end
22
+
23
+ def statistics_for_envelope(id)
24
+ self.stats
25
+ end
26
+
27
+ def js_map
28
+ root_path = find_gem_root_path
29
+ root_path += "/" unless root_path.empty?
30
+ @js_map ||= File.read("#{root_path}lib/mailcannon/reduces/envelope_bag_map.js")
31
+ #TODO cache this
32
+ @js_map
33
+ end
34
+
35
+ def js_reduce
36
+ root_path = find_gem_root_path
37
+ root_path += "/" unless root_path.empty?
38
+ @js_reduce ||= File.read("#{root_path}lib/mailcannon/reduces/envelope_bag_reduce.js")
39
+ #TODO cache this
40
+ @js_reduce
41
+ end
42
+
43
+ # [from|to]sym = :new, :lock, :processed
44
+ def change_events_status_for_envelope_bag(id, from_sym, to_sym)
45
+ from_status = processed_status_for(from_sym)
46
+ to_status = processed_status_for(to_sym)
47
+ if from_sym
48
+ query = MailCannon::SendgridEvent.where(envelope_bag_id: id, processed: from_status)
49
+ else
50
+ query = MailCannon::SendgridEvent.where(envelope_bag_id: id)
51
+ end
52
+ if query.kind_of?(Mongoid::Criteria)
53
+ query.update_all(processed: to_status)
54
+ else
55
+ query.processed=to_status
56
+ query.save
57
+ end
58
+ query
59
+ end
60
+
61
+ #private
62
+ def set_events_to(events,status)
63
+ status = processed_status_for(status)
64
+ if events.kind_of?(Mongoid::Criteria)
65
+ events.update_all(processed: status)
66
+ else
67
+ events.processed=status
68
+ events.save
69
+ end
70
+ end
71
+
72
+ def processed_status_for(input)
73
+ status_map = {
74
+ lock: false,
75
+ processed: true,
76
+ new: nil
77
+ }
78
+ if [false,true,nil].include?(input)
79
+ status_map = status_map.invert
80
+ raise "Unexpected input(#{input})" unless status_map.has_key?(input)
81
+ end
82
+ status_map[input]
83
+ end
84
+
85
+ end
86
+
87
+ module InstanceMethods
88
+ def reduce_statistics
89
+ self.class.reduce_statistics_for_envelope_bag(self.id)
90
+ end
91
+
92
+ def statistics
93
+ self.class.statistics_for_envelope(self.id)
94
+ end
95
+
96
+ def change_events_status(from_sym, to_sym)
97
+ self.set_processed_status_for_envelope(self.id, from_sym, to_sym)
98
+ end
99
+ end
100
+
101
+ def self.included(receiver)
102
+ receiver.extend ClassMethods
103
+ receiver.send :include, InstanceMethods
104
+ end
105
+ end
106
+
@@ -0,0 +1,5 @@
1
+ # Holds information about a recipient's email event, like deliveries and bounces.
2
+ class MailCannon::EnvelopeBagStatistic
3
+ include Mongoid::Document
4
+ include Mongoid::Timestamps
5
+ end
@@ -0,0 +1,3 @@
1
+ function () {
2
+ emit(this.envelope_bag_id, { target_id: this.target_id, event: this.event, type: this.type });
3
+ }
@@ -0,0 +1,116 @@
1
+ function (key, values) {
2
+ var result = {
3
+ 'posted': {
4
+ 'count': 0,
5
+ 'targets': []
6
+ },
7
+ 'processed': {
8
+ 'count': 0,
9
+ 'targets': []
10
+ },
11
+ 'delivered': {
12
+ 'count': 0,
13
+ 'targets': []
14
+ },
15
+ 'open': {
16
+ 'count': 0,
17
+ 'targets': []
18
+ },
19
+ 'click': {
20
+ 'count': 0,
21
+ 'targets': []
22
+ },
23
+ 'deferred': {
24
+ 'count': 0,
25
+ 'targets': []
26
+ },
27
+ 'spam_report': {
28
+ 'count': 0,
29
+ 'targets': []
30
+ },
31
+ 'spam': {
32
+ 'count': 0,
33
+ 'targets': []
34
+ },
35
+ 'unsubscribe': {
36
+ 'count': 0,
37
+ 'targets': []
38
+ },
39
+ 'drop': {
40
+ 'count': 0,
41
+ 'targets': []
42
+ },
43
+ 'hard_bounce': {
44
+ 'count': 0,
45
+ 'targets': []
46
+ },
47
+ 'soft_bounce': {
48
+ 'count': 0,
49
+ 'targets': []
50
+ },
51
+ 'unknown': {
52
+ 'count': 0,
53
+ 'targets': []
54
+ }
55
+ };
56
+
57
+ values.forEach(function(value) {
58
+ switch (value['event']) {
59
+ case 'posted':
60
+ result['posted']['count']++;
61
+ result['posted']['targets'].push(value['target_id']);
62
+ break;
63
+ case 'processed':
64
+ result['processed']['count']++;
65
+ result['processed']['targets'].push(value['target_id']);
66
+ break;
67
+ case 'delivered':
68
+ result['delivered']['count']++;
69
+ result['delivered']['targets'].push(value['target_id']);
70
+ break;
71
+ case 'open':
72
+ result['open']['count']++;
73
+ result['open']['targets'].push(value['target_id']);
74
+ break;
75
+ case 'click':
76
+ result['click']['count']++;
77
+ result['click']['targets'].push(value['target_id']);
78
+ break;
79
+ case 'deferred':
80
+ result['deferred']['count']++;
81
+ result['deferred']['targets'].push(value['target_id']);
82
+ break;
83
+ case 'spam_report':
84
+ result['spam_report']['count']++;
85
+ result['spam_report']['targets'].push(value['target_id']);
86
+ break;
87
+ case 'spam':
88
+ result['spam']['count']++;
89
+ result['spam']['targets'].push(value['target_id']);
90
+ break;
91
+ case 'unsubscribe':
92
+ result['unsubscribe']['count']++;
93
+ result['unsubscribe']['targets'].push(value['target_id']);
94
+ break;
95
+ case 'drop':
96
+ result['drop']['count']++;
97
+ result['drop']['targets'].push(value['target_id']);
98
+ break;
99
+ case 'bounce':
100
+ if(value['type'] == "bounce"){
101
+ result['hard_bounce']['count']++;
102
+ result['hard_bounce']['targets'].push(value['target_id']);
103
+ }
104
+ else {
105
+ result['soft_bounce']['count']++;
106
+ result['soft_bounce']['targets'].push(value['target_id']);
107
+ }
108
+ break;
109
+ default:
110
+ result['unknown']['count']++;
111
+ result['unknown']['targets'].push(value['target_id']);
112
+ break;
113
+ }
114
+ });
115
+ return result;
116
+ }
@@ -0,0 +1,18 @@
1
+ class MailCannon::SendgridEvent
2
+ include Mongoid::Document
3
+ field :envelope_id, type: String
4
+ field :envelope_bag_id, type: String
5
+ field :email, type: String
6
+ field :timestamp, type: String
7
+ field :unique_arg, type: String
8
+ field :event, type: String
9
+ field :type, type: String
10
+ field :processed, type: Boolean, default: nil
11
+
12
+ belongs_to :envelope
13
+ belongs_to :envelope_bag
14
+
15
+ def self.insert_bulk(tha_huge_string)
16
+ MailCannon::SendgridEvent.collection.insert(tha_huge_string)
17
+ end
18
+ end
@@ -2,8 +2,9 @@
2
2
  module MailCannon
3
3
  module Version
4
4
  MAJOR = 0
5
- MINOR = 0
6
- PATCH = 8
7
- STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
5
+ MINOR = 1
6
+ PATCH = 0
7
+ ALPHA = '.pre.1'
8
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}#{ALPHA}"
8
9
  end
9
10
  end
@@ -4,23 +4,10 @@ class MailCannon::Barrel
4
4
 
5
5
  def perform(envelope_id)
6
6
  envelope_id = envelope_id['$oid'] if envelope_id['$oid']
7
- if MailCannon::Librato.available?
8
- shoot_with_librato!(envelope_id)
9
- else
10
- shoot!(envelope_id)
11
- end
7
+ shoot!(envelope_id)
12
8
  end
13
9
 
14
10
  private
15
- def shoot_with_librato!(envelope_id)
16
- MailCannon::Librato.authenticate
17
- aggregator = Librato::Metrics::Aggregator.new
18
- aggregator.time 'mailcannon.shoot' do
19
- shoot!(envelope_id)
20
- end
21
- aggregator.submit
22
- end
23
-
24
11
  def shoot!(envelope_id)
25
12
  logger.info "sending MailCannon::Envelope.find('#{envelope_id}')"
26
13
  begin
@@ -34,8 +21,7 @@ class MailCannon::Barrel
34
21
  rescue Mongoid::Errors::DocumentNotFound
35
22
  logger.error "unable to find the document MailCannon::Envelope.find('#{envelope_id}')"
36
23
  rescue Exception => e
37
- logger.error "unable to send MailCannon::Envelope.find('#{envelope_id}')\n#{e.backtrace}"
38
- Airbrake.notify(e, parameters: {'envelope_id'=>envelope_id}, cgi_data: ENV.to_hash) if MailCannon::Airbrake.available?
24
+ logger.error "unable to send MailCannon::Envelope.find('#{envelope_id}') #{e.backtrace}"
39
25
  end
40
26
  end
41
27
  end
@@ -0,0 +1,11 @@
1
+ class MailCannon::EnvelopeBagReduceJob
2
+ include Sidekiq::Worker
3
+
4
+ def perform(envelope_bag_ids)
5
+ envelope_bag_ids.each do |id|
6
+ id = id['$oid'] if id['$oid']
7
+ MailCannon::EnvelopeBag.reduce_statistics_for_envelope_bag(id)
8
+ end
9
+ end
10
+
11
+ end
data/lib/mailcannon.rb CHANGED
@@ -7,8 +7,6 @@ require 'sidekiq'
7
7
  require 'sendgrid_webapi'
8
8
  require 'yajl-ruby' if RUBY_PLATFORM=='ruby'
9
9
  require 'jruby-openssl' if RUBY_PLATFORM=='jruby'
10
- require 'librato/metrics'
11
- require 'airbrake'
12
10
 
13
11
  Encoding.default_internal = "utf-8"
14
12
  Encoding.default_external = "utf-8"
@@ -16,14 +14,16 @@ Encoding.default_external = "utf-8"
16
14
  module MailCannon
17
15
  require_relative 'mailcannon/adapter'
18
16
  require_relative 'mailcannon/adapters/sendgrid_web'
17
+ require_relative 'mailcannon/envelope_bag_map_reduce'
18
+ require_relative 'mailcannon/envelope_bag_statistic'
19
19
  require_relative 'mailcannon/envelope_bag'
20
- require_relative 'mailcannon/envelope'
20
+ require_relative 'mailcannon/envelope'
21
21
  require_relative 'mailcannon/mail'
22
22
  require_relative 'mailcannon/stamp'
23
23
  require_relative 'mailcannon/event'
24
+ require_relative 'mailcannon/sendgrid_event'
24
25
  require_relative 'mailcannon/workers/barrel'
25
- require_relative 'mailcannon/librato'
26
- require_relative 'mailcannon/airbrake'
26
+ require_relative 'mailcannon/workers/envelope_bag_reduce_job'
27
27
  require_relative 'mailcannon/version'
28
28
 
29
29
  # To be used with caution
data/mailcannon.gemspec CHANGED
@@ -36,8 +36,6 @@ Gem::Specification.new do |s|
36
36
  s.add_dependency 'sidekiq'
37
37
  s.add_dependency 'sendgrid_webapi'
38
38
  s.add_dependency 'json-schema'
39
- s.add_dependency 'librato-metrics'
40
- s.add_dependency 'airbrake'
41
39
 
42
40
  s.add_development_dependency "vcr"
43
41
  s.add_development_dependency "rspec"
@@ -1,10 +1,24 @@
1
1
  require "spec_helper"
2
+ require 'benchmark'
3
+
2
4
 
3
5
  describe "shoot 1k emails!" do
4
- let(:envelope) { build(:envelope_multi_1k) }
6
+ let!(:envelope_a) { build(:envelope_multi_1k) }
5
7
  it "sends http request for Sendgrid web API" do
6
8
  VCR.use_cassette('mailcannon_integration_1k') do
7
- expect(envelope.send_bulk!).to be_true
9
+ Sidekiq::Testing.inline! do
10
+ bm = Benchmark.measure do
11
+ envelope_a.post!
12
+ end
13
+ puts "1k test real time: #{bm.real}"
14
+ expect(envelope_a.reload.processed?).to be_true
15
+
16
+ # Travis has been showing unstable performance, not feasible to include performance tests.
17
+ # The performance varies from machine to machine, specially when using dedicated servers for each service.
18
+ if ENV['PERFORMANCE_TEST']
19
+ expect(bm.real<0.2).to be_true
20
+ end
21
+ end
8
22
  end
9
23
  end
10
24
  end