Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifiers #8

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--require spec_helper
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ rvm:
- 2.1
- 2.2
- 2.3.0
- rbx-2
- ruby-head
matrix:
allow_failures:
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ gem 'rspec'
gem 'guard'
gem 'guard-rspec'
gem 'coveralls', :require => false
gem 'pry-byebug'
gem 'rubocop'
83 changes: 81 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ ErrbitPlugins are Ruby gems that extend the functionality of Errbit. To get
started, create a Ruby gem and add 'errbit_plugin' as a dependency in your
gem's gemspec.

Now you can start adding plugins. At the moment, there is only one kind of
plugin you can create, and that is the issue tracker.
Now you can start adding plugins. Keep reading to learn about what you can do
with Errbit plugins.

### Issue Trackers
An issue tracker plugin is a Ruby class that enables you to link errors within
Expand Down Expand Up @@ -122,7 +122,86 @@ class MyIssueTracker < ErrbitPlugin::IssueTracker
end
```

### Notifiers
A Notifier is a Ruby class that can notify an external system when Errbit
receives notices. When configuring an app within the Errbit UI, you can choose
to enable a notifier which will delegate the business of sending notifications
to the selected notifier.

Your notifier plugin is responsible for implementing the interface defined by
ErrbitPlugin::Notifier. All of the required methods must be implemented and the
class must extend ErrbitPlugin::Notifier. Here's an example:
```ruby
class MyNotifier < ErrbitPlugin::Notifier

# A unique label for your notifier plugin used internally by errbit
def self.label
'my-notifier'
end

def self.note
'a note about this notifier that users will see in the UI'
end

# Form fields that will be presented to the administrator when setting up
# or editing the errbit app. The values we collect will be availble for use
# later when its time to notify.
def self.fields
{
username: {
placeholder: "Some placeholder text"
},
password: {
placeholder: "Some more placeholder text"
}
}
end

# Icons to display during user interactions with this notifier. This method
# should return a hash of two-tuples, the key names being 'create', 'goto',
# and 'inactive'. The two-tuples should contain the icon media type and the
# binary icon data.
def self.icons
@icons ||= {
create: [ 'image/png', File.read('./path/to/create.png') ],
goto: [ 'image/png', File.read('./path/to/goto.png') ],
inactive: [ 'image/png', File.read('./path/to/inactive.png') ],
}
end

# If this notifier can be in a configured or non-configured state, you can let
# errbit know by returning a Boolean here
def configured?
# In this case, we'll say this notifier is configured when username
# is set
!!params['username']
end

# Called to validate user input. Just return a hash of errors if there are
# any
def errors
if @params['field_one']
{}
else
{ :field_one, 'Field One must be present' }
end
end

# notify is called when Errbit decides its time to notify external systems
# about a new error notice. notify receives an instance of the notice's problem
# which you can interrogate for any information you'd like to include in the
# notification message.
def notify(problem)
# Send a notification! Use HTTP, SMTP, FTP, RabbitMQ or whatever you want
# to notify your external system.
end

# The URL for your remote issue tracker
def url
'http://some-remote-tracker.com'
end
end
```

## Contributing

Expand Down
12 changes: 7 additions & 5 deletions lib/errbit_plugin.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "errbit_plugin/version"
require "errbit_plugin/registry"
require "errbit_plugin/issue_tracker"
require "errbit_plugin/validate_issue_tracker"
require "errbit_plugin/issue_trackers/none"
require 'errbit_plugin/version'
require 'errbit_plugin/registry'
require 'errbit_plugin/issue_tracker'
require 'errbit_plugin/validate_issue_tracker'
require 'errbit_plugin/notifier'
require 'errbit_plugin/validate_notifier'
require 'errbit_plugin/issue_trackers/none'
10 changes: 10 additions & 0 deletions lib/errbit_plugin/notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module ErrbitPlugin
# abstract class for issue trackers
class Notifier
attr_reader :options

def initialize(options)
@options = options
end
end
end
27 changes: 27 additions & 0 deletions lib/errbit_plugin/notifiers/none.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module ErrbitPlugin
class NoneNotifier < Notifier
LABEL = 'none'.freeze
NOTE = 'No notifications'.freeze

def self.label; LABEL; end
def self.note; NOTE; end
def self.fields; {}; end
def self.icons
@icons ||= {
create: ['image/png', read_static_file('none_create.png')],
goto: ['image/png', read_static_file('none_create.png')],
inactive: ['image/png', read_static_file('none_inactive.png')],
}
end
def self.read_static_file(file)
File.read(File.expand_path(File.join(
File.dirname(__FILE__), '..', '..', '..', 'static', file)))
end
def configured?; false; end
def errors; {}; end
def url; ''; end
def notify(*); true; end
end
end

ErrbitPlugin::Registry.add_notifier(ErrbitPlugin::NoneNotifier)
42 changes: 34 additions & 8 deletions lib/errbit_plugin/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,56 @@ class AlreadyRegisteredError < StandardError; end

module Registry
@issue_trackers = {}
@notifiers = {}

def self.add_issue_tracker(klass)
validate = ValidateIssueTracker.new(klass)

unless validate.valid?
raise IncompatibilityError.new(validate.errors.join('; '))
end

key = klass.label

if issue_trackers.has_key?(key)
raise AlreadyRegisteredError,
"issue_tracker '#{key}' already registered"
end

validate = ValidateIssueTracker.new(klass)

if validate.valid?
@issue_trackers[key] = klass
else
raise IncompatibilityError.new(validate.errors.join('; '))
end
@issue_trackers[key] = klass
end

def self.clear_issue_trackers
@issue_trackers = {}
@issue_trackers.clear
end

def self.issue_trackers
@issue_trackers
end

def self.add_notifier(klass)
validate = ValidateNotifier.new(klass)

unless validate.valid?
raise IncompatibilityError.new(validate.errors.join('; '))
end

key = klass.label

if notifiers.has_key?(key)
raise AlreadyRegisteredError,
"notifier '#{key}' already registered"
end

@notifiers[key] = klass
end

def self.clear_notifiers
@notifiers.clear
end

def self.notifiers
@notifiers
end
end
end
62 changes: 62 additions & 0 deletions lib/errbit_plugin/validate_notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
module ErrbitPlugin
class ValidateNotifier
def initialize(klass)
@klass = klass
@errors = []
end
attr_reader :errors

def valid?
valid_inherit = good_inherit?
valid_instance_methods = implements_instance_methods?
valid_class_methods = implements_class_methods?

valid_inherit && valid_instance_methods && valid_class_methods
end

private

def good_inherit?
unless @klass.ancestors.include?(ErrbitPlugin::Notifier)
add_errors(:not_inherited)
false
else
true
end
end

def implements_instance_methods?
impl = [:configured?, :errors, :notify, :url].map do |method|
if instance.respond_to?(method)
true
else
add_errors(:instance_method_missing, method)
false
end
end

impl.all? { |value| value == true }
end

def implements_class_methods?
impl = [:label, :fields, :note, :icons].map do |method|
if @klass.respond_to?(method)
true
else
add_errors(:class_method_missing, method)
false
end
end

impl.all? { |value| value == true }
end

def instance
@instance ||= @klass.new({})
end

def add_errors(key, value=nil)
@errors << [key, value].compact
end
end
end
9 changes: 9 additions & 0 deletions spec/errbit_plugin/notifier_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
describe ErrbitPlugin::Notifier do
describe '#initialize' do
it 'stores options' do
opts = { one: 'two' }
obj = described_class.new(opts)
expect(obj.options).to eq(opts)
end
end
end
Loading