zmqmachine 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/zm/timers.rb ADDED
@@ -0,0 +1,220 @@
1
+ #--
2
+ #
3
+ # Author:: Chuck Remes
4
+ # Homepage:: http://github.com/chuckremes/zmqmachine
5
+ # Date:: 20100602
6
+ #
7
+ #----------------------------------------------------------------------------
8
+ #
9
+ # Copyright (C) 2010 by Chuck Remes. All Rights Reserved.
10
+ # Email: cremes at mac dot com
11
+ #
12
+ # (The MIT License)
13
+ #
14
+ # Permission is hereby granted, free of charge, to any person obtaining
15
+ # a copy of this software and associated documentation files (the
16
+ # 'Software'), to deal in the Software without restriction, including
17
+ # without limitation the rights to use, copy, modify, merge, publish,
18
+ # distribute, sublicense, and/or sell copies of the Software, and to
19
+ # permit persons to whom the Software is furnished to do so, subject to
20
+ # the following conditions:
21
+ #
22
+ # The above copyright notice and this permission notice shall be
23
+ # included in all copies or substantial portions of the Software.
24
+ #
25
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
26
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
28
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
29
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
30
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
31
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
+ #
33
+ #---------------------------------------------------------------------------
34
+ #
35
+ #
36
+
37
+ module ZMQMachine
38
+
39
+ # Manages the addition and cancellation of all timers. Each #Reactor
40
+ # maintains its own set of timers; the timer belongs to the
41
+ # reactor context.
42
+ #
43
+ # This should never be instantiated directly
44
+ # by user code. A timer must be known to the #Reactor in which it
45
+ # is running, so use the #Reactor#oneshot_timer and
46
+ # #Reactor#periodical_timer convenience methods. It ensures that
47
+ # new timers are installed in the correct #Reactor.
48
+ #
49
+ class Timers
50
+ def initialize
51
+ @timers = SortedSet.new
52
+ end
53
+
54
+ # Adds a non-periodical, one-shot timer in order of
55
+ # first-to-fire to last-to-fire.
56
+ #
57
+ # Returns nil unless a +timer_proc+ or +blk+ are
58
+ # provided. There is no point to an empty timer that
59
+ # does nothing when fired.
60
+ #
61
+ def add_oneshot delay, timer_proc = nil, &blk
62
+ blk ||= timer_proc
63
+ return nil unless blk
64
+
65
+ timer = Timer.new self, delay, false, blk
66
+ @timers.add timer
67
+ timer
68
+ end
69
+
70
+ # Adds a periodical timer in order of
71
+ # first-to-fire to last-to-fire.
72
+ #
73
+ # Returns nil unless a +timer_proc+ or +blk+ are
74
+ # provided. There is no point to an empty timer that
75
+ # does nothing when fired.
76
+ #
77
+ def add_periodical delay, timer_proc = nil, &blk
78
+ blk ||= timer_proc
79
+ return nil unless blk
80
+
81
+ timer = Timer.new self, delay, true, blk
82
+ @timers.add timer
83
+ timer
84
+ end
85
+
86
+ # Cancel the +timer+.
87
+ #
88
+ # Returns +true+ when cancellation succeeds.
89
+ # Returns +false+ when it fails to find the
90
+ # given +timer+.
91
+ #
92
+ def cancel timer
93
+ @timers.delete(timer) ? true : false
94
+ end
95
+
96
+ # A convenience method that loops through all known timers
97
+ # and fires all of the expired timers.
98
+ #
99
+ #--
100
+ # Internally the list is sorted whenever a timer is added or
101
+ # deleted. It stops processing this list when the next timer
102
+ # is not yet expired; it knows all subsequent timers are not
103
+ # expired too.
104
+ #
105
+ def fire_expired
106
+ # all time is expected as milliseconds
107
+ now = Timers.now
108
+ save = []
109
+
110
+ # timers should be sorted by expiration time
111
+ @timers.delete_if do |timer|
112
+ break unless timer.expired?(now)
113
+ timer.fire
114
+ save << timer if timer.periodical?
115
+ true
116
+ end
117
+
118
+ # reinstate the periodicals; necessary to do in two steps
119
+ # since changing the timer.fire_time inside the loop would
120
+ # not retain proper ordering in the sorted set; re-adding it
121
+ # ensures the timer is in sorted order
122
+ save.each { |timer| @timers.add timer }
123
+ end
124
+
125
+ # Returns the current time using the following algo:
126
+ #
127
+ # (Time.now.to_f * 1000).to_i
128
+ #
129
+ # Added as a class method so that it can be overridden by a user
130
+ # who wants to provide their own time source. For example, a user
131
+ # could use a third-party gem that provides a better performing
132
+ # time source.
133
+ #
134
+ def self.now
135
+ (Time.now.to_f * 1000).to_i
136
+ end
137
+
138
+ # Convert Timers.now to a number usable by the Time class.
139
+ #
140
+ def self.now_converted
141
+ now / 1000.0
142
+ end
143
+ end # class Timers
144
+
145
+
146
+ # Used to track the specific expiration time and execution
147
+ # code for each timer.
148
+ #
149
+ # This should never be instantiated directly
150
+ # by user code. A timer must be known to the #Reactor in which it
151
+ # is running, so use the #Reactor#oneshot_timer and
152
+ # #Reactor#periodical_timer convenience methods. It ensures that
153
+ # new timers are installed in the correct #Reactor.
154
+ #
155
+ class Timer
156
+ include Comparable
157
+
158
+ attr_reader :fire_time
159
+
160
+ # +time+ is in milliseconds
161
+ def initialize timers, delay, periodical, timer_proc = nil, &blk
162
+ @timers = timers
163
+ @delay = delay.to_i
164
+ @periodical = periodical
165
+ @timer_proc = timer_proc || blk
166
+ schedule_firing_time
167
+ end
168
+
169
+ # Executes the callback.
170
+ #
171
+ # Returns +true+ when the timer is a one-shot;
172
+ # Returns +false+ when the timer is periodical and has rescheduled
173
+ # itself.
174
+ #
175
+ def fire
176
+ @timer_proc.call
177
+
178
+ schedule_firing_time if @periodical
179
+ end
180
+
181
+ def cancel
182
+ @timers.cancel self
183
+ end
184
+
185
+ def <=>(other)
186
+ self.fire_time <=> other.fire_time
187
+ end
188
+
189
+ def expired? time
190
+ time ||= now
191
+ time > @fire_time
192
+ end
193
+
194
+ def periodical?
195
+ @periodical
196
+ end
197
+
198
+
199
+ private
200
+
201
+ # calculate the time when this should fire; note that
202
+ # the use of #now grabs the current time and adds the
203
+ # delay on top. When this timer is late in firing (due
204
+ # to a blocking operation or other blocker preventing
205
+ # this from running on time) then we will see the next
206
+ # scheduled firing time slowly drift.
207
+ # e.g. Fire at time 10 + delay 5 = 15
208
+ # timer late, fires at 17
209
+ # next timer to fire at, 17 + delay 5 = 22
210
+ # had it not been late, it would fire at 20
211
+ def schedule_firing_time
212
+ @fire_time = now + @delay
213
+ end
214
+
215
+ def now
216
+ Timers.now
217
+ end
218
+ end # class Timer
219
+
220
+ end # module ZMQMachine
data/lib/zmqmachine.rb ADDED
@@ -0,0 +1,76 @@
1
+
2
+ module ZMQMachine
3
+
4
+ # :stopdoc:
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ @version ||= File.read(path('version.txt')).strip
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args, &block )
20
+ rv = args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ if block
22
+ begin
23
+ $LOAD_PATH.unshift LIBPATH
24
+ rv = block.call
25
+ ensure
26
+ $LOAD_PATH.shift
27
+ end
28
+ end
29
+ return rv
30
+ end
31
+
32
+ # Returns the lpath for the module. If any arguments are given,
33
+ # they will be joined to the end of the path using
34
+ # <tt>File.join</tt>.
35
+ #
36
+ def self.path( *args, &block )
37
+ rv = args.empty? ? PATH : ::File.join(PATH, args.flatten)
38
+ if block
39
+ begin
40
+ $LOAD_PATH.unshift PATH
41
+ rv = block.call
42
+ ensure
43
+ $LOAD_PATH.shift
44
+ end
45
+ end
46
+ return rv
47
+ end
48
+
49
+ # Utility method used to require all files ending in .rb that lie in the
50
+ # directory below this file that has the same name as the filename passed
51
+ # in. Optionally, a specific _directory_ name can be passed in such that
52
+ # the _filename_ does not have to be equivalent to the directory.
53
+ #
54
+ def self.require_all_libs_relative_to( fname, dir = nil )
55
+ dir ||= ::File.basename(fname, '.*')
56
+ search_me = ::File.expand_path(
57
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
58
+
59
+ Dir.glob(search_me).sort.each {|rb| require rb}
60
+ end
61
+
62
+ end # module ZMQMachine
63
+
64
+ require 'singleton'
65
+ require 'set'
66
+ require 'ffi-rzmq'
67
+
68
+ # the order of files is important
69
+ %w(address exceptions timers deferrable reactor message sockets).each do |file|
70
+ require ZMQMachine.libpath(['zm', file])
71
+ end
72
+
73
+ ZMQMachine.require_all_libs_relative_to(__FILE__)
74
+
75
+ # save some typing
76
+ ZM = ZMQMachine
@@ -0,0 +1,15 @@
1
+
2
+ require File.expand_path(
3
+ File.join(File.dirname(__FILE__), %w[.. lib zmqmachine]))
4
+
5
+ Spec::Runner.configure do |config|
6
+ # == Mock Framework
7
+ #
8
+ # RSpec uses it's own mocking framework by default. If you prefer to
9
+ # use mocha, flexmock or RR, uncomment the appropriate line:
10
+ #
11
+ # config.mock_with :mocha
12
+ # config.mock_with :flexmock
13
+ # config.mock_with :rr
14
+ end
15
+
@@ -0,0 +1,6 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
3
+
4
+ describe ZMQMachine do
5
+ end
6
+
data/version.txt ADDED
@@ -0,0 +1 @@
1
+ 0.3.0
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{zmqmachine}
5
+ s.version = "0.3.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Chuck Remes"]
9
+ s.date = %q{2010-08-15}
10
+ s.description = %q{ZMQMachine is another Ruby implementation of the reactor pattern but this
11
+ time using 0mq sockets rather than POSIX sockets.
12
+
13
+ Unlike the great Eventmachine ruby project and the Python Twisted
14
+ project which work with POSIX sockets, ZMQMachine is inherently threaded. The
15
+ 0mq sockets backing the reactor use a thread pool for performing
16
+ their work so already it is different from most other reactors. Also, a
17
+ single program may create multiple reactor instances which runs in
18
+ its own thread. All activity within the reactor is single-threaded
19
+ and asynchronous.
20
+
21
+ It is possible to extend the 0mq library to "poll" normal file
22
+ descriptors. This isn't on my roadmap but patches are accepted.}
23
+ s.email = %q{cremes@mac.com}
24
+ s.extra_rdoc_files = ["History.txt", "README.rdoc", "version.txt"]
25
+ s.files = [".bnsignore", "History.txt", "README.rdoc", "Rakefile", "examples/fake_ftp.rb", "examples/one_handed_ping_pong.rb", "examples/ping_pong.rb", "examples/pub_sub.rb", "examples/throttled_ping_pong.rb", "lib/zm/address.rb", "lib/zm/deferrable.rb", "lib/zm/exceptions.rb", "lib/zm/message.rb", "lib/zm/reactor.rb", "lib/zm/sockets.rb", "lib/zm/sockets/base.rb", "lib/zm/sockets/pair.rb", "lib/zm/sockets/pub.rb", "lib/zm/sockets/rep.rb", "lib/zm/sockets/req.rb", "lib/zm/sockets/sub.rb", "lib/zm/sockets/xrep.rb", "lib/zm/sockets/xreq.rb", "lib/zm/timers.rb", "lib/zmqmachine.rb", "spec/spec_helper.rb", "spec/zmqmachine_spec.rb", "version.txt", "zmqmachine.gemspec"]
26
+ s.homepage = %q{http://github.com/chuckremes/zmqmachine}
27
+ s.rdoc_options = ["--main", "README.rdoc"]
28
+ s.require_paths = ["lib"]
29
+ s.rubyforge_project = %q{zmqmachine}
30
+ s.rubygems_version = %q{1.3.6}
31
+ s.summary = %q{ZMQMachine is another Ruby implementation of the reactor pattern but this time using 0mq sockets rather than POSIX sockets}
32
+
33
+ if s.respond_to? :specification_version then
34
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
35
+ s.specification_version = 3
36
+
37
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
38
+ s.add_runtime_dependency(%q<ffi-rzmq>, [">= 0.5.0"])
39
+ s.add_development_dependency(%q<bones>, [">= 3.4.3"])
40
+ else
41
+ s.add_dependency(%q<ffi-rzmq>, [">= 0.5.0"])
42
+ s.add_dependency(%q<bones>, [">= 3.4.3"])
43
+ end
44
+ else
45
+ s.add_dependency(%q<ffi-rzmq>, [">= 0.5.0"])
46
+ s.add_dependency(%q<bones>, [">= 3.4.3"])
47
+ end
48
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zmqmachine
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
+ platform: ruby
11
+ authors:
12
+ - Chuck Remes
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-08-15 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: ffi-rzmq
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ - 5
30
+ - 0
31
+ version: 0.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: bones
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 3
43
+ - 4
44
+ - 3
45
+ version: 3.4.3
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: |-
49
+ ZMQMachine is another Ruby implementation of the reactor pattern but this
50
+ time using 0mq sockets rather than POSIX sockets.
51
+
52
+ Unlike the great Eventmachine ruby project and the Python Twisted
53
+ project which work with POSIX sockets, ZMQMachine is inherently threaded. The
54
+ 0mq sockets backing the reactor use a thread pool for performing
55
+ their work so already it is different from most other reactors. Also, a
56
+ single program may create multiple reactor instances which runs in
57
+ its own thread. All activity within the reactor is single-threaded
58
+ and asynchronous.
59
+
60
+ It is possible to extend the 0mq library to "poll" normal file
61
+ descriptors. This isn't on my roadmap but patches are accepted.
62
+ email: cremes@mac.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files:
68
+ - History.txt
69
+ - README.rdoc
70
+ - version.txt
71
+ files:
72
+ - .bnsignore
73
+ - History.txt
74
+ - README.rdoc
75
+ - Rakefile
76
+ - examples/fake_ftp.rb
77
+ - examples/one_handed_ping_pong.rb
78
+ - examples/ping_pong.rb
79
+ - examples/pub_sub.rb
80
+ - examples/throttled_ping_pong.rb
81
+ - lib/zm/address.rb
82
+ - lib/zm/deferrable.rb
83
+ - lib/zm/exceptions.rb
84
+ - lib/zm/message.rb
85
+ - lib/zm/reactor.rb
86
+ - lib/zm/sockets.rb
87
+ - lib/zm/sockets/base.rb
88
+ - lib/zm/sockets/pair.rb
89
+ - lib/zm/sockets/pub.rb
90
+ - lib/zm/sockets/rep.rb
91
+ - lib/zm/sockets/req.rb
92
+ - lib/zm/sockets/sub.rb
93
+ - lib/zm/sockets/xrep.rb
94
+ - lib/zm/sockets/xreq.rb
95
+ - lib/zm/timers.rb
96
+ - lib/zmqmachine.rb
97
+ - spec/spec_helper.rb
98
+ - spec/zmqmachine_spec.rb
99
+ - version.txt
100
+ - zmqmachine.gemspec
101
+ has_rdoc: true
102
+ homepage: http://github.com/chuckremes/zmqmachine
103
+ licenses: []
104
+
105
+ post_install_message:
106
+ rdoc_options:
107
+ - --main
108
+ - README.rdoc
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ segments:
116
+ - 0
117
+ version: "0"
118
+ required_rubygems_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project: zmqmachine
128
+ rubygems_version: 1.3.6
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: ZMQMachine is another Ruby implementation of the reactor pattern but this time using 0mq sockets rather than POSIX sockets
132
+ test_files: []
133
+