benotified 0.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.
@@ -0,0 +1,34 @@
1
+ module BeNotified
2
+ module Commands
3
+ # Extend Fixnum class and add some handy methods
4
+ #
5
+ # For example:
6
+ # size_of_file('file.log') > 4.MB
7
+ class ::Fixnum
8
+ def KB
9
+ self * 1000
10
+ end
11
+
12
+ def MB
13
+ self * 1000 * 1000
14
+ end
15
+
16
+ def GB
17
+ self * 1000 * 1000 * 1000
18
+ end
19
+ end
20
+ # Get size of the file
21
+ #
22
+ # file - The String with file name
23
+ #
24
+ # For example:
25
+ #
26
+ # size_of_file('abc.txt')
27
+ #
28
+ # Returns Integer value, size of the file
29
+ def size_of_file(file)
30
+ raise ArgumentError if ! File.exists?(file)
31
+ File.size(file)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,91 @@
1
+ require 'json'
2
+
3
+ module BeNotified
4
+ module Configuration
5
+
6
+ CONFIG_FILE = "#{ENV['HOME']}/.be_notified"
7
+
8
+ # Singleton storing configuration for this library
9
+ class << self
10
+ # Main configuration method. It merges default configuration and properties
11
+ # read from the configuration file. Then it does basic validation
12
+ #
13
+ # Returns Hash with options
14
+ def options
15
+ @options ||= begin
16
+ opts = default_options.merge!(config_file_options)
17
+ raise ArgumentError, "Invalid configuration options for emails #{opts}" if ! email_options_valid?(opts)
18
+ opts
19
+ end
20
+ end
21
+
22
+ # Set of default options
23
+ #
24
+ # Returns Hash with default options
25
+ def default_options
26
+ {
27
+ :notifier_type => BeNotified::Notifiers::Log,
28
+ :email => Hash.new { |k,h| k[h] = {}}
29
+ }
30
+ end
31
+
32
+ # Read configuration from the file.
33
+ #
34
+ # Returns options in JSON format if everything goes fine
35
+ # Returns empty string in case of error
36
+ def load_config_file
37
+ File.open(CONFIG_FILE).read
38
+ rescue Errno::ENOENT
39
+ ""
40
+ end
41
+
42
+ # Parse options from configuration file
43
+ #
44
+ # Returns hash with options
45
+ # Returns empty hash in case of parse exception
46
+ # Returns empty hash if there were problems with reading the file
47
+ def config_file_options
48
+ # Make sure that keys are symbols
49
+ opts = load_config_file
50
+ opts == "" ? {} : JSON.parse(opts).symbolize_keys!
51
+
52
+ rescue JSON::ParserError => e
53
+ puts "Error while parsing the configuration file: #{e.message}"
54
+ {}
55
+ end
56
+
57
+ # Validate email properties
58
+ #
59
+ # opts - Hash with options
60
+ #
61
+ # Returns true if all options exists and are not empty
62
+ # Returns true if notifier_type is pointing to use emails
63
+ def email_options_valid?(opts)
64
+ # We don't really care about email validation if not using emails for notifications
65
+ return true if ! email_notifier?(opts)
66
+
67
+ opts[:email].symbolize_keys!
68
+ opts[:email].key?(:smtp_address) && opts[:email][:smtp_address] != "" &&
69
+ opts[:email].key?(:smtp_port) && opts[:email][:smtp_port] != "" &&
70
+ opts[:email].key?(:domain) && opts[:email][:domain] != "" &&
71
+ opts[:email].key?(:username) && opts[:email][:username] != "" &&
72
+ opts[:email].key?(:password) && opts[:email][:password] != "" &&
73
+ opts[:email].key?(:to) && opts[:email][:to] != "" &&
74
+ opts[:email].key?(:from) && opts[:email][:from] != "" &&
75
+ opts[:email].key?(:subject) && opts[:email][:subject] != ""
76
+ end
77
+
78
+ # When we parse configuration file, notifier_type is a String
79
+ # However, when we define them in the code, usually we use a class type
80
+ def email_notifier?(opts)
81
+ opts[:notifier_type] == BeNotified::Notifiers::Email || opts[:notifier_type] == "BeNotified::Notifiers::Email"
82
+ end
83
+ end
84
+
85
+ # Easier and nicer? way for classes that include this module
86
+ # to access the configuration options
87
+ def options
88
+ Configuration.options
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,77 @@
1
+ require 'be_notified/notifier'
2
+ require 'be_notified/commands'
3
+
4
+ module BeNotified
5
+ class Monitor
6
+ # Include all available commands and configuration
7
+ include Commands
8
+ include Configuration
9
+
10
+ def initialize(&block)
11
+ # Provide a nice way of accessing methods in this class
12
+ instance_eval(&block)
13
+ end
14
+
15
+ # Main method provided by the library
16
+ # Will send the message to the user when block returns true
17
+ #
18
+ # message - The String with the message send back to user
19
+ # block - Block with a THING to monitor
20
+ #
21
+ # For example:
22
+ #
23
+ # alert_when("block returns true") { true }
24
+ #
25
+ # Returns nothing.
26
+ def alert_when(message, &block)
27
+ if block.call == true
28
+ notify(message)
29
+ end
30
+ end
31
+
32
+ # Get notifier type form configuration
33
+ def notifier_type
34
+ options[:notifier_type]
35
+ end
36
+
37
+ # It is possible to define some configuraiton options
38
+ # directly in the code (besides configuration file)
39
+ #
40
+ # opts - The Hash with options. Currently available:
41
+ # {
42
+ # :logger_file => Location of the file where application sends log
43
+ # :notifier_type => Notifier type. Currenty available: BeNotified::Notifiers::Log, BeNotified::Notifiers::Email
44
+ # :email => {
45
+ # :smtp_address => SMTP address
46
+ # :smtp_port => SMTP port
47
+ # :domain => Domain name, eg: example.com
48
+ # :username => Login
49
+ # :password => Password
50
+ # :to => Recipient
51
+ # :from => Sender
52
+ # :subject => Subject of the message
53
+ # }
54
+ #
55
+ # For example:
56
+ # configuration({ :notifier_type => BeNotified::Notifiers::Email })
57
+ #
58
+ # Returns Hash, merged options from configuration file and defined in the class
59
+ def configuration(opts)
60
+ options.merge!(opts)
61
+ end
62
+ # Method responsible for notifing the user. Shouldn't be called directly
63
+ # It gets notifier_type, creates a proper notifier and notifies the user
64
+ #
65
+ # message - The String with the message send back to user
66
+ #
67
+ # For example:
68
+ #
69
+ # notify("too many files in this directory")
70
+ #
71
+ # Returns nothing.
72
+ def notify(message)
73
+ notifier = BeNotified::Notifier.new(notifier_type, message)
74
+ notifier.notify
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,104 @@
1
+ require 'log4r'
2
+ require 'action_mailer'
3
+
4
+ module BeNotified
5
+ # Delegate notification to other classes
6
+ class Notifier
7
+ # Initializer requires two arguments:
8
+ #
9
+ # notifier_type - String or a Class represending notifier, can be:
10
+ # BeNotified::Notifiers::Log (default)
11
+ # BeNotified::Notifiers::Email
12
+ # message - The String, message send to the user
13
+ def initialize(notifier_type, message)
14
+ @notifier = notifier_type.class === "String" ? eval("#{notifier_type}.new") : notifier_type.new
15
+ @message = message
16
+ end
17
+
18
+ # This method will actually inform the user about the problem
19
+ #
20
+ # message - The String with the message that should be passed to user
21
+ #
22
+ # For example:
23
+ # notify("Server is down")
24
+ #
25
+ # Returns nothing
26
+ def notify
27
+ @notifier.notify(@message)
28
+ end
29
+ end
30
+
31
+
32
+ module Notifiers
33
+
34
+ # This class will send notifications by email.
35
+ # It is using ActionMailer >= 3.0.0
36
+ class Email
37
+ include BeNotified::Configuration
38
+
39
+ # Delegated method from BeNotified::Notifier class
40
+ def notify(message)
41
+ raise ArgumentError, "You can't use Email notification when Log is set in your configuration" if options[:notifier_type] == BeNotified::Notifiers::Log
42
+ Mailer.logger = Logger.new(STDOUT)
43
+
44
+ Mailer.smtp_settings = {
45
+ :address => options[:email][:smtp_address],
46
+ :port => options[:email][:smtp_port].to_i,
47
+ :domain => options[:email][:domain],
48
+ :user_name => options[:email][:username],
49
+ :password => options[:email][:password],
50
+ :authentication => 'plain',
51
+ :enable_starttls_auto => true }
52
+
53
+ Mailer.email(message, options).deliver
54
+ end
55
+
56
+ # Inner class responsible for sending emails
57
+ class Mailer < ActionMailer::Base
58
+
59
+ def email(message, options)
60
+ mail_options = {
61
+ :to => options[:email][:to],
62
+ :from => options[:email][:from],
63
+ :subject => options[:email][:subject]
64
+ }
65
+
66
+ mail(mail_options) do |format|
67
+ # Create the body of the message
68
+ format.text { render :text => "#{message}"}
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ # This class will notify the user by writing the message to log
75
+ # Requires Log4r in any version
76
+ class Log
77
+ include Log4r
78
+ include BeNotified::Configuration
79
+
80
+ # Delegated method from BeNotified::Notifier class
81
+ def notify(message)
82
+ logger.warn "#{message}"
83
+ end
84
+
85
+ # This method creates logger instance. Depending on configuration it will
86
+ # either log to the STDOUT or to the log file.
87
+ #
88
+ # Returns instance of logger
89
+ def logger
90
+ @logger ||= begin
91
+
92
+ logger = Logger.new 'notify_logger'
93
+ outputter = options[:logger_file].nil? ? Outputter.stdout
94
+ : FileOutputter.new('notify_logger', :filename => options[:logger_file])
95
+
96
+ logger.outputters << outputter
97
+ logger.level = Log4r::WARN
98
+
99
+ logger
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,3 @@
1
+ module BeNotified
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ $:.push File.dirname(__FILE__)
2
+
3
+ require 'be_notified'
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+
4
+ class TestHostNotAlive < Test::Unit::TestCase
5
+ include BeNotified::Commands
6
+
7
+ def setup
8
+ @packets = 1
9
+ end
10
+
11
+ def test_host_is_up
12
+ expects(:run_system).with('ping -c 1 example.com').returns(PING_SUCCESS)
13
+ assert ! host_not_alive?('example.com', @packets )
14
+ end
15
+
16
+ def test_host_is_down_timeout
17
+ expects(:run_system).with('ping -c 1 example.com').returns(PING_FAIL)
18
+ assert host_not_alive?('example.com', @packets)
19
+ end
20
+
21
+ PING_SUCCESS =<<-END
22
+ 64 bytes from example.com: icmp_seq=0 ttl=64 time=0.033 ms
23
+ END
24
+
25
+ PING_FAIL =<<-END
26
+ Request timeout for icmp_seq 0
27
+ END
28
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class TestCommands < Test::Unit::TestCase
4
+ include BeNotified::Commands
5
+
6
+ def setup
7
+ files = mock
8
+ files.stubs(:each).multiple_yields('test1.rb', 'test2.rb', 'test.txt')
9
+
10
+ Dir.stubs(:glob).returns(files)
11
+ end
12
+
13
+ def test_number_of_files_in_directory
14
+ File.stubs(:file?).returns(true)
15
+
16
+ assert_equal 3, number_of_files('.')
17
+ end
18
+
19
+ def test_number_of_files_in_directory_with_given_mask
20
+ File.stubs(:file?).returns(true)
21
+
22
+ assert_equal(2, number_of_files('.', /\.rb$/))
23
+ end
24
+
25
+ def test_number_of_files_in_directory_that_doesnt_exist
26
+ File.stubs(:exists?).returns(false)
27
+
28
+ assert_raise ArgumentError do
29
+ number_of_files('.')
30
+ end
31
+ end
32
+
33
+ def test_number_of_files_in_directory_with_mask
34
+ File.stubs(:exists?).returns(true)
35
+
36
+ assert_raise ArgumentError do
37
+ number_of_files('.', "")
38
+ end
39
+ end
40
+
41
+ def test_number_of_files_returns_zero_if_there_is_no_files_in_directory
42
+ File.stubs(:file?).returns(false)
43
+
44
+ assert_equal 0, number_of_files(".")
45
+ end
46
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ class TestProgramRunning < Test::Unit::TestCase
4
+ include BeNotified::Commands
5
+
6
+ def test_program_not_running
7
+ expects(:run_system).with("ps -ef | grep -i Foobar | grep -v grep").returns("")
8
+ assert_equal true, program_not_running?('Foobar')
9
+ end
10
+
11
+ def test_program_running
12
+ expects(:run_system).with("ps -ef | grep -i Dock | grep -v grep").returns(PS_OUT)
13
+ assert_equal true, program_running?("Dock")
14
+ end
15
+
16
+ def test_raising_error_on_windows
17
+ expects(:running_on_windows?).returns(true)
18
+
19
+ assert_raise RuntimeError do
20
+ program_running?("Dock")
21
+ end
22
+ end
23
+
24
+ PS_OUT =<<-END
25
+ 502 101 97 0 0:08.43 ?? 0:44.19 /System/Library/CoreServices/Dock.app/Contents/MacOS/Dock
26
+ END
27
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ class TestSizeOfFile < Test::Unit::TestCase
4
+ include BeNotified::Commands
5
+
6
+ def test_file_doesnt_exist
7
+ File.expects(:exists?).returns(false)
8
+
9
+ assert_raise ArgumentError do
10
+ size_of_file("test.txt")
11
+ end
12
+ end
13
+
14
+ def test_file_size
15
+ File.expects(:exists?).returns(true)
16
+ File.expects(:size).returns(200)
17
+
18
+ assert_equal(200, size_of_file("test.txt"))
19
+ end
20
+
21
+ def test_file_size_in_KB
22
+ assert_equal(4_000, 4.KB)
23
+ end
24
+
25
+ def test_file_size_in_MB
26
+ assert_equal(4_000_000, 4.MB)
27
+ end
28
+
29
+ def test_file_size_in_GB
30
+ assert_equal(4_000_000_000, 4.GB)
31
+ end
32
+ end