observer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2d13d39ae847146041e907bf28d80e935ffb9a66fe17732aee6d771a2b498c70
4
+ data.tar.gz: d3d8c323e5123580182123608f1edee5d1809b1425b252de6d0565a0b4ebb469
5
+ SHA512:
6
+ metadata.gz: ac108bd869e763925361421caadaf54b42d106ae6542334b82b016d297418ab9567bdf9b6683ef0413409b37b6cee1ca8c31abc963caaa5054b91e33a1140915
7
+ data.tar.gz: 897947b7a0cfa4a46ad4354dabc5a20abc18e11a9f5217853fa714db044ca66c87fea8aba31df76bb7d1fab018e98a33aead44659c818375518e29d63e8a0579
@@ -0,0 +1,24 @@
1
+ name: ubuntu
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ name: build (${{ matrix.ruby }} / ${{ matrix.os }})
8
+ strategy:
9
+ matrix:
10
+ ruby: [ 2.7, 2.6, 2.5, 2.4, head ]
11
+ os: [ ubuntu-latest, macos-latest ]
12
+ runs-on: ${{ matrix.os }}
13
+ steps:
14
+ - uses: actions/checkout@master
15
+ - name: Set up Ruby
16
+ uses: ruby/setup-ruby@v1
17
+ with:
18
+ ruby-version: ${{ matrix.ruby }}
19
+ - name: Install dependencies
20
+ run: |
21
+ gem install bundler --no-document
22
+ bundle install
23
+ - name: Run test
24
+ run: rake test
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem "bundler"
7
+ gem "rake"
8
+ gem "test-unit"
9
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
@@ -0,0 +1,139 @@
1
+ # Observer
2
+
3
+ The Observer pattern (also known as publish/subscribe) provides a simple
4
+ mechanism for one object to inform a set of interested third-party objects
5
+ when its state changes.
6
+
7
+ ## Mechanism
8
+
9
+ The notifying class mixes in the +Observable+
10
+ module, which provides the methods for managing the associated observer
11
+ objects.
12
+
13
+ The observable object must:
14
+
15
+ * assert that it has +#changed+
16
+ * call +#notify_observers+
17
+
18
+ An observer subscribes to updates using Observable#add_observer, which also
19
+ specifies the method called via #notify_observers. The default method for
20
+ notify_observers is #update.
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'observer'
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ $ bundle
33
+
34
+ Or install it yourself as:
35
+
36
+ $ gem install observer
37
+
38
+ ## Usage
39
+
40
+ The following example demonstrates this nicely. A +Ticker+, when run,
41
+ continually receives the stock +Price+ for its <tt>@symbol</tt>. A +Warner+
42
+ is a general observer of the price, and two warners are demonstrated, a
43
+ +WarnLow+ and a +WarnHigh+, which print a warning if the price is below or
44
+ above their set limits, respectively.
45
+
46
+ The +update+ callback allows the warners to run without being explicitly
47
+ called. The system is set up with the +Ticker+ and several observers, and the
48
+ observers do their duty without the top-level code having to interfere.
49
+
50
+ Note that the contract between publisher and subscriber (observable and
51
+ observer) is not declared or enforced. The +Ticker+ publishes a time and a
52
+ price, and the warners receive that. But if you don't ensure that your
53
+ contracts are correct, nothing else can warn you.
54
+
55
+ ```ruby
56
+ require "observer"
57
+
58
+ class Ticker ### Periodically fetch a stock price.
59
+
60
+ include Observable
61
+
62
+ def initialize(symbol)
63
+ @symbol = symbol
64
+ end
65
+
66
+ def run
67
+ last_price = nil
68
+ loop do
69
+ price = Price.fetch(@symbol)
70
+ print "Current price: #{price}\n"
71
+ if price != last_price
72
+ changed # notify observers
73
+ last_price = price
74
+ notify_observers(Time.now, price)
75
+ end
76
+ sleep 1
77
+ end
78
+ end
79
+ end
80
+
81
+ class Price ### A mock class to fetch a stock price (60 - 140).
82
+ def self.fetch(symbol)
83
+ 60 + rand(80)
84
+ end
85
+ end
86
+
87
+ class Warner ### An abstract observer of Ticker objects.
88
+ def initialize(ticker, limit)
89
+ @limit = limit
90
+ ticker.add_observer(self)
91
+ end
92
+ end
93
+
94
+ class WarnLow < Warner
95
+ def update(time, price) # callback for observer
96
+ if price < @limit
97
+ print "--- #{time.to_s}: Price below #@limit: #{price}\n"
98
+ end
99
+ end
100
+ end
101
+
102
+ class WarnHigh < Warner
103
+ def update(time, price) # callback for observer
104
+ if price > @limit
105
+ print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
106
+ end
107
+ end
108
+ end
109
+
110
+ ticker = Ticker.new("MSFT")
111
+ WarnLow.new(ticker, 80)
112
+ WarnHigh.new(ticker, 120)
113
+ ticker.run
114
+ ```
115
+
116
+ Produces:
117
+
118
+ ```
119
+ Current price: 83
120
+ Current price: 75
121
+ --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
122
+ Current price: 90
123
+ Current price: 134
124
+ +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
125
+ Current price: 134
126
+ Current price: 112
127
+ Current price: 79
128
+ --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
129
+ ```
130
+
131
+ ## Development
132
+
133
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
134
+
135
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
136
+
137
+ ## Contributing
138
+
139
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/observer.
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/test_*.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "observer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # Implementation of the _Observer_ object-oriented design pattern. The
4
+ # following documentation is copied, with modifications, from "Programming
5
+ # Ruby", by Hunt and Thomas; http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_patterns.html.
6
+ #
7
+ # See Observable for more info.
8
+
9
+ # The Observer pattern (also known as publish/subscribe) provides a simple
10
+ # mechanism for one object to inform a set of interested third-party objects
11
+ # when its state changes.
12
+ #
13
+ # == Mechanism
14
+ #
15
+ # The notifying class mixes in the +Observable+
16
+ # module, which provides the methods for managing the associated observer
17
+ # objects.
18
+ #
19
+ # The observable object must:
20
+ # * assert that it has +#changed+
21
+ # * call +#notify_observers+
22
+ #
23
+ # An observer subscribes to updates using Observable#add_observer, which also
24
+ # specifies the method called via #notify_observers. The default method for
25
+ # #notify_observers is #update.
26
+ #
27
+ # === Example
28
+ #
29
+ # The following example demonstrates this nicely. A +Ticker+, when run,
30
+ # continually receives the stock +Price+ for its <tt>@symbol</tt>. A +Warner+
31
+ # is a general observer of the price, and two warners are demonstrated, a
32
+ # +WarnLow+ and a +WarnHigh+, which print a warning if the price is below or
33
+ # above their set limits, respectively.
34
+ #
35
+ # The +update+ callback allows the warners to run without being explicitly
36
+ # called. The system is set up with the +Ticker+ and several observers, and the
37
+ # observers do their duty without the top-level code having to interfere.
38
+ #
39
+ # Note that the contract between publisher and subscriber (observable and
40
+ # observer) is not declared or enforced. The +Ticker+ publishes a time and a
41
+ # price, and the warners receive that. But if you don't ensure that your
42
+ # contracts are correct, nothing else can warn you.
43
+ #
44
+ # require "observer"
45
+ #
46
+ # class Ticker ### Periodically fetch a stock price.
47
+ # include Observable
48
+ #
49
+ # def initialize(symbol)
50
+ # @symbol = symbol
51
+ # end
52
+ #
53
+ # def run
54
+ # last_price = nil
55
+ # loop do
56
+ # price = Price.fetch(@symbol)
57
+ # print "Current price: #{price}\n"
58
+ # if price != last_price
59
+ # changed # notify observers
60
+ # last_price = price
61
+ # notify_observers(Time.now, price)
62
+ # end
63
+ # sleep 1
64
+ # end
65
+ # end
66
+ # end
67
+ #
68
+ # class Price ### A mock class to fetch a stock price (60 - 140).
69
+ # def self.fetch(symbol)
70
+ # 60 + rand(80)
71
+ # end
72
+ # end
73
+ #
74
+ # class Warner ### An abstract observer of Ticker objects.
75
+ # def initialize(ticker, limit)
76
+ # @limit = limit
77
+ # ticker.add_observer(self)
78
+ # end
79
+ # end
80
+ #
81
+ # class WarnLow < Warner
82
+ # def update(time, price) # callback for observer
83
+ # if price < @limit
84
+ # print "--- #{time.to_s}: Price below #@limit: #{price}\n"
85
+ # end
86
+ # end
87
+ # end
88
+ #
89
+ # class WarnHigh < Warner
90
+ # def update(time, price) # callback for observer
91
+ # if price > @limit
92
+ # print "+++ #{time.to_s}: Price above #@limit: #{price}\n"
93
+ # end
94
+ # end
95
+ # end
96
+ #
97
+ # ticker = Ticker.new("MSFT")
98
+ # WarnLow.new(ticker, 80)
99
+ # WarnHigh.new(ticker, 120)
100
+ # ticker.run
101
+ #
102
+ # Produces:
103
+ #
104
+ # Current price: 83
105
+ # Current price: 75
106
+ # --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75
107
+ # Current price: 90
108
+ # Current price: 134
109
+ # +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134
110
+ # Current price: 134
111
+ # Current price: 112
112
+ # Current price: 79
113
+ # --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
114
+ module Observable
115
+
116
+ #
117
+ # Add +observer+ as an observer on this object. So that it will receive
118
+ # notifications.
119
+ #
120
+ # +observer+:: the object that will be notified of changes.
121
+ # +func+:: Symbol naming the method that will be called when this Observable
122
+ # has changes.
123
+ #
124
+ # This method must return true for +observer.respond_to?+ and will
125
+ # receive <tt>*arg</tt> when #notify_observers is called, where
126
+ # <tt>*arg</tt> is the value passed to #notify_observers by this
127
+ # Observable
128
+ def add_observer(observer, func=:update)
129
+ @observer_peers = {} unless defined? @observer_peers
130
+ unless observer.respond_to? func
131
+ raise NoMethodError, "observer does not respond to `#{func}'"
132
+ end
133
+ @observer_peers[observer] = func
134
+ end
135
+
136
+ #
137
+ # Remove +observer+ as an observer on this object so that it will no longer
138
+ # receive notifications.
139
+ #
140
+ # +observer+:: An observer of this Observable
141
+ def delete_observer(observer)
142
+ @observer_peers.delete observer if defined? @observer_peers
143
+ end
144
+
145
+ #
146
+ # Remove all observers associated with this object.
147
+ #
148
+ def delete_observers
149
+ @observer_peers.clear if defined? @observer_peers
150
+ end
151
+
152
+ #
153
+ # Return the number of observers associated with this object.
154
+ #
155
+ def count_observers
156
+ if defined? @observer_peers
157
+ @observer_peers.size
158
+ else
159
+ 0
160
+ end
161
+ end
162
+
163
+ #
164
+ # Set the changed state of this object. Notifications will be sent only if
165
+ # the changed +state+ is +true+.
166
+ #
167
+ # +state+:: Boolean indicating the changed state of this Observable.
168
+ #
169
+ def changed(state=true)
170
+ @observer_state = state
171
+ end
172
+
173
+ #
174
+ # Returns true if this object's state has been changed since the last
175
+ # #notify_observers call.
176
+ #
177
+ def changed?
178
+ if defined? @observer_state and @observer_state
179
+ true
180
+ else
181
+ false
182
+ end
183
+ end
184
+
185
+ #
186
+ # Notify observers of a change in state *if* this object's changed state is
187
+ # +true+.
188
+ #
189
+ # This will invoke the method named in #add_observer, passing <tt>*arg</tt>.
190
+ # The changed state is then set to +false+.
191
+ #
192
+ # <tt>*arg</tt>:: Any arguments to pass to the observers.
193
+ def notify_observers(*arg)
194
+ if defined? @observer_state and @observer_state
195
+ if defined? @observer_peers
196
+ @observer_peers.each do |k, v|
197
+ k.send v, *arg
198
+ end
199
+ end
200
+ @observer_state = false
201
+ end
202
+ end
203
+
204
+ end
@@ -0,0 +1,3 @@
1
+ module Observer
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ begin
2
+ require_relative "lib/observer/version"
3
+ rescue LoadError # Fallback to load version file in ruby core repository
4
+ require_relative "version"
5
+ end
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "observer"
9
+ spec.version = Observer::VERSION
10
+ spec.authors = ["Yukihiro Matsumoto"]
11
+ spec.email = ["matz@ruby-lang.org"]
12
+
13
+ spec.summary = %q{Implementation of the Observer object-oriented design pattern.}
14
+ spec.description = spec.summary
15
+ spec.homepage = "https://github.com/ruby/observer"
16
+ spec.license = "BSD-2-Clause"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: observer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yukihiro Matsumoto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-04-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Implementation of the Observer object-oriented design pattern.
14
+ email:
15
+ - matz@ruby-lang.org
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".github/workflows/test.yml"
21
+ - ".gitignore"
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - bin/console
27
+ - bin/setup
28
+ - lib/observer.rb
29
+ - lib/observer/version.rb
30
+ - observer.gemspec
31
+ homepage: https://github.com/ruby/observer
32
+ licenses:
33
+ - BSD-2-Clause
34
+ metadata:
35
+ homepage_uri: https://github.com/ruby/observer
36
+ source_code_uri: https://github.com/ruby/observer
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ requirements: []
52
+ rubygems_version: 3.2.0.pre1
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Implementation of the Observer object-oriented design pattern.
56
+ test_files: []