notify_on 1.0.1

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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +29 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/notify_on_manifest.js +2 -0
  6. data/app/assets/javascripts/notify_on/application.js +13 -0
  7. data/app/assets/stylesheets/notify_on/application.css +15 -0
  8. data/app/controllers/notify_on/application_controller.rb +5 -0
  9. data/app/helpers/notify_on/application_helper.rb +4 -0
  10. data/app/helpers/notify_on/mailer_helper.rb +9 -0
  11. data/app/jobs/notify_on/application_job.rb +4 -0
  12. data/app/mailers/notify_on/application_mailer.rb +6 -0
  13. data/app/mailers/notify_on/notification_mailer.rb +26 -0
  14. data/app/models/concerns/notify_on/email_support.rb +97 -0
  15. data/app/models/concerns/notify_on/pusher_support.rb +66 -0
  16. data/app/models/concerns/notify_on/string_interpolation.rb +39 -0
  17. data/app/models/notify_on/application_record.rb +5 -0
  18. data/app/models/notify_on/notification.rb +86 -0
  19. data/app/views/layouts/notify_on/application.html.erb +17 -0
  20. data/app/views/notifications/notify.html.erb +9 -0
  21. data/config/database.travis.yml +4 -0
  22. data/config/routes.rb +2 -0
  23. data/lib/generators/notify_on/install_generator.rb +29 -0
  24. data/lib/generators/templates/notifications.yml +19 -0
  25. data/lib/generators/templates/notify_on.rb +64 -0
  26. data/lib/notify_on.rb +29 -0
  27. data/lib/notify_on/bulk_config.rb +36 -0
  28. data/lib/notify_on/configuration.rb +22 -0
  29. data/lib/notify_on/creator.rb +85 -0
  30. data/lib/notify_on/engine.rb +19 -0
  31. data/lib/notify_on/notify_on.rb +36 -0
  32. data/lib/notify_on/receives_notifications.rb +11 -0
  33. data/lib/notify_on/utilities.rb +13 -0
  34. data/lib/notify_on/version.rb +3 -0
  35. data/lib/tasks/notify_on_tasks.rake +4 -0
  36. data/spec/dummy/Rakefile +6 -0
  37. data/spec/dummy/app/assets/config/manifest.js +5 -0
  38. data/spec/dummy/app/assets/javascripts/application.js +7 -0
  39. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  40. data/spec/dummy/app/assets/stylesheets/application.scss +28 -0
  41. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  42. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  44. data/spec/dummy/app/controllers/messages_controller.rb +31 -0
  45. data/spec/dummy/app/helpers/application_helper.rb +8 -0
  46. data/spec/dummy/app/jobs/application_job.rb +2 -0
  47. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  48. data/spec/dummy/app/mailers/notification_mailer.rb +15 -0
  49. data/spec/dummy/app/models/application_record.rb +3 -0
  50. data/spec/dummy/app/models/comment.rb +10 -0
  51. data/spec/dummy/app/models/message.rb +45 -0
  52. data/spec/dummy/app/models/post.rb +11 -0
  53. data/spec/dummy/app/models/user.rb +21 -0
  54. data/spec/dummy/app/views/application/_header.html.erb +44 -0
  55. data/spec/dummy/app/views/application/home.html.erb +1 -0
  56. data/spec/dummy/app/views/layouts/application.html.erb +19 -0
  57. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  58. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  59. data/spec/dummy/app/views/messages/_message.html.erb +6 -0
  60. data/spec/dummy/app/views/messages/index.html.erb +33 -0
  61. data/spec/dummy/app/views/messages/new.html.erb +5 -0
  62. data/spec/dummy/app/views/messages/show.html.erb +7 -0
  63. data/spec/dummy/app/views/notifications/new_message.html.erb +9 -0
  64. data/spec/dummy/bin/bundle +3 -0
  65. data/spec/dummy/bin/rails +4 -0
  66. data/spec/dummy/bin/rake +4 -0
  67. data/spec/dummy/bin/setup +34 -0
  68. data/spec/dummy/bin/update +29 -0
  69. data/spec/dummy/config.ru +5 -0
  70. data/spec/dummy/config/application.rb +35 -0
  71. data/spec/dummy/config/boot.rb +5 -0
  72. data/spec/dummy/config/cable.yml +9 -0
  73. data/spec/dummy/config/database.yml +12 -0
  74. data/spec/dummy/config/environment.rb +5 -0
  75. data/spec/dummy/config/environments/development.rb +58 -0
  76. data/spec/dummy/config/environments/production.rb +86 -0
  77. data/spec/dummy/config/environments/test.rb +42 -0
  78. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  79. data/spec/dummy/config/initializers/assets.rb +11 -0
  80. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  81. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  82. data/spec/dummy/config/initializers/devise.rb +274 -0
  83. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  84. data/spec/dummy/config/initializers/inflections.rb +16 -0
  85. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  86. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  87. data/spec/dummy/config/initializers/notify_on.rb +64 -0
  88. data/spec/dummy/config/initializers/session_store.rb +3 -0
  89. data/spec/dummy/config/initializers/simple_form.rb +165 -0
  90. data/spec/dummy/config/initializers/simple_form_bootstrap.rb +149 -0
  91. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  92. data/spec/dummy/config/locales/devise.en.yml +62 -0
  93. data/spec/dummy/config/locales/en.yml +23 -0
  94. data/spec/dummy/config/locales/simple_form.en.yml +31 -0
  95. data/spec/dummy/config/notifications.yml +20 -0
  96. data/spec/dummy/config/puma.rb +47 -0
  97. data/spec/dummy/config/routes.rb +14 -0
  98. data/spec/dummy/config/secrets.yml +22 -0
  99. data/spec/dummy/config/spring.rb +6 -0
  100. data/spec/dummy/db/migrate/20160801112429_devise_create_users.rb +42 -0
  101. data/spec/dummy/db/migrate/20160801130804_create_messages.rb +11 -0
  102. data/spec/dummy/db/migrate/20160823102904_create_notify_on_notifications.rb +20 -0
  103. data/spec/dummy/db/migrate/20161021100707_create_comments.rb +11 -0
  104. data/spec/dummy/db/migrate/20161021100842_create_posts.rb +11 -0
  105. data/spec/dummy/db/schema.rb +77 -0
  106. data/spec/dummy/db/seeds.rb +4 -0
  107. data/spec/dummy/lib/templates/erb/scaffold/_form.html.erb +13 -0
  108. data/spec/dummy/public/404.html +67 -0
  109. data/spec/dummy/public/422.html +67 -0
  110. data/spec/dummy/public/500.html +66 -0
  111. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  112. data/spec/dummy/public/apple-touch-icon.png +0 -0
  113. data/spec/dummy/public/favicon.ico +0 -0
  114. data/spec/dummy/spec/controllers/messages_controller_spec.rb +5 -0
  115. data/spec/dummy/spec/factories/comments.rb +7 -0
  116. data/spec/dummy/spec/factories/messages.rb +7 -0
  117. data/spec/dummy/spec/factories/posts.rb +6 -0
  118. data/spec/dummy/spec/factories/users.rb +6 -0
  119. data/spec/dummy/spec/features/send_message_spec.rb +25 -0
  120. data/spec/dummy/spec/mailers/notify_on/notification_mailer_spec.rb +30 -0
  121. data/spec/dummy/spec/mailers/previews/notification_mailer_preview.rb +4 -0
  122. data/spec/dummy/spec/models/comment_spec.rb +22 -0
  123. data/spec/dummy/spec/models/message_spec.rb +60 -0
  124. data/spec/dummy/spec/models/post_spec.rb +23 -0
  125. data/spec/dummy/spec/models/user_spec.rb +21 -0
  126. data/spec/factories/notify_on_notifications.rb +10 -0
  127. data/spec/lib/notify_on/configuration_spec.rb +53 -0
  128. data/spec/mailers/notify_on/notification_mailer_spec.rb +6 -0
  129. data/spec/mailers/previews/notify_on/notification_mailer_preview.rb +6 -0
  130. data/spec/models/notify_on/notification_spec.rb +335 -0
  131. data/spec/rails_helper.rb +95 -0
  132. data/spec/spec_helper.rb +103 -0
  133. data/spec/support/feature_helpers.rb +19 -0
  134. data/spec/support/general_helpers.rb +19 -0
  135. metadata +543 -0
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Notify on</title>
5
+ <%= stylesheet_link_tag "notify_on/application", media: "all" %>
6
+ <%= javascript_include_tag "notify_on/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <p class="notice"><%= notice %></p>
12
+ <p class="alert"><%= alert %></p>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
@@ -0,0 +1,9 @@
1
+ <p>Hey there, <%= @recipient.to_s %>!</p>
2
+
3
+ <p>You have a new notification:</p>
4
+
5
+ <blockquote>
6
+ <%= simple_format @notification.description %>
7
+ </blockquote>
8
+
9
+ <p>Cheers!</p>
@@ -0,0 +1,4 @@
1
+ test:
2
+ adapter: postgresql
3
+ database: travis_ci_test
4
+ username: postgres
@@ -0,0 +1,2 @@
1
+ NotifyOn::Engine.routes.draw do
2
+ end
@@ -0,0 +1,29 @@
1
+ module NotifyOn
2
+ class InstallGenerator < Rails::Generators::Base
3
+
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ def generate_migration
7
+ attrs = "recipient_id:integer recipient_type:string sender_id:integer "
8
+ attrs += "sender_type:string unread:boolean trigger_id:integer "
9
+ attrs += "trigger_type:string description_raw:text "
10
+ attrs += "description_cached:text link_raw:string link_cached:string "
11
+ attrs += "options:text"
12
+ generate "migration create_notify_on_notifications #{attrs}"
13
+
14
+ gsub_file Dir.glob('db/migrate/*.rb').last, /t\.boolean\ \:unread/,
15
+ 't.boolean :unread, :default => true'
16
+ gsub_file Dir.glob('db/migrate/*.rb').last, /t\.text\ \:options/,
17
+ "t.text :options\n t.timestamps"
18
+ end
19
+
20
+ def copy_initializer
21
+ template "notify_on.rb", "config/initializers/notify_on.rb"
22
+ end
23
+
24
+ def copy_bulk_config
25
+ template "notifications.yml", "config/notifications.yml"
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # This is the file that lets you configure notifications in bulk, instead of
2
+ # using individual "notify_on" calls within your models.
3
+ #
4
+ # Do so by first specifying the underscored class name, and give each
5
+ # notification a name. For example:
6
+ #
7
+ # message:
8
+ # message_sent:
9
+ # when: :save
10
+ # if: :active?
11
+ # unless: :from_unless?
12
+ # to: :user
13
+ # from: :author
14
+ # message: '{author.first_name} has sent you a message.'
15
+ # link: chat_path(:chat)
16
+ # email:
17
+ # template: new_message
18
+ # unless: :pusher_recipient_active?
19
+ #
@@ -0,0 +1,64 @@
1
+ # Use this file to set your application's configuration for NotifyOn.
2
+ NotifyOn.configure do |config|
3
+
4
+ # ---------------------------------------- Email
5
+
6
+ # Email messages are a common way to let a user know they have a new
7
+ # notification. In some cases, you want that notification to come from another
8
+ # user. Other times, your system may handle it. Defining a default email
9
+ # address means that if you don't specify "to" when calling "notify_on", the
10
+ # default email address will be used.
11
+ #
12
+ # config.default_email = 'No Reply <noreply@yourdomain.com>'
13
+ #
14
+ # Note: You can use NotifyOn's string interpolation here. It will first
15
+ # attempt methods on the trigger, and then fallback to the notification.
16
+ # Therefore, you have access to the "sender" object, and could choose to
17
+ # default to the sender's email address, like so:
18
+ #
19
+ # config.default_email = '{sender.name} <{sender.email}>'
20
+ #
21
+ # You can also override the notification mailer class if you need to add
22
+ # custom functionality not supported in NotifyOn's NotificationMailer class.
23
+ #
24
+ # config.mailer_class = 'NotifyOn::NotificationMailer'
25
+ #
26
+ # NotifyOn's email notification service provides the option to use Active Job
27
+ # to send email messages in the background instead of during the request. If
28
+ # you enable Active Job, NotifyOn will use your application settings for
29
+ # Active Job. Learn more at
30
+ # http://guides.rubyonrails.org/active_job_basics.html
31
+ #
32
+ # config.deliver_mail = :now # or :later
33
+
34
+ # ---------------------------------------- Pusher
35
+
36
+ # Pusher enables you to send notifications in real-time. Learn more about
37
+ # Pusher at https://pusher.com. If you are going to use Pusher, you need to
38
+ # include the following values:
39
+ #
40
+ # config.pusher_app_id = 'my_app_id'
41
+ # config.pusher_key = 'my_key'
42
+ # config.pusher_secret = 'my_secret'
43
+ #
44
+ # Note: You may want to use environment-dependent values, not tracked by git,
45
+ # so your secrets are not exposed. You may choose to use Rails' secrets for
46
+ # this. For example:
47
+ #
48
+ # config.pusher_app_id = Rails.application.secrets.pusher_app_id
49
+ # config.pusher_key = Rails.application.secrets.pusher_key
50
+ # config.pusher_secret = Rails.application.secrets.pusher_secret
51
+ #
52
+ # While you can configure your Pusher event in each "notify_on" call, you can
53
+ # also set your default configuration so you don't have to restate it for
54
+ # every notification.
55
+ #
56
+ # config.default_pusher_channel = 'presence_{:env}_notification_{:recipient_id}'
57
+ # config.default_pusher_event = 'new_notification'
58
+ #
59
+ # You can use Pusher by default (which requires the channel and event be set
60
+ # above). Uncomment the following setting to do so.
61
+ #
62
+ # config.use_pusher_by_default = true
63
+
64
+ end
@@ -0,0 +1,29 @@
1
+ require 'notify_on/engine'
2
+ require 'notify_on/configuration'
3
+ require 'notify_on/utilities'
4
+ require 'notify_on/receives_notifications'
5
+ require 'notify_on/creator'
6
+ require 'notify_on/notify_on'
7
+ require 'notify_on/bulk_config'
8
+
9
+ require 'em-http-request'
10
+ require 'hashie'
11
+ require 'pusher'
12
+
13
+ module NotifyOn
14
+ class << self
15
+ attr_writer :configuration
16
+ end
17
+
18
+ def self.configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def self.reset
23
+ @configuration = Configuration.new
24
+ end
25
+
26
+ def self.configure
27
+ yield(configuration)
28
+ end
29
+ end
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ module NotifyOn
4
+ class BulkConfig
5
+
6
+ def initialize(options = {})
7
+ end
8
+
9
+ def self.load
10
+ new.load
11
+ end
12
+
13
+ def load
14
+ return unless File.exists?(config_file) && config
15
+ config.each do |model_name, notification_name|
16
+ notification_name.each do |name, notify_on_config|
17
+ model_name.classify.constantize.class_eval do
18
+ notify_on (notify_on_config['action'] || notify_on_config['when']),
19
+ notify_on_config.deep_symbolize_keys
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def config_file
28
+ Rails.root.join('config', 'notifications.yml').to_s
29
+ end
30
+
31
+ def config
32
+ YAML.load_file(config_file)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,22 @@
1
+ module NotifyOn
2
+ class Configuration
3
+
4
+ attr_accessor \
5
+ :default_email,
6
+ :default_pusher_channel,
7
+ :default_pusher_event,
8
+ :deliver_mail,
9
+ :mailer_class,
10
+ :pusher_app_id,
11
+ :pusher_key,
12
+ :pusher_secret,
13
+ :use_pusher_by_default
14
+
15
+ def initialize
16
+ @deliver_mail = :now
17
+ @mailer_class = 'NotifyOn::NotificationMailer'
18
+ @use_pusher_by_default = false
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ module NotifyOn
2
+ module Creator
3
+
4
+ private
5
+
6
+ def create_notify_on_notifications(options)
7
+
8
+ # skip_notifications disables all notifications for an instance.
9
+ return if skip_notifications == true
10
+
11
+ # Collect notifications so we can return the set if we are creating more
12
+ # than one (when "to" is an array or collection).
13
+ notifications = []
14
+
15
+ # "to" must be a method or attribute, and here we resolve it.
16
+ to = options[:to].to_s == 'self' ? self : send(options[:to].to_s)
17
+
18
+ # We don't need a sender all the time, but we figure out who it is now
19
+ # so we can more easily work with the object.
20
+ sender = options[:from].blank? ? nil : send(options[:from].to_s)
21
+
22
+ # Ensure "to" is an array so we can iterate over it, even if that's only
23
+ # happening once.
24
+ (to.respond_to?(:each) ? to : [to]).each do |recipient|
25
+
26
+ # If we have an update strategy, then we first need to look for an
27
+ # exiting notification.
28
+ notification = find_from_update_strategy(recipient, sender, options)
29
+
30
+ # Create a new notification if we didn't have an update strategy or
31
+ # couldn't find one. Much of the advanced work happens within the
32
+ # model, which is why we store "options" in the record.
33
+ notification = NotifyOn::Notification.new(
34
+ :recipient => recipient,
35
+ :sender => sender,
36
+ :description_raw => options[:message],
37
+ :options => options
38
+ ) if notification.nil?
39
+
40
+ # Update the shared attributes regardless of whether the notification
41
+ # is being created or updated.
42
+ notification.assign_attributes(
43
+ :unread => true,
44
+ :trigger => self,
45
+ :link_raw => options[:link]
46
+ )
47
+
48
+ # Make sure we can save the notification.
49
+ notification.save!
50
+
51
+ # Attempt to send the notification via Pusher. There are catches in
52
+ # place to prevent it from being pushed if disabled.
53
+ # -- NotifyOn::PusherSupport
54
+ notification.push!
55
+
56
+ # Send the notification via email, if requested.
57
+ # -- NotifyOn::EmailSupport
58
+ notification.send_email! if options[:email].present?
59
+
60
+ # Add notification to the collection for response.
61
+ notifications << notification
62
+ end
63
+
64
+ # Return the collection of notifications that we created.
65
+ notifications
66
+ end
67
+
68
+ def find_from_update_strategy(to, from, options)
69
+ # Exit if we don't have a strategy, and currently we only support the
70
+ # :sender strategy.
71
+ return nil unless options[:update] && options[:update][:strategy] &&
72
+ options[:update][:strategy] == :sender &&
73
+ from.present?
74
+ if (scope = options[:update][:scope]).present?
75
+ to.notifications.from_with_type(from, self.class.name).recent
76
+ .includes(:trigger => [scope])
77
+ .select{ |n| n.trigger.send(scope) == self.send(scope)}[0]
78
+ else
79
+ to.notifications.from_with_type(from, self.class.name).recent
80
+ .limit(1)[0]
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -0,0 +1,19 @@
1
+ module NotifyOn
2
+ class Engine < ::Rails::Engine
3
+
4
+ isolate_namespace NotifyOn
5
+
6
+ config.generators do |g|
7
+ g.test_framework :rspec, :fixture => false
8
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
9
+ g.assets false
10
+ g.helper false
11
+ end
12
+
13
+ config.after_initialize do
14
+ Rails.application.eager_load!
15
+ NotifyOn::BulkConfig.load
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,36 @@
1
+ class << ActiveRecord::Base
2
+
3
+ def notify_on(action, options = {})
4
+
5
+ include NotifyOn::Creator
6
+
7
+ ([self] + self.descendants).each do |klass|
8
+ klass.class_eval do
9
+ has_many :notifications, -> { preloaded },
10
+ :class_name => NotifyOn::Notification, :as => :trigger,
11
+ :dependent => :destroy
12
+ end
13
+ end
14
+
15
+ attr_accessor :skip_notifications
16
+
17
+ method_to_s = NotifyOn::Utilities.callback_method_name(action, options)
18
+ method_sym = method_to_s.to_sym
19
+
20
+ send("after_#{(action.to_s == 'create') ? 'create' : 'update'}", method_sym)
21
+
22
+ define_method(method_to_s) do
23
+ # The action trigger needs to be create, save, or a true condition.
24
+ return unless %w(create update).include?(action.to_s) || send(action.to_sym)
25
+ # An optional if condition must be missing or true.
26
+ return unless options[:if].blank? || send(options[:if])
27
+ # An optional unless condition must be missing or false.
28
+ return unless options[:unless].blank? || !send(options[:unless])
29
+ # Create the notification if we get past all our checks.
30
+ create_notify_on_notifications(options)
31
+ end
32
+
33
+ private method_to_s.to_sym
34
+
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ class << ActiveRecord::Base
2
+
3
+ def receives_notifications
4
+
5
+ has_many :notifications, -> { preloaded },
6
+ :class_name => NotifyOn::Notification, :as => :recipient,
7
+ :dependent => :destroy
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ module NotifyOn
2
+ class Utilities
3
+
4
+ def self.callback_method_name(action, options)
5
+ opts = options.merge(:action => action)
6
+ opts.each { |k, v| opts[k] = v.to_s.gsub(/\?/, '') }
7
+ "notify_#{opts[:to]}_on_#{opts[:action]}" +
8
+ ("_if_#{opts[:if]}" if opts[:if].present?).to_s +
9
+ ("_unless_#{opts[:unless]}" if opts[:unless].present?).to_s
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module NotifyOn
2
+ VERSION = '1.0.1'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :notify_on do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,5 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
5
+ //= link notify_on_manifest.js
@@ -0,0 +1,7 @@
1
+ //= require jquery
2
+ //= require jquery_ujs
3
+ //= require bootstrap-sprockets
4
+
5
+ $(document).ready(function(){
6
+ $('.dropdown-toggle').dropdown();
7
+ });