mark_as_read 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +12 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +54 -0
  8. data/Rakefile +6 -0
  9. data/app/controllers/mark_as_read/mark_as_read_controller.rb +17 -0
  10. data/app/helpers/mark_as_read/mailer_helper.rb +12 -0
  11. data/config/routes.rb +3 -0
  12. data/gemfiles/Gemfile.rails-3.2.x +5 -0
  13. data/gemfiles/Gemfile.rails-4.0.x +6 -0
  14. data/gemfiles/Gemfile.rails-edge +5 -0
  15. data/lib/mark_as_read.rb +10 -0
  16. data/lib/mark_as_read/engine.rb +12 -0
  17. data/lib/mark_as_read/readable.rb +25 -0
  18. data/lib/mark_as_read/version.rb +3 -0
  19. data/mark_as_read.gemspec +25 -0
  20. data/spec/controllers/mark_as_read/mark_as_read_controller_spec.rb +55 -0
  21. data/spec/fixtures/bar_model.rb +5 -0
  22. data/spec/fixtures/foo_model.rb +6 -0
  23. data/spec/mailers/mailer_spec.rb +12 -0
  24. data/spec/models/readable_spec.rb +30 -0
  25. data/spec/rails_app/.gitignore +16 -0
  26. data/spec/rails_app/Rakefile +6 -0
  27. data/spec/rails_app/app/assets/images/.keep +0 -0
  28. data/spec/rails_app/app/assets/javascripts/application.js +16 -0
  29. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  30. data/spec/rails_app/app/controllers/application_controller.rb +5 -0
  31. data/spec/rails_app/app/controllers/concerns/.keep +0 -0
  32. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  33. data/spec/rails_app/app/mailers/.keep +0 -0
  34. data/spec/rails_app/app/mailers/foo_mailer.rb +6 -0
  35. data/spec/rails_app/app/models/.keep +0 -0
  36. data/spec/rails_app/app/models/concerns/.keep +0 -0
  37. data/spec/rails_app/app/views/foo_mailer/foo.html.erb +2 -0
  38. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  39. data/spec/rails_app/bin/bundle +3 -0
  40. data/spec/rails_app/bin/rails +4 -0
  41. data/spec/rails_app/bin/rake +4 -0
  42. data/spec/rails_app/config.ru +4 -0
  43. data/spec/rails_app/config/application.rb +28 -0
  44. data/spec/rails_app/config/boot.rb +4 -0
  45. data/spec/rails_app/config/environment.rb +5 -0
  46. data/spec/rails_app/config/environments/development.rb +26 -0
  47. data/spec/rails_app/config/environments/production.rb +80 -0
  48. data/spec/rails_app/config/environments/test.rb +36 -0
  49. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  51. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  52. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  53. data/spec/rails_app/config/initializers/secret_token.rb +12 -0
  54. data/spec/rails_app/config/initializers/session_store.rb +3 -0
  55. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  56. data/spec/rails_app/config/locales/en.yml +23 -0
  57. data/spec/rails_app/config/routes.rb +56 -0
  58. data/spec/rails_app/db/seeds.rb +7 -0
  59. data/spec/rails_app/lib/assets/.keep +0 -0
  60. data/spec/rails_app/lib/tasks/.keep +0 -0
  61. data/spec/rails_app/log/.keep +0 -0
  62. data/spec/rails_app/public/404.html +58 -0
  63. data/spec/rails_app/public/422.html +58 -0
  64. data/spec/rails_app/public/500.html +57 -0
  65. data/spec/rails_app/public/favicon.ico +0 -0
  66. data/spec/rails_app/public/robots.txt +5 -0
  67. data/spec/routing/routing_spec.rb +13 -0
  68. data/spec/spec_helper.rb +22 -0
  69. metadata +223 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b514a65b8ece9190711d84ccb55e27c003060cc5
4
+ data.tar.gz: 4f3d62466c62f14f02f0a626993f0feb0e1ac7a4
5
+ SHA512:
6
+ metadata.gz: d553dd7a81c42ba80e3ffaa4704564863ef63fe6954f88d346c20c922d46df8849bc2ccab5038590abf3a872ab528ac34c0d5301ee2a1c98e751feb0060420c2
7
+ data.tar.gz: 831bfab4e936aa4de2d7a0f6179c01ae9434678c8e672731b3d9c4ae86b60260d004555087f0f95db8d52274f2d8722bed0695fd1aa3d50063d86e22f418d3fa
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ gemfile:
7
+ - gemfiles/Gemfile.rails-3.2.x
8
+ - gemfiles/Gemfile.rails-4.0.x
9
+ - gemfiles/Gemfile.rails-edge
10
+ matrix:
11
+ allow_failures:
12
+ - gemfile: gemfiles/Gemfile.rails-edge
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
6
+ gem 'rails', '4.0.2'
7
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Intrepidd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # mark_as_read [![Build Status](https://travis-ci.org/Intrepidd/mark_as_read.png?branch=master)](https://travis-ci.org/Intrepidd/mark_as_read) [![Code Climate](https://codeclimate.com/github/Intrepidd/mark_as_read.png)](https://codeclimate.com/github/Intrepidd/mark_as_read)
2
+
3
+ This gem allows you to be notified when one of your users reads an e-mail you sent to him via a tracking gif.
4
+ It's compatible with ruby on rails and any ORM that implements ``find`` and has an ``id`` field for its models.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'mark_as_read'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install mark_as_read
19
+
20
+ ## Usage
21
+
22
+ Let's say you have a ``Message`` class, simply add the following to describe the behavior to execute when a message is read:
23
+
24
+ class Message < ActiveRecord::Base
25
+
26
+ include MarkAsRead::Readable
27
+
28
+ mark_as_read do |message|
29
+ message.update_attributes(:read => true)
30
+ end
31
+
32
+ end
33
+
34
+ The read message is yielded to the ``mark_as_read`` function so you can do anything you want on it.
35
+
36
+
37
+ Then, in your mailer view, you just have to add the tracking image, let's assume you have the message in the ``@message`` variable :
38
+
39
+ <%= mark_as_read_img(@message) %>
40
+
41
+ ## Disclaimer
42
+
43
+ Tracking email openings with an image is not an exact science, depending on the client, the image may not been displayed unless the user asks for it.
44
+
45
+ Many clients will cache the image after its first load so it won't be loaded again, this gem is useful for knowing IF an e-mail has been opened, not really for knowing how many times.
46
+
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it ( http://github.com/Intrepidd/mark_as_read/fork )
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,17 @@
1
+ module MarkAsRead
2
+ class MarkAsReadController < ActionController::Base
3
+
4
+ def read
5
+ verifier = ActiveSupport::MessageVerifier.new(Rails.application.config.secret_key_base)
6
+ type = params[:type].constantize rescue nil
7
+ raise UnknownType.new("No such type: #{type}") unless type
8
+ raise InvalidType.new("Type #{type} is not a valid model type") unless type.respond_to?(:find)
9
+
10
+ model = type.find(verifier.verify(params[:id]))
11
+ raise InvalidType.new("Type #{type} does not include mark_as_read") unless model.respond_to?(:mark_as_read!)
12
+ model.mark_as_read!
13
+ send_data(Base64.decode64("R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="), :type => "image/gif", :disposition => "inline")
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,12 @@
1
+ module MarkAsRead
2
+
3
+ module MailerHelper
4
+
5
+ def mark_as_read_img(model)
6
+ verifier = ActiveSupport::MessageVerifier.new(Rails.application.config.secret_key_base)
7
+ image_tag(mark_as_read_url(:type => model.class, :id => verifier.generate(model.id)))
8
+ end
9
+
10
+ end
11
+
12
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ get '/mark_as_read/:type/:id.gif' => 'mark_as_read/mark_as_read#read', :as => 'mark_as_read'
3
+ end
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'rails', '~> 3.2.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'rails', '~> 4.0.0'
6
+
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec :path => '..'
4
+
5
+ gem 'rails', :github => 'rails/rails'
@@ -0,0 +1,10 @@
1
+ require "mark_as_read/version"
2
+ require "mark_as_read/engine"
3
+ require "mark_as_read/readable"
4
+
5
+ module MarkAsRead
6
+
7
+ class UnknownType < StandardError; end
8
+ class InvalidType < StandardError; end
9
+
10
+ end
@@ -0,0 +1,12 @@
1
+ require 'rails'
2
+ require 'active_record'
3
+
4
+ module MarkAsRead
5
+ class Engine < Rails::Engine
6
+
7
+ initializer "Add helper to ActionMailer" do
8
+ ActionMailer::Base.helper MarkAsRead::MailerHelper
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ require 'active_support/concern'
2
+
3
+ module MarkAsRead
4
+ module Readable
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ cattr_accessor :mark_as_read_proc
10
+ end
11
+
12
+ module ClassMethods
13
+ def mark_as_read(&block)
14
+ self.mark_as_read_proc = block
15
+ end
16
+ end
17
+
18
+ def mark_as_read!
19
+ if self.mark_as_read_proc
20
+ self.mark_as_read_proc.call(self)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module MarkAsRead
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mark_as_read/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mark_as_read"
8
+ spec.version = MarkAsRead::VERSION
9
+ spec.authors = ["Adrien Siami"]
10
+ spec.email = ["adrien@siami.fr"]
11
+ spec.summary = "Allows to easily track when an e-mail has been opened"
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.5"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec-rails"
23
+
24
+ spec.add_dependency("rails", ">= 3.2.6", "< 5")
25
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/foo_model'
3
+ require 'fixtures/bar_model'
4
+
5
+ describe MarkAsRead::MarkAsReadController do
6
+
7
+ before do
8
+ verifier = ActiveSupport::MessageVerifier.new(Rails.application.config.secret_key_base)
9
+ @id = verifier.generate(42)
10
+ end
11
+
12
+ it 'should raise if invalid type is provided' do
13
+ expect {
14
+ get :read, :type => 'notatype', :id => @id
15
+ }.to raise_error(MarkAsRead::UnknownType)
16
+ end
17
+
18
+ it 'should raise if not model type is provided' do
19
+ expect {
20
+ get :read, :type => 'Fixnum', :id => @id
21
+ }.to raise_error(MarkAsRead::InvalidType)
22
+ end
23
+
24
+ it 'should raise if not mark as read model type is provided' do
25
+ BarModel.stub(:find).and_return(BarModel.new)
26
+ expect {
27
+ get :read, :type => 'BarModel', :id => @id
28
+ }.to raise_error(MarkAsRead::InvalidType)
29
+ end
30
+
31
+ it 'should mark the model as read if everything is alright' do
32
+ model = FooModel.new
33
+ model.id = 42
34
+
35
+ FooModel.should_receive(:find).with(42).and_return(model)
36
+ proc = lambda { |model| }
37
+ FooModel.class_eval do
38
+ mark_as_read(&proc)
39
+ end
40
+
41
+ proc.should_receive(:call).with(model)
42
+
43
+
44
+ get :read, :type => 'FooModel', :id => @id
45
+
46
+ end
47
+
48
+ it 'should raise ActiveSupport::MessageVerifier::InvalidSignature if bad message' do
49
+ FooModel.stub(:find).and_return(nil)
50
+ expect {
51
+ get :read, :type => 'FooModel', :id => 'foobar'
52
+ }.to raise_error(ActiveSupport::MessageVerifier::InvalidSignature)
53
+ end
54
+
55
+ end
@@ -0,0 +1,5 @@
1
+ class BarModel
2
+ extend ActiveModel::Naming
3
+
4
+ attr_accessor :id
5
+ end
@@ -0,0 +1,6 @@
1
+ class FooModel
2
+ include MarkAsRead::Readable
3
+ extend ActiveModel::Naming
4
+
5
+ attr_accessor :id
6
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/foo_model'
3
+
4
+ describe FooMailer do
5
+ it 'should include a link to the image' do
6
+ model = FooModel.new
7
+ model.id = 42
8
+ FooModel.stub(:first).and_return model
9
+ verifier = ActiveSupport::MessageVerifier.new(Rails.application.config.secret_key_base)
10
+ FooMailer.foo.body.to_s.should include("example.com/mark_as_read/FooModel/#{verifier.generate(model.id)}.gif")
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/foo_model'
3
+
4
+ describe MarkAsRead::Readable do
5
+
6
+ describe '#mark_as_read' do
7
+ it 'should store the proc' do
8
+ proc = lambda { puts "Hello world" }
9
+ FooModel.class_eval do
10
+ mark_as_read(&proc)
11
+ end
12
+
13
+ FooModel.mark_as_read_proc.should == proc
14
+ end
15
+ end
16
+
17
+ describe '#mark_as_read!' do
18
+ it 'should call the proc' do
19
+ proc = lambda { puts "Hello world" }
20
+ FooModel.class_eval do
21
+ mark_as_read(&proc)
22
+ end
23
+
24
+ instance = FooModel.new
25
+ proc.should_receive(:call).with(instance)
26
+ instance.mark_as_read!
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,16 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*.log
16
+ /tmp
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ RailsApp::Application.load_tasks
File without changes
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require turbolinks
16
+ //= require_tree .