Module: Cosell

Defined in:
lib/cosell.rb,
lib/cosell/announcer.rb

Constant Summary collapse

VERSION =

:stopdoc:

'0.0.2'
LIBPATH =
::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
PATH =
::File.dirname(LIBPATH) + ::File::SEPARATOR

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.libpath(*args) ⇒ Object

Returns the library path for the module. If any arguments are given, they will be joined to the end of the libray path using File.join.



18
19
20
# File 'lib/cosell.rb', line 18

def self.libpath( *args )
  args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
end

.path(*args) ⇒ Object

Returns the lpath for the module. If any arguments are given, they will be joined to the end of the path using File.join.



26
27
28
# File 'lib/cosell.rb', line 26

def self.path( *args )
  args.empty? ? PATH : ::File.join(PATH, args.flatten)
end

.require_all_libs_relative_to(fname, dir = nil) ⇒ Object

Utility method used to require all files ending in .rb that lie in the directory below this file that has the same name as the filename passed in. Optionally, a specific directory name can be passed in such that the filename does not have to be equivalent to the directory.



35
36
37
38
39
40
41
# File 'lib/cosell.rb', line 35

def self.require_all_libs_relative_to( fname, dir = nil )
  dir ||= ::File.basename(fname, '.*')
  search_me = ::File.expand_path(
      ::File.join(::File.dirname(fname), dir, '**', '*.rb'))

  Dir.glob(search_me).sort.each {|rb| require rb}
end

.versionObject

Returns the version string for the library.



10
11
12
# File 'lib/cosell.rb', line 10

def self.version
  VERSION
end

Instance Method Details

#announce(announcement) ⇒ Object

If queue_announcements? true, puts announcement in a Queue. Otherwise, calls announce_now! Queued announcements are announced in a background thread in batches (see the #initialize method doc for details).



116
117
118
119
120
121
122
# File 'lib/cosell/announcer.rb', line 116

def announce announcement
  if self.queue_announcements?
    self.announcements_queue << announcement
  else
    self.announce_now! announcement
  end
end

#announce_now!(an_announcement_or_announcement_factory) ⇒ Object

First, an announcement is made by calling ‘as_announcement’ on an_announcement_or_announcement_factory, and subscribers to the announcement’s class are then notified

subscribers to this announcer will be filtered to those that match to the announcement’s class, and those subscriptions will be ‘fired’. Subscribers should use the ‘subscribe’ method (also called ‘when_announcing’) to configure actions to take when a given announcement is made.

Typically, an announcement is passed in for an_announcement_factory, in which case as_announcement does nothing but return the announcement. But any class can override as_announcement to adapt into an anouncement as they see fit.

(see Cossell::Announcer for full explanation)



138
139
140
141
142
143
144
145
146
147
148
# File 'lib/cosell/announcer.rb', line 138

def announce_now! an_announcement_or_announcement_factory
  announcement = an_announcement_or_announcement_factory.as_announcement

  self.subscriptions.each do |subscription_type, subscriptions_for_type |
    if announcement.is_a?(subscription_type)
      subscriptions_for_type.each{|subscription| subscription.call(announcement) }
    end
  end

  return announcement 
end

#initialize(*args) ⇒ Object



5
6
7
8
# File 'lib/cosell/announcer.rb', line 5

def initialize *args
  initialize_cosell!
  super
end

#initialize_cosell!Object

Will blow away any queue, and reset all state. Should not be necessary to call this, but left public for testing.



181
182
183
184
185
186
187
188
189
190
191
# File 'lib/cosell/announcer.rb', line 181

def initialize_cosell!
  # Using pseudo-scoped var names. 
  # Unfortunately cant lazily init these w/out ruby warnings going berzerk in verbose mode,
  # So explicitly declaring them here.
  @__queue_announcements ||= false
  @__announcements_queue ||= nil
  @__kill_announcement_queue ||= false
  @__announcements_thread ||= nil
  @__subscriptions ||= {}
  @__queue_logger ||= {}
end

#initialize_cosell_if_neededObject

lazy initialization of cosell. Optional – calling this will get rid of any subsequent warnings about uninitialized ivs In most cases not necessary, and should never have an effect except to get rid of some warnings.



175
176
177
# File 'lib/cosell/announcer.rb', line 175

def initialize_cosell_if_needed
  self.initialize_cosell! if @__subscriptions.nil? 
end

#kill_queue!Object

Kill the announcments queue. This is called automatically if you call queue_announcements!, before starting the next announcments thread, so it’s optional. A way of stopping announcments.



196
197
198
# File 'lib/cosell/announcer.rb', line 196

def kill_queue!
  @__kill_announcement_queue = true
end

#queue_announcements!(opts = {}) ⇒ Object

Place all announcments in a queue, and make announcements in a background thread.

Arguments:

:logger => a logger. Where to log exceptions and warnings.
           This argument is mandatory -- it is too hard to debug exceptions in announcement 
           handler code without a logger. If you _really_ want your code to fail silently, 
           you will have to create a logger on /dev/null.
:sleep_time => how long to sleep (in seconds) after making a batch of announchements 
               optional arg, default: 0.01
:announcements_per_cycle => how many announcements to make before sleeping for sleep_time
               optional arg, default: 25

WARNING: If you do not pass in a logger, announcement code will fail silently (the queue is in a background thread).

Note: at the moment, this method may only be called once, and cannot be undone. There is no way to interrupt the thread.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/cosell/announcer.rb', line 34

def queue_announcements!(opts = {})

  self.initialize_cosell_if_needed

  # The logger in mandatory
  if opts[:logger]
    self.queue_logger = opts[:logger]
  else
    raise "Cosell error: You have to provide a logger, otherwise failures in announcement handler code are to hard to debug"
  end

  # kill off the last queue first
  if self.announcements_thread
    kill_queue!
    sleep 0.01
    queue_announcements! opts
  end

  self.should_queue_announcements = true
  @__announcements_queue ||= Queue.new

  how_many_per_cycle = opts[:announcements_per_cycle] || 25
  cycle_duration = opts[:sleep_time] || 0.01
  count = 0

  self.announcements_thread = Thread.new do 
    loop do
      if queue_killed?
        self.kill_announcement_queue = false
        self.announcements_thread = nil
        log "Announcement queue killed with #{self.announcements_queue.size} announcements still queued", :info
        break
      else
        begin
          self.announce_now! self.announcements_queue.pop
          count += 1
          if (count%how_many_per_cycle).eql?(0)
            log "Announcement queue finished batch of #{how_many_per_cycle}, sleeping for #{cycle_duration} sec", :debug
            count = 0
            sleep cycle_duration
          end
        rescue Exception => x
          log "Exception: #{x}, trace: \n\t#{x.backtrace.join("\n\t")}", :error
        end
      end
    end
  end

end

#queue_announcements?Boolean

return whether annoucements are queued or sent out immediately when the #announce method is called.

Returns:

  • (Boolean)


201
202
203
# File 'lib/cosell/announcer.rb', line 201

def queue_announcements?
  return @__queue_announcements.eql?(true)
end

#spy!(opts = {}) ⇒ Object

Log a message every time this announcer makes an announcement

Options:

:on => Which class of announcements to spy on. Default is Object (ie. all announcements)
:logger => The log to log to. Default is a logger on STDOUT
:level => The log level to log with. Default is :info
:preface_with => A message to prepend to all log messages. Default is "Announcement Spy: "


164
165
166
167
168
169
170
# File 'lib/cosell/announcer.rb', line 164

def spy!(opts = {})
  on = opts[:on] || Object
  logger = opts[:logger] || Logger.new(STDOUT)
  level = opts[:level] || :info
  preface = opts[:preface_with] || "Announcement Spy: "
  self.subscribe(on){|ann| logger.send(level, "#{preface} #{ann.as_announcement_trace}")}
end

#subscribe(*announce_classes, &block) ⇒ Object Also known as: when_announcing

Pass in an anouncement class (or array of announcement classes), along with a block defining the action to be taken when an announcment of one of the specified classes is announced by this announcer. (see Cossell::Announcer for full explanation)



93
94
95
96
97
98
99
100
101
102
# File 'lib/cosell/announcer.rb', line 93

def subscribe *announce_classes, &block

  self.initialize_cosell_if_needed

  Array(announce_classes).each do |announce_class|
    raise "Can only subscribe to classes. Not a class: #{announce_class}" unless announce_class.is_a?(Class)
    self.subscriptions[announce_class] ||= []
    self.subscriptions[announce_class] << lambda(&block)
  end
end

#subscriptionsObject



206
# File 'lib/cosell/announcer.rb', line 206

def subscriptions; @__subscriptions ||= []; end

#subscriptions=(x) ⇒ Object



205
# File 'lib/cosell/announcer.rb', line 205

def subscriptions= x; @__subscriptions = x; end

#unsubscribe(*announce_classes) ⇒ Object

Stop announcing for a given announcement class (or array of classes)



106
107
108
109
110
# File 'lib/cosell/announcer.rb', line 106

def unsubscribe *announce_classes
  Array(announce_classes).each do |announce_class|
    self.subscriptions.delete announce_class
  end
end