Change soft-fail to use the config, rather than env
[rbx.git] / stdlib / rinda / rinda.rb
blob6c59e68654c8cad24e8a1d3e127641e7e5d90254
1 require 'drb/drb'
2 require 'thread'
4 ##
5 # A module to implement the Linda distributed computing paradigm in Ruby.
7 # Rinda is part of DRb (dRuby).
9 # == Example(s)
11 # See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards.
13 #--
14 # TODO
15 # == Introduction to Linda/rinda?
17 # == Why is this library separate from DRb?
19 module Rinda
21   ##
22   # Rinda error base class
24   class RindaError < RuntimeError; end
26   ##
27   # Raised when a hash-based tuple has an invalid key.
29   class InvalidHashTupleKey < RindaError; end
31   ##
32   # Raised when trying to use a canceled tuple.
34   class RequestCanceledError < ThreadError; end
36   ##
37   # Raised when trying to use an expired tuple.
39   class RequestExpiredError < ThreadError; end
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.
46   class Tuple
48     ##
49     # Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash.
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
59     ##
60     # The number of elements in the tuple.
61     
62     def size
63       @tuple.size
64     end
66     ##
67     # Accessor method for elements of the tuple.
69     def [](k)
70       @tuple[k]
71     end
73     ##
74     # Fetches item +k+ from the tuple.
76     def fetch(k)
77       @tuple.fetch(k)
78     end
80     ##
81     # Iterate through the tuple, yielding the index or key, and the
82     # value, thus ensuring arrays are iterated similarly to hashes.
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
92     ##
93     # Return the tuple itself
94     def value
95       @tuple
96     end
98     private
100     def hash?(ary_or_hash)
101       ary_or_hash.respond_to?(:keys)
102     end
104     ##
105     # Munges +ary+ into a valid Tuple.
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
114     ##
115     # Ensures +hash+ is a valid Tuple.
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
125   end
127   ##
128   # Templates are used to match tuples in Rinda.
130   class Template < Tuple
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
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.
169     def ===(tuple)
170       match(tuple)
171     end
173   end
174   
175   ##
176   # <i>Documentation?</i>
178   class DRbObjectTemplate
180     ##
181     # Creates a new DRbObjectTemplate that will match against +uri+ and +ref+.
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.
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
203   end
205   ##
206   # TupleSpaceProxy allows a remote Tuplespace to appear as local.
208   class TupleSpaceProxy
210     ##
211     # Creates a new TupleSpaceProxy to wrap +ts+.
213     def initialize(ts)
214       @ts = ts
215     end
216     
217     ##
218     # Adds +tuple+ to the proxied TupleSpace.  See TupleSpace#write.
220     def write(tuple, sec=nil)
221       @ts.write(tuple, sec)
222     end
223     
224     ##
225     # Takes +tuple+ from the proxied TupleSpace.  See TupleSpace#take.
227     def take(tuple, sec=nil, &block)
228       port = []
229       @ts.move(DRbObject.new(port), tuple, sec, &block)
230       port[0]
231     end
232     
233     ##
234     # Reads +tuple+ from the proxied TupleSpace.  See TupleSpace#read.
236     def read(tuple, sec=nil, &block)
237       @ts.read(tuple, sec, &block)
238     end
239     
240     ##
241     # Reads all tuples matching +tuple+ from the proxied TupleSpace.  See
242     # TupleSpace#read_all.
244     def read_all(tuple)
245       @ts.read_all(tuple)
246     end
247     
248     ##
249     # Registers for notifications of event +ev+ on the proxied TupleSpace.
250     # See TupleSpace#notify
252     def notify(ev, tuple, sec=nil)
253       @ts.notify(ev, tuple, sec)
254     end
256   end
258   ##
259   # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
260   # alive.
262   class SimpleRenewer
264     include DRbUndumped
266     ##
267     # Creates a new SimpleRenewer that keeps an object alive for another +sec+
268     # seconds.
270     def initialize(sec=180)
271       @sec = sec
272     end
274     ##
275     # Called by the TupleSpace to check if the object is still alive.
277     def renew
278       @sec
279     end
280   end