PageRenderTime 95ms CodeModel.GetById 68ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/rinda/rinda.rb

http://github.com/agross/netopenspace
Ruby | 283 lines | 122 code | 68 blank | 93 comment | 8 complexity | a91845d874cfd1e9584a361a0e924425 MD5 | raw file
  1require 'drb/drb'
  2require 'thread'
  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
 19module 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    ##
211    # Creates a new TupleSpaceProxy to wrap +ts+.
212
213    def initialize(ts)
214      @ts = ts
215    end
216    
217    ##
218    # Adds +tuple+ to the proxied TupleSpace.  See TupleSpace#write.
219
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.
226
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.
235
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.
243
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
251
252    def notify(ev, tuple, sec=nil)
253      @ts.notify(ev, tuple, sec)
254    end
255
256  end
257
258  ##
259  # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
260  # alive.
261
262  class SimpleRenewer
263
264    include DRbUndumped
265
266    ##
267    # Creates a new SimpleRenewer that keeps an object alive for another +sec+
268    # seconds.
269
270    def initialize(sec=180)
271      @sec = sec
272    end
273
274    ##
275    # Called by the TupleSpace to check if the object is still alive.
276
277    def renew
278      @sec
279    end
280  end
281
282end
283