eventifier 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -8
  4. data/Gemfile +1 -1
  5. data/README.textile +44 -3
  6. data/app/assets/images/eventifier/notifications-bell.png +0 -0
  7. data/app/assets/images/eventifier/notifications-tower.png +0 -0
  8. data/app/assets/javascripts/eventifier/notifications.coffee +196 -0
  9. data/app/assets/javascripts/eventifier/templates/dropdown.hamlc +10 -0
  10. data/app/assets/javascripts/eventifier/templates/settings.hamlc +11 -0
  11. data/app/assets/stylesheets/eventifier/notifications.scss +167 -0
  12. data/app/controllers/eventifier/application_controller.rb +4 -0
  13. data/app/controllers/eventifier/notifications_controller.rb +36 -0
  14. data/app/controllers/eventifier/preferences_controller.rb +19 -0
  15. data/app/helpers/eventifier/notification_helper.rb +87 -0
  16. data/app/helpers/eventifier/path_helper.rb +15 -0
  17. data/app/mailers/eventifier/mailer.rb +12 -0
  18. data/app/models/event_tracking.rb +3 -0
  19. data/app/models/eventifier/event.rb +22 -0
  20. data/app/models/eventifier/ghost.rb +21 -0
  21. data/app/models/eventifier/notification.rb +38 -0
  22. data/app/models/eventifier/notification_setting.rb +14 -0
  23. data/app/views/eventifier/_notification.haml +1 -0
  24. data/app/views/eventifier/email/_notification.haml +2 -0
  25. data/app/views/eventifier/mailer/notifications.haml +5 -0
  26. data/app/views/eventifier/notifications/index.jbuilder +6 -0
  27. data/config/routes.rb +7 -0
  28. data/config.ru +7 -0
  29. data/{lib/generators/eventifier/install/templates/migration.rb → db/migrate/1_eventifier_setup.rb} +2 -3
  30. data/db/migrate/2_eventifier_notification_settings.rb +11 -0
  31. data/db/migrate/3_notification_sent_status.rb +15 -0
  32. data/db/migrate/4_event_groupable.rb +19 -0
  33. data/eventifier.gemspec +12 -15
  34. data/lib/eventifier/delivery.rb +59 -0
  35. data/lib/eventifier/{railtie.rb → engine.rb} +4 -2
  36. data/lib/eventifier/event_subscriber.rb +44 -0
  37. data/lib/eventifier/event_tracking.rb +5 -58
  38. data/lib/eventifier/mailers/helpers.rb +70 -0
  39. data/lib/eventifier/notifier/notification_mapping.rb +34 -0
  40. data/lib/eventifier/notifier/notification_subscriber.rb +23 -0
  41. data/lib/eventifier/notifier/notifier.rb +21 -0
  42. data/lib/eventifier/preferences.rb +44 -0
  43. data/lib/eventifier/relationship.rb +36 -0
  44. data/lib/eventifier/trackable_class.rb +74 -0
  45. data/lib/eventifier/tracker.rb +18 -0
  46. data/lib/eventifier.rb +35 -11
  47. data/lib/generators/eventifier/install/install_generator.rb +2 -5
  48. data/lib/generators/eventifier/install/templates/events.en.yaml +1 -0
  49. data/lib/tasks/email.rake +8 -0
  50. data/spec/controllers/eventifier/notifications_controller_spec.rb +24 -0
  51. data/spec/controllers/eventifier/preferences_controller_spec.rb +56 -0
  52. data/spec/eventifier/delivery_spec.rb +110 -0
  53. data/spec/eventifier/preferences_spec.rb +69 -0
  54. data/spec/eventifier/relationship_spec.rb +33 -0
  55. data/spec/eventifier_spec.rb +6 -48
  56. data/spec/fabricators/fabricator.rb +11 -11
  57. data/spec/helpers/eventifier/notification_helper_spec.rb +169 -0
  58. data/spec/helpers/eventifier/path_helper_spec.rb +19 -0
  59. data/spec/integration/eventifier_spec.rb +113 -70
  60. data/spec/integration/internationalisation_spec.rb +39 -0
  61. data/spec/internal/app/controllers/application_controller.rb +3 -0
  62. data/spec/internal/app/lib/event_tracking.rb +12 -0
  63. data/spec/internal/app/models/category.rb +3 -0
  64. data/spec/internal/app/models/post.rb +8 -0
  65. data/spec/internal/app/models/subscription.rb +4 -0
  66. data/spec/internal/app/models/user.rb +4 -0
  67. data/spec/internal/app/views/eventifier/dropdown/_awesome_object.haml +0 -0
  68. data/spec/internal/config/database.yml +4 -0
  69. data/spec/internal/config/initializers/eventifier.rb +1 -0
  70. data/spec/internal/config/locales/events.en.yml +20 -0
  71. data/spec/internal/config/routes.rb +5 -0
  72. data/spec/internal/db/schema.rb +31 -0
  73. data/spec/internal/log/.gitignore +1 -0
  74. data/spec/internal/public/favicon.ico +0 -0
  75. data/spec/models/eventifier/event_spec.rb +23 -0
  76. data/spec/{ghost_spec.rb → models/eventifier/ghost_spec.rb} +3 -3
  77. data/spec/{notification_spec.rb → models/eventifier/notification_spec.rb} +1 -9
  78. data/spec/notification_mailer_spec.rb +8 -4
  79. data/spec/notifier/notification_mapping_spec.rb +28 -0
  80. data/spec/spec_helper.rb +14 -8
  81. data/spec/support/controller_helpers.rb +14 -0
  82. metadata +205 -128
  83. data/Gemfile.lock +0 -97
  84. data/lib/eventifier/active_record/event.rb +0 -10
  85. data/lib/eventifier/active_record/event_observer.rb +0 -8
  86. data/lib/eventifier/active_record/event_tracking.rb +0 -11
  87. data/lib/eventifier/active_record/ghost.rb +0 -9
  88. data/lib/eventifier/active_record/notification.rb +0 -13
  89. data/lib/eventifier/active_record_support.rb +0 -5
  90. data/lib/eventifier/event_helper.rb +0 -40
  91. data/lib/eventifier/event_mixin.rb +0 -45
  92. data/lib/eventifier/event_observer_mixin.rb +0 -44
  93. data/lib/eventifier/ghost_mixin.rb +0 -27
  94. data/lib/eventifier/helper_methods.rb +0 -36
  95. data/lib/eventifier/mongoid/event.rb +0 -16
  96. data/lib/eventifier/mongoid/event_observer.rb +0 -7
  97. data/lib/eventifier/mongoid/event_tracking.rb +0 -13
  98. data/lib/eventifier/mongoid/ghost.rb +0 -15
  99. data/lib/eventifier/mongoid/notification.rb +0 -19
  100. data/lib/eventifier/mongoid/user_patch.rb +0 -3
  101. data/lib/eventifier/mongoid_support.rb +0 -6
  102. data/lib/eventifier/notification_helper.rb +0 -42
  103. data/lib/eventifier/notification_mailer.rb +0 -18
  104. data/lib/eventifier/notification_mixin.rb +0 -50
  105. data/lib/eventifier/version.rb +0 -3
  106. data/spec/event_helper_spec.rb +0 -67
  107. data/spec/event_observer_spec.rb +0 -20
  108. data/spec/event_spec.rb +0 -39
  109. data/spec/helper_methods_spec.rb +0 -36
  110. data/spec/notification_helper_spec.rb +0 -65
  111. data/spec/support/action_mailer.rb +0 -3
  112. data/spec/support/database_cleaner.rb +0 -3
  113. data/spec/test_classes/active_record_support.rb +0 -72
  114. data/spec/test_classes/mongoid_support.rb +0 -34
@@ -0,0 +1,22 @@
1
+ module Eventifier
2
+ class Event < ActiveRecord::Base
3
+ attr_accessible :user, :eventable, :verb, :change_data, :groupable
4
+
5
+ belongs_to :user
6
+ belongs_to :eventable, polymorphic: true
7
+ belongs_to :groupable, polymorphic: true
8
+ has_many :notifications, class_name: 'Eventifier::Notification',
9
+ dependent: :destroy
10
+
11
+ validates :user, presence: true
12
+ validates :eventable, presence: true
13
+ validates :verb, presence: true
14
+ validates :groupable, presence: true
15
+
16
+ serialize :change_data
17
+
18
+ def self.find_all_by_eventable object
19
+ where :eventable_id => object.id, :eventable_type => object.class.name
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module Eventifier
2
+ class Ghost < ActiveRecord::Base
3
+ validates :ghost_class, :presence => true
4
+ validates :ghost_id, :presence => true
5
+ validates :data_hash, :presence => true
6
+
7
+ serialize :data_hash
8
+
9
+ def self.create_from_object object
10
+ create :ghost_class => object.class.name, :ghost_id => object.id, :data_hash => object.serializable_hash
11
+ end
12
+
13
+ def ghost
14
+ klass = Object.const_get(ghost_class)
15
+ ghost_object = klass.new data_hash
16
+ ghost_object.id = ghost_id
17
+
18
+ ghost_object
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module Eventifier
2
+ class Notification < ActiveRecord::Base
3
+ attr_accessible :event, :user, :relations, :event_id, :user_id
4
+
5
+ belongs_to :event, :class_name => 'Eventifier::Event'
6
+ belongs_to :user
7
+
8
+ serialize :relations, MultiJson
9
+
10
+ validates :event, :presence => true
11
+ validates :user, :presence => true
12
+ validates :event_id, :uniqueness => { :scope => :user_id }
13
+
14
+ default_scope order("notifications.created_at DESC")
15
+ scope :for_events, -> ids { where(event_id: ids) }
16
+ scope :for_user, -> user { where(user_id: user.id) }
17
+ scope :since, -> date { where("created_at > ?", date) }
18
+ scope :latest, order('notifications.created_at DESC').limit(5)
19
+ scope :unsent, -> { where(sent: false) }
20
+
21
+ def self.expire_for_past_events!(time_limit = 1.day.ago)
22
+ self.for_events(Event.expired_ids(time_limit)).each &:expire!
23
+ end
24
+
25
+ def self.unread_for(user)
26
+ if user.notifications_last_read_at
27
+ for_user(user).since(user.notifications_last_read_at)
28
+ else
29
+ for_user(user)
30
+ end
31
+ end
32
+
33
+ def unread_for?(user)
34
+ return true if user.notifications_last_read_at.nil?
35
+ created_at > user.notifications_last_read_at
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ class Eventifier::NotificationSetting < ActiveRecord::Base
2
+ belongs_to :user
3
+
4
+ serialize :preferences, MultiJson
5
+
6
+ attr_accessible :user
7
+
8
+ validates :user, :presence => true
9
+ validates :user_id, :uniqueness => true
10
+
11
+ def self.for_user(user)
12
+ where(:user_id => user.id).first || create!(:user => user)
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ #{event.user.try :to_s} #{pastize(event.verb)} a #{event.eventable_type.titleize}
@@ -0,0 +1,2 @@
1
+ %a{ href: root_url }
2
+ #{event.user.try :to_s} #{pastize(event.verb)} a #{event.eventable_type.titleize}
@@ -0,0 +1,5 @@
1
+ %p You have new notifications
2
+
3
+ %ul.notifications
4
+ - @notifications.each do |notification|
5
+ %li= render_partial_view(notification, :email)
@@ -0,0 +1,6 @@
1
+ json.last_read_at current_user.notifications_last_read_at.to_i
2
+ json.notifications @notifications do |notification|
3
+ json.(notification, :id)
4
+ json.created_at notification.created_at.to_i
5
+ json.html render_partial_view(notification, :dropdown)
6
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ Eventifier::Engine.routes.draw do
2
+ resources :notifications, module: 'eventifier' do
3
+ collection { post :touch }
4
+ end
5
+
6
+ resource :preferences, module: 'eventifier'
7
+ end
data/config.ru ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ Bundler.require :default, :development
5
+
6
+ Combustion.initialize!
7
+ run Combustion::Application
@@ -9,7 +9,7 @@ class EventifierSetup < ActiveRecord::Migration
9
9
 
10
10
  t.timestamps
11
11
  end
12
-
12
+
13
13
  add_index :events, :user_id
14
14
  add_index :events, [:eventable_id, :eventable_type]
15
15
 
@@ -23,8 +23,7 @@ class EventifierSetup < ActiveRecord::Migration
23
23
  add_index :notifications, :event_id
24
24
  add_index :notifications, :user_id
25
25
  add_index :notifications, :parent_id
26
-
26
+
27
27
  add_column :users, :notifications_last_read_at, :datetime
28
28
  end
29
-
30
29
  end
@@ -0,0 +1,11 @@
1
+ class EventifierNotificationSettings < ActiveRecord::Migration
2
+ def change
3
+ create_table :notification_settings do |t|
4
+ t.integer :user_id, :null => false
5
+ t.text :preferences, :null => false, :default => '{}'
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :notification_settings, :user_id, :unique => true
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ class NotificationSentStatus < ActiveRecord::Migration
2
+ def up
3
+ add_column :notifications, :sent, :boolean, :default => false
4
+ add_column :notifications, :relations, :text, :default => '[]'
5
+ add_index :notifications, :sent
6
+
7
+ Eventifier::Notification.reset_column_information
8
+ Eventifier::Notification.update_all :sent => true
9
+ end
10
+
11
+ def down
12
+ remove_column :notifications, :sent
13
+ remove_column :notifications, :relations
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ class EventGroupable < ActiveRecord::Migration
2
+ def up
3
+ add_column :events, :groupable_id, :integer
4
+ add_column :events, :groupable_type, :string
5
+
6
+ Eventifier::Event.reset_column_information
7
+ Eventifier::Event.update_all(
8
+ 'groupable_id = eventable_id, groupable_type = eventable_type'
9
+ )
10
+
11
+ change_column :events, :groupable_id, :integer, :null => false
12
+ change_column :events, :groupable_type, :string, :null => false
13
+ end
14
+
15
+ def down
16
+ remove_column :events, :groupable_id
17
+ remove_column :events, :groupable_type
18
+ end
19
+ end
data/eventifier.gemspec CHANGED
@@ -1,10 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "eventifier/version"
4
-
5
2
  Gem::Specification.new do |s|
6
3
  s.name = "eventifier"
7
- s.version = Eventifier::VERSION
4
+ s.version = '0.0.8'
8
5
  s.authors = ["Nathan Sampimon", "Peter Murray"]
9
6
  s.email = ["nathan@inspire9.com"]
10
7
  s.homepage = "http://github.com/inspire9/eventifier"
@@ -18,16 +15,16 @@ Gem::Specification.new do |s|
18
15
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
16
  s.require_paths = ["lib"]
20
17
 
21
- s.add_development_dependency "activerecord"
22
- s.add_development_dependency "mongoid"
23
- s.add_runtime_dependency "actionmailer"
24
- s.add_development_dependency "fabrication"
25
- s.add_development_dependency "database_cleaner"
26
- s.add_development_dependency "pg"
27
- s.add_development_dependency "rspec"
28
-
29
- s.add_runtime_dependency "activerecord"
18
+ s.add_runtime_dependency 'rails', '~> 3.2.0'
30
19
  s.add_runtime_dependency "bson_ext"
31
- s.add_runtime_dependency "mongoid"
32
- s.add_runtime_dependency "actionmailer"
20
+ s.add_runtime_dependency 'haml-rails', '~> 0.4'
21
+ s.add_runtime_dependency 'haml_coffee_assets'
22
+ s.add_runtime_dependency 'coffee-rails', '~> 3.2.1'
23
+ s.add_runtime_dependency 'compass-rails'
24
+ s.add_runtime_dependency 'multi_json', '~> 1.7.4'
25
+
26
+ s.add_development_dependency 'combustion', '~> 0.5.0'
27
+ s.add_development_dependency 'fabrication', '~> 2.7.1'
28
+ s.add_development_dependency "pg"
29
+ s.add_development_dependency 'rspec-rails', '~> 2.13.2'
33
30
  end
@@ -0,0 +1,59 @@
1
+ class Eventifier::Delivery
2
+ def self.deliver
3
+ unsent = Eventifier::Notification.unsent
4
+ unsent.group_by(&:user).each do |user, notifications|
5
+ new(user, notifications).deliver
6
+ end
7
+ end
8
+
9
+ def initialize(user, notifications)
10
+ @user, @notifications = user, notifications
11
+ end
12
+
13
+ def deliver
14
+ if anything_to_send?
15
+ Eventifier.mailer.notifications(user, notifications_to_send).deliver
16
+ end
17
+
18
+ notifications.each do |notification|
19
+ notification.update_attribute :sent, true
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :user, :notifications
26
+
27
+ def anything_to_send?
28
+ !notifications_to_send.empty?
29
+ end
30
+
31
+ def notifications_to_send
32
+ @notifications_to_send ||= notifications.select { |notification|
33
+ include_notification? notification
34
+ }
35
+ end
36
+
37
+ def settings
38
+ @settings ||= Eventifier::NotificationSetting.for_user user
39
+ end
40
+
41
+ def include_notification?(notification)
42
+ return true if settings.preferences['email'].nil?
43
+
44
+ specifics = notification.relations.collect { |relation|
45
+ key = [
46
+ notification.event.verb,
47
+ notification.event.eventable_type.underscore.pluralize,
48
+ 'notify',
49
+ Eventifier::Relationship.new(self, relation).key
50
+ ].join('_')
51
+ settings.preferences['email'][key]
52
+ }.compact
53
+
54
+ return specifics.any? unless specifics.empty?
55
+
56
+ default = settings.preferences['email']['default']
57
+ default.nil? || default
58
+ end
59
+ end
@@ -1,7 +1,9 @@
1
1
  module Eventifier
2
- class Railtie < Rails::Railtie
2
+ class Engine < Rails::Engine
3
+ engine_name :eventifier
4
+
3
5
  config.after_initialize do
4
6
  ::EventTracking.new
5
7
  end
6
8
  end
7
- end
9
+ end
@@ -0,0 +1,44 @@
1
+ class Eventifier::EventSubscriber
2
+ def self.subscribe_to_all klass, names
3
+ names.each do |method_name|
4
+ new.subscribe_to_method klass, method_name
5
+ end
6
+ end
7
+
8
+ def subscribe_to_method klass, method_name
9
+ name = "#{method_name}.#{klass.name.tableize}.event.eventifier"
10
+
11
+ return if ActiveSupport::Notifications.notifier.listening?(name)
12
+
13
+ ActiveSupport::Notifications.subscribe name do |*args|
14
+ event = ActiveSupport::Notifications::Event.new(*args)
15
+ event_user = if event.payload[:user]
16
+ Eventifier::Relationship.new(event.payload[:object], event.payload[:user]).users.first
17
+ else
18
+ event.payload[:object].user
19
+ end
20
+ groupable = event.payload[:object]
21
+ groupable = Eventifier::Relationship.new(groupable, event.payload[:group_by]).users.first if event.payload[:group_by]
22
+
23
+ eventifier_event = Eventifier::Event.create(
24
+ user: event_user,
25
+ eventable: event.payload[:object],
26
+ groupable: groupable,
27
+ verb: event.payload[:event],
28
+ change_data: change_data(event.payload[:object], event.payload[:options])
29
+ )
30
+
31
+ ActiveSupport::Notifications.instrument("#{method_name}.#{klass.name.tableize}.notification.eventifier", verb: :create, event: eventifier_event, object: event.payload[:object])
32
+ end
33
+ end
34
+
35
+ private
36
+ def change_data object, options
37
+ change_data = object.changes.stringify_keys
38
+
39
+ change_data = change_data.reject { |attribute, value| options[:except].include?(attribute) } if options[:except]
40
+ change_data = change_data.select { |attribute, value| options[:only].include?(attribute) } if options[:only]
41
+
42
+ change_data.symbolize_keys
43
+ end
44
+ end
@@ -8,74 +8,22 @@ module Eventifier
8
8
  methods = options.delete(:track_on)
9
9
  attributes = options.delete(:attributes)
10
10
 
11
- create_observers
12
-
13
11
  if block.nil?
14
12
  track_on methods, :attributes => attributes
15
13
  else
16
14
  instance_eval(&block)
17
15
  end
18
- end
19
-
20
- def create_observers
21
- # Create all the observer classes
22
- @klasses.each do |target_klass|
23
16
 
24
- # If the observer doesn't exist, create the class
25
- unless self.class.const_defined?("#{target_klass}Observer")
26
- constant_name = "#{target_klass}Observer"
27
- klass = Class.new(OBSERVER_CLASS)
28
- self.class.qualified_const_set(constant_name, klass)
29
- end
30
- end
17
+ @klasses.each { |klass| Eventifier.tracked_classes << klass }
18
+ Eventifier.tracked_classes.uniq!
31
19
  end
32
20
 
33
- def track_on methods, options = { }
34
- methods = methods.kind_of?(Array) ? methods : [methods]
35
- attributes = options.delete(:attributes) || {}
36
- raise 'No events defined to track' if methods.compact.empty?
37
- User.class_eval { has_many :notifications, :class_name => 'Eventifier::Notification' } unless User.respond_to?(:notifications)
38
- Eventifier::EventObserver.instance
39
-
40
- # set up each class with an observer and relationships
41
- @klasses.each do |target_klass|
42
- # Add relations to class
43
- target_klass.class_eval { has_many :events, :as => :eventable, :class_name => 'Eventifier::Event', :dependent => :destroy }
44
- add_notification_association(target_klass)
45
-
46
- # create an observer and have it observe the class
47
- klass = self.class.const_get("#{target_klass}Observer")
48
- klass.observe target_klass
49
-
50
- # create a callback for the methods we want to track
51
- klass.class_eval do
52
- methods.each do |event|
53
- define_method "after_#{event}" do |object|
54
- # create an event when the callback is fired
55
- Eventifier::Event.create_event(event.to_sym, object, attributes) if object.changed?
56
- end
57
- end
58
- end
59
- # instantiate the observer
60
- klass.instance
61
- end
21
+ def track_on methods, options = {}
22
+ Eventifier::Tracker.new @klasses, methods, options
62
23
  end
63
24
 
64
25
  def notify *args
65
- # args will either be [:relation, {:on => :create}] or [{:relation => :second_relation, :on => :create}]
66
- # if its the first one, relation is the first in the array, otherwise treat the whole thing like a hash
67
- relation = args.delete_at(0) if args.length == 2
68
- args = args.first
69
-
70
- methods = args.delete(:on)
71
- methods = methods.kind_of?(Array) ? methods : [methods]
72
- relation ||= args
73
-
74
- @klasses.each do |target_klass|
75
- methods.each do |method|
76
- Eventifier::Event.add_notification target_klass, relation, method
77
- end
78
- end
26
+ Eventifier::Notifier.new @klasses, *args
79
27
  end
80
28
 
81
29
  def url url_proc
@@ -87,6 +35,5 @@ module Eventifier
87
35
  def self.url_mappings
88
36
  @url_mapppings ||= {}
89
37
  end
90
-
91
38
  end
92
39
  end
@@ -0,0 +1,70 @@
1
+ module Eventifier
2
+ module Mailers
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ attr_reader :scope_name, :resource
8
+ end
9
+
10
+ protected
11
+
12
+ # Configure default email options
13
+ def eventifier_mail(user, records, action)
14
+ initialize_from_record(user, records)
15
+ mail headers_for(action)
16
+ end
17
+
18
+ def initialize_from_record(user, records)
19
+ @user, @notifications = user, records
20
+ end
21
+
22
+ def headers_for(action)
23
+ headers = {
24
+ :subject => I18n.t(:email_subject, :scope => [:notifications]),
25
+ :from => mailer_sender,
26
+ :to => @user.email,
27
+ :template_path => template_paths
28
+ }
29
+
30
+ headers[:reply_to] = headers[:from] unless headers.key?(:reply_to)
31
+
32
+ headers
33
+ end
34
+
35
+ def mailer_sender
36
+ if default_params[:from].present?
37
+ default_params[:from]
38
+ else
39
+ Eventifier.mailer_sender
40
+ end
41
+ end
42
+
43
+ def template_paths
44
+ self.class.mailer_name
45
+ end
46
+
47
+ # Setup a subject doing an I18n lookup. At first, it attemps to set a subject
48
+ # based on the current mapping:
49
+ #
50
+ # en:
51
+ # devise:
52
+ # mailer:
53
+ # confirmation_instructions:
54
+ # user_subject: '...'
55
+ #
56
+ # If one does not exist, it fallbacks to ActionMailer default:
57
+ #
58
+ # en:
59
+ # devise:
60
+ # mailer:
61
+ # confirmation_instructions:
62
+ # subject: '...'
63
+ #
64
+ def translate(mapping, key)
65
+ I18n.t(:"notifications_subject", :scope => [:eventifier, :notifications, key],
66
+ :default => [:subject, key.to_s.humanize])
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,34 @@
1
+ class Eventifier::NotificationMapping
2
+ def self.add(key, relation)
3
+ notification_mappings[key] << relation
4
+ end
5
+
6
+ def self.find(key)
7
+ notification_mappings[key]
8
+ end
9
+
10
+ def self.all
11
+ notification_mappings
12
+ end
13
+
14
+ def self.users_and_relations(event, key, &block)
15
+ users = Hash.new { |hash, key| hash[key] = [] }
16
+
17
+ find(key).each do |relation|
18
+ Eventifier::Relationship.new(event.eventable, relation).users.each do |user|
19
+ users[user] << relation
20
+ users[user].uniq!
21
+ end
22
+ end
23
+
24
+ users.each(&block)
25
+ end
26
+
27
+ private
28
+
29
+ def self.notification_mappings
30
+ @notification_mapppings ||= Hash.new { |hash, key|
31
+ hash[key] = []
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ class Eventifier::NotificationSubscriber
2
+ def self.subscribe_to_method klass, method_name
3
+ key = "#{method_name}.#{klass.name.tableize}"
4
+ name = "#{key}.notification.eventifier"
5
+
6
+ return if ActiveSupport::Notifications.notifier.listening?(name)
7
+
8
+ ActiveSupport::Notifications.subscribe name do |*args|
9
+ event = ActiveSupport::Notifications::Event.new(*args)
10
+ eventifier_event = event.payload[:event]
11
+
12
+ Eventifier::NotificationMapping.users_and_relations(eventifier_event, key) do |user, relations|
13
+ next if user == eventifier_event.user
14
+
15
+ Eventifier::Notification.create(
16
+ event: eventifier_event,
17
+ user: user,
18
+ relations: relations
19
+ )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ class Eventifier::Notifier
2
+ def initialize klasses, *args
3
+ # args will either be [:relation, {:on => :create}] or [{:relation => :second_relation, :on => :create}]
4
+ # if its the first one, relation is the first in the array, otherwise treat the whole thing like a hash
5
+
6
+ @klasses = klasses
7
+ relation = args.delete_at(0) if args.length == 2
8
+ args = args.first || {}
9
+
10
+ methods = args.delete(:on)
11
+ methods = methods.kind_of?(Array) ? methods : [methods]
12
+ relation ||= args
13
+
14
+ @klasses.each do |target_klass|
15
+ methods.each do |method_name|
16
+ Eventifier::NotificationMapping.add "#{method_name}.#{target_klass.name.tableize}", relation
17
+ Eventifier::NotificationSubscriber.subscribe_to_method target_klass, method_name
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,44 @@
1
+ class Eventifier::Preferences
2
+ def initialize(user)
3
+ @user = user
4
+ end
5
+
6
+ def to_hashes
7
+ keys.collect do |key|
8
+ {
9
+ :key => key,
10
+ :label => label_for(key),
11
+ :value => value_for(key)
12
+ }
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :user
19
+
20
+ def keys
21
+ @keys ||= begin
22
+ hash = Eventifier::NotificationMapping.notification_mappings
23
+ ['default'] + hash.keys.collect { |key|
24
+ hash[key].collect { |value|
25
+ (key.split('.') + ['notify', Eventifier::Relationship.new(user, value).key]).join('_')
26
+ }
27
+ }.flatten
28
+ end
29
+ end
30
+
31
+ def label_for(key)
32
+ I18n.translate :"events.labels.preferences.#{key}", default: key
33
+ end
34
+
35
+ def settings
36
+ @settings ||= Eventifier::NotificationSetting.for_user user
37
+ end
38
+
39
+ def value_for(key)
40
+ settings.preferences['email'].nil? ||
41
+ settings.preferences['email'][key].nil? ||
42
+ settings.preferences['email'][key]
43
+ end
44
+ end