activity_notification 0.0.9 → 0.0.10

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.
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