/usr/lib/ruby/2.5.0/observer.rb is in libruby2.5 2.5.1-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | # frozen_string_literal: true
#
# Implementation of the _Observer_ object-oriented design pattern. The
# following documentation is copied, with modifications, from "Programming
# Ruby", by Hunt and Thomas; http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_patterns.html.
#
# See Observable for more info.
# The Observer pattern (also known as publish/subscribe) provides a simple
# mechanism for one object to inform a set of interested third-party objects
# when its state changes.
#
# == Mechanism
#
# The notifying class mixes in the +Observable+
# module, which provides the methods for managing the associated observer
# objects.
#
# The observable object must:
# * assert that it has +#changed+
# * call +#notify_observers+
#
# An observer subscribes to updates using Observable#add_observer, which also
# specifies the method called via #notify_observers. The default method for
# #notify_observers is #update.
#
# === Example
#
# The following example demonstrates this nicely. A +Ticker+, when run,
# continually receives the stock +Price+ for its <tt>@symbol</tt>. A +Warner+
# is a general observer of the price, and two warners are demonstrated, a
# +WarnLow+ and a +WarnHigh+, which print a warning if the price is below or
# above their set limits, respectively.
#
# The +update+ callback allows the warners to run without being explicitly
# called. The system is set up with the +Ticker+ and several observers, and the
# observers do their duty without the top-level code having to interfere.
#
# Note that the contract between publisher and subscriber (observable and
# observer) is not declared or enforced. The +Ticker+ publishes a time and a
# price, and the warners receive that. But if you don't ensure that your
# contracts are correct, nothing else can warn you.
#
# require "observer"
#
# class Ticker ### Periodically fetch a stock price.
# include Observable
#
# def initialize(symbol)
# @symbol = symbol
# end
#
# def run
# last_price = nil
# loop do
# price = Price.fetch(@symbol)
# print "Current price: #{price}\n"
# if price != last_price
# changed # notify observers
# last_price = price
# notify_observers(Time.now, price)
# end
# sleep 1
# end
# end
# end
#
# class Price ### A mock class to fetch a stock price (60 - 140).
# def self.fetch(symbol)
# 60 + rand(80)
# end
# end
#
# class Warner ### An abstract observer of Ticker objects.
# def initialize(ticker, limit)
# @limit = limit
# ticker.add_observer(self)
# end
# end
#
# class WarnLow < Warner
# def update(time, price) # callback for observer
# if price < @limit
# print "--- #{time.to_s}: Price below #@limit: #{price}\n"
# end
# end
# end
#
# class WarnHigh < Warner
# def update(time, price) # callback for observer
# if price > @limit
# print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
# end
# end
# end
#
# ticker = Ticker.new("MSFT")
# WarnLow.new(ticker, 80)
# WarnHigh.new(ticker, 120)
# ticker.run
#
# Produces:
#
# Current price: 83
# Current price: 75
# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
# Current price: 90
# Current price: 134
# +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
# Current price: 134
# Current price: 112
# Current price: 79
# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
module Observable
#
# Add +observer+ as an observer on this object. So that it will receive
# notifications.
#
# +observer+:: the object that will be notified of changes.
# +func+:: Symbol naming the method that will be called when this Observable
# has changes.
#
# This method must return true for +observer.respond_to?+ and will
# receive <tt>*arg</tt> when #notify_observers is called, where
# <tt>*arg</tt> is the value passed to #notify_observers by this
# Observable
def add_observer(observer, func=:update)
@observer_peers = {} unless defined? @observer_peers
unless observer.respond_to? func
raise NoMethodError, "observer does not respond to `#{func}'"
end
@observer_peers[observer] = func
end
#
# Remove +observer+ as an observer on this object so that it will no longer
# receive notifications.
#
# +observer+:: An observer of this Observable
def delete_observer(observer)
@observer_peers.delete observer if defined? @observer_peers
end
#
# Remove all observers associated with this object.
#
def delete_observers
@observer_peers.clear if defined? @observer_peers
end
#
# Return the number of observers associated with this object.
#
def count_observers
if defined? @observer_peers
@observer_peers.size
else
0
end
end
#
# Set the changed state of this object. Notifications will be sent only if
# the changed +state+ is +true+.
#
# +state+:: Boolean indicating the changed state of this Observable.
#
def changed(state=true)
@observer_state = state
end
#
# Returns true if this object's state has been changed since the last
# #notify_observers call.
#
def changed?
if defined? @observer_state and @observer_state
true
else
false
end
end
#
# Notify observers of a change in state *if* this object's changed state is
# +true+.
#
# This will invoke the method named in #add_observer, passing <tt>*arg</tt>.
# The changed state is then set to +false+.
#
# <tt>*arg</tt>:: Any arguments to pass to the observers.
def notify_observers(*arg)
if defined? @observer_state and @observer_state
if defined? @observer_peers
@observer_peers.each do |k, v|
k.send v, *arg
end
end
@observer_state = false
end
end
end
|