mailcannon 0.0.6 → 0.0.8.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: ce8f8af0c1764d42f8fce3439f12656173aa38a0
4
- data.tar.gz: fda77250125034d2e845af16f97dd9e7e052b514
3
+ metadata.gz: e6f70f3c3924e3fc9da5c29330ca8015df42ff3f
4
+ data.tar.gz: c5908d8e8cc1dc99f740336762d46109a7e40116
5
5
  SHA512:
6
- metadata.gz: 801226bd8decb385e2db1c560e713ce918a3a117be730db2f3f6dfabd0146acb0a95098fecea68f63a3a77ac9ad8c8e59506f9f37e18709175fc59c487c6f8e7
7
- data.tar.gz: 9dbf0a1f4260b54826006092437fa0e26766c6a0e2f35c94dad4685b6ee1014641bef9c04a7f1ba0165e52d182077d129abf19e0ddd7c7e11b96d49dc3797a59
6
+ metadata.gz: 2a9a225bdeb47b4e1df914b3ce708797f7b801c3edd0e497782ef20fe6b96cb1a0128b84caea6fa6f3756e3e5dc05e437d7905495307034164fefc773dc43d88
7
+ data.tar.gz: e7556aa2122b198b96143e8152514de44e1c91772d4f54831b63dfbc3fae2e9a9920d296f5a6f9b0ffdf99c7a334812d12b0f896fb1dca643c4cd7202d8b2784
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,9 +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
- #gem 'librato-metrics'
8
+ gem 'librato-metrics'
9
9
  gem 'rubysl', platform: :rbx
10
10
  gem 'jruby-openssl', platform: :jruby
11
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
@@ -1,13 +1,17 @@
1
1
  [![Gem version](https://badge.fury.io/rb/mailcannon.png)](http://rubygems.org/gems/mailcannon) [![Code Climate](https://codeclimate.com/github/lucasmartins/mailcannon.png)](https://codeclimate.com/github/lucasmartins/mailcannon) [![Build Status](https://travis-ci.org/lucasmartins/mailcannon.png?branch=master)](https://travis-ci.org/lucasmartins/mailcannon) [![Coverage Status](https://coveralls.io/repos/lucasmartins/mailcannon/badge.png)](https://coveralls.io/r/lucasmartins/mailcannon) [![Dependency Status](https://gemnasium.com/lucasmartins/mailcannon.png)](https://gemnasium.com/lucasmartins/mailcannon) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lucasmartins/mailcannon/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
2
2
 
3
+ ![MailCannon_logo](http://blog.railsnapraia.com/images/mailcannon_seal_128.png)
4
+
3
5
  MailCannon
4
6
  ==========
5
7
 
6
- This is a **WORK IN PROGRESS**
8
+ Although this is a **WORK IN PROGRESS**, we're getting great results on **production** at [Resultados Digitais](http://resultadosdigitais.com.br/).
7
9
 
8
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 ).
9
11
 
10
- This Gem provides workers 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).
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
+
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.
11
15
 
12
16
  Install
13
17
  =======
@@ -18,7 +22,7 @@ You can:
18
22
  ```
19
23
 
20
24
  Or just add it to your Gemfile
21
- ```
25
+ ```ruby
22
26
  gem 'mailcannon'
23
27
  ```
24
28
 
@@ -64,7 +68,7 @@ envelope = MailCannon::Envelope.create(
64
68
  subject: 'Test',
65
69
  mail: MailCannon::Mail.new(text: 'you will see this when no HTML reader is available', html: 'this should be an HTML'))
66
70
  envelope_bag.push envelope
67
- envelope_bag.post! # this will sent using the 'hot-account'.
71
+ envelope_bag.post! # this will be sent using the 'hot-account'.
68
72
  ```
69
73
 
70
74
  ### Configuration file
@@ -83,17 +87,52 @@ Edit the file to meet your environemnt needs.
83
87
 
84
88
  Check the [specs](https://github.com/lucasmartins/mailcannon/tree/master/spec) to see the testing example, it will surely make it clearer.
85
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
+
86
117
  Docs
87
118
  ====
88
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).
89
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
+
90
129
  Contribute
91
130
  ==========
92
131
 
93
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.
94
133
 
95
134
  **NOTICE**: The project is at embrionary stage, breaking changes will apply.
96
-
135
+
97
136
  Support
98
137
  =======
99
138
 
data/env.sample.sh CHANGED
@@ -1,7 +1,9 @@
1
1
  export REDIS_URL='redis://localhost:6379'
2
- export SENDGRID_PASSWORD=''
3
2
  export SENDGRID_USERNAME=''
4
- export LIBRATO_SOURCE='staging'
5
- export LIBRATO_USER=''
6
- export LIBRATO_TOKEN=''
3
+ export SENDGRID_PASSWORD=''
4
+ #export AIRBRAKE_TOKEN=''
5
+ #export LIBRATO_SOURCE='staging'
6
+ #export LIBRATO_USER=''
7
+ #export LIBRATO_TOKEN=''
7
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
 
@@ -50,30 +50,66 @@ module MailCannon::Adapter::SendgridWeb
50
50
  validate_envelope!
51
51
  self.xsmtpapi = {} if self.xsmtpapi.nil?
52
52
  self.xsmtpapi['sub']={} unless self.xsmtpapi['sub']
53
- self.xsmtpapi = self.xsmtpapi.merge(build_xsmtpapi({'to'=>self.to},{'sub'=>self.substitutions}))
53
+ self.xsmtpapi = self.xsmtpapi.deep_merge(build_xsmtpapi({'to'=>self.to},{'sub'=>self.substitutions}))
54
54
  validate_xsmtpapi!
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|
89
+ to = extract_values(recipients[:to],:email)
90
+ xsmtpapi.merge!({'to' => to}) if to
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 })
95
+ return xsmtpapi
96
+ end
97
+
98
+ def extract_values(values,key)
99
+ extract=[]
100
+ values.each do |h|
70
101
  h.symbolize_keys!
71
- to.push h[:email]
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
72
112
  end
73
- xsmtpapi.merge!({'to' => to})
74
- xsmtpapi.deep_merge!(subs) if subs!=nil && subs.is_a?(Hash)
75
- xsmtpapi.deep_merge!(build_name_subs) if build_name_subs!=nil && build_name_subs.is_a?(Hash)
76
- return xsmtpapi
77
113
  end
78
114
 
79
115
  def validate_xsmtpapi!
@@ -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
@@ -29,15 +30,8 @@ class MailCannon::Envelope
29
30
  self.save if self.changed?
30
31
  raise "Envelope(#{self.id}) has no mail! Didn't you already send it?" unless self.mail
31
32
  if validate_xsmtpapi(self.xsmtpapi)
32
- if MailCannon.config['waiting_time'] && MailCannon.config['waiting_time'].to_i>0
33
- self.jid = MailCannon::Barrel.perform_in(MailCannon.config['waiting_time'].seconds,self.id)
34
- else
35
- self.jid = MailCannon::Barrel.perform_async(self.id)
36
- end
37
- if self.jid
38
- self.stamp! MailCannon::Event::Posted.stamp
39
- return self.jid
40
- end
33
+ jid = schedule_send_job
34
+ self.save if self.changed?
41
35
  else
42
36
  raise 'Invalid xsmtpapi hash!'
43
37
  end
@@ -72,8 +66,28 @@ class MailCannon::Envelope
72
66
  false
73
67
  end
74
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
75
77
 
76
78
  private
79
+ def schedule_send_job
80
+ if MailCannon.config['waiting_time'].to_i>0
81
+ self.jid = MailCannon::Barrel.perform_in(MailCannon.config['waiting_time'].seconds,self.id)
82
+ else
83
+ self.jid = MailCannon::Barrel.perform_async(self.id)
84
+ end
85
+ if self.jid
86
+ self.stamp! MailCannon::Event::Posted.stamp
87
+ return self.jid
88
+ end
89
+ end
90
+
77
91
  def self.valid_code_kind?(code)
78
92
  unless [Fixnum, MailCannon::Stamp].include?(code.class) || MailCannon::Event.constants.include?(code.to_s.camelize.to_sym)
79
93
  raise 'code must be an Integer, MailCannon::Event::*, or MailCannon::Stamp !'
@@ -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
@@ -3,7 +3,8 @@ module MailCannon
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 0
6
- PATCH = 6
7
- STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
6
+ PATCH = 8
7
+ ALPHA = '.pre.1'
8
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}#{ALPHA}"
8
9
  end
9
10
  end
@@ -1,9 +1,14 @@
1
1
  # This worker handles Envelopes dispatch
2
2
  class MailCannon::Barrel
3
3
  include Sidekiq::Worker
4
-
4
+
5
5
  def perform(envelope_id)
6
6
  envelope_id = envelope_id['$oid'] if envelope_id['$oid']
7
+ shoot!(envelope_id)
8
+ end
9
+
10
+ private
11
+ def shoot!(envelope_id)
7
12
  logger.info "sending MailCannon::Envelope.find('#{envelope_id}')"
8
13
  begin
9
14
  envelope = MailCannon::Envelope.find(envelope_id.to_s)
@@ -16,7 +21,7 @@ class MailCannon::Barrel
16
21
  rescue Mongoid::Errors::DocumentNotFound
17
22
  logger.error "unable to find the document MailCannon::Envelope.find('#{envelope_id}')"
18
23
  rescue Exception => e
19
- logger.error "unable to send MailCannon::Envelope.find('#{envelope_id}')\n#{e.backtrace}"
24
+ logger.error "unable to send MailCannon::Envelope.find('#{envelope_id}') #{e.backtrace}"
20
25
  end
21
26
  end
22
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,7 +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
10
 
12
11
  Encoding.default_internal = "utf-8"
13
12
  Encoding.default_external = "utf-8"
@@ -15,16 +14,18 @@ Encoding.default_external = "utf-8"
15
14
  module MailCannon
16
15
  require_relative 'mailcannon/adapter'
17
16
  require_relative 'mailcannon/adapters/sendgrid_web'
17
+ require_relative 'mailcannon/envelope_bag_map_reduce'
18
+ require_relative 'mailcannon/envelope_bag_statistic'
18
19
  require_relative 'mailcannon/envelope_bag'
19
- require_relative 'mailcannon/envelope'
20
+ require_relative 'mailcannon/envelope'
20
21
  require_relative 'mailcannon/mail'
21
22
  require_relative 'mailcannon/stamp'
22
23
  require_relative 'mailcannon/event'
24
+ require_relative 'mailcannon/sendgrid_event'
23
25
  require_relative 'mailcannon/workers/barrel'
26
+ require_relative 'mailcannon/workers/envelope_bag_reduce_job'
24
27
  require_relative 'mailcannon/version'
25
28
 
26
- #Librato::Metrics.authenticate(ENV['LIBRATO_USER'], ENV['LIBRATO_TOKEN']) if ENV['LIBRATO_TOKEN'] && ENV['LIBRATO_USER'] # change to initializer
27
-
28
29
  # To be used with caution
29
30
  def self.warmode
30
31
  #Mongoid.load!("config/mongoid.yml", ENV['RACK_ENV']||'development') # change to env URL
data/mailcannon.gemspec CHANGED
@@ -31,7 +31,6 @@ Gem::Specification.new do |s|
31
31
 
32
32
  s.add_runtime_dependency 'activemodel', '>= 3.0.0'
33
33
 
34
- #s.add_dependency 'librato-metrics'
35
34
  s.add_dependency 'redis'
36
35
  s.add_dependency 'mongoid'
37
36
  s.add_dependency 'sidekiq'