mailcannon 0.0.6 → 0.0.8.pre.1

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