benotified 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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