activity_notification 1.0.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +33 -0
- data/.rubocop.yml +1157 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +25 -0
- data/Gemfile.lock +15 -17
- data/README.md +154 -27
- data/activity_notification.gemspec +1 -1
- data/app/controllers/activity_notification/notifications_controller.rb +30 -104
- data/app/controllers/activity_notification/notifications_with_devise_controller.rb +1 -33
- data/app/controllers/activity_notification/subscriptions_controller.rb +184 -0
- data/app/controllers/activity_notification/subscriptions_with_devise_controller.rb +6 -0
- data/app/mailers/activity_notification/mailer.rb +3 -3
- data/app/views/activity_notification/notifications/default/_index.html.erb +3 -0
- data/app/views/activity_notification/notifications/default/index.html.erb +8 -10
- data/app/views/activity_notification/notifications/default/show.html.erb +24 -2
- data/app/views/activity_notification/subscriptions/default/_form.html.erb +52 -0
- data/app/views/activity_notification/subscriptions/default/_notification_keys.html.erb +89 -0
- data/app/views/activity_notification/subscriptions/default/_subscription.html.erb +73 -0
- data/app/views/activity_notification/subscriptions/default/_subscriptions.html.erb +13 -0
- data/app/views/activity_notification/subscriptions/default/create.js.erb +5 -0
- data/app/views/activity_notification/subscriptions/default/destroy.js.erb +5 -0
- data/app/views/activity_notification/subscriptions/default/index.html.erb +197 -0
- data/app/views/activity_notification/subscriptions/default/show.html.erb +177 -0
- data/app/views/activity_notification/subscriptions/default/subscribe.js.erb +8 -0
- data/app/views/activity_notification/subscriptions/default/subscribe_to_email.js.erb +6 -0
- data/app/views/activity_notification/subscriptions/default/unsubscribe.js.erb +8 -0
- data/app/views/activity_notification/subscriptions/default/unsubscribe_to_email.js.erb +6 -0
- data/gemfiles/Gemfile.rails-4.2.lock +18 -20
- data/gemfiles/Gemfile.rails-5.0.lock +18 -20
- data/lib/activity_notification.rb +7 -3
- data/lib/activity_notification/apis/notification_api.rb +95 -61
- data/lib/activity_notification/apis/subscription_api.rb +51 -0
- data/lib/activity_notification/common.rb +1 -1
- data/lib/activity_notification/config.rb +65 -17
- data/lib/activity_notification/controllers/common_controller.rb +118 -0
- data/lib/activity_notification/controllers/devise_authentication_controller.rb +41 -0
- data/lib/activity_notification/helpers/view_helpers.rb +131 -3
- data/lib/activity_notification/mailers/helpers.rb +6 -8
- data/lib/activity_notification/models/concerns/notifiable.rb +45 -27
- data/lib/activity_notification/models/concerns/subscriber.rb +149 -0
- data/lib/activity_notification/models/concerns/target.rb +100 -66
- data/lib/activity_notification/models/notification.rb +7 -5
- data/lib/activity_notification/models/subscription.rb +93 -0
- data/lib/activity_notification/rails/routes.rb +148 -33
- data/lib/activity_notification/renderable.rb +3 -4
- data/lib/activity_notification/roles/acts_as_notifiable.rb +14 -1
- data/lib/activity_notification/roles/acts_as_target.rb +11 -8
- data/lib/activity_notification/version.rb +1 -1
- data/lib/generators/activity_notification/controllers_generator.rb +2 -2
- data/lib/generators/activity_notification/install_generator.rb +0 -1
- data/lib/generators/activity_notification/migration/migration_generator.rb +8 -2
- data/lib/generators/activity_notification/models_generator.rb +53 -0
- data/lib/generators/activity_notification/views_generator.rb +7 -7
- data/lib/generators/templates/activity_notification.rb +17 -3
- data/lib/generators/templates/controllers/notifications_controller.rb +18 -17
- data/lib/generators/templates/controllers/notifications_with_devise_controller.rb +18 -17
- data/lib/generators/templates/controllers/subscriptions_controller.rb +79 -0
- data/lib/generators/templates/controllers/subscriptions_with_devise_controller.rb +87 -0
- data/lib/generators/templates/migrations/migration.rb +57 -0
- data/lib/generators/templates/models/README +10 -0
- data/lib/generators/templates/{notification → models}/notification.rb +1 -3
- data/lib/generators/templates/models/subscription.rb +4 -0
- data/spec/concerns/apis/notification_api_spec.rb +48 -11
- data/spec/concerns/apis/subscription_api_spec.rb +167 -0
- data/spec/concerns/models/notifiable_spec.rb +60 -0
- data/spec/concerns/models/subscriber_spec.rb +525 -0
- data/spec/concerns/models/target_spec.rb +271 -42
- data/spec/controllers/common_controller_spec.rb +25 -0
- data/spec/controllers/dummy_common_controller.rb +5 -0
- data/spec/controllers/notifications_controller_shared_examples.rb +2 -6
- data/spec/controllers/subscriptions_controller_shared_examples.rb +735 -0
- data/spec/controllers/subscriptions_controller_spec.rb +12 -0
- data/spec/controllers/subscriptions_with_devise_controller_spec.rb +91 -0
- data/spec/factories/dummy/dummy_subscriber.rb +4 -0
- data/spec/factories/subscriptions.rb +8 -0
- data/spec/generators/controllers_generator_spec.rb +25 -2
- data/spec/generators/migration/migration_generator_spec.rb +3 -3
- data/spec/generators/models_generator_spec.rb +96 -0
- data/spec/generators/views_generator_spec.rb +84 -0
- data/spec/helpers/view_helpers_spec.rb +143 -0
- data/spec/mailers/mailer_spec.rb +5 -4
- data/spec/models/dummy/dummy_subscriber_spec.rb +5 -0
- data/spec/models/notification_spec.rb +7 -7
- data/spec/models/subscription_spec.rb +158 -0
- data/spec/rails_app/app/controllers/users/notifications_controller.rb +67 -0
- data/spec/rails_app/app/controllers/users/notifications_with_devise_controller.rb +75 -0
- data/spec/rails_app/app/controllers/users/subscriptions_controller.rb +79 -0
- data/spec/rails_app/app/controllers/users/subscriptions_with_devise_controller.rb +87 -0
- data/spec/rails_app/app/models/admin.rb +1 -0
- data/spec/rails_app/app/models/dummy/dummy_subscriber.rb +4 -0
- data/spec/rails_app/app/models/user.rb +2 -1
- data/spec/rails_app/app/views/activity_notification/mailer/dummy_subscribers/test_key.text.erb +1 -0
- data/spec/rails_app/app/views/articles/index.html.erb +6 -0
- data/spec/rails_app/config/initializers/activity_notification.rb +17 -3
- data/spec/rails_app/config/routes.rb +2 -2
- data/spec/rails_app/db/migrate/20160715050420_create_activity_notification_tables.rb +33 -0
- data/spec/rails_app/db/schema.rb +18 -0
- data/spec/roles/acts_as_notifiable_spec.rb +1 -1
- data/spec/roles/acts_as_target_spec.rb +1 -1
- metadata +70 -11
- data/lib/generators/activity_notification/notification/notification_generator.rb +0 -20
- data/lib/generators/templates/active_record/migration.rb +0 -18
- data/spec/generators/notification/notification_generator_spec.rb +0 -41
- data/spec/rails_app/db/migrate/20160715050420_create_notifications.rb +0 -18
| @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            module ActivityNotification
         | 
| 2 | 
            +
              # Subscription model implementation generated by ActivityNotification.
         | 
| 3 | 
            +
              class Subscription < ActiveRecord::Base
         | 
| 4 | 
            +
                include SubscriptionApi
         | 
| 5 | 
            +
                self.table_name = ActivityNotification.config.subscription_table_name
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                # Belongs to target instance of this subscription as polymorphic association.
         | 
| 8 | 
            +
                # @scope instance
         | 
| 9 | 
            +
                # @return [Object] Target instance of this subscription
         | 
| 10 | 
            +
                belongs_to :target,               polymorphic: true
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                # Serialize parameters Hash
         | 
| 13 | 
            +
                serialize  :parameters, Hash
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                validates  :target,               presence: true
         | 
| 16 | 
            +
                validates  :key,                  presence: true
         | 
| 17 | 
            +
                validates_inclusion_of :subscribing,          in: [true, false]
         | 
| 18 | 
            +
                validates_inclusion_of :subscribing_to_email, in: [true, false]
         | 
| 19 | 
            +
                validate   :subscribing_to_email_cannot_be_true_when_subscribing_is_false
         | 
| 20 | 
            +
                validates  :subscribed_at,            presence: true, if:     :subscribing
         | 
| 21 | 
            +
                validates  :unsubscribed_at,          presence: true, unless: :subscribing
         | 
| 22 | 
            +
                validates  :subscribed_to_email_at,   presence: true, if:     :subscribing_to_email
         | 
| 23 | 
            +
                validates  :unsubscribed_to_email_at, presence: true, unless: :subscribing_to_email
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # Selects filtered subscriptions by target instance.
         | 
| 26 | 
            +
                #   ActivityNotification::Subscription.filtered_by_target(@user)
         | 
| 27 | 
            +
                # is the same as
         | 
| 28 | 
            +
                #   @user.subscriptions
         | 
| 29 | 
            +
                # @scope class
         | 
| 30 | 
            +
                # @param [Object] target Target instance for filter
         | 
| 31 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Array or database query of filtered subscriptions
         | 
| 32 | 
            +
                scope :filtered_by_target,  ->(target)       { where(target: target) }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                # Selects filtered subscriptions by key.
         | 
| 35 | 
            +
                # @example Get filtered subscriptions of the @user with key 'comment.reply'
         | 
| 36 | 
            +
                #   @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
         | 
| 37 | 
            +
                # @scope class
         | 
| 38 | 
            +
                # @param [String] key Key of the subscription for filter
         | 
| 39 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Array or database query of filtered subscriptions
         | 
| 40 | 
            +
                scope :filtered_by_key,     ->(key)          { where(key: key) }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                # Selects filtered subscriptions by key with filter options.
         | 
| 43 | 
            +
                # @example Get filtered subscriptions of the @user with key 'comment.reply'
         | 
| 44 | 
            +
                #   @subscriptions = @user.subscriptions.filtered_by_key('comment.reply')
         | 
| 45 | 
            +
                # @example Get custom filtered subscriptions of the @user
         | 
| 46 | 
            +
                #   @subscriptions = @user.subscriptions.filtered_by_options({ custom_filter: ["created_at >= ?", time.hour.ago] })
         | 
| 47 | 
            +
                # @scope class
         | 
| 48 | 
            +
                # @param [Hash] options Options for filter
         | 
| 49 | 
            +
                # @option options [String]     :filtered_by_key        (nil) Key of the subscription for filter 
         | 
| 50 | 
            +
                # @option options [Array|Hash] :custom_filter          (nil) Custom subscription filter (e.g. ["created_at >= ?", time.hour.ago])
         | 
| 51 | 
            +
                # @return [ActiveRecord_AssociationRelation<Notificaion>] Array or database query of filtered subscriptions
         | 
| 52 | 
            +
                scope :filtered_by_options, ->(options = {}) {
         | 
| 53 | 
            +
                  options = ActivityNotification.cast_to_indifferent_hash(options)
         | 
| 54 | 
            +
                  filtered_subscriptions = all
         | 
| 55 | 
            +
                  if options.has_key?(:filtered_by_key)
         | 
| 56 | 
            +
                    filtered_subscriptions = filtered_subscriptions.filtered_by_key(options[:filtered_by_key])
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                  if options.has_key?(:custom_filter)
         | 
| 59 | 
            +
                    filtered_subscriptions = filtered_subscriptions.where(options[:custom_filter])
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                  filtered_subscriptions
         | 
| 62 | 
            +
                }
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                # Orders by latest (newest) first as created_at: :desc.
         | 
| 65 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest first
         | 
| 66 | 
            +
                scope :latest_order,              -> { order(created_at: :desc) }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                # Orders by earliest (older) first as created_at: :asc.
         | 
| 69 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest first
         | 
| 70 | 
            +
                scope :earliest_order,            -> { order(created_at: :asc) }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                # Orders by latest (newest) first as subscribed_at: :desc.
         | 
| 73 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by latest subscribed_at first
         | 
| 74 | 
            +
                scope :latest_subscribed_order,   -> { order(subscribed_at: :desc) }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                # Orders by earliest (older) first as subscribed_at: :asc.
         | 
| 77 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by earliest subscribed_at first
         | 
| 78 | 
            +
                scope :earliest_subscribed_order, -> { order(subscribed_at: :asc) }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                # Orders by key name as key: :asc.
         | 
| 81 | 
            +
                # @return [ActiveRecord_AssociationRelation<Subscription>] Database query of subscriptions ordered by key name
         | 
| 82 | 
            +
                scope :key_order,                 -> { order(key: :asc) }
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                private
         | 
| 85 | 
            +
                  # Validates subscribing_to_email cannot be true when subscribing isfalse.
         | 
| 86 | 
            +
                  def subscribing_to_email_cannot_be_true_when_subscribing_is_false
         | 
| 87 | 
            +
                    if !subscribing && subscribing_to_email
         | 
| 88 | 
            +
                      errors.add(:subscribing_to_email, "cannot be true when subscribing is false")
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
            end
         | 
| @@ -4,7 +4,9 @@ require "active_support/core_ext/hash/slice" | |
| 4 4 | 
             
            module ActionDispatch::Routing
         | 
| 5 5 | 
             
              # Extended ActionDispatch::Routing::Mapper implementation to add routing method of ActivityNotification.
         | 
| 6 6 | 
             
              class Mapper
         | 
| 7 | 
            -
                 | 
| 7 | 
            +
                include ActivityNotification::PolymorphicHelpers
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                # Includes notify_to method for routes, which is responsible to generate all necessary routes for notifications of activity_notification.
         | 
| 8 10 | 
             
                #
         | 
| 9 11 | 
             
                # When you have an User model configured as a target (e.g. defined acts_as_target),
         | 
| 10 12 | 
             
                # you can create as follows in your routes:
         | 
| @@ -19,7 +21,7 @@ module ActionDispatch::Routing | |
| 19 21 | 
             
                #       { controller:"activity_notification/notifications", action:"destroy", target_type:"users" }
         | 
| 20 22 | 
             
                #     open_all_user_notifications POST   /users/:user_id/notifications/open_all(.:format)
         | 
| 21 23 | 
             
                #       { controller:"activity_notification/notifications", action:"open_all", target_type:"users" }
         | 
| 22 | 
            -
                #     move_user_notification       | 
| 24 | 
            +
                #     move_user_notification      GET    /users/:user_id/notifications/:id/move(.:format)
         | 
| 23 25 | 
             
                #       { controller:"activity_notification/notifications", action:"move", target_type:"users" }
         | 
| 24 26 | 
             
                #     open_user_notification      POST   /users/:user_id/notifications/:id/open(.:format)
         | 
| 25 27 | 
             
                #       { controller:"activity_notification/notifications", action:"open", target_type:"users" }
         | 
| @@ -37,50 +39,46 @@ module ActionDispatch::Routing | |
| 37 39 | 
             
                #       { controller:"activity_notification/notifications_with_devise", action:"destroy", target_type:"users", devise_type:"users" }
         | 
| 38 40 | 
             
                #     open_all_user_notifications POST   /users/:user_id/notifications/open_all(.:format)
         | 
| 39 41 | 
             
                #       { controller:"activity_notification/notifications_with_devise", action:"open_all", target_type:"users", devise_type:"users" }
         | 
| 40 | 
            -
                #     move_user_notification       | 
| 42 | 
            +
                #     move_user_notification      GET    /users/:user_id/notifications/:id/move(.:format)
         | 
| 41 43 | 
             
                #       { controller:"activity_notification/notifications_with_devise", action:"move", target_type:"users", devise_type:"users" }
         | 
| 42 44 | 
             
                #     open_user_notification      POST   /users/:user_id/notifications/:id/open(.:format)
         | 
| 43 45 | 
             
                #       { controller:"activity_notification/notifications_with_devise", action:"open", target_type:"users", devise_type:"users" }
         | 
| 44 46 | 
             
                #
         | 
| 47 | 
            +
                # When you would like to define subscription management paths with notification paths,
         | 
| 48 | 
            +
                # you can create as follows in your routes:
         | 
| 49 | 
            +
                #   notify_to :users, with_subscription: true
         | 
| 50 | 
            +
                # or you can also set options for subscription path:
         | 
| 51 | 
            +
                #   notify_to :users, with_subscription: { except: [:index] }
         | 
| 52 | 
            +
                # If you configure this :with_subscription option with :with_devise option, with_subscription paths are also automatically configured with devise authentication as the same as notifications
         | 
| 53 | 
            +
                #   notify_to :users, with_devise: :users, with_subscription: true
         | 
| 54 | 
            +
                #
         | 
| 45 55 | 
             
                # @example Define notify_to in config/routes.rb
         | 
| 46 56 | 
             
                #   notify_to :users
         | 
| 47 57 | 
             
                # @example Define notify_to with options
         | 
| 48 58 | 
             
                #   notify_to :users, only: [:open, :open_all, :move]
         | 
| 49 59 | 
             
                # @example Integrated with Devise authentication
         | 
| 50 60 | 
             
                #   notify_to :users, with_devise: :users
         | 
| 61 | 
            +
                # @example Define notification paths including subscription paths
         | 
| 62 | 
            +
                #   notify_to :users, with_subscription: true
         | 
| 51 63 | 
             
                #
         | 
| 52 64 | 
             
                # @overload notify_to(*resources, *options)
         | 
| 53 | 
            -
                #   @param          [Symbol] | 
| 54 | 
            -
                #   @option options [Symbol] | 
| 55 | 
            -
                #   @option options [ | 
| 56 | 
            -
                #   @option options [ | 
| 57 | 
            -
                #   @option options [ | 
| 58 | 
            -
                #   @option options [ | 
| 65 | 
            +
                #   @param          [Symbol]       resources Resources to notify
         | 
| 66 | 
            +
                #   @option options [Symbol]       :with_devise       (false)          Devise resources name for devise integration. Devise integration will be enabled by this option.
         | 
| 67 | 
            +
                #   @option options [Hash|Boolean] :with_subscription (false)          Subscription path options to define subscription management paths with notification paths. Calls subscribed_by routing when truthy value is passed as this option.
         | 
| 68 | 
            +
                #   @option options [String]       :model             (:notifications) Model name of notifications
         | 
| 69 | 
            +
                #   @option options [String]       :controller        ("activity_notification/notifications" | activity_notification/notifications_with_devise") :controller option as resources routing
         | 
| 70 | 
            +
                #   @option options [Symbol]       :as                (nil)            :as option as resources routing
         | 
| 71 | 
            +
                #   @option options [Array]        :only              (nil)            :only option as resources routing
         | 
| 72 | 
            +
                #   @option options [Array]        :except            (nil)            :except option as resources routing
         | 
| 59 73 | 
             
                # @return [ActionDispatch::Routing::Mapper] Routing mapper instance
         | 
| 60 74 | 
             
                def notify_to(*resources)
         | 
| 61 | 
            -
                  options = resources.extract_options | 
| 62 | 
            -
                  
         | 
| 63 | 
            -
                  #TODO check resources if it includes target module
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                  if (with_devise = options.delete(:with_devise)).present?
         | 
| 66 | 
            -
                    options[:controller] ||= "activity_notification/notifications_with_devise"
         | 
| 67 | 
            -
                    options[:as]         ||= "notifications"
         | 
| 68 | 
            -
                    #TODO check devise configuration in model
         | 
| 69 | 
            -
                    devise_defaults        = { devise_type: with_devise.to_s }
         | 
| 70 | 
            -
                  else
         | 
| 71 | 
            -
                    options[:controller] ||= "activity_notification/notifications"
         | 
| 72 | 
            -
                  end
         | 
| 73 | 
            -
                  options[:except]       ||= []
         | 
| 74 | 
            -
                  options[:except].concat( [:new, :create, :edit, :update] )
         | 
| 75 | 
            -
                  notification_resources   = options[:model] || :notifications
         | 
| 75 | 
            +
                  options = create_options(:notifications, resources.extract_options!, [:new, :create, :edit, :update])
         | 
| 76 76 |  | 
| 77 | 
            -
                   | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
                      options[:defaults] = (devise_defaults || {}).merge({ target_type: resource.to_s })
         | 
| 83 | 
            -
                      self.resources notification_resources, options do
         | 
| 77 | 
            +
                  resources.each do |target|
         | 
| 78 | 
            +
                    self.resources target, only: :none do
         | 
| 79 | 
            +
                      options[:defaults] = { target_type: target.to_s }.merge(options[:devise_defaults])
         | 
| 80 | 
            +
                      resources_options = options.select { |key, _| [:with_devise, :with_subscription, :subscription_option, :model, :devise_defaults].exclude? key }
         | 
| 81 | 
            +
                      self.resources options[:model], resources_options do
         | 
| 84 82 | 
             
                        collection do
         | 
| 85 83 | 
             
                          post :open_all unless ignore_path?(:open_all, options)
         | 
| 86 84 | 
             
                        end
         | 
| @@ -90,18 +88,135 @@ module ActionDispatch::Routing | |
| 90 88 | 
             
                        end
         | 
| 91 89 | 
             
                      end
         | 
| 92 90 | 
             
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    if options[:with_subscription].present? && target.to_s.to_model_class.subscription_enabled?
         | 
| 93 | 
            +
                      subscribed_by target, options[:subscription_option]
         | 
| 94 | 
            +
                    end
         | 
| 93 95 | 
             
                  end
         | 
| 94 96 |  | 
| 95 97 | 
             
                  self
         | 
| 96 98 | 
             
                end
         | 
| 97 99 |  | 
| 100 | 
            +
                # Includes subscribed_by method for routes, which is responsible to generate all necessary routes for subscriptions of activity_notification.
         | 
| 101 | 
            +
                #
         | 
| 102 | 
            +
                # When you have an User model configured as a target (e.g. defined acts_as_target),
         | 
| 103 | 
            +
                # you can create as follows in your routes:
         | 
| 104 | 
            +
                #   subscribed_by :users
         | 
| 105 | 
            +
                # This method creates the needed routes:
         | 
| 106 | 
            +
                #   # Subscription routes
         | 
| 107 | 
            +
                #     user_subscriptions          GET    /users/:user_id/subscriptions(.:format)
         | 
| 108 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"index", target_type:"users" }
         | 
| 109 | 
            +
                #     user_subscription           GET    /users/:user_id/subscriptions/:id(.:format)
         | 
| 110 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"show", target_type:"users" }
         | 
| 111 | 
            +
                #     open_all_user_subscriptions POST   /users/:user_id/subscriptions(.:format)
         | 
| 112 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"create", target_type:"users" }
         | 
| 113 | 
            +
                #     user_subscription           DELETE /users/:user_id/subscriptions/:id(.:format)
         | 
| 114 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"destroy", target_type:"users" }
         | 
| 115 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/subscribe(.:format)
         | 
| 116 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"subscribe", target_type:"users" }
         | 
| 117 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/unsubscribe(.:format)
         | 
| 118 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"unsubscribe", target_type:"users" }
         | 
| 119 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
         | 
| 120 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"subscribe_to_email", target_type:"users" }
         | 
| 121 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
         | 
| 122 | 
            +
                #       { controller:"activity_notification/subscriptions", action:"unsubscribe_to_email", target_type:"users" }
         | 
| 123 | 
            +
                #
         | 
| 124 | 
            +
                # When you use devise authentication and you want make subscription targets assciated with devise,
         | 
| 125 | 
            +
                # you can create as follows in your routes:
         | 
| 126 | 
            +
                #   notify_to :users, with_devise: :users
         | 
| 127 | 
            +
                # This with_devise option creates the needed routes assciated with devise authentication:
         | 
| 128 | 
            +
                #   # Subscription with devise routes
         | 
| 129 | 
            +
                #     user_subscriptions          GET    /users/:user_id/subscriptions(.:format)
         | 
| 130 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"index", target_type:"users", devise_type:"users" }
         | 
| 131 | 
            +
                #     user_subscription           GET    /users/:user_id/subscriptions/:id(.:format)
         | 
| 132 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"show", target_type:"users", devise_type:"users" }
         | 
| 133 | 
            +
                #     open_all_user_subscriptions POST   /users/:user_id/subscriptions(.:format)
         | 
| 134 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"create", target_type:"users", devise_type:"users" }
         | 
| 135 | 
            +
                #     user_subscription           DELETE /users/:user_id/subscriptions/:id(.:format)
         | 
| 136 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"destroy", target_type:"users", devise_type:"users" }
         | 
| 137 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/subscribe(.:format)
         | 
| 138 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"subscribe", target_type:"users", devise_type:"users" }
         | 
| 139 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/unsubscribe(.:format)
         | 
| 140 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe", target_type:"users", devise_type:"users" }
         | 
| 141 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/subscribe_to_email(.:format)
         | 
| 142 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"subscribe_to_email", target_type:"users", devise_type:"users" }
         | 
| 143 | 
            +
                #     open_user_subscription      POST   /users/:user_id/subscriptions/:id/unsubscribe_to_email(.:format)
         | 
| 144 | 
            +
                #       { controller:"activity_notification/subscriptions_with_devise", action:"unsubscribe_to_email", target_type:"users", devise_type:"users" }
         | 
| 145 | 
            +
                #
         | 
| 146 | 
            +
                # @example Define subscribed_by in config/routes.rb
         | 
| 147 | 
            +
                #   subscribed_by :users
         | 
| 148 | 
            +
                # @example Define subscribed_by with options
         | 
| 149 | 
            +
                #   subscribed_by :users, except: [:index, :show]
         | 
| 150 | 
            +
                # @example Integrated with Devise authentication
         | 
| 151 | 
            +
                #   subscribed_by :users, with_devise: :users
         | 
| 152 | 
            +
                #
         | 
| 153 | 
            +
                # @overload subscribed_by(*resources, *options)
         | 
| 154 | 
            +
                #   @param          [Symbol]       resources Resources to notify
         | 
| 155 | 
            +
                #   @option options [Symbol]       :with_devise (false)          Devise resources name for devise integration. Devise integration will be enabled by this option.
         | 
| 156 | 
            +
                #   @option options [String]       :model       (:subscriptions) Model name of subscriptions
         | 
| 157 | 
            +
                #   @option options [String]       :controller  ("activity_notification/subscriptions" | activity_notification/subscriptions_with_devise") :controller option as resources routing
         | 
| 158 | 
            +
                #   @option options [Symbol]       :as          (nil)            :as option as resources routing
         | 
| 159 | 
            +
                #   @option options [Array]        :only        (nil)            :only option as resources routing
         | 
| 160 | 
            +
                #   @option options [Array]        :except      (nil)            :except option as resources routing
         | 
| 161 | 
            +
                # @return [ActionDispatch::Routing::Mapper] Routing mapper instance
         | 
| 162 | 
            +
                def subscribed_by(*resources)
         | 
| 163 | 
            +
                  options = create_options(:subscriptions, resources.extract_options!, [:new, :edit, :update])
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                  resources.each do |target|
         | 
| 166 | 
            +
                    self.resources target, only: :none do
         | 
| 167 | 
            +
                      options[:defaults] = { target_type: target.to_s }.merge(options[:devise_defaults])
         | 
| 168 | 
            +
                      resources_options = options.select { |key, _| [:with_devise, :model, :devise_defaults].exclude? key }
         | 
| 169 | 
            +
                      self.resources options[:model], resources_options do
         | 
| 170 | 
            +
                        member do
         | 
| 171 | 
            +
                          post :subscribe            unless ignore_path?(:subscribe, options)
         | 
| 172 | 
            +
                          post :unsubscribe          unless ignore_path?(:unsubscribe, options)
         | 
| 173 | 
            +
                          post :subscribe_to_email   unless ignore_path?(:subscribe_to_email, options)
         | 
| 174 | 
            +
                          post :unsubscribe_to_email unless ignore_path?(:unsubscribe_to_email, options)
         | 
| 175 | 
            +
                        end
         | 
| 176 | 
            +
                      end
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  self
         | 
| 181 | 
            +
                end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
             | 
| 98 184 | 
             
                private
         | 
| 99 185 |  | 
| 186 | 
            +
                  # Check whether action path is ignored by :except or :only options
         | 
| 187 | 
            +
                  # @api private
         | 
| 188 | 
            +
                  # @return [Boolean] Whether action path is ignored
         | 
| 100 189 | 
             
                  def ignore_path?(action, options)
         | 
| 101 | 
            -
                     | 
| 102 | 
            -
                     | 
| 190 | 
            +
                    options[:except].present? &&  options[:except].include?(action) and return true
         | 
| 191 | 
            +
                    options[:only].present?   && !options[:only].include?(action)   and return true
         | 
| 103 192 | 
             
                    false
         | 
| 104 193 | 
             
                  end
         | 
| 105 194 |  | 
| 195 | 
            +
                  # Create options fo routing
         | 
| 196 | 
            +
                  # @api private
         | 
| 197 | 
            +
                  # @param [Symbol] resource Name of the resource model
         | 
| 198 | 
            +
                  # @return [Boolean] Whether action path is ignored
         | 
| 199 | 
            +
                  def create_options(resource, options = {}, except_actions = [])
         | 
| 200 | 
            +
                    #TODO check resources if it includes target module
         | 
| 201 | 
            +
                    resources_name = resource.to_s.pluralize.underscore
         | 
| 202 | 
            +
                    options[:model] ||= resources_name.to_sym
         | 
| 203 | 
            +
                    if options[:with_devise].present?
         | 
| 204 | 
            +
                      options[:controller] ||= "activity_notification/#{resources_name}_with_devise"
         | 
| 205 | 
            +
                      options[:as]         ||= resources_name
         | 
| 206 | 
            +
                      #TODO check devise configuration in model
         | 
| 207 | 
            +
                      options[:devise_defaults] = { devise_type: options[:with_devise].to_s }
         | 
| 208 | 
            +
                    else
         | 
| 209 | 
            +
                      options[:controller] ||= "activity_notification/#{resources_name}"
         | 
| 210 | 
            +
                      options[:devise_defaults] = {}
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                    (options[:except] ||= []).concat(except_actions)
         | 
| 213 | 
            +
                    if options[:with_subscription].present?
         | 
| 214 | 
            +
                      options[:subscription_option] = (options[:with_subscription].is_a?(Hash) ? options[:with_subscription] : {})
         | 
| 215 | 
            +
                                                        .merge(with_devise: options[:with_devise])
         | 
| 216 | 
            +
                    end
         | 
| 217 | 
            +
                    #TODO other options like :as, :path_prefix, :path_names ...
         | 
| 218 | 
            +
                    options
         | 
| 219 | 
            +
                  end
         | 
| 220 | 
            +
             | 
| 106 221 | 
             
              end
         | 
| 107 222 | 
             
            end
         | 
| @@ -120,7 +120,6 @@ module ActivityNotification | |
| 120 120 | 
             
                # == Variables in templates
         | 
| 121 121 | 
             
                #
         | 
| 122 122 | 
             
                # From within a template there are three variables at your disposal:
         | 
| 123 | 
            -
                #
         | 
| 124 123 | 
             
                # * notification
         | 
| 125 124 | 
             
                # * controller
         | 
| 126 125 | 
             
                # * parameters [converted into a HashWithIndifferentAccess]
         | 
| @@ -136,7 +135,7 @@ module ActivityNotification | |
| 136 135 | 
             
                # @param [Hash] params Parameters for rendering notifications
         | 
| 137 136 | 
             
                # @option params [String, Symbol] :target       (nil)                     Target type name to find template or i18n text
         | 
| 138 137 | 
             
                # @option params [String]         :partial_root ("activity_notification/notifications/#{target}", controller.target_view_path, 'activity_notification/notifications/default') Partial template name
         | 
| 139 | 
            -
                # @option params [String]         :partial      (self.key. | 
| 138 | 
            +
                # @option params [String]         :partial      (self.key.tr('.', '/'))   Root path of partial template
         | 
| 140 139 | 
             
                # @option params [String]         :layout       (nil)                     Layout template name
         | 
| 141 140 | 
             
                # @option params [String]         :layout_root  ('layouts')               Root path of layout template
         | 
| 142 141 | 
             
                # @option params [String, Symbol] :fallback     (nil)                     Fallback template to use when MissingTemplate is raised. Set :text to use i18n text as fallback.
         | 
| @@ -172,9 +171,9 @@ module ActivityNotification | |
| 172 171 | 
             
                def partial_path(path = nil, root = nil, target = nil)
         | 
| 173 172 | 
             
                  controller = ActivityNotification.get_controller         if ActivityNotification.respond_to?(:get_controller)
         | 
| 174 173 | 
             
                  root ||= "activity_notification/notifications/#{target}" if target.present?
         | 
| 175 | 
            -
                  root ||= controller.target_view_path                     if controller.present?  | 
| 174 | 
            +
                  root ||= controller.target_view_path                     if controller.present? && controller.respond_to?(:target_view_path)
         | 
| 176 175 | 
             
                  root ||= 'activity_notification/notifications/default'
         | 
| 177 | 
            -
                  path ||= self.key. | 
| 176 | 
            +
                  path ||= self.key.tr('.', '/')
         | 
| 178 177 | 
             
                  select_path(path, root)
         | 
| 179 178 | 
             
                end
         | 
| 180 179 |  | 
| @@ -39,6 +39,17 @@ module ActivityNotification | |
| 39 39 | 
             
                  #     acts_as_notifiable :users, targets: User.all, group: :article
         | 
| 40 40 | 
             
                  #   end
         | 
| 41 41 | 
             
                  #
         | 
| 42 | 
            +
                  # * :group_expiry_delay
         | 
| 43 | 
            +
                  #   * Expiry period of a notification group.
         | 
| 44 | 
            +
                  #     Notifications will be bundled within the group expiry period.
         | 
| 45 | 
            +
                  #     This parameter is a optional.
         | 
| 46 | 
            +
                  # @example All *unopened* notifications to the same target within 1 day will be grouped by `article`
         | 
| 47 | 
            +
                  #   # app/models/comment.rb
         | 
| 48 | 
            +
                  #   class Comment < ActiveRecord::Base
         | 
| 49 | 
            +
                  #     belongs_to :article
         | 
| 50 | 
            +
                  #     acts_as_notifiable :users, targets: User.all, group: :article, :group_expiry_delay: 1.day
         | 
| 51 | 
            +
                  #   end
         | 
| 52 | 
            +
                  #
         | 
| 42 53 | 
             
                  # * :notifier
         | 
| 43 54 | 
             
                  #   * Notifier of the notification.
         | 
| 44 55 | 
             
                  #     This will be stored as notifier with notification record.
         | 
| @@ -119,6 +130,7 @@ module ActivityNotification | |
| 119 130 | 
             
                  # @param [Hash] options Options for notifiable model configuration
         | 
| 120 131 | 
             
                  # @option options [Symbol, Proc, Array]   :targets                 (nil)                    Targets to send notifications
         | 
| 121 132 | 
             
                  # @option options [Symbol, Proc, Object]  :group                   (nil)                    Group unit of the notifications
         | 
| 133 | 
            +
                  # @option options [Symbol, Proc, Object]  :group_expiry_delay      (nil)                    Expiry period of a notification group
         | 
| 122 134 | 
             
                  # @option options [Symbol, Proc, Object]  :notifier                (nil)                    Notifier of the notifications
         | 
| 123 135 | 
             
                  # @option options [Symbol, Proc, Hash]    :parameters              ({})                     Additional parameters of the notifications
         | 
| 124 136 | 
             
                  # @option options [Symbol, Proc, Boolean] :email_allowed           (ActivityNotification.config.email_enabled) Whether activity_notification sends notification email
         | 
| @@ -137,7 +149,7 @@ module ActivityNotification | |
| 137 149 | 
             
                    end
         | 
| 138 150 |  | 
| 139 151 | 
             
                    options[:printable_notifiable_name] ||= options.delete(:printable_name)
         | 
| 140 | 
            -
                    set_acts_as_parameters_for_target(target_type, [:targets, :group, :parameters, :email_allowed], options, "notification_")
         | 
| 152 | 
            +
                    set_acts_as_parameters_for_target(target_type, [:targets, :group, :group_expiry_delay, :parameters, :email_allowed], options, "notification_")
         | 
| 141 153 | 
             
                      .merge set_acts_as_parameters_for_target(target_type, [:notifier, :notifiable_path, :printable_notifiable_name], options)
         | 
| 142 154 | 
             
                  end
         | 
| 143 155 |  | 
| @@ -146,6 +158,7 @@ module ActivityNotification | |
| 146 158 | 
             
                  def available_notifiable_options
         | 
| 147 159 | 
             
                    [ :targets,
         | 
| 148 160 | 
             
                      :group,
         | 
| 161 | 
            +
                      :group_expiry_delay,
         | 
| 149 162 | 
             
                      :notifier,
         | 
| 150 163 | 
             
                      :parameters,
         | 
| 151 164 | 
             
                      :email_allowed,
         | 
| @@ -85,26 +85,29 @@ module ActivityNotification | |
| 85 85 | 
             
                  #   end
         | 
| 86 86 | 
             
                  #
         | 
| 87 87 | 
             
                  # @param [Hash] options Options for notifiable model configuration
         | 
| 88 | 
            -
                  # @option options [Symbol, Proc, String]  :email | 
| 89 | 
            -
                  # @option options [Symbol, Proc, Boolean] :email_allowed | 
| 90 | 
            -
                  # @option options [Symbol, Proc, Boolean] : | 
| 91 | 
            -
                  # @option options [Symbol, Proc,  | 
| 92 | 
            -
                  # @option options [Symbol, Proc,  | 
| 88 | 
            +
                  # @option options [Symbol, Proc, String]  :email                (nil)                                              Email address to send notification email
         | 
| 89 | 
            +
                  # @option options [Symbol, Proc, Boolean] :email_allowed        (ActivityNotification.config.email_enabled)        Whether activity_notification sends notification email to this target
         | 
| 90 | 
            +
                  # @option options [Symbol, Proc, Boolean] :batch_email_allowed  (ActivityNotification.config.email_enabled)        Whether activity_notification sends batch notification email to this target
         | 
| 91 | 
            +
                  # @option options [Symbol, Proc, Boolean] :subscription_allowed (ActivityNotification.config.subscription_enabled) Whether activity_notification manages subscriptions of this target
         | 
| 92 | 
            +
                  # @option options [Symbol, Proc, Object]  :devise_resource      (nil)                                              Integrated resource with devise authentication
         | 
| 93 | 
            +
                  # @option options [Symbol, Proc, String]  :printable_name       (ActivityNotification::Common.printable_name)      Printable notification target name
         | 
| 93 94 | 
             
                  # @return [Hash] Configured parameters as target model
         | 
| 94 95 | 
             
                  def acts_as_target(options = {})
         | 
| 95 96 | 
             
                    include Target
         | 
| 96 97 |  | 
| 97 98 | 
             
                    options[:printable_notification_target_name] ||= options.delete(:printable_name)
         | 
| 98 99 | 
             
                    options[:batch_notification_email_allowed] ||= options.delete(:batch_email_allowed)
         | 
| 99 | 
            -
                    set_acts_as_parameters([:email, :email_allowed, :devise_resource], options, "notification_")
         | 
| 100 | 
            -
             | 
| 100 | 
            +
                    acts_as_params = set_acts_as_parameters([:email, :email_allowed, :subscription_allowed, :devise_resource], options, "notification_")
         | 
| 101 | 
            +
                                       .merge set_acts_as_parameters([:batch_notification_email_allowed, :printable_notification_target_name], options)
         | 
| 102 | 
            +
                    include Subscriber if subscription_enabled?
         | 
| 103 | 
            +
                    acts_as_params
         | 
| 101 104 | 
             
                  end
         | 
| 102 105 | 
             
                  alias_method :acts_as_notification_target, :acts_as_target
         | 
| 103 106 |  | 
| 104 107 | 
             
                  # Returns array of available target options in acts_as_target.
         | 
| 105 108 | 
             
                  # @return [Array<Symbol>] Array of available target options
         | 
| 106 109 | 
             
                  def available_target_options
         | 
| 107 | 
            -
                    [:email, :email_allowed, :batch_email_allowed, :devise_resource, :printable_notification_target_name, :printable_name].freeze
         | 
| 110 | 
            +
                    [:email, :email_allowed, :batch_email_allowed, :subscription_allowed, :devise_resource, :printable_notification_target_name, :printable_name].freeze
         | 
| 108 111 | 
             
                  end
         | 
| 109 112 | 
             
                end
         | 
| 110 113 | 
             
              end
         | 
| @@ -6,7 +6,7 @@ module ActivityNotification | |
| 6 6 | 
             
                # @example Run controller generator for users as target
         | 
| 7 7 | 
             
                #   rails generate activity_notification:controllers users
         | 
| 8 8 | 
             
                class ControllersGenerator < Rails::Generators::Base
         | 
| 9 | 
            -
                  CONTROLLERS = ['notifications', 'notifications_with_devise'].freeze
         | 
| 9 | 
            +
                  CONTROLLERS = ['notifications', 'notifications_with_devise', 'subscriptions', 'subscriptions_with_devise'].freeze
         | 
| 10 10 |  | 
| 11 11 | 
             
                  desc <<-DESC.strip_heredoc
         | 
| 12 12 | 
             
                    Create inherited ActivityNotification controllers in your app/controllers folder.
         | 
| @@ -15,7 +15,7 @@ module ActivityNotification | |
| 15 15 | 
             
                    If you do no specify a controller, all controllers will be created.
         | 
| 16 16 | 
             
                    For example:
         | 
| 17 17 |  | 
| 18 | 
            -
                      rails generate activity_notification:controllers users -c | 
| 18 | 
            +
                      rails generate activity_notification:controllers users -c notifications
         | 
| 19 19 |  | 
| 20 20 | 
             
                    This will create a controller class at app/controllers/users/notifications_controller.rb like this:
         | 
| 21 21 |  |