rinda 0.1.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.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +24 -0
- data/.gitignore +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +9 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rinda/rinda.rb +327 -0
- data/lib/rinda/ring.rb +484 -0
- data/lib/rinda/tuplespace.rb +641 -0
- data/rinda.gemspec +24 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e451398199be205b7e5de56d4764525709c6a0f7e68c67989bd3b792b5fd8ce
|
4
|
+
data.tar.gz: 68eb2f17f82fbc9dc2bee6f2770b81a05cb16133965166b821c54da90df575cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9aace6a91d22a4aca501ca02390c0b2da349154e19e819b68c2b7d977f33318fac199d07b13dca7b07195de75843dd7af67b9070d04ab923ed7717d92772d74d
|
7
|
+
data.tar.gz: 3ab9372edeab86fcb407cf281a1276f72d0236e9b25c6b811b2edfc99d617194db9f9e2fe8f0cfbda5e891eebc0dbec6aa748abd31710c6776a1f17c4684aa9a
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: test
|
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, 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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Rinda
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rinda`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'rinda'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install rinda
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
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.
|
30
|
+
|
31
|
+
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).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/hsbt/rinda.
|
36
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rinda"
|
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__)
|
data/bin/setup
ADDED
data/lib/rinda/rinda.rb
ADDED
@@ -0,0 +1,327 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'drb/drb'
|
3
|
+
|
4
|
+
##
|
5
|
+
# A module to implement the Linda distributed computing paradigm in Ruby.
|
6
|
+
#
|
7
|
+
# Rinda is part of DRb (dRuby).
|
8
|
+
#
|
9
|
+
# == Example(s)
|
10
|
+
#
|
11
|
+
# See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards.
|
12
|
+
#
|
13
|
+
#--
|
14
|
+
# TODO
|
15
|
+
# == Introduction to Linda/rinda?
|
16
|
+
#
|
17
|
+
# == Why is this library separate from DRb?
|
18
|
+
|
19
|
+
module Rinda
|
20
|
+
|
21
|
+
##
|
22
|
+
# Rinda error base class
|
23
|
+
|
24
|
+
class RindaError < RuntimeError; end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Raised when a hash-based tuple has an invalid key.
|
28
|
+
|
29
|
+
class InvalidHashTupleKey < RindaError; end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Raised when trying to use a canceled tuple.
|
33
|
+
|
34
|
+
class RequestCanceledError < ThreadError; end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Raised when trying to use an expired tuple.
|
38
|
+
|
39
|
+
class RequestExpiredError < ThreadError; end
|
40
|
+
|
41
|
+
##
|
42
|
+
# A tuple is the elementary object in Rinda programming.
|
43
|
+
# Tuples may be matched against templates if the tuple and
|
44
|
+
# the template are the same size.
|
45
|
+
|
46
|
+
class Tuple
|
47
|
+
|
48
|
+
##
|
49
|
+
# Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash.
|
50
|
+
|
51
|
+
def initialize(ary_or_hash)
|
52
|
+
if hash?(ary_or_hash)
|
53
|
+
init_with_hash(ary_or_hash)
|
54
|
+
else
|
55
|
+
init_with_ary(ary_or_hash)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# The number of elements in the tuple.
|
61
|
+
|
62
|
+
def size
|
63
|
+
@tuple.size
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# Accessor method for elements of the tuple.
|
68
|
+
|
69
|
+
def [](k)
|
70
|
+
@tuple[k]
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Fetches item +k+ from the tuple.
|
75
|
+
|
76
|
+
def fetch(k)
|
77
|
+
@tuple.fetch(k)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Iterate through the tuple, yielding the index or key, and the
|
82
|
+
# value, thus ensuring arrays are iterated similarly to hashes.
|
83
|
+
|
84
|
+
def each # FIXME
|
85
|
+
if Hash === @tuple
|
86
|
+
@tuple.each { |k, v| yield(k, v) }
|
87
|
+
else
|
88
|
+
@tuple.each_with_index { |v, k| yield(k, v) }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# Return the tuple itself
|
94
|
+
def value
|
95
|
+
@tuple
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def hash?(ary_or_hash)
|
101
|
+
ary_or_hash.respond_to?(:keys)
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Munges +ary+ into a valid Tuple.
|
106
|
+
|
107
|
+
def init_with_ary(ary)
|
108
|
+
@tuple = Array.new(ary.size)
|
109
|
+
@tuple.size.times do |i|
|
110
|
+
@tuple[i] = ary[i]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
##
|
115
|
+
# Ensures +hash+ is a valid Tuple.
|
116
|
+
|
117
|
+
def init_with_hash(hash)
|
118
|
+
@tuple = Hash.new
|
119
|
+
hash.each do |k, v|
|
120
|
+
raise InvalidHashTupleKey unless String === k
|
121
|
+
@tuple[k] = v
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Templates are used to match tuples in Rinda.
|
129
|
+
|
130
|
+
class Template < Tuple
|
131
|
+
|
132
|
+
##
|
133
|
+
# Matches this template against +tuple+. The +tuple+ must be the same
|
134
|
+
# size as the template. An element with a +nil+ value in a template acts
|
135
|
+
# as a wildcard, matching any value in the corresponding position in the
|
136
|
+
# tuple. Elements of the template match the +tuple+ if the are #== or
|
137
|
+
# #===.
|
138
|
+
#
|
139
|
+
# Template.new([:foo, 5]).match Tuple.new([:foo, 5]) # => true
|
140
|
+
# Template.new([:foo, nil]).match Tuple.new([:foo, 5]) # => true
|
141
|
+
# Template.new([String]).match Tuple.new(['hello']) # => true
|
142
|
+
#
|
143
|
+
# Template.new([:foo]).match Tuple.new([:foo, 5]) # => false
|
144
|
+
# Template.new([:foo, 6]).match Tuple.new([:foo, 5]) # => false
|
145
|
+
# Template.new([:foo, nil]).match Tuple.new([:foo]) # => false
|
146
|
+
# Template.new([:foo, 6]).match Tuple.new([:foo]) # => false
|
147
|
+
|
148
|
+
def match(tuple)
|
149
|
+
return false unless tuple.respond_to?(:size)
|
150
|
+
return false unless tuple.respond_to?(:fetch)
|
151
|
+
return false unless self.size == tuple.size
|
152
|
+
each do |k, v|
|
153
|
+
begin
|
154
|
+
it = tuple.fetch(k)
|
155
|
+
rescue
|
156
|
+
return false
|
157
|
+
end
|
158
|
+
next if v.nil?
|
159
|
+
next if v == it
|
160
|
+
next if v === it
|
161
|
+
return false
|
162
|
+
end
|
163
|
+
return true
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Alias for #match.
|
168
|
+
|
169
|
+
def ===(tuple)
|
170
|
+
match(tuple)
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
##
|
176
|
+
# <i>Documentation?</i>
|
177
|
+
|
178
|
+
class DRbObjectTemplate
|
179
|
+
|
180
|
+
##
|
181
|
+
# Creates a new DRbObjectTemplate that will match against +uri+ and +ref+.
|
182
|
+
|
183
|
+
def initialize(uri=nil, ref=nil)
|
184
|
+
@drb_uri = uri
|
185
|
+
@drb_ref = ref
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# This DRbObjectTemplate matches +ro+ if the remote object's drburi and
|
190
|
+
# drbref are the same. +nil+ is used as a wildcard.
|
191
|
+
|
192
|
+
def ===(ro)
|
193
|
+
return true if super(ro)
|
194
|
+
unless @drb_uri.nil?
|
195
|
+
return false unless (@drb_uri === ro.__drburi rescue false)
|
196
|
+
end
|
197
|
+
unless @drb_ref.nil?
|
198
|
+
return false unless (@drb_ref === ro.__drbref rescue false)
|
199
|
+
end
|
200
|
+
true
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# TupleSpaceProxy allows a remote Tuplespace to appear as local.
|
207
|
+
|
208
|
+
class TupleSpaceProxy
|
209
|
+
##
|
210
|
+
# A Port ensures that a moved tuple arrives properly at its destination
|
211
|
+
# and does not get lost.
|
212
|
+
#
|
213
|
+
# See https://bugs.ruby-lang.org/issues/8125
|
214
|
+
|
215
|
+
class Port # :nodoc:
|
216
|
+
attr_reader :value
|
217
|
+
|
218
|
+
def self.deliver
|
219
|
+
port = new
|
220
|
+
|
221
|
+
begin
|
222
|
+
yield(port)
|
223
|
+
ensure
|
224
|
+
port.close
|
225
|
+
end
|
226
|
+
|
227
|
+
port.value
|
228
|
+
end
|
229
|
+
|
230
|
+
def initialize
|
231
|
+
@open = true
|
232
|
+
@value = nil
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# Don't let the DRb thread push to it when remote sends tuple
|
237
|
+
|
238
|
+
def close
|
239
|
+
@open = false
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# Stores +value+ and ensure it does not get marshaled multiple times.
|
244
|
+
|
245
|
+
def push value
|
246
|
+
raise 'port closed' unless @open
|
247
|
+
|
248
|
+
@value = value
|
249
|
+
|
250
|
+
nil # avoid Marshal
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
##
|
255
|
+
# Creates a new TupleSpaceProxy to wrap +ts+.
|
256
|
+
|
257
|
+
def initialize(ts)
|
258
|
+
@ts = ts
|
259
|
+
end
|
260
|
+
|
261
|
+
##
|
262
|
+
# Adds +tuple+ to the proxied TupleSpace. See TupleSpace#write.
|
263
|
+
|
264
|
+
def write(tuple, sec=nil)
|
265
|
+
@ts.write(tuple, sec)
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.
|
270
|
+
|
271
|
+
def take(tuple, sec=nil, &block)
|
272
|
+
Port.deliver do |port|
|
273
|
+
@ts.move(DRbObject.new(port), tuple, sec, &block)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Reads +tuple+ from the proxied TupleSpace. See TupleSpace#read.
|
279
|
+
|
280
|
+
def read(tuple, sec=nil, &block)
|
281
|
+
@ts.read(tuple, sec, &block)
|
282
|
+
end
|
283
|
+
|
284
|
+
##
|
285
|
+
# Reads all tuples matching +tuple+ from the proxied TupleSpace. See
|
286
|
+
# TupleSpace#read_all.
|
287
|
+
|
288
|
+
def read_all(tuple)
|
289
|
+
@ts.read_all(tuple)
|
290
|
+
end
|
291
|
+
|
292
|
+
##
|
293
|
+
# Registers for notifications of event +ev+ on the proxied TupleSpace.
|
294
|
+
# See TupleSpace#notify
|
295
|
+
|
296
|
+
def notify(ev, tuple, sec=nil)
|
297
|
+
@ts.notify(ev, tuple, sec)
|
298
|
+
end
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
##
|
303
|
+
# An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
|
304
|
+
# alive.
|
305
|
+
|
306
|
+
class SimpleRenewer
|
307
|
+
|
308
|
+
include DRbUndumped
|
309
|
+
|
310
|
+
##
|
311
|
+
# Creates a new SimpleRenewer that keeps an object alive for another +sec+
|
312
|
+
# seconds.
|
313
|
+
|
314
|
+
def initialize(sec=180)
|
315
|
+
@sec = sec
|
316
|
+
end
|
317
|
+
|
318
|
+
##
|
319
|
+
# Called by the TupleSpace to check if the object is still alive.
|
320
|
+
|
321
|
+
def renew
|
322
|
+
@sec
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|