yodeler 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 530dac4896c916cff39b3290396117d2e03b9751
4
+ data.tar.gz: 4011479ed5dee9fb800330038a50613d2dd48da3
5
+ SHA512:
6
+ metadata.gz: 7bd42ef38e14b8b7945eeaf5f892cfd74d5be6d628bf5b7760b69ad39899f07c3d81bea1aa7973de10a012909a3f1dbe22bde601706105eefb795aefdfdc57b4
7
+ data.tar.gz: b99ceca1697e0546b238540ae19500177d3e99f8e460eb31392cedd9b95f1b8e124848bd57ba797ade4ed01503c6706a49ad33f12e0f55882b6fa482fd033bbb
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ Yodeler
2
+ =======
3
+
4
+ Spouting noise to whoever is listening.
5
+
6
+ Yodeler is an easy way to notify users of different events that occur in your app.
7
+
8
+ * Notify your marketing team on the 10,000 sign-up.
9
+ * Notify user's when their profile has been viewed
10
+ * Notify your dev team about events that are not quite exception worthy, but you don't want to dig through a bunch of logs to find.
11
+
12
+
13
+ Features
14
+ ========
15
+
16
+ * store notifications via ActiveRecord
17
+ * benchmark event duration
18
+ * i18n support
19
+ * flexible payloads
20
+
21
+
22
+ What's wrong with XMPP?
23
+ =======================
24
+
25
+ Why don't you fly to Walmart on a rocket?
26
+
27
+ Don't even say it's because you don't have a rocket.
28
+
29
+ Why don't you kill ants with a bazooka?
30
+
31
+ Nevermind, you get it.
32
+
33
+
34
+ Usage
35
+ =====
36
+
37
+ ```ruby
38
+ # in config/initializers/yodeler.rb
39
+ Yodeler.register :another_thousand_users
40
+
41
+ Yodeler.register :view_user do
42
+ # Default states are unread: 0, read: 1
43
+ config.states = {
44
+ unread: 0,
45
+ read: 1,
46
+ ignored: 2
47
+ }
48
+ end
49
+ ```
50
+
51
+ ```ruby
52
+ # in a controller or Anywhere you want to hear some noise
53
+ class UserController < ApplicationController
54
+ around_action :track_user_view, only: :show
55
+
56
+ def show
57
+ # do your User#show as normal
58
+ end
59
+
60
+ private
61
+
62
+ def track_user_view
63
+ # This will also benchmark the action since it is in a block
64
+ Yodeler.dispatch :view_user, {viewer_id: 3, viewee_id: 23} do |payload|
65
+ yield #yield the action dispatch, the payload is available
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ ```ruby
72
+ # off in some scorned rails-observer
73
+ class UserObserver
74
+ observers :user
75
+
76
+ def after_create
77
+ # let the marketing know another thousandth user has registered!
78
+ if (User.count % 1000).zero?
79
+ Yodeler.dispatch :another_thousand_users, {give_him_a_prize: @user.id}
80
+ end
81
+ end
82
+ end
83
+ ```
84
+
85
+
86
+ Getting around the objects
87
+ ==========================
88
+
89
+ ```ruby
90
+ @event.subscriptions #=> Array<Yodeler::Subscription> all subscriber subscriptions
91
+ @event.notifications #=> Array<Yodeler::Notifications>
92
+
93
+ @notification.subscriber #=> Your 'subscriber' class, delegated to #subscription
94
+ @notification.subscription.subscriber
95
+ @notification.event #=> The subscriber to event, delegated to #subscription
96
+ @notification.message #=> i18n message interpolated w/ the payload
97
+
98
+ @subscription.subscriber #=> the 'subscriber' subscribed to this event
99
+ @subscription.notifications #=> all notifications of this event type for this subscriber
100
+ @subscription.event
101
+
102
+ ```
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Yodeler'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/test_app/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module Yodeler
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Yodeler
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Yodeler</title>
5
+ <%= stylesheet_link_tag "yodeler/application", media: "all" %>
6
+ <%= javascript_include_tag "yodeler/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Yodeler::Engine.routes.draw do
2
+ end
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Generates (but does not run) a migration to add yodeler tables and an initializer.
@@ -0,0 +1,23 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/active_record'
3
+
4
+ module Yodeler
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ include ::Rails::Generators::Migration
7
+
8
+ source_root File.expand_path('../templates', __FILE__)
9
+ desc 'Generates (but does not run) a migration to add yodeler tables and an initializer.'
10
+
11
+ def create_migration_file
12
+ migration_template 'migration.rb', 'db/migrate/create_yodeler_tables.rb'
13
+ end
14
+
15
+ def create_initializer
16
+ copy_file 'initializer.rb', 'config/initializers/yodeler.rb'
17
+ end
18
+
19
+ def self.next_migration_number(dirname)
20
+ ::ActiveRecord::Generators::Base.next_migration_number(dirname)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ Yodeler.configure do |config|
2
+ #config.noop_event_type.all_the_settings_that_are_normally_exposed_via_dsl
3
+ end
@@ -0,0 +1,50 @@
1
+ class CreateYodelerTables < ActiveRecord::Migration
2
+ def change
3
+ create_table :yodeler_event_types do |t|
4
+ t.string :name, null: false # Maps to registered name in DSL
5
+ t.string :type #STI
6
+ t.datetime :created_at
7
+ end
8
+
9
+ create_table :yodeler_events do |t|
10
+ t.integer :yodeler_event_type_id, null: false
11
+ t.datetime :started_at
12
+ t.datetime :finished_at
13
+ t.text :payload
14
+ t.datetime :created_at, null: false
15
+ end
16
+
17
+ create_table :yodeler_subscriptions do |t|
18
+ t.integer :yodeler_event_type_id, null: false
19
+
20
+ # polymorphic, support for whatever type of class is owning the subscription
21
+ t.string :subscriber_type, null: false
22
+ t.integer :subscriber_id, null: false
23
+
24
+ t.datetime :created_at
25
+ t.datetime :updated_at
26
+ end
27
+
28
+ create_table :yodeler_notifications do |t|
29
+ t.integer :yodeler_event_id, null: false
30
+ t.integer :yodeler_subscription_id, null: false
31
+ t.integer :state
32
+ t.datetime :created_at, null: false
33
+ t.datetime :updated_at, null: false
34
+ end
35
+
36
+ # sqlite3 has an index name length limitation of 62(?!) characters
37
+ unless Rails.env.test?
38
+ add_index :yodeler_event_types, :name
39
+
40
+ add_index :yodeler_events, :yodeler_event_type_id
41
+
42
+ add_index :yodeler_subscriptions, :yodeler_event_type_id
43
+ add_index :yodeler_subscriptions, [:subscriber_id, :subscriber_type]
44
+
45
+ add_index :yodeler_notifications, :yodeler_event_id
46
+ add_index :yodeler_notifications, :yodeler_subscription_id
47
+ add_index :yodeler_notifications, :state
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :yodeler do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,6 @@
1
+ module Yodeler
2
+ class Configuration
3
+ include ActiveSupport::Configurable
4
+ config_accessor(:test) { false }
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ module Yodeler
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Yodeler
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec
7
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module Yodeler
2
+ module ListensToYodler
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ end
7
+
8
+ module ClassMethods
9
+ def listens_to_yodler(subscriptions_assocation_name = :yodeler_subscriptions)
10
+ cattr_accessor :yodeler_subscriptions_name
11
+ self.yodeler_subscriptions_name = subscriptions_assocation_name
12
+
13
+ has_many self.yodeler_subscriptions_name,
14
+ dependent: :destroy,
15
+ class_name: "Yodeler::Subscription",
16
+ foreign_key: :subscriber_id
17
+
18
+ include Yodeler::ListensToYodler::LocalInstanceMethods
19
+ end
20
+ end
21
+
22
+ module LocalInstanceMethods;end;
23
+ end
24
+ end
25
+
26
+ ActiveRecord::Base.send :include, Yodeler::ListensToYodler
@@ -0,0 +1,17 @@
1
+ module Yodeler
2
+ class Event < ActiveRecord::Base
3
+ serialize :payload, Hash
4
+
5
+ validates_presence_of :event_type
6
+
7
+ belongs_to :event_type, class_name: "Yodeler::EventType::Base", foreign_key: :yodeler_event_type_id
8
+ has_many :notifications, class_name: "Yodeler::Notification", foreign_key: :yodeler_event_id
9
+
10
+ delegate :subscriptions, to: :event_type
11
+ delegate :name, to: :event_type
12
+
13
+ def duration
14
+ finished_at - started_at if finished_at.present? and started_at.present?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ module Yodeler
2
+ module EventType
3
+ autoload :Base, "yodeler/models/event_types/base"
4
+ autoload :NoopEventType, "yodeler/models/event_types/noop_event_type"
5
+ end
6
+ end
@@ -0,0 +1,53 @@
1
+ module Yodeler
2
+ module EventType
3
+ class Base < ActiveRecord::Base
4
+ self.table_name= "yodeler_event_types"
5
+
6
+ class Configuration
7
+ include ActiveSupport::Configurable
8
+ config_accessor(:states) do
9
+ { unread: 0, read: 1 }
10
+ end
11
+ end
12
+
13
+ def self.configuration
14
+ @configuration ||= Configuration.new
15
+ end
16
+
17
+ validates_presence_of :name
18
+ validates_uniqueness_of :name
19
+
20
+ has_many :events,
21
+ dependent: :destroy,
22
+ class_name: "Yodeler::Event",
23
+ foreign_key: :yodeler_event_type_id
24
+
25
+ has_many :subscriptions,
26
+ dependent: :destroy,
27
+ class_name: "Yodeler::Subscription",
28
+ foreign_key: :yodeler_event_type_id
29
+
30
+
31
+ # Logs the occurrence of a {Yodeler::Event} and dispatches notifications
32
+ #
33
+ # @param [Hash] params additional params to log
34
+ # @option params [String] :started_at Benchmark started at time
35
+ # @option params [String] :finished_at Benchmark finished at time
36
+ #
37
+ # @param [Hash] payload Serialized hash, anything you want
38
+ #
39
+ # @return [~Yodeler::EventType::Base] the logged event
40
+ def self.yodel!(params)
41
+ current_event_type = self.first
42
+ current_event = current_event_type.events.create(params)
43
+
44
+ current_event_type.subscriptions.each do |subscriber|
45
+
46
+ end
47
+
48
+ current_event
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ module Yodeler
2
+ module EventType
3
+ # The event that is dispatch if an Yodeler.dispatch is called on an event type that
4
+ # does not exist
5
+ #
6
+ # @example
7
+ # Yodeler.dispatch :i_dont_exist, my_payload
8
+ # # => Yodeler.dispatch :noop, my_payload
9
+ class NoopEventType < Yodeler::EventType::Base
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,21 @@
1
+ module Yodeler
2
+ class Notification < ActiveRecord::Base
3
+ validates_presence_of :subscription
4
+ validates_presence_of :event
5
+
6
+ belongs_to :event, class_name: "Yodeler::Event", foreign_key: :yodeler_event_id
7
+ belongs_to :subscription, class_name: "Yodeler::Subscription", foreign_key: :yodeler_subscription_id
8
+
9
+ delegate :event_type, to: :event
10
+ delegate :subscriber, to: :subscription
11
+
12
+ def message
13
+ I18n.t("yodeler.event_types.#{event_type_key}.message", event.payload)
14
+ end
15
+
16
+ protected
17
+ def event_type_key
18
+ event_type.name.to_sym
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module Yodeler
2
+ class Subscription < ActiveRecord::Base
3
+ validates_presence_of :subscriber, :event_type
4
+ belongs_to :event_type, class_name: "Yodeler::EventType::Base", foreign_key: :yodeler_event_type_id
5
+ belongs_to :subscriber, polymorphic: true
6
+
7
+ has_many :notifications, class_name: "Yodeler::Notification", foreign_key: :yodeler_subscription_id
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Yodeler
2
+ VERSION = "0.0.1"
3
+ end
data/lib/yodeler.rb ADDED
@@ -0,0 +1,122 @@
1
+ require "yodeler/engine"
2
+
3
+ module Yodeler
4
+ autoload :Configuration, 'yodeler/configuration'
5
+ autoload :EventType, 'yodeler/models/event_type'
6
+ autoload :Event, 'yodeler/models/event'
7
+ autoload :Subscription, 'yodeler/models/subscription'
8
+ autoload :Notification, 'yodeler/models/notification'
9
+ autoload :ListensToYodler, 'yodeler/listens_to_yodler'
10
+ autoload :EventType, 'yodeler/models/event_type'
11
+
12
+ mattr_accessor :registrations
13
+ self.registrations = {}
14
+
15
+ # Configuring the yodeler gem
16
+ def self.configuration
17
+ @configuration ||= Configuration.new
18
+ end
19
+
20
+ def self.configure
21
+ yield configuration
22
+ end
23
+
24
+ # Set the ActiveRecord table name prefix to yodeler_
25
+ #
26
+ # This makes 'events' into 'yodeler_events' and also respects any
27
+ # 'global' table_name_prefix set on ActiveRecord::Base.
28
+ def self.table_name_prefix
29
+ "#{ActiveRecord::Base.table_name_prefix}yodeler_"
30
+ end
31
+
32
+ # Creates an event_type model e.g.: Yodeler::#{event_type}EventType < Yodeler::EventType::Base
33
+ #
34
+ # @note
35
+ # you could also create your own class inheriting #{Yodeler::EventType::Base}
36
+ def self.register(event_type,&block)
37
+ event_type_klass = Yodeler.define_event_type(event_type)
38
+
39
+ if block_given?
40
+ yield event_type_klass.configuration
41
+ end
42
+
43
+ # Add the event type to the yodeler_event_types table
44
+ event_type_klass.first_or_create name: event_type
45
+
46
+ # keep track of the registrations
47
+ Yodeler.registrations[event_type] = event_type_klass
48
+ event_type_klass
49
+ end
50
+
51
+ # Dispatches an event
52
+ # @param [Symbol] event_type the registered event type
53
+ # @param [Hash] payload a hash of values to serialize with the event
54
+ #
55
+ def self.dispatch(event_type, payload={})
56
+ event_type_klass = Yodeler.registrations[event_type]
57
+
58
+ # If the class wasn't registered dispatch the default class noop
59
+ if event_type_klass.nil?
60
+ # Register it the first time
61
+ Yodeler.register(:noop) if Yodeler.registrations[:noop].nil?
62
+
63
+ event_type_klass = Yodeler::EventType::NoopEventType
64
+ end
65
+
66
+ started_at = nil
67
+ finished_at = nil
68
+
69
+ if block_given?
70
+ started_at = Time.now
71
+ yield payload
72
+ finished_at = Time.now
73
+ end
74
+
75
+ event_type_klass.yodel!({
76
+ started_at: started_at,
77
+ finished_at: finished_at,
78
+ payload: payload
79
+ })
80
+ end
81
+
82
+ # Clears all the registrations and removes the EventType classes
83
+ def self.flush_registrations!
84
+ Yodeler.registrations.keys.each do |event_type|
85
+ Yodeler.undefine_event_type(event_type)
86
+ end
87
+ Yodeler.registrations = {}
88
+ end
89
+
90
+ private
91
+ def self.event_type_class_name(event_type)
92
+ "#{event_type.to_s.classify}EventType"
93
+ end
94
+
95
+ def self.undefine_event_type(event_type)
96
+ event_klass = Yodeler.registrations[event_type]
97
+ if event_klass && ( defined?( event_klass ) == 'constant' )
98
+ Yodeler::EventType.instance_eval{
99
+ remove_const( event_type.to_s.classify.to_sym )
100
+ }
101
+ end
102
+ end
103
+
104
+ def self.define_event_type(event_type)
105
+ undefine_event_type(event_type)
106
+
107
+ # Define an ancestor event type of Yodeler::EventType::Base and
108
+ # add AS Configurable to it
109
+ new_event_type_class = Class.new(Yodeler::EventType::Base) do
110
+ end
111
+
112
+ # Define the event type class, e.g.: Yodeler::EventType::UserViewedEventType
113
+ Yodeler::EventType.const_set(Yodeler.event_type_class_name(event_type), new_event_type_class)
114
+ end
115
+
116
+ # def self.cleanup!(hard=false)
117
+ # # defunct or delete any event_types, et al that aren't currently registered
118
+ # if hard
119
+ # else
120
+ # end
121
+ # end
122
+ end
metadata ADDED
@@ -0,0 +1,222 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yodeler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Cory O'Daniel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: railties
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-rails
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: factory_girl_rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: generator_spec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: shoulda-matchers
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: simplecov
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - '>='
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: database_cleaner
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - '>='
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description:
168
+ email:
169
+ - yodel@coryodaniel.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - MIT-LICENSE
175
+ - README.md
176
+ - Rakefile
177
+ - app/assets/javascripts/yodeler/application.js
178
+ - app/assets/stylesheets/yodeler/application.css
179
+ - app/controllers/yodeler/application_controller.rb
180
+ - app/helpers/yodeler/application_helper.rb
181
+ - app/views/layouts/yodeler/application.html.erb
182
+ - config/routes.rb
183
+ - lib/generators/yodeler/USAGE
184
+ - lib/generators/yodeler/install_generator.rb
185
+ - lib/generators/yodeler/templates/initializer.rb
186
+ - lib/generators/yodeler/templates/migration.rb
187
+ - lib/tasks/yodeler_tasks.rake
188
+ - lib/yodeler.rb
189
+ - lib/yodeler/configuration.rb
190
+ - lib/yodeler/engine.rb
191
+ - lib/yodeler/listens_to_yodler.rb
192
+ - lib/yodeler/models/event.rb
193
+ - lib/yodeler/models/event_type.rb
194
+ - lib/yodeler/models/event_types/base.rb
195
+ - lib/yodeler/models/event_types/noop_event_type.rb
196
+ - lib/yodeler/models/notification.rb
197
+ - lib/yodeler/models/subscription.rb
198
+ - lib/yodeler/version.rb
199
+ homepage: http://github.com/coryodaniel/yodeler
200
+ licenses: []
201
+ metadata: {}
202
+ post_install_message:
203
+ rdoc_options: []
204
+ require_paths:
205
+ - lib
206
+ required_ruby_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - '>='
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - '>='
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ requirements: []
217
+ rubyforge_project: yodeler
218
+ rubygems_version: 2.2.2
219
+ signing_key:
220
+ specification_version: 4
221
+ summary: Subscribable database-stored event notifications for Rails
222
+ test_files: []