/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

  1. require 'drb/drb'
  2. require 'thread'
  3. ##
  4. # A module to implement the Linda distributed computing paradigm in Ruby.
  5. #
  6. # Rinda is part of DRb (dRuby).
  7. #
  8. # == Example(s)
  9. #
  10. # See the sample/drb/ directory in the Ruby distribution, from 1.8.2 onwards.
  11. #
  12. #--
  13. # TODO
  14. # == Introduction to Linda/rinda?
  15. #
  16. # == Why is this library separate from DRb?
  17. module Rinda
  18. ##
  19. # Rinda error base class
  20. class RindaError < RuntimeError; end
  21. ##
  22. # Raised when a hash-based tuple has an invalid key.
  23. class InvalidHashTupleKey < RindaError; end
  24. ##
  25. # Raised when trying to use a canceled tuple.
  26. class RequestCanceledError < ThreadError; end
  27. ##
  28. # Raised when trying to use an expired tuple.
  29. class RequestExpiredError < ThreadError; end
  30. ##
  31. # A tuple is the elementary object in Rinda programming.
  32. # Tuples may be matched against templates if the tuple and
  33. # the template are the same size.
  34. class Tuple
  35. ##
  36. # Creates a new Tuple from +ary_or_hash+ which must be an Array or Hash.
  37. def initialize(ary_or_hash)
  38. if hash?(ary_or_hash)
  39. init_with_hash(ary_or_hash)
  40. else
  41. init_with_ary(ary_or_hash)
  42. end
  43. end
  44. ##
  45. # The number of elements in the tuple.
  46. def size
  47. @tuple.size
  48. end
  49. ##
  50. # Accessor method for elements of the tuple.
  51. def [](k)
  52. @tuple[k]
  53. end
  54. ##
  55. # Fetches item +k+ from the tuple.
  56. def fetch(k)
  57. @tuple.fetch(k)
  58. end
  59. ##
  60. # Iterate through the tuple, yielding the index or key, and the
  61. # value, thus ensuring arrays are iterated similarly to hashes.
  62. def each # FIXME
  63. if Hash === @tuple
  64. @tuple.each { |k, v| yield(k, v) }
  65. else
  66. @tuple.each_with_index { |v, k| yield(k, v) }
  67. end
  68. end
  69. ##
  70. # Return the tuple itself
  71. def value
  72. @tuple
  73. end
  74. private
  75. def hash?(ary_or_hash)
  76. ary_or_hash.respond_to?(:keys)
  77. end
  78. ##
  79. # Munges +ary+ into a valid Tuple.
  80. def init_with_ary(ary)
  81. @tuple = Array.new(ary.size)
  82. @tuple.size.times do |i|
  83. @tuple[i] = ary[i]
  84. end
  85. end
  86. ##
  87. # Ensures +hash+ is a valid Tuple.
  88. def init_with_hash(hash)
  89. @tuple = Hash.new
  90. hash.each do |k, v|
  91. raise InvalidHashTupleKey unless String === k
  92. @tuple[k] = v
  93. end
  94. end
  95. end
  96. ##
  97. # Templates are used to match tuples in Rinda.
  98. class Template < Tuple
  99. ##
  100. # Matches this template against +tuple+. The +tuple+ must be the same
  101. # size as the template. An element with a +nil+ value in a template acts
  102. # as a wildcard, matching any value in the corresponding position in the
  103. # tuple. Elements of the template match the +tuple+ if the are #== or
  104. # #===.
  105. #
  106. # Template.new([:foo, 5]).match Tuple.new([:foo, 5]) # => true
  107. # Template.new([:foo, nil]).match Tuple.new([:foo, 5]) # => true
  108. # Template.new([String]).match Tuple.new(['hello']) # => true
  109. #
  110. # Template.new([:foo]).match Tuple.new([:foo, 5]) # => false
  111. # Template.new([:foo, 6]).match Tuple.new([:foo, 5]) # => false
  112. # Template.new([:foo, nil]).match Tuple.new([:foo]) # => false
  113. # Template.new([:foo, 6]).match Tuple.new([:foo]) # => false
  114. def match(tuple)
  115. return false unless tuple.respond_to?(:size)
  116. return false unless tuple.respond_to?(:fetch)
  117. return false unless self.size == tuple.size
  118. each do |k, v|
  119. begin
  120. it = tuple.fetch(k)
  121. rescue
  122. return false
  123. end
  124. next if v.nil?
  125. next if v == it
  126. next if v === it
  127. return false
  128. end
  129. return true
  130. end
  131. ##
  132. # Alias for #match.
  133. def ===(tuple)
  134. match(tuple)
  135. end
  136. end
  137. ##
  138. # <i>Documentation?</i>
  139. class DRbObjectTemplate
  140. ##
  141. # Creates a new DRbObjectTemplate that will match against +uri+ and +ref+.
  142. def initialize(uri=nil, ref=nil)
  143. @drb_uri = uri
  144. @drb_ref = ref
  145. end
  146. ##
  147. # This DRbObjectTemplate matches +ro+ if the remote object's drburi and
  148. # drbref are the same. +nil+ is used as a wildcard.
  149. def ===(ro)
  150. return true if super(ro)
  151. unless @drb_uri.nil?
  152. return false unless (@drb_uri === ro.__drburi rescue false)
  153. end
  154. unless @drb_ref.nil?
  155. return false unless (@drb_ref === ro.__drbref rescue false)
  156. end
  157. true
  158. end
  159. end
  160. ##
  161. # TupleSpaceProxy allows a remote Tuplespace to appear as local.
  162. class TupleSpaceProxy
  163. ##
  164. # Creates a new TupleSpaceProxy to wrap +ts+.
  165. def initialize(ts)
  166. @ts = ts
  167. end
  168. ##
  169. # Adds +tuple+ to the proxied TupleSpace. See TupleSpace#write.
  170. def write(tuple, sec=nil)
  171. @ts.write(tuple, sec)
  172. end
  173. ##
  174. # Takes +tuple+ from the proxied TupleSpace. See TupleSpace#take.
  175. def take(tuple, sec=nil, &block)
  176. port = []
  177. @ts.move(DRbObject.new(port), tuple, sec, &block)
  178. port[0]
  179. end
  180. ##
  181. # Reads +tuple+ from the proxied TupleSpace. See TupleSpace#read.
  182. def read(tuple, sec=nil, &block)
  183. @ts.read(tuple, sec, &block)
  184. end
  185. ##
  186. # Reads all tuples matching +tuple+ from the proxied TupleSpace. See
  187. # TupleSpace#read_all.
  188. def read_all(tuple)
  189. @ts.read_all(tuple)
  190. end
  191. ##
  192. # Registers for notifications of event +ev+ on the proxied TupleSpace.
  193. # See TupleSpace#notify
  194. def notify(ev, tuple, sec=nil)
  195. @ts.notify(ev, tuple, sec)
  196. end
  197. end
  198. ##
  199. # An SimpleRenewer allows a TupleSpace to check if a TupleEntry is still
  200. # alive.
  201. class SimpleRenewer
  202. include DRbUndumped
  203. ##
  204. # Creates a new SimpleRenewer that keeps an object alive for another +sec+
  205. # seconds.
  206. def initialize(sec=180)
  207. @sec = sec
  208. end
  209. ##
  210. # Called by the TupleSpace to check if the object is still alive.
  211. def renew
  212. @sec
  213. end
  214. end
  215. end