summaryrefslogtreecommitdiff
path: root/lib/rinda/rinda.rb
blob: 0352a7be90927034ea1cb73d1f73995817310815 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#
# rinda.rb: A Ruby implementation of the Linda distributed computing paradigm.
#
# <i>Introduction to Linda/rinda?</i>
#
# <i>Why is this library separate from <tt>drb</tt>?</i> 
#
# <i>Example(s)</i> 
#
# (See the samples directory in the Ruby distribution, from 1.8.2 onwards.)
#

require 'drb/drb'
require 'thread'

#
# A module to implement the Linda programming paradigm in Ruby.
# This is part of +drb+ (dRuby).
#
module Rinda
  class RindaError < RuntimeError; end
  class InvalidHashTupleKey < RindaError; end
  class RequestCanceledError < ThreadError; end
  class RequestExpiredError < ThreadError; end

  #
  # A tuple is the elementary object in Rinda programming.
  # Tuples may be matched against templates if the tuple and
  # the template are the same size.
  #
  class Tuple
    # Initialize a tuple with an Array or a Hash.
    def initialize(ary_or_hash)
      if Hash === ary_or_hash
	init_with_hash(ary_or_hash)
      else
	init_with_ary(ary_or_hash)
      end
    end

    # The number of elements in the tuple.
    def size
      @tuple.size
    end

    # Accessor method for elements of the tuple.
    def [](k)
      @tuple[k]
    end

    def fetch(k)
      @tuple.fetch(k)
    end

    # Iterate through the tuple, yielding the index or key, and the
    # value, thus ensuring arrays are iterated similarly to hashes.
    def each # FIXME
      if Hash === @tuple
	@tuple.each { |k, v| yield(k, v) }
      else
	@tuple.each_with_index { |v, k| yield(k, v) }
      end
    end

    # Return the tuple itself -- i.e the Array or hash.
    def value
      @tuple
    end

    private
    def init_with_ary(ary)
      @tuple_size = ary.size
      @tuple = Array.new(@tuple_size)
      @tuple.size.times do |i|
	@tuple[i] = ary[i]
      end
    end

    def init_with_hash(hash)
      @tuple_size = hash[:size]
      @tuple = Hash.new
      hash.each do |k, v|
        next if k == :size
        raise InvalidHashTupleKey unless String === k
	@tuple[k] = v
      end
    end
  end

  #
  # Templates are used to match tuples in Rinda.
  #
  class Template < Tuple
    # Perform the matching of a tuple against a template.  An
    # element with a +nil+ value in a template acts as a wildcard,
    # matching any value in the corresponding position in the tuple.
    def match(tuple)
      return false unless tuple.respond_to?(:size)
      return false unless tuple.respond_to?(:fetch)
      return false if @tuple_size && (@tuple_size != tuple.size)
      each do |k, v|
        begin
          it = tuple.fetch(k)
        rescue
          return false
        end
	next if v.nil?
        next if v == it
        next if v === it
	return false
      end
      return true
    end
    
    # Alias for #match.
    def ===(tuple)
      match(tuple)
    end
  end
  
  #
  # <i>Documentation?</i>
  #
  class DRbObjectTemplate
    def initialize(uri=nil, ref=nil)
      @drb_uri = uri
      @drb_ref = ref
    end
    
    def ===(ro)
      return true if super(ro)
      unless @drb_uri.nil?
	return false unless (@drb_uri === ro.__drburi rescue false)
      end
      unless @drb_ref.nil?
	return false unless (@drb_ref === ro.__drbref rescue false)
      end
      true
    end
  end

  #
  # TupleSpaceProxy allows a remote Tuplespace to appear as local.
  #
  class TupleSpaceProxy
    def initialize(ts)
      @ts = ts
    end
    
    def write(tuple, sec=nil)
      @ts.write(tuple, sec)
    end
    
    def take(tuple, sec=nil, &block)
      port = []
      @ts.move(DRbObject.new(port), tuple, sec, &block)
      port[0]
    end
    
    def read(tuple, sec=nil, &block)
      @ts.read(tuple, sec, &block)
    end
    
    def read_all(tuple)
      @ts.read_all(tuple)
    end
    
    def notify(ev, tuple, sec=nil)
      @ts.notify(ev, tuple, sec)
    end
  end

  #
  # <i>Documentation?</i>
  #
  class SimpleRenewer
    include DRbUndumped
    def initialize(sec=180)
      @sec = sec
    end

    def renew
      @sec
    end
  end
end