harbinger 0.0.1.pre → 0.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/.gitignore +3 -0
 - data/.hound.yml +818 -0
 - data/.travis.yml +20 -0
 - data/Gemfile +23 -3
 - data/README.md +8 -27
 - data/Rakefile +47 -1
 - data/app/controllers/harbinger/messages_controller.rb +24 -0
 - data/app/models/harbinger/database_channel_message.rb +51 -0
 - data/app/models/harbinger/database_channel_message_element.rb +19 -0
 - data/app/views/harbinger/messages/index.html.erb +43 -0
 - data/app/views/harbinger/messages/show.html.erb +24 -0
 - data/config/routes.rb +3 -0
 - data/db/migrate/20140310185338_create_harbinger_database_channel_message.rb +11 -0
 - data/db/migrate/20140310185339_create_harbinger_database_channel_message_elements.rb +14 -0
 - data/gemfiles/rails4.1.gemfile +12 -0
 - data/gemfiles/rails4.gemfile +13 -0
 - data/harbinger.gemspec +22 -7
 - data/lib/generators/harbinger/install/install_generator.rb +23 -0
 - data/lib/generators/harbinger/install/templates/harbinger_initializer.rb.erb +6 -0
 - data/lib/harbinger.rb +100 -1
 - data/lib/harbinger/channels.rb +25 -0
 - data/lib/harbinger/channels/database_channel.rb +15 -0
 - data/lib/harbinger/channels/logger_channel.rb +31 -0
 - data/lib/harbinger/channels/null_channel.rb +7 -0
 - data/lib/harbinger/configuration.rb +58 -0
 - data/lib/harbinger/engine.rb +8 -1
 - data/lib/harbinger/exceptions.rb +4 -0
 - data/lib/harbinger/message.rb +20 -0
 - data/lib/harbinger/reporters.rb +34 -0
 - data/lib/harbinger/reporters/exception_reporter.rb +26 -0
 - data/lib/harbinger/reporters/null_reporter.rb +14 -0
 - data/lib/harbinger/reporters/request_reporter.rb +20 -0
 - data/lib/harbinger/reporters/user_reporter.rb +20 -0
 - data/lib/harbinger/version.rb +1 -1
 - data/run_each_spec_in_isolation +9 -0
 - data/script/fast_specs +20 -0
 - data/spec/controllers/harbinger/messages_controller_spec.rb +26 -0
 - data/spec/features/end_to_end_exception_handling_spec.rb +39 -0
 - data/spec/lib/harbinger/channels/database_channel_spec.rb +18 -0
 - data/spec/lib/harbinger/channels/logger_channel_spec.rb +21 -0
 - data/spec/lib/harbinger/channels/null_channel_spec.rb +8 -0
 - data/spec/lib/harbinger/channels_spec.rb +40 -0
 - data/spec/lib/harbinger/configuration_spec.rb +53 -0
 - data/spec/lib/harbinger/message_spec.rb +15 -0
 - data/spec/lib/harbinger/reporters/exception_reporter_spec.rb +24 -0
 - data/spec/lib/harbinger/reporters/null_reporter_spec.rb +21 -0
 - data/spec/lib/harbinger/reporters/request_reporter_spec.rb +23 -0
 - data/spec/lib/harbinger/reporters/user_reporter_spec.rb +17 -0
 - data/spec/lib/harbinger/reporters_spec.rb +46 -0
 - data/spec/lib/harbinger_spec.rb +60 -0
 - data/spec/models/harbinger/database_channel_message_element_spec.rb +16 -0
 - data/spec/models/harbinger/database_channel_message_spec.rb +68 -0
 - data/spec/routing/harbinger/messages_routing_spec.rb +16 -0
 - data/spec/spec_active_record_helper.rb +41 -0
 - data/spec/spec_fast_helper.rb +70 -0
 - data/spec/spec_slow_helper.rb +57 -0
 - data/spec/spec_view_helper.rb +38 -0
 - data/spec/test_app_templates/lib/generators/test_app_generator.rb +13 -0
 - data/spec/views/harbinger/messages/index.html.erb_spec.rb +31 -0
 - data/spec/views/harbinger/messages/show.html.erb_spec.rb +36 -0
 - metadata +205 -20
 - data/MIT-LICENSE +0 -20
 
    
        data/lib/harbinger.rb
    CHANGED
    
    | 
         @@ -1,5 +1,104 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require "harbinger/engine"
         
     | 
| 
      
 1 
     | 
    
         
            +
            require "harbinger/engine" if defined?(Rails)
         
     | 
| 
       2 
2 
     | 
    
         
             
            require "harbinger/version"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "harbinger/reporters"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "harbinger/channels"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "harbinger/exceptions"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "harbinger/configuration"
         
     | 
| 
       3 
7 
     | 
    
         | 
| 
       4 
8 
     | 
    
         
             
            module Harbinger
         
     | 
| 
      
 9 
     | 
    
         
            +
              module_function
         
     | 
| 
      
 10 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_writer :configuration
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def configuration
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @configuration ||= Configuration.new
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              module_function
         
     | 
| 
      
 19 
     | 
    
         
            +
              def configure
         
     | 
| 
      
 20 
     | 
    
         
            +
                yield(configuration)
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              # Responsible for building a :message from the various :contexts and then
         
     | 
| 
      
 24 
     | 
    
         
            +
              # delivering the :message to the appropriate :channels.
         
     | 
| 
      
 25 
     | 
    
         
            +
              #
         
     | 
| 
      
 26 
     | 
    
         
            +
              # @see .build_message
         
     | 
| 
      
 27 
     | 
    
         
            +
              # @see .deliver_message
         
     | 
| 
      
 28 
     | 
    
         
            +
              #
         
     | 
| 
      
 29 
     | 
    
         
            +
              # @param [Hash] options
         
     | 
| 
      
 30 
     | 
    
         
            +
              # @option options [Message] :message The message you want to amend.
         
     | 
| 
      
 31 
     | 
    
         
            +
              #   If none is provided, then one is created.
         
     | 
| 
      
 32 
     | 
    
         
            +
              # @option options [Object, Array<Object>] :contexts One or more Objects that
         
     | 
| 
      
 33 
     | 
    
         
            +
              #   Harbinger will visit and extract message elements from.
         
     | 
| 
      
 34 
     | 
    
         
            +
              # @option options [Symbol, Array<Symbol>] :channels One or more channels that
         
     | 
| 
      
 35 
     | 
    
         
            +
              #   Harbinger will deliver the :message to
         
     | 
| 
      
 36 
     | 
    
         
            +
              def call(options)
         
     | 
| 
      
 37 
     | 
    
         
            +
                message = build_message(options)
         
     | 
| 
      
 38 
     | 
    
         
            +
                deliver_message(message, options)
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              # Responsible for building a :message from the various :contexts.
         
     | 
| 
      
 42 
     | 
    
         
            +
              #
         
     | 
| 
      
 43 
     | 
    
         
            +
              # @see .call
         
     | 
| 
      
 44 
     | 
    
         
            +
              #
         
     | 
| 
      
 45 
     | 
    
         
            +
              # @param [Hash] options
         
     | 
| 
      
 46 
     | 
    
         
            +
              # @option options [Message] :message The message you want to amend.
         
     | 
| 
      
 47 
     | 
    
         
            +
              #   If none is provided, then one is created.
         
     | 
| 
      
 48 
     | 
    
         
            +
              # @option options [Object, Array<Object>] :contexts One or more Objects that
         
     | 
| 
      
 49 
     | 
    
         
            +
              #   Harbinger will visit and extract message elements from.
         
     | 
| 
      
 50 
     | 
    
         
            +
              def build_message(options = {})
         
     | 
| 
      
 51 
     | 
    
         
            +
                contexts = Array(options.fetch(:contexts)).flatten.compact
         
     | 
| 
      
 52 
     | 
    
         
            +
                message = options.fetch(:message) { default_message }
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                contexts.each { |context| reporter_for(context).accept(message) }
         
     | 
| 
      
 55 
     | 
    
         
            +
                message
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              # Responsible for delivering a :message to the appropriate :channels.
         
     | 
| 
      
 59 
     | 
    
         
            +
              #
         
     | 
| 
      
 60 
     | 
    
         
            +
              # @see .call
         
     | 
| 
      
 61 
     | 
    
         
            +
              #
         
     | 
| 
      
 62 
     | 
    
         
            +
              # @param message [Message] The message you want to amend.
         
     | 
| 
      
 63 
     | 
    
         
            +
              #   If none is provided, then one is created.
         
     | 
| 
      
 64 
     | 
    
         
            +
              # @param [Hash] options
         
     | 
| 
      
 65 
     | 
    
         
            +
              # @option options [Symbol, Array<Symbol>] :channels One or more channels that
         
     | 
| 
      
 66 
     | 
    
         
            +
              #   Harbinger will deliver the :message to
         
     | 
| 
      
 67 
     | 
    
         
            +
              def deliver_message(message, options = {})
         
     | 
| 
      
 68 
     | 
    
         
            +
                channels = options.fetch(:channels) { default_channels }
         
     | 
| 
      
 69 
     | 
    
         
            +
                Array(channels).flatten.compact.each do |channel_name|
         
     | 
| 
      
 70 
     | 
    
         
            +
                  channel = channel_for(channel_name)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  channel.deliver(message)
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
                true
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              def default_message
         
     | 
| 
      
 77 
     | 
    
         
            +
                require 'harbinger/message'
         
     | 
| 
      
 78 
     | 
    
         
            +
                Message.new
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
              private_class_method :default_message
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              def reporter_for(context)
         
     | 
| 
      
 83 
     | 
    
         
            +
                Reporters.find_for(context)
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
              private_class_method :reporter_for
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              def channel_for(name)
         
     | 
| 
      
 88 
     | 
    
         
            +
                Channels.find_for(name)
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
              private_class_method :channel_for
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              def default_channels
         
     | 
| 
      
 93 
     | 
    
         
            +
                configuration.default_channels
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
              def logger
         
     | 
| 
      
 97 
     | 
    
         
            +
                configuration.logger
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              def database_storage
         
     | 
| 
      
 101 
     | 
    
         
            +
                configuration.database_storage
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
       5 
104 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Channels
         
     | 
| 
      
 3 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 4 
     | 
    
         
            +
                def find_for(channel_name)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  channel_class_name = channel_name_for_instance(channel_name)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  if const_defined?(channel_class_name)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    const_get(channel_class_name)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  else
         
     | 
| 
      
 9 
     | 
    
         
            +
                    NullChannel
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
                rescue StandardError
         
     | 
| 
      
 12 
     | 
    
         
            +
                  NullChannel
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def channel_name_for_instance(channel_name)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  channel_name.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase } + "Channel"
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                private_class_method :channel_name_for_instance
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            # Note: I am not requiring all of the channels for this Gem. Some
         
     | 
| 
      
 23 
     | 
    
         
            +
            # implementations my want to override the defaults. Look to
         
     | 
| 
      
 24 
     | 
    
         
            +
            # `lib/harbinger/engine.rb` for how things are required for Rails applications.
         
     | 
| 
      
 25 
     | 
    
         
            +
            require 'harbinger/channels/null_channel'
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger::Channels
         
     | 
| 
      
 2 
     | 
    
         
            +
              module DatabaseChannel
         
     | 
| 
      
 3 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 4 
     | 
    
         
            +
                def deliver(message, options = {})
         
     | 
| 
      
 5 
     | 
    
         
            +
                  storage = options.fetch(:storage) { default_storage }
         
     | 
| 
      
 6 
     | 
    
         
            +
                  storage.store_message(message)
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def default_storage
         
     | 
| 
      
 10 
     | 
    
         
            +
                  Harbinger.database_storage
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
                private_class_method :default_storage
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger::Channels
         
     | 
| 
      
 2 
     | 
    
         
            +
              module LoggerChannel
         
     | 
| 
      
 3 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 4 
     | 
    
         
            +
                def deliver(message, options = {})
         
     | 
| 
      
 5 
     | 
    
         
            +
                  logger = options.fetch(:logger) { default_logger }
         
     | 
| 
      
 6 
     | 
    
         
            +
                  severity = options.fetch(:severity) { default_severity }
         
     | 
| 
      
 7 
     | 
    
         
            +
                  read(message) do |line|
         
     | 
| 
      
 8 
     | 
    
         
            +
                    logger.add(severity, line)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def read(message)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  yield("BEGIN MESSAGE OBJECT ID=#{message.object_id}")
         
     | 
| 
      
 14 
     | 
    
         
            +
                  message.attributes.each do |key, value|
         
     | 
| 
      
 15 
     | 
    
         
            +
                    yield("#{key.inspect} => #{value.inspect}")
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  yield("END MESSAGE OBJECT ID=#{message.object_id}")
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
                private_class_method :read
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def default_logger
         
     | 
| 
      
 22 
     | 
    
         
            +
                  Harbinger.logger
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
                private_class_method :default_logger
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def default_severity
         
     | 
| 
      
 27 
     | 
    
         
            +
                  5 # ::Logger::UNKNOWN
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
                private_class_method :default_severity
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,58 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'harbinger/exceptions'
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Harbinger
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Configuration
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def default_channels
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @default_channels || __default_channels
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def default_channels=(*channel_names)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @default_channels = Array(channel_names).flatten.compact.collect{|name|
         
     | 
| 
      
 11 
     | 
    
         
            +
                    word = name.to_s
         
     | 
| 
      
 12 
     | 
    
         
            +
                    word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
         
     | 
| 
      
 13 
     | 
    
         
            +
                    word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
         
     | 
| 
      
 14 
     | 
    
         
            +
                    word.tr!("-", "_")
         
     | 
| 
      
 15 
     | 
    
         
            +
                    word.downcase!
         
     | 
| 
      
 16 
     | 
    
         
            +
                    word.to_sym
         
     | 
| 
      
 17 
     | 
    
         
            +
                  }
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def logger
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @logger || default_logger
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def logger=(object)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  raise ConfigurationError.new("Expected Harbinger.database_storage to respond_to #add. #{object.inspect} does not respond to #add") unless object.respond_to?(:add)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @logger = object
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def database_storage
         
     | 
| 
      
 30 
     | 
    
         
            +
                  @database_storage || default_database_storage
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def database_storage=(object)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  raise ConfigurationError.new("Expected Harbinger.database_storage to respond_to #store_message. #{object.inspect} does not respond to #add") unless object.respond_to?(:store_message)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @database_storage = object
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                private
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def default_logger
         
     | 
| 
      
 41 
     | 
    
         
            +
                  if defined?(Rails)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    Rails.logger
         
     | 
| 
      
 43 
     | 
    
         
            +
                  else
         
     | 
| 
      
 44 
     | 
    
         
            +
                    require 'logger'
         
     | 
| 
      
 45 
     | 
    
         
            +
                    ::Logger.new(STDOUT)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def default_database_storage
         
     | 
| 
      
 50 
     | 
    
         
            +
                  require 'harbinger/database_channel_message'
         
     | 
| 
      
 51 
     | 
    
         
            +
                  DatabaseChannelMessage
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def __default_channels
         
     | 
| 
      
 55 
     | 
    
         
            +
                  [:logger]
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/harbinger/engine.rb
    CHANGED
    
    | 
         @@ -1,5 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Harbinger
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Engine < ::Rails::Engine
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
      
 3 
     | 
    
         
            +
                engine_name 'harbinger'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                config.to_prepare do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Because I don't want to auto-require all of the dependent channels
         
     | 
| 
      
 7 
     | 
    
         
            +
                  Harbinger.default_channels.each do |channel_name|
         
     | 
| 
      
 8 
     | 
    
         
            +
                    require "harbinger/channels/#{channel_name}_channel"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
       4 
11 
     | 
    
         
             
              end
         
     | 
| 
       5 
12 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Message
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :attributes
         
     | 
| 
      
 4 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @attributes = {}
         
     | 
| 
      
 6 
     | 
    
         
            +
                  yield(self) if block_given?
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def append(container, key, value)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  composite_key = "#{container}.#{key}"
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @attributes[composite_key] ||= []
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @attributes[composite_key] << value
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def contexts
         
     | 
| 
      
 16 
     | 
    
         
            +
                  attributes.keys.collect { |key| key.split('.')[0] }.uniq
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Reporters
         
     | 
| 
      
 3 
     | 
    
         
            +
                module_function
         
     | 
| 
      
 4 
     | 
    
         
            +
                def find_for(context)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  if context.respond_to?(:to_harbinger_reporter)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    context.to_harbinger_reporter
         
     | 
| 
      
 7 
     | 
    
         
            +
                  else
         
     | 
| 
      
 8 
     | 
    
         
            +
                    # @TODO - Handle inheritence; KeyError is not an Exception
         
     | 
| 
      
 9 
     | 
    
         
            +
                    reporter_class_name = reporter_name_for_instance(context)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    if const_defined?(reporter_class_name)
         
     | 
| 
      
 11 
     | 
    
         
            +
                      const_get(reporter_class_name).new(context)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    else
         
     | 
| 
      
 13 
     | 
    
         
            +
                      NullReporter.new(context)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                rescue StandardError
         
     | 
| 
      
 17 
     | 
    
         
            +
                  NullReporter.new(context)
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def reporter_name_for_instance(context)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if context.is_a?(Exception)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    "ExceptionReporter"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  else
         
     | 
| 
      
 24 
     | 
    
         
            +
                    context.class.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase } + "Reporter"
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
                private_class_method :reporter_name_for_instance
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            require "harbinger/reporters/user_reporter"
         
     | 
| 
      
 32 
     | 
    
         
            +
            require "harbinger/reporters/request_reporter"
         
     | 
| 
      
 33 
     | 
    
         
            +
            require "harbinger/reporters/exception_reporter"
         
     | 
| 
      
 34 
     | 
    
         
            +
            require "harbinger/reporters/null_reporter"
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger::Reporters
         
     | 
| 
      
 2 
     | 
    
         
            +
              class ExceptionReporter
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :exception
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(exception)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @exception = exception
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def accept(message)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  if exception.respond_to?(:class)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    message.append('exception', 'class_name', exception.class.to_s)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  if exception.respond_to?(:backtrace)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    message.append('exception', 'backtrace', Array(exception.backtrace).join("\n"))
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  if exception.respond_to?(:message)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    message.append('exception', 'message', exception.message)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Reporters
         
     | 
| 
      
 3 
     | 
    
         
            +
                class RequestReporter
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :request, :method_names
         
     | 
| 
      
 5 
     | 
    
         
            +
                  private :method_names
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(request, config = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @request = request
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @method_names = config.fetch(:method_names) { ['path', 'params', 'user_agent'] }
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def accept(message)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    method_names.each do |method_name|
         
     | 
| 
      
 13 
     | 
    
         
            +
                      if request.respond_to?(method_name)
         
     | 
| 
      
 14 
     | 
    
         
            +
                        message.append('request', method_name, request.public_send(method_name))
         
     | 
| 
      
 15 
     | 
    
         
            +
                      end
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Harbinger::Reporters
         
     | 
| 
      
 2 
     | 
    
         
            +
              class UserReporter
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :user, :method_names
         
     | 
| 
      
 4 
     | 
    
         
            +
                private :method_names
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def initialize(user, config = {})
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @user = user
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @method_names = config.fetch(:method_names) { ['username'] }
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def accept(message)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  method_names.each do |method_name|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    if user.respond_to?(method_name)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      message.append('user', method_name, user.public_send(method_name))
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/harbinger/version.rb
    CHANGED
    
    
    
        data/script/fast_specs
    ADDED
    
    | 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby -w
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'rake'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Fast
         
     | 
| 
      
 6 
     | 
    
         
            +
              module_function
         
     | 
| 
      
 7 
     | 
    
         
            +
              def spec?(fn)
         
     | 
| 
      
 8 
     | 
    
         
            +
                open(fn) { |f| f.gets =~ /fast_helper/ }
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            fast_specs = FileList['spec/**/*_spec.rb'].select { |fn|
         
     | 
| 
      
 13 
     | 
    
         
            +
              Fast.spec?(fn)
         
     | 
| 
      
 14 
     | 
    
         
            +
            }
         
     | 
| 
      
 15 
     | 
    
         
            +
            if fast_specs.any?
         
     | 
| 
      
 16 
     | 
    
         
            +
              system "rspec #{fast_specs}"
         
     | 
| 
      
 17 
     | 
    
         
            +
            else
         
     | 
| 
      
 18 
     | 
    
         
            +
              puts "Unable to find any fast specs"
         
     | 
| 
      
 19 
     | 
    
         
            +
              exit(-1)
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     |