mail_jack 0.2.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,49 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+ #
31
+ #.DS_Store
32
+
33
+ # For TextMate
34
+ #*.tmproj
35
+ #tmtags
36
+
37
+ # For emacs:
38
+ #*~
39
+ #\#*
40
+ #.\#*
41
+
42
+ # For vim:
43
+ #*.swp
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Peter P
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,51 @@
1
+ = MailJack
2
+
3
+ Like LoJack...get it?
4
+
5
+ This module will append query parameters of your choosing to every link
6
+ in the mailers you specify so that you can track click throughs. The parameters
7
+ are dynamically evaluated at mail send time with the Proc of your choosing. Cool, huh.
8
+
9
+ == Usage:
10
+
11
+ # config/initializers/mail_tracker.rb
12
+ MailJack.config do |config|
13
+
14
+ # specify what mailer classes you want to add tracking to
15
+ config.mailers = [:user_notifier]
16
+
17
+ # specify the regex that should be used filter hrefs
18
+ # (this is useful so you don't add tracking params to 3rd party urls off your site)
19
+ config.href_filter = /myapplication.com/
20
+
21
+ # MOST IMPORTANT PART
22
+ # Specify what attributes you want to track and the Proc that figures them out
23
+ # The attributes can be any name. The value must be an object that responds to #call
24
+ config.trackable do |track|
25
+ track.campaign = lambda{|mailer| mailer.action_name}
26
+ track.campaign_group = lambda{|mailer| mailer.class.name}
27
+ track.foobarbizbat = lambda{|mailer| Time.now}
28
+ end
29
+
30
+ # You can enable Base64 encoding of all the parameters like so:
31
+ config.encode_to = :your_param_name_that_will_hold_the_encoded_string
32
+ end
33
+
34
+ == Under the Covers
35
+
36
+ * You specify what mailer classes you want to track, and any other options
37
+ * You specify the attributes that will be tracked the Proc that should be used to figure out the value
38
+ * Mail::Message class has the trackable keys added as attr_accessors
39
+ * ActionMailer::Base#mail method is decorated via alias_method_chain for each mailer class specified
40
+ * MailJack registers a Mail::Interceptor to intercept all outgoing mail
41
+ * When ActionMailer::Base#mail is called, the undecorated method is called first, then MailJack
42
+ * fetches the values of the attributes specified by calling the Proc specified and passing in the mailer
43
+ * instance, so You can figure out what value should be returned
44
+ * Those values are then assigned to the Mail::Message class via the accessors we added in step 3
45
+ * The mail is sent and is subsequently intercepted by MailJack::Interceptor which then reads the values passed along into the Mail::Message, creates a query string, and finds all relevant href's and appends the tracking parameters
46
+
47
+ == Copyright
48
+
49
+ Copyright (c) 2013 Peter Philips. See LICENSE.txt for
50
+ further details.
51
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "mail-jack"
18
+ gem.homepage = "http://github.com/synth/mail-jack"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{TODO: one-line summary of your gem}
21
+ gem.description = %Q{TODO: longer description of your gem}
22
+ gem.email = "pete@p373.net"
23
+ gem.authors = ["Peter P"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "mail-jack #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/lib/mail_jack.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'mail_jack/config'
2
+ require 'mail_jack/mailer'
3
+ require 'mail_jack/interceptor'
4
+ require 'mail_jack/params_decoder'
5
+ require 'mail_jack/railtie'
6
+ ##################################################################################
7
+ #
8
+ # module MailJack
9
+ #
10
+ # - like LoJack...get it?
11
+ #
12
+ # This module will append query parameters of your choosing to every link
13
+ # in the mailers you specify so that you can track click throughs. The parameters
14
+ # are dynamically evaluated at mail send time with the Proc of your choosing. Cool, huh.
15
+ #
16
+ # Usage:
17
+ #
18
+ # # config/initializers/mail_tracker.rb
19
+ # MailJack.config do |config|
20
+ #
21
+ # # specify what mailer classes you want to add tracking to
22
+ # config.mailers = [:user_notifier]
23
+ #
24
+ # # specify the regex that should be used filter hrefs
25
+ # # (this is useful so you don't add tracking params to 3rd party urls off your site)
26
+ # config.href_filter = /myapplication.com/
27
+ #
28
+ # # MOST IMPORTANT PART
29
+ # # Specify what attributes you want to track and the Proc that figures them out
30
+ # # The attributes can be any name. The value must be an object that responds to #call
31
+ # config.trackable do |track|
32
+ # track.campaign = lambda{|mailer| mailer.action_name}
33
+ # track.campaign_group = lambda{|mailer| mailer.class.name}
34
+ # track.foobarbizbat = lambda{|mailer| Time.now}
35
+ # end
36
+ # end
37
+ #
38
+ # Under the Covers
39
+ #
40
+ # 1. You specify what mailer classes you want to track, and any other options
41
+ # 2. You specify the attributes that will be tracked the Proc that should be used to figure out the value
42
+ # 3. Mail::Message class has the trackable keys added as attr_accessors
43
+ # 4. ActionMailer::Base#mail method is decorated via alias_method_chain for each mailer class specified
44
+ # 5. MailJack registers a Mail::Interceptor to intercept all outgoing mail
45
+ # 5. When ActionMailer::Base#mail is called, the undecorated method is called first, then MailJack
46
+ # fetches the values of the attributes specified by calling the Proc specified and passing in the mailer
47
+ # instance, so You can figure out what value should be returned
48
+ # 6. Those values are then assigned to the Mail::Message class via the accessors we added in step 3
49
+ # 7. The mail is sent and is subsequently intercepted by MailJack::Interceptor which then reads the
50
+ # the values passed along into the Mail::Message, creates a query string, and finds all relevant href's
51
+ # and appends the tracking parameters
52
+ #
53
+ ##################################################################################
54
+ module MailJack
55
+ def self.config
56
+
57
+ # return configuration if already configured
58
+ return @@config if defined?(@@config) and @@config.kind_of?(Config)
59
+
60
+ # yield configuration
61
+ @@config = Config.new
62
+ yield @@config
63
+
64
+ # dynamically define the accessors onto Mail::Message
65
+ # so we can assign them later in the Interceptor
66
+ Mail::Message.class_eval do
67
+ attr_accessor *@@config.trackables.keys
68
+ end
69
+
70
+ # include the module that decorates(ie monkey patches)
71
+ # the Mail::Message#mail method
72
+ @@config.mailers.each do |mailer|
73
+ mailer.to_s.classify.constantize.send(:include, Mailer)
74
+ end
75
+
76
+ # register the interceptor
77
+ Mail.register_interceptor(MailJack::Interceptor)
78
+
79
+ end
80
+
81
+ # Fetch attributes gets the dynamically defined attributes
82
+ # off the object passed in, the object is ActionMailer::Base instance
83
+ def self.fetch_attributes(mailer)
84
+ map = {}
85
+ self.trackables.each do |attribute, proc|
86
+ map[attribute] = proc.call(mailer)
87
+ end
88
+ return map
89
+ end
90
+
91
+ def self.trackables
92
+ @@config.trackables
93
+ end
94
+ end
@@ -0,0 +1,19 @@
1
+ module MailJack
2
+ class Config
3
+ attr_accessor :mailers, :href_filter, :trackables, :encode_to
4
+ def trackable
5
+
6
+ # use OpenStruct for sugary assignment
7
+ @trackables = OpenStruct.new
8
+ yield @trackables
9
+
10
+ #convert from a struct to a map
11
+ @trackables = @trackables.methods(false).map(&:to_s).reject{|m| m.match(/\=$/) }.inject({}) {|hash, m| hash[m.to_sym] = @trackables.send(m); hash}
12
+ end
13
+
14
+ def enable_encoding?
15
+ self.encode_to.present?
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module MailJack
2
+ class Interceptor
3
+ def self.delivering_email(message)
4
+ body = message.body.to_s
5
+ hrefs = body.scan(/href=\"(.*?)\"/).flatten
6
+ hrefs = hrefs.grep(MailJack.config.href_filter)
7
+ hrefs = hrefs.uniq
8
+
9
+ querystr = MailJack.trackables.keys.inject({}) {|hash, a| hash[a] = message.send(a); hash}.to_query
10
+ hrefs.each do |h|
11
+ q = "?#{querystr}" unless h.include?("?")
12
+ body.gsub!(h, "#{h}#{q}")
13
+ end
14
+ message.body = body
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module MailJack
2
+ module Mailer
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method_chain :mail, :tracking
6
+ end
7
+ end
8
+
9
+ def mail_with_tracking(*args)
10
+ message = mail_without_tracking(*args)
11
+
12
+ # get a map of the required attributes
13
+ attributes = MailJack.fetch_attributes(self)
14
+
15
+ # assign them to the mail message
16
+ attributes.each do |key, val|
17
+ message.send("#{key}=", val)
18
+ end
19
+ return message
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module MailJack
2
+ class ParamsDecoder
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ decode_params(env) if MailJack.config.enable_encoding?
9
+ @status, @headers, @response = @app.call(env)
10
+ [@status, @headers, @response]
11
+ end
12
+
13
+ def decode_params(env)
14
+ param_name = MailJack.config.encode_to
15
+ return unless env['QUERY_STRING'].present? and env['QUERY_STRING'].match(/#{param_name}\=/)
16
+
17
+ params = {'QUERY_STRING' => env["QUERY_STRING"], 'REQUEST_URI' => env["REQUEST_URI"].split("?")[1]}
18
+
19
+ params.each do |key, value|
20
+ next unless value.present? and value.match(/#{param_name}\=/)
21
+
22
+ hash = Rack::Utils.parse_query(value)
23
+ encoded = hash.delete(param_name.to_s)
24
+ decoded = Base64.decode64(encoded)
25
+
26
+ env[key].gsub!("#{param_name}=#{encoded}", decoded)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ class Railtie < Rails::Railtie
2
+ initializer "mail_jack.configure_rails_initialization" do |app|
3
+ app.middleware.use MailJack::ParamsDecoder
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module MailJack
2
+ # The version of the gem
3
+ VERSION = '0.2.1'.freeze unless defined?(::MailJack::VERSION)
4
+ end
data/mail_jack.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "mail_jack/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Peter Philips"]
7
+ gem.description = %q{Autogenerate query parameters to links in emails to track click throughs}
8
+ gem.email = ['pete@p373.net']
9
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.homepage = 'https://github.com/synth/mail_jack'
12
+ gem.name = 'mail_jack'
13
+ gem.require_paths = ['lib']
14
+ gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
15
+ gem.summary = %q{Autogenerate query parameters to links in emails to track click throughs}
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.version = MailJack::VERSION.dup
18
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mail_jack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Philips
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-06-06 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Autogenerate query parameters to links in emails to track click throughs
15
+ email:
16
+ - pete@p373.net
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .document
22
+ - .gitignore
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.rdoc
26
+ - Rakefile
27
+ - lib/mail_jack.rb
28
+ - lib/mail_jack/config.rb
29
+ - lib/mail_jack/interceptor.rb
30
+ - lib/mail_jack/mailer.rb
31
+ - lib/mail_jack/params_decoder.rb
32
+ - lib/mail_jack/railtie.rb
33
+ - lib/mail_jack/version.rb
34
+ - mail_jack.gemspec
35
+ homepage: https://github.com/synth/mail_jack
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - '>='
51
+ - !ruby/object:Gem::Version
52
+ version: 1.3.6
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 1.8.25
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Autogenerate query parameters to links in emails to track click throughs
59
+ test_files: []