activity_notification 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +3 -0
  5. data/Gemfile +5 -0
  6. data/Gemfile.lock +50 -44
  7. data/README.md +242 -81
  8. data/Rakefile +13 -13
  9. data/activity_notification.gemspec +6 -8
  10. data/app/controllers/activity_notification/notifications_controller.rb +89 -11
  11. data/app/controllers/activity_notification/notifications_with_devise_controller.rb +12 -3
  12. data/app/mailers/activity_notification/mailer.rb +3 -0
  13. data/gemfiles/Gemfile.rails-4.2 +13 -0
  14. data/gemfiles/Gemfile.rails-4.2.lock +190 -0
  15. data/gemfiles/Gemfile.rails-5.0 +14 -0
  16. data/gemfiles/Gemfile.rails-5.0.lock +201 -0
  17. data/lib/activity_notification.rb +10 -6
  18. data/lib/activity_notification/apis/notification_api.rb +137 -27
  19. data/lib/activity_notification/common.rb +48 -24
  20. data/lib/activity_notification/config.rb +68 -10
  21. data/lib/activity_notification/controllers/store_controller.rb +13 -5
  22. data/lib/activity_notification/helpers/polymorphic_helpers.rb +17 -3
  23. data/lib/activity_notification/helpers/view_helpers.rb +161 -45
  24. data/lib/activity_notification/mailers/helpers.rb +121 -83
  25. data/lib/activity_notification/models/concerns/notifiable.rb +162 -69
  26. data/lib/activity_notification/models/concerns/notifier.rb +2 -0
  27. data/lib/activity_notification/models/concerns/target.rb +124 -25
  28. data/lib/activity_notification/models/notification.rb +168 -4
  29. data/lib/activity_notification/rails/routes.rb +50 -48
  30. data/lib/activity_notification/renderable.rb +106 -26
  31. data/lib/activity_notification/roles/acts_as_notifiable.rb +99 -26
  32. data/lib/activity_notification/roles/acts_as_notifier.rb +3 -0
  33. data/lib/activity_notification/roles/acts_as_target.rb +70 -0
  34. data/lib/activity_notification/version.rb +1 -1
  35. data/lib/generators/activity_notification/active_record/migration_generator.rb +3 -1
  36. data/lib/generators/activity_notification/controllers_generator.rb +5 -0
  37. data/lib/generators/activity_notification/install_generator.rb +7 -3
  38. data/lib/generators/activity_notification/models/notification_generator.rb +4 -2
  39. data/lib/generators/activity_notification/views_generator.rb +20 -0
  40. data/spec/concerns/apis/notification_api_spec.rb +105 -36
  41. data/spec/concerns/common_spec.rb +1 -1
  42. data/spec/concerns/models/notifiable_spec.rb +2 -2
  43. data/spec/concerns/models/notifier_spec.rb +1 -1
  44. data/spec/concerns/models/target_spec.rb +9 -8
  45. data/spec/controllers/notifications_controller_shared_examples.rb +101 -28
  46. data/spec/controllers/notifications_with_devise_controller_spec.rb +14 -4
  47. data/spec/helpers/view_helpers_spec.rb +3 -3
  48. data/spec/mailers/mailer_spec.rb +1 -1
  49. data/spec/models/notification_spec.rb +57 -3
  50. data/spec/rails_app/app/models/article.rb +1 -2
  51. data/spec/rails_app/app/models/comment.rb +8 -6
  52. data/spec/rails_app/app/models/user.rb +1 -1
  53. data/spec/rails_app/app/views/layouts/_header.html.erb +2 -0
  54. data/spec/rails_app/config/application.rb +3 -1
  55. data/spec/rails_app/config/environment.rb +12 -2
  56. data/spec/rails_app/config/environments/test.rb +11 -2
  57. data/spec/roles/acts_as_notifiable_spec.rb +2 -2
  58. data/spec/roles/acts_as_notifier_spec.rb +1 -1
  59. data/spec/roles/acts_as_target_spec.rb +3 -3
  60. data/spec/spec_helper.rb +6 -0
  61. metadata +35 -40
  62. data/spec/rails_app/app/models/concerns/.keep +0 -0
@@ -1,8 +1,11 @@
1
1
  module ActivityNotification
2
+ # Manages to add all required configurations to notifier models of notification.
2
3
  module ActsAsNotifier
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  class_methods do
7
+ # Adds required configurations to notifier models.
8
+ # @return [nil] nil
6
9
  def acts_as_notifier
7
10
  include Notifier
8
11
  end
@@ -1,8 +1,76 @@
1
1
  module ActivityNotification
2
+ # Manages to add all required configurations to target models of notification.
2
3
  module ActsAsTarget
3
4
  extend ActiveSupport::Concern
4
5
 
5
6
  class_methods do
7
+ # Adds required configurations to notifiable models.
8
+ #
9
+ # == Parameters:
10
+ # * :email
11
+ # * Email address to send notification email.
12
+ # This is a necessary option when you enables email notification.
13
+ # @example Simply use :email field
14
+ # class User < ActiveRecord::Base
15
+ # validates :email, presence: true
16
+ # acts_as_target email: :email
17
+ # end
18
+ #
19
+ # * :email_allowed
20
+ # * Whether activity_notification sends notification email to this target.
21
+ # Specified method or symbol is expected to return true (not nil) or false (nil).
22
+ # This parameter is a optional since default value is false.
23
+ # To use notification email, email_allowed option must return true (not nil) in both of notifiable and target model.
24
+ # This can be also configured default option in initializer.
25
+ # @example Always enable email notification for this target
26
+ # class User < ActiveRecord::Base
27
+ # acts_as_target email: :email, email_allowed: true
28
+ # end
29
+ # @example Use confirmed_at of devise field to decide whether activity_notification sends notification email to this user
30
+ # class User < ActiveRecord::Base
31
+ # acts_as_target email: :email, email_allowed: :confirmed_at
32
+ # end
33
+ #
34
+ # * :devise_resource
35
+ # * Integrated resource with devise authentication.
36
+ # This parameter is a optional since `self` is used as default value.
37
+ # You also have to configure routing for devise inroutes.rb
38
+ # @example No :devise_resource is needed when notification target is the same as authenticated resource
39
+ # # config/routes.rb
40
+ # devise_for :users
41
+ # notify_to :users
42
+ #
43
+ # # app/models/user.rb
44
+ # class User < ActiveRecord::Base
45
+ # devise :database_authenticatable, :registerable, :confirmable
46
+ # acts_as_target email: :email, email_allowed: :confirmed_at
47
+ # end
48
+ #
49
+ # @example Send Admin model and use associated User model with devise authentication
50
+ # # config/routes.rb
51
+ # devise_for :users
52
+ # notify_to :admins, with_devise: :users
53
+ #
54
+ # # app/models/user.rb
55
+ # class User < ActiveRecord::Base
56
+ # devise :database_authenticatable, :registerable, :confirmable
57
+ # end
58
+ #
59
+ # # app/models/admin.rb
60
+ # class Admin < ActiveRecord::Base
61
+ # belongs_to :user
62
+ # validates :user, presence: true
63
+ # acts_as_notification_target email: :email,
64
+ # email_allowed: ->(admin, key) { admin.user.confirmed_at.present? },
65
+ # devise_resource: :user
66
+ # end
67
+ #
68
+ # @param [Symbol] target_type Type of notification target as symbol
69
+ # @param [Hash] options Options for notifiable model configuration
70
+ # @option options [Symbol, Proc, Array] :email (nil) Email address to send notification email
71
+ # @option options [Symbol, Proc, Object] :email_allowed (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email to this target
72
+ # @option options [Symbol, Proc, Object] :devise_resource (nil) Integrated resource with devise authentication
73
+ # @return [Hash] Configured parameters as target model
6
74
  def acts_as_target(options = {})
7
75
  include Target
8
76
  available_target_options.map { |key|
@@ -13,6 +81,8 @@ module ActivityNotification
13
81
  end
14
82
  alias_method :acts_as_notification_target, :acts_as_target
15
83
 
84
+ # Returns array of available target options in acts_as_target.
85
+ # @return [Array<Symbol>] Array of available target options
16
86
  def available_target_options
17
87
  [:email, :email_allowed, :devise_resource].freeze
18
88
  end
@@ -1,3 +1,3 @@
1
1
  module ActivityNotification
2
- VERSION = "0.0.9"
2
+ VERSION = "0.0.10"
3
3
  end
@@ -2,7 +2,9 @@ require 'rails/generators/active_record'
2
2
 
3
3
  module ActivityNotification
4
4
  module Generators
5
- # Migration generator that creates migration file from template
5
+ # Migration generator to create migration files from templates.
6
+ # @example Run migration generator
7
+ # rails generate activity_notification:migration
6
8
  class MigrationGenerator < ActiveRecord::Generators::Base
7
9
  source_root File.expand_path("../../../templates/active_record", __FILE__)
8
10
 
@@ -2,6 +2,9 @@ require 'rails/generators/base'
2
2
 
3
3
  module ActivityNotification
4
4
  module Generators
5
+ # Controller generator to create customizable controller files from templates.
6
+ # @example Run controller generator for users as target
7
+ # rails generate activity_notification:controllers users
5
8
  class ControllersGenerator < Rails::Generators::Base
6
9
  CONTROLLERS = ['notifications', 'notifications_with_devise'].freeze
7
10
 
@@ -27,6 +30,7 @@ module ActivityNotification
27
30
  class_option :controllers, aliases: "-c", type: :array,
28
31
  desc: "Select specific controllers to generate (#{CONTROLLERS.join(', ')})"
29
32
 
33
+ # Creates controller files in application directory
30
34
  def create_controllers
31
35
  @target_prefix = target.blank? ? '' : (target.camelize + '::')
32
36
  controllers = options[:controllers] || CONTROLLERS
@@ -36,6 +40,7 @@ module ActivityNotification
36
40
  end
37
41
  end
38
42
 
43
+ # Shows readme to console
39
44
  def show_readme
40
45
  readme "README" if behavior == :invoke
41
46
  end
@@ -2,16 +2,18 @@ require 'rails/generators/base'
2
2
  require 'securerandom'
3
3
 
4
4
  module ActivityNotification
5
- module Generators
6
-
5
+ module Generators #:nodoc:
6
+ # Install generator to copy initializer and locale file to rails application.
7
+ # @example Run install generator
8
+ # rails generate activity_notification:install
7
9
  class InstallGenerator < Rails::Generators::Base
8
10
  source_root File.expand_path("../../templates", __FILE__)
9
11
 
10
12
  desc "Creates a ActivityNotification initializer and copy locale files to your application."
11
13
  class_option :orm
12
14
 
15
+ # Copies initializer file in application directory
13
16
  def copy_initializer
14
-
15
17
  #TODO suport other orm e.g. mongoid
16
18
  unless options[:orm] == :active_record
17
19
  raise TypeError, <<-ERROR.strip_heredoc
@@ -29,10 +31,12 @@ module ActivityNotification
29
31
  template "activity_notification.rb", "config/initializers/activity_notification.rb"
30
32
  end
31
33
 
34
+ # Copies locale files in application directory
32
35
  def copy_locale
33
36
  template "locales/en.yml", "config/locales/activity_notification.en.yml"
34
37
  end
35
38
 
39
+ # Shows readme to console
36
40
  def show_readme
37
41
  readme "README" if behavior == :invoke
38
42
  end
@@ -2,13 +2,15 @@ require 'rails/generators/active_record'
2
2
 
3
3
  module ActivityNotification
4
4
  module Generators
5
- # Notification generator that creates notification model file from template
5
+ # Notification generator to create customizable notification model from templates.
6
+ # @example Run notification generator to create customizable notification model
7
+ # rails generate activity_notification:notification
6
8
  class NotificationGenerator < ActiveRecord::Generators::Base
7
9
  source_root File.expand_path("../../../templates/notification", __FILE__)
8
10
 
9
11
  argument :name, type: :string, default: 'Notification'
10
12
 
11
- # Create model in application directory
13
+ # Create notification model in application directory
12
14
  def create_models
13
15
  @model_name = name
14
16
  template 'notification.rb', "app/models/#{name.underscore}.rb"
@@ -2,8 +2,17 @@ require 'rails/generators/base'
2
2
 
3
3
  module ActivityNotification
4
4
  module Generators
5
+ # View generator to copy customizable view files to rails application.
5
6
  # Include this module in your generator to generate ActivityNotification views.
6
7
  # `copy_views` is the main method and by default copies all views of ActivityNotification.
8
+ # @example Run view generator to create customizable default views for all targets
9
+ # rails generate activity_notification:views
10
+ # @example Run view generator to create views for users as the specified target
11
+ # rails generate activity_notification:views users
12
+ # @example Run view generator to create only notification views
13
+ # rails generate activity_notification:views -v notifications
14
+ # @example Run view generator to create only notification email views
15
+ # rails generate activity_notification:views -v mailer
7
16
  class ViewsGenerator < Rails::Generators::Base
8
17
  VIEWS = [:notifications, :mailer].freeze
9
18
 
@@ -16,6 +25,7 @@ module ActivityNotification
16
25
  desc: "Select specific view directories to generate (notifications, mailer)"
17
26
  public_task :copy_views
18
27
 
28
+ # Copies view files in application directory
19
29
  def copy_views
20
30
  target_views = options[:views] || VIEWS
21
31
  target_views.each do |directory|
@@ -25,14 +35,24 @@ module ActivityNotification
25
35
 
26
36
  protected
27
37
 
38
+ # Copies view files to target directory
39
+ # @api protected
40
+ # @param [String] name Set name of views (notifications or mailer)
41
+ # @param [String] _target_path Target path to create views
28
42
  def view_directory(name, _target_path = nil)
29
43
  directory "#{name.to_s}/default", _target_path || "#{target_path}/#{name}/#{plural_target || :default}"
30
44
  end
31
45
 
46
+ # Gets target_path from an argument or default value
47
+ # @api protected
48
+ # @return [String ] target_path from an argument or default value
32
49
  def target_path
33
50
  @target_path ||= "app/views/activity_notification"
34
51
  end
35
52
 
53
+ # Gets plural_target from target argument or default value
54
+ # @api protected
55
+ # @return [String] target_path from target argument or default value
36
56
  def plural_target
37
57
  @plural_target ||= target.presence && target.to_s.underscore.pluralize
38
58
  end
@@ -22,7 +22,7 @@ shared_examples_for :notification_api do
22
22
  expect(@user_2.notifications.count).to eq(0)
23
23
  end
24
24
 
25
- describe "#notify" do
25
+ describe ".notify" do
26
26
  it "returns array of created notifications" do
27
27
  notifications = described_class.notify(:users, @comment_2)
28
28
  expect(notifications).to be_a Array
@@ -72,7 +72,7 @@ shared_examples_for :notification_api do
72
72
  end
73
73
  end
74
74
 
75
- describe "#notify_all" do
75
+ describe ".notify_all" do
76
76
  it "returns array of created notifications" do
77
77
  notifications = described_class.notify_all([@author_user, @user_1], @comment_2)
78
78
  expect(notifications).to be_a Array
@@ -117,7 +117,7 @@ shared_examples_for :notification_api do
117
117
  end
118
118
  end
119
119
 
120
- describe "#notify_to" do
120
+ describe ".notify_to" do
121
121
  it "returns reated notification" do
122
122
  notification = described_class.notify_to(@user_1, @comment_2)
123
123
  validate_expected_notification(notification, @user_1, @comment_2)
@@ -302,10 +302,10 @@ shared_examples_for :notification_api do
302
302
  end
303
303
  end
304
304
 
305
- describe "#open_all_of" do
305
+ describe ".open_all_of" do
306
306
  before do
307
- described_class.notify_to(@user_1, @comment_2)
308
- described_class.notify_to(@user_1, @comment_2)
307
+ described_class.notify_to(@user_1, @article, group: @article, key: 'key.1')
308
+ described_class.notify_to(@user_1, @comment_2, group: @comment_2, key: 'key.2')
309
309
  expect(@user_1.notifications.unopened_only.count).to eq(2)
310
310
  expect(@user_1.notifications.opened_only!.count).to eq(0)
311
311
  end
@@ -325,9 +325,58 @@ shared_examples_for :notification_api do
325
325
  expect(@user_1.notifications.unopened_only.count).to eq(2)
326
326
  expect(@user_1.notifications.opened_only!.count).to eq(0)
327
327
  end
328
+
329
+ it "opens all notification with current time" do
330
+ expect(@user_1.notifications.first.opened_at).to be_nil
331
+ Timecop.freeze(DateTime.now)
332
+ described_class.open_all_of(@user_1)
333
+ expect(@user_1.notifications.first.opened_at.to_i).to eq(DateTime.now.to_i)
334
+ Timecop.return
335
+ end
336
+
337
+ context "with opened_at option" do
338
+ it "opens all notification with specified time" do
339
+ expect(@user_1.notifications.first.opened_at).to be_nil
340
+ opened_at = DateTime.now - 1.months
341
+ described_class.open_all_of(@user_1, opened_at: opened_at)
342
+ expect(@user_1.notifications.first.opened_at.to_i).to eq(opened_at.to_i)
343
+ end
344
+ end
345
+
346
+ context 'with filtered_by_type options' do
347
+ it "opens filtered notifications only" do
348
+ described_class.open_all_of(@user_1, { filtered_by_type: @comment_2.to_class_name })
349
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
350
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
351
+ end
352
+ end
353
+
354
+ context 'with filtered_by_group options' do
355
+ it "opens filtered notifications only" do
356
+ described_class.open_all_of(@user_1, { filtered_by_group: @comment_2 })
357
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
358
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
359
+ end
360
+ end
361
+
362
+ context 'with filtered_by_group_type and :filtered_by_group_id options' do
363
+ it "opens filtered notifications only" do
364
+ described_class.open_all_of(@user_1, { filtered_by_group_type: 'Comment', filtered_by_group_id: @comment_2.id.to_s })
365
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
366
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
367
+ end
368
+ end
369
+
370
+ context 'with filtered_by_key options' do
371
+ it "opens filtered notifications only" do
372
+ described_class.open_all_of(@user_1, { filtered_by_key: 'key.2' })
373
+ expect(@user_1.notifications.unopened_only.count).to eq(1)
374
+ expect(@user_1.notifications.opened_only!.count).to eq(1)
375
+ end
376
+ end
328
377
  end
329
378
 
330
- describe "#group_member_exists?" do
379
+ describe ".group_member_exists?" do
331
380
  context "when specified notifications have any group members" do
332
381
  let(:owner_notifications) do
333
382
  target = create(:confirmed_user)
@@ -337,10 +386,15 @@ shared_examples_for :notification_api do
337
386
  target.notifications.group_owners_only
338
387
  end
339
388
 
340
- it "returns true" do
389
+ it "returns true for DB query" do
341
390
  expect(described_class.group_member_exists?(owner_notifications))
342
391
  .to be_truthy
343
392
  end
393
+
394
+ it "returns true for Array" do
395
+ expect(described_class.group_member_exists?(owner_notifications.to_a))
396
+ .to be_truthy
397
+ end
344
398
  end
345
399
 
346
400
  context "when specified notifications have no group members" do
@@ -358,7 +412,7 @@ shared_examples_for :notification_api do
358
412
  end
359
413
  end
360
414
 
361
- describe "#available_options" do
415
+ describe ".available_options" do
362
416
  it "returns list of available options in notify api" do
363
417
  expect(described_class.available_options)
364
418
  .to eq([:key, :group, :parameters, :notifier, :send_email, :send_later])
@@ -367,7 +421,7 @@ shared_examples_for :notification_api do
367
421
  end
368
422
 
369
423
  describe "as private class methods" do
370
- describe "#store_notification" do
424
+ describe ".store_notification" do
371
425
  it "is defined as private method" do
372
426
  expect(described_class.respond_to?(:store_notification)).to be_falsey
373
427
  expect(described_class.respond_to?(:store_notification, true)).to be_truthy
@@ -430,38 +484,52 @@ shared_examples_for :notification_api do
430
484
  Timecop.return
431
485
  end
432
486
 
433
- #TODO
434
- # it "open group member notifications with current time" do
435
- # group_member = create(test_class_name, group_owner: test_instance)
436
- # expect(group_member.opened_at.blank?).to be_truthy
437
- # Timecop.freeze(DateTime.now)
438
- # test_instance.open!
439
- # group_member = group_member.reload
440
- # expect(group_member.opened_at.blank?).to be_falsey
441
- # expect(group_member.opened_at).to eq(DateTime.now)
442
- # Timecop.return
443
- # end
487
+ it "open group member notifications with current time" do
488
+ group_member = create(test_class_name, group_owner: test_instance)
489
+ expect(group_member.opened_at.blank?).to be_truthy
490
+ Timecop.freeze(DateTime.now)
491
+ test_instance.open!
492
+ group_member = group_member.reload
493
+ expect(group_member.opened_at.blank?).to be_falsey
494
+ expect(group_member.opened_at.to_i).to eq(DateTime.now.to_i)
495
+ Timecop.return
496
+ end
444
497
  end
445
498
 
446
- context "with an argument" do
499
+ context "with opened_at option" do
447
500
  it "open notification with specified time" do
448
501
  expect(test_instance.opened_at.blank?).to be_truthy
449
- datetime = DateTime.now - 1.months
450
- test_instance.open!(datetime)
502
+ opened_at = DateTime.now - 1.months
503
+ test_instance.open!(opened_at: opened_at)
451
504
  expect(test_instance.opened_at.blank?).to be_falsey
452
- expect(test_instance.opened_at).to eq(datetime)
505
+ expect(test_instance.opened_at).to eq(opened_at)
506
+ end
507
+
508
+ it "open group member notifications with specified time" do
509
+ group_member = create(test_class_name, group_owner: test_instance)
510
+ expect(group_member.opened_at.blank?).to be_truthy
511
+ opened_at = DateTime.now - 1.months
512
+ test_instance.open!(opened_at: opened_at)
513
+ group_member = group_member.reload
514
+ expect(group_member.opened_at.blank?).to be_falsey
515
+ expect(group_member.opened_at.to_i).to eq(opened_at.to_i)
516
+ end
517
+ end
518
+
519
+ context "with false as with_members" do
520
+ it "does not open group member notifications" do
521
+ group_member = create(test_class_name, group_owner: test_instance)
522
+ expect(group_member.opened_at.blank?).to be_truthy
523
+ opened_at = DateTime.now - 1.months
524
+ test_instance.open!(with_members: false)
525
+ group_member = group_member.reload
526
+ expect(group_member.opened_at.blank?).to be_truthy
453
527
  end
454
528
 
455
- #TODO
456
- # it "open group member notifications with current time" do
457
- # group_member = create(test_class_name, group_owner: test_instance)
458
- # expect(group_member.opened_at.blank?).to be_truthy
459
- # datetime = DateTime.now - 1.months
460
- # test_instance.open!(datetime)
461
- # group_member = group_member.reload
462
- # expect(group_member.opened_at.blank?).to be_falsey
463
- # expect(group_member.opened_at).to eq(datetime)
464
- # end
529
+ it "returns the number of opened notification records" do
530
+ create(test_class_name, group_owner: test_instance, opened_at: nil)
531
+ expect(test_instance.open!(with_members: false)).to eq(1)
532
+ end
465
533
  end
466
534
  end
467
535
 
@@ -624,7 +692,8 @@ shared_examples_for :notification_api do
624
692
 
625
693
  describe "#notifiable_path" do
626
694
  it "returns notifiable.notifiable_path" do
627
- expect(test_instance.notifiable_path).to eq(test_instance.notifiable.notifiable_path(test_instance.target_type))
695
+ expect(test_instance.notifiable_path)
696
+ .to eq(test_instance.notifiable.notifiable_path(test_instance.target_type, test_instance.key))
628
697
  end
629
698
  end
630
699
  end