Class: FlowMonitor::Controller

Inherits:
Object
  • Object
show all
Defined in:
lib/libcfruby/flowmonitor.rb

Overview

The Controller class provides an encapsulation and message passing mechanism that can be used to add fine-grained flow control and logging facilities to a library. For general use a library defines one or more Controller objects that calling programs register Observer (interface) objects to. The observers are called in order of registration and are told, via Message objects, what the library intends to do at a given point, as well as given a chance to override the action.

Instance Method Summary collapse

Constructor Details

#initializeController

Returns a new instance of Controller.



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/libcfruby/flowmonitor.rb', line 30

def initialize()
	# Array to hold registered observers
	@observers = Array.new()
	
	# simple mutex for handling multi-threaded access.  NOTE: the mutex is
	# currently only used to handle registering and unregistering observers.
	# To avoid overhead messages are sent without the aid of the mutex.  This
	# could mean that an unregistered observer *could* get an extra message or
	# two from another thread during the unregistering process.
	@mutex = Mutex.new()
end

Instance Method Details

#attempt(intention, *tags, &block) ⇒ Object

attempt is called with an intention str, a list of tags, and a code block which, if run, would carry out the intention. The intention and the tags are bundled into a Message object and passed, in order, to any listening Observer objects, which may then abort the block. If all the listening Observers allow, the block is run and is assumed successful unless an Exception is raised. Exceptions raised are first passed as a message to the Observer and are then allowed to bubble up through their normal paths.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/libcfruby/flowmonitor.rb', line 83

def attempt(intention, *tags, &block)
	tags.flatten!
	if(!tags.kind_of?(Array))
		tags = Array.[](tags)
	end

	# build the message object
	if(send_message(build_message('intention', "#{intention} - attempt", tags)))
		done = false
		reason = catch(:attemptabort) {
			begin
				yield(block)
			rescue Exception
				exception("Error handling attempt \"#{intention}\" - #{$!.to_s}", ['error', 'attempt'])
				# we've passed it on, re-raise it
				raise($!)
			end
			tags << 'attempt'
			tags << 'attemptdone'
			inform('info', "#{intention} - done", tags)
			done = true
		}

		# if we bailed we should send a message explaining why
		if(!done)
			if(!reason)
				reason = "no reason given"
			end
			tags << 'attempt'
			tags << 'attemptabort'
			inform('debug', "#{intention} - aborted - #{reason}", tags)
		end
	else
		tags << 'attempt'
		tags << 'attemptpreempt'
		inform('verbose', "#{intention} - pre-empted", tags)
	end
end

#attempt_abort(reason = "no reason given") ⇒ Object

helper method to make it easier to understand the attempt flow control. Causes an attempt to preemptively return with an optional message. May do unpredicatable and unpleasant things if called outside of an attempt block.



126
127
128
# File 'lib/libcfruby/flowmonitor.rb', line 126

def attempt_abort(reason="no reason given")
	throw(:attemptabort, reason)
end

#build_message(type, message, *tags) ⇒ Object

builds a message object from the given components



149
150
151
152
153
154
155
156
157
# File 'lib/libcfruby/flowmonitor.rb', line 149

def build_message(type, message, *tags)
	tags.flatten!
	message = Message.new(type, message)
	tags.each() { |t|
		message.add_tag(t)
	}
	
	return(message)
end

#exception(message, *tags) ⇒ Object

A helper function to send an exception message



139
140
141
142
143
144
145
# File 'lib/libcfruby/flowmonitor.rb', line 139

def exception(message, *tags)
	tags.flatten!
	if(!tags.include?('error'))
		tags << 'error'
	end
	send_message(build_message('exception', message, tags))
end

#inform(level, message, *tags) ⇒ Object

A helper function to send a simple informative message with a named level



132
133
134
135
# File 'lib/libcfruby/flowmonitor.rb', line 132

def inform(level, message, *tags)
	tags.flatten!
	send_message(build_message(level.to_s, message, tags))
end

#register(observer) ⇒ Object

registers the observer with this Controller. observer is expected to implement the Observer interface.



45
46
47
48
49
50
51
52
53
# File 'lib/libcfruby/flowmonitor.rb', line 45

def register(observer)
	if(!observer.respond_to?(:handle_message))
		raise(FlowControllerInterfaceError, "observer does not implement a message_handler method")
	end
	
	@mutex.synchronize {
		@observers << observer
	}
end

#send_message(message) ⇒ Object

sends a message to all the registered observers until one of them returns false or all of them have been sent. Returns false if any observer returns false or true if it sends the message to all of them without receiving a false in return.



163
164
165
166
167
168
169
170
171
# File 'lib/libcfruby/flowmonitor.rb', line 163

def send_message(message)
	@observers.each() { |observer|
		if(observer.handle_message(message) == false)
			return(false)
		end
	}
	
	return(true)
end

#unregister(observer) ⇒ Object

Unregisters a listening observer



57
58
59
60
61
62
63
64
65
# File 'lib/libcfruby/flowmonitor.rb', line 57

def unregister(observer)
	@mutex.synchronize {
		@observers.each_index() { |i|
			if(@observers[i] === observer)
				@observers.delete_at(i)
			end
		}
	}
end

#unregister_allObject

Clears all listening observers



69
70
71
72
73
# File 'lib/libcfruby/flowmonitor.rb', line 69

def unregister_all()
	@mutex.synchronize() {
		@observers = Array.new()
	}
end