erlectricity 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/History.txt +14 -1
  2. data/LICENSE +20 -0
  3. data/README.md +130 -0
  4. data/Rakefile +63 -60
  5. data/VERSION.yml +4 -0
  6. data/examples/echo/README.md +12 -0
  7. data/examples/echo/echo.erl +13 -0
  8. data/examples/echo/echo.rb +11 -0
  9. data/examples/gruff/gruff.erl +17 -18
  10. data/examples/gruff/gruff_provider.rb +12 -19
  11. data/examples/gruff/{gruff_run.erl → gruff_run.sh} +5 -3
  12. data/examples/gruff/{stat_run.erl → stat_run.sh} +5 -3
  13. data/examples/gruff/stat_writer.erl +6 -6
  14. data/examples/simple/README.md +5 -0
  15. data/examples/simple/rerl.rb +111 -0
  16. data/examples/simple/rerl.sh +37 -0
  17. data/examples/tinderl/README.md +14 -0
  18. data/examples/tinderl/tinderl.erl +19 -21
  19. data/examples/tinderl/tinderl.rb +11 -10
  20. data/ext/decoder.c +67 -60
  21. data/lib/erlectricity.rb +4 -7
  22. data/lib/erlectricity/condition.rb +35 -20
  23. data/lib/erlectricity/conditions/boolean.rb +11 -0
  24. data/lib/erlectricity/conditions/hash.rb +9 -10
  25. data/lib/erlectricity/conditions/static.rb +31 -10
  26. data/lib/erlectricity/conditions/type.rb +14 -14
  27. data/lib/erlectricity/constants.rb +3 -4
  28. data/lib/erlectricity/decoder.rb +205 -199
  29. data/lib/erlectricity/encoder.rb +49 -35
  30. data/lib/erlectricity/errors/decode_error.rb +1 -1
  31. data/lib/erlectricity/errors/encode_error.rb +1 -1
  32. data/lib/erlectricity/errors/erlectricity_error.rb +1 -1
  33. data/lib/erlectricity/matcher.rb +15 -32
  34. data/lib/erlectricity/port.rb +11 -11
  35. data/lib/erlectricity/receiver.rb +54 -64
  36. data/lib/erlectricity/types/list.rb +3 -1
  37. data/test/condition_spec.rb +8 -9
  38. data/test/decode_spec.rb +27 -28
  39. data/test/encode_spec.rb +31 -24
  40. data/test/matcher_spec.rb +24 -12
  41. data/test/port_spec.rb +3 -4
  42. data/test/receiver_spec.rb +18 -20
  43. data/test/test_helper.rb +9 -5
  44. metadata +36 -29
  45. data/CONTRIBUTORS +0 -2
  46. data/Manifest.txt +0 -45
  47. data/README.txt +0 -43
  48. data/setup.rb +0 -1585
  49. data/test/test_erlectricity.rb +0 -2
data/lib/erlectricity.rb CHANGED
@@ -1,19 +1,18 @@
1
1
  $:.unshift File.join(File.dirname(__FILE__), *%w[.. ext])
2
2
 
3
+ require 'stringio'
3
4
 
4
5
  require 'erlectricity/constants'
5
-
6
6
  require 'erlectricity/types/new_reference'
7
7
  require 'erlectricity/types/pid'
8
8
  require 'erlectricity/types/function'
9
9
  require 'erlectricity/types/list'
10
10
 
11
-
12
11
  begin
13
- #try to load the decoder C extension
12
+ # try to load the decoder C extension
14
13
  require 'decoder'
15
14
  rescue LoadError
16
- #load the pure ruby instead
15
+ # fall back on the pure ruby version
17
16
  require 'erlectricity/decoder'
18
17
  end
19
18
 
@@ -21,14 +20,12 @@ require 'erlectricity/encoder'
21
20
 
22
21
  require 'erlectricity/port'
23
22
  require 'erlectricity/matcher'
24
-
25
23
  require 'erlectricity/condition'
24
+ require 'erlectricity/conditions/boolean'
26
25
  require 'erlectricity/conditions/hash'
27
26
  require 'erlectricity/conditions/static'
28
27
  require 'erlectricity/conditions/type'
29
-
30
28
  require 'erlectricity/receiver'
31
-
32
29
  require 'erlectricity/errors/erlectricity_error'
33
30
  require 'erlectricity/errors/decode_error'
34
31
  require 'erlectricity/errors/encode_error'
@@ -1,50 +1,65 @@
1
1
  module Erlectricity
2
2
  class Condition
3
-
3
+ def self.for(a)
4
+ case a
5
+ when Condition then a
6
+ when Class then TypeCondition.new(a)
7
+ else StaticCondition.new(a)
8
+ end
9
+ end
10
+
4
11
  def initialize
5
12
  end
6
-
13
+
7
14
  def binding_for(arg)
8
15
  nil
9
16
  end
10
-
17
+
11
18
  def satisfies?(arg)
12
19
  false
13
20
  end
14
-
21
+
15
22
  alias === satisfies?
16
23
  end
17
24
 
18
25
  module Conditions
19
- def atom()
26
+ def atom
20
27
  TypeCondition.new(Symbol)
21
28
  end
22
-
23
- def any()
29
+
30
+ def any
24
31
  TypeCondition.new(Object)
25
32
  end
26
-
27
- def number()
33
+
34
+ def number
28
35
  TypeCondition.new(Fixnum)
29
36
  end
30
-
31
- def pid()
37
+
38
+ def pid
32
39
  TypeCondition.new(Erlectricity::Pid)
33
40
  end
34
-
35
- def string()
36
- TypeCondition.new(String)
41
+
42
+ def ref
43
+ TypeCondition.new(Erlectricity::NewReference)
44
+ end
45
+
46
+ def string
47
+ TypeCondition.new(String)
37
48
  end
38
-
39
- def list()
40
- TypeCondition.new(Array)
49
+
50
+ def list
51
+ TypeCondition.new(Array)
41
52
  end
42
-
43
- def hash()
53
+
54
+ def hash
44
55
  HashCondition.new()
45
56
  end
57
+
58
+ def boolean
59
+ BooleanCondition.new()
60
+ end
46
61
  end
47
-
62
+
48
63
  extend Conditions
49
64
  end
50
65
 
@@ -0,0 +1,11 @@
1
+ module Erlectricity
2
+ class BooleanCondition < Condition
3
+ def satisfies?(arg)
4
+ [TrueClass, FalseClass].include?(arg.class)
5
+ end
6
+
7
+ def binding_for(arg)
8
+ arg
9
+ end
10
+ end
11
+ end
@@ -1,14 +1,13 @@
1
1
  module Erlectricity
2
- class HashCondition < Condition
2
+ class HashCondition < Condition
3
+ def satisfies?(arg)
4
+ return false unless arg.class == Array
5
+ arg.all? { |x| x.class == Array && x.length == 2 }
6
+ end
3
7
 
4
- def satisfies?(arg)
5
- return false unless arg.class == Array
6
- arg.all?{|x| x.class == Array && x.length == 2}
8
+ def binding_for(arg)
9
+ flattened = arg.inject([]) { |memo, kv| memo + kv }
10
+ Hash[*flattened]
11
+ end
7
12
  end
8
-
9
- def binding_for(arg)
10
- flattened = arg.inject([]){|memo, kv| memo + kv}
11
- Hash[*flattened]
12
- end
13
- end
14
13
  end
@@ -1,13 +1,34 @@
1
1
  module Erlectricity
2
- class StaticCondition < Condition
3
- attr_accessor :value
4
- def initialize(value)
5
- self.value = value
6
- end
7
-
8
- def satisfies?(arg)
9
- arg.eql? value
10
- end
2
+ class StaticCondition < Condition
3
+ attr_accessor :value
4
+ def initialize(value)
5
+ if value.is_a?(Array)
6
+ self.value = value.map do |v|
7
+ Condition.for(v)
8
+ end
9
+ else
10
+ self.value = value
11
+ end
12
+ end
11
13
 
14
+ def satisfies?(arg)
15
+ if value.is_a?(Array)
16
+ return false unless arg.is_a?(Array)
17
+ return false if value.length != arg.length
18
+ value.zip(arg).all? do |l, r|
19
+ l.respond_to?(:satisfies?) ? l.satisfies?(r) : l.eql?(r)
20
+ end
21
+ else
22
+ arg.eql?(value)
23
+ end
24
+ end
25
+
26
+ def binding_for(arg)
27
+ if value.is_a?(Array)
28
+ value.zip(arg).map { |l, r| l.binding_for(r) }.compact
29
+ else
30
+ nil
31
+ end
32
+ end
33
+ end
12
34
  end
13
- end
@@ -1,17 +1,17 @@
1
1
  module Erlectricity
2
- class TypeCondition < Condition
3
- attr_accessor :type
4
-
5
- def initialize(type)
6
- self.type = type
2
+ class TypeCondition < Condition
3
+ attr_accessor :type
4
+
5
+ def initialize(type)
6
+ self.type = type
7
+ end
8
+
9
+ def satisfies?(arg)
10
+ arg.is_a?(self.type)
11
+ end
12
+
13
+ def binding_for(arg)
14
+ arg
15
+ end
7
16
  end
8
-
9
- def satisfies?(arg)
10
- arg.is_a? self.type
11
- end
12
-
13
- def binding_for(arg)
14
- arg
15
- end
16
- end
17
17
  end
@@ -11,7 +11,7 @@ module Erlectricity
11
11
 
12
12
  ATOM = 100
13
13
  REF = 101 #old style reference
14
- NEW_REF = 114
14
+ NEW_REF = 114
15
15
  PORT = 102 #not supported accross node boundaries
16
16
  PID = 103
17
17
 
@@ -22,16 +22,15 @@ module Erlectricity
22
22
  STRING = 107
23
23
  LIST = 108
24
24
  BIN = 109
25
-
25
+
26
26
  FUN = 117
27
27
  NEW_FUN = 112
28
28
  end
29
29
 
30
30
  VERSION = 131
31
-
31
+
32
32
  MAX_INT = (1 << 27) -1
33
33
  MIN_INT = -(1 << 27)
34
34
  MAX_ATOM = 255
35
35
  end
36
-
37
36
  end
@@ -1,204 +1,210 @@
1
1
  module Erlectricity
2
- class Decoder
3
- attr_accessor :in
4
- include Erlectricity::External::Types
5
-
6
- def self.read_any_from(string)
7
- new(StringIO.new(string)).read_any
8
- end
9
-
10
- def initialize(ins)
11
- @in = ins
12
- @peeked = ""
13
- end
14
-
15
- def read_any
16
- fail("Bad Magic") unless read_1 == Erlectricity::External::VERSION
17
- read_any_raw
18
- end
19
-
20
- def read_any_raw
21
- case peek_1
22
- when ATOM then read_atom
23
- when SMALL_INT then read_small_int
24
- when INT then read_int
25
- when SMALL_BIGNUM then read_small_bignum
26
- when LARGE_BIGNUM then read_large_bignum
27
- when FLOAT then read_float
28
- when NEW_REF then read_new_reference
29
- when PID then read_pid
30
- when SMALL_TUPLE then read_small_tuple
31
- when LARGE_TUPLE then read_large_tuple
32
- when NIL then read_nil
33
- when STRING then read_erl_string
34
- when LIST then read_list
35
- when BIN then read_bin
36
- else
37
- fail("Unknown term tag: #{peek_1}")
2
+ class Decoder
3
+ attr_accessor :in
4
+ include Erlectricity::External::Types
5
+
6
+ def self.decode(string)
7
+ new(StringIO.new(string)).read_any
38
8
  end
39
- end
40
-
41
- def read(length)
42
- if length < @peeked.length
43
- result = @peeked[0...length]
44
- @peeked = @peeked[length..-1]
45
- length = 0
46
- else
47
- result = @peeked
48
- @peeked = ''
49
- length -= result.length
50
- end
51
-
52
- if length > 0
53
- result << @in.read(length)
54
- end
55
- result
56
- end
57
-
58
- def peek(length)
59
- if length <= @peeked.length
60
- @peeked[0...length]
61
- else
62
- read_bytes = @in.read(length - @peeked.length)
63
- @peeked << read_bytes if read_bytes
64
- @peeked
9
+
10
+ def initialize(ins)
11
+ @in = ins
12
+ @peeked = ""
13
+ end
14
+
15
+ def read_any
16
+ fail("Bad Magic") unless read_1 == Erlectricity::External::VERSION
17
+ read_any_raw
18
+ end
19
+
20
+ def read_any_raw
21
+ case peek_1
22
+ when ATOM then read_atom
23
+ when SMALL_INT then read_small_int
24
+ when INT then read_int
25
+ when SMALL_BIGNUM then read_small_bignum
26
+ when LARGE_BIGNUM then read_large_bignum
27
+ when FLOAT then read_float
28
+ when NEW_REF then read_new_reference
29
+ when PID then read_pid
30
+ when SMALL_TUPLE then read_small_tuple
31
+ when LARGE_TUPLE then read_large_tuple
32
+ when NIL then read_nil
33
+ when STRING then read_erl_string
34
+ when LIST then read_list
35
+ when BIN then read_bin
36
+ else
37
+ fail("Unknown term tag: #{peek_1}")
38
+ end
39
+ end
40
+
41
+ def read(length)
42
+ if length < @peeked.length
43
+ result = @peeked[0...length]
44
+ @peeked = @peeked[length..-1]
45
+ length = 0
46
+ else
47
+ result = @peeked
48
+ @peeked = ''
49
+ length -= result.length
50
+ end
51
+
52
+ if length > 0
53
+ result << @in.read(length)
54
+ end
55
+ result
56
+ end
57
+
58
+ def peek(length)
59
+ if length <= @peeked.length
60
+ @peeked[0...length]
61
+ else
62
+ read_bytes = @in.read(length - @peeked.length)
63
+ @peeked << read_bytes if read_bytes
64
+ @peeked
65
+ end
66
+ end
67
+
68
+ def peek_1
69
+ peek(1).unpack("C").first
70
+ end
71
+
72
+ def peek_2
73
+ peek(2).unpack("n").first
74
+ end
75
+
76
+ def read_1
77
+ read(1).unpack("C").first
78
+ end
79
+
80
+ def read_2
81
+ read(2).unpack("n").first
82
+ end
83
+
84
+ def read_4
85
+ read(4).unpack("N").first
86
+ end
87
+
88
+ def read_string(length)
89
+ read(length)
90
+ end
91
+
92
+ def read_atom
93
+ fail("Invalid Type, not an atom") unless read_1 == ATOM
94
+ length = read_2
95
+ a = read_string(length)
96
+ case a
97
+ when "true"
98
+ true
99
+ when "false"
100
+ false
101
+ else
102
+ a.to_sym
103
+ end
104
+ end
105
+
106
+ def read_small_int
107
+ fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
108
+ read_1
109
+ end
110
+
111
+ def read_int
112
+ fail("Invalid Type, not an int") unless read_1 == INT
113
+ value = read_4
114
+ negative = (value >> 31)[0] == 1
115
+ value = (value - (1 << 32)) if negative
116
+ value = Fixnum.induced_from(value)
117
+ end
118
+
119
+ def read_small_bignum
120
+ fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM
121
+ size = read_1
122
+ sign = read_1
123
+ bytes = read_string(size).unpack("C" * size)
124
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
125
+ byte, index = *byte_index
126
+ value = (byte * (256 ** index))
127
+ sign != 0 ? (result - value) : (result + value)
128
+ end
129
+ Bignum.induced_from(added)
130
+ end
131
+
132
+ def read_large_bignum
133
+ fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM
134
+ size = read_4
135
+ sign = read_1
136
+ bytes = read_string(size).unpack("C" * size)
137
+ added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
138
+ byte, index = *byte_index
139
+ value = (byte * (256 ** index))
140
+ sign != 0 ? (result - value) : (result + value)
141
+ end
142
+ Bignum.induced_from(added)
143
+ end
144
+
145
+ def read_float
146
+ fail("Invalid Type, not a float") unless read_1 == FLOAT
147
+ string_value = read_string(31)
148
+ result = string_value.to_f
149
+ end
150
+
151
+ def read_new_reference
152
+ fail("Invalid Type, not a new-style reference") unless read_1 == NEW_REF
153
+ size = read_2
154
+ node = read_atom
155
+ creation = read_1
156
+ id = (0...size).map { |i| read_4 }
157
+ NewReference.new(node, creation, id)
158
+ end
159
+
160
+ def read_pid
161
+ fail("Invalid Type, not a pid") unless read_1 == PID
162
+ node = read_atom
163
+ id = read_4
164
+ serial = read_4
165
+ creation = read_1
166
+ Pid.new(node, id, serial, creation)
167
+ end
168
+
169
+ def read_small_tuple
170
+ fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
171
+ arity = read_1
172
+ (0...arity).map { |i| read_any_raw }
173
+ end
174
+
175
+ def read_large_tuple
176
+ fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
177
+ arity = read_4
178
+ (0...arity).map { |i| read_any_raw }
179
+ end
180
+
181
+ def read_nil
182
+ fail("Invalid Type, not a nil list") unless read_1 == NIL
183
+ []
184
+ end
185
+
186
+ def read_erl_string
187
+ fail("Invalid Type, not an erlang string") unless read_1 == STRING
188
+ length = read_2
189
+ read_string(length).unpack('C' * length)
190
+ end
191
+
192
+ def read_list
193
+ fail("Invalid Type, not an erlang list") unless read_1 == LIST
194
+ length = read_4
195
+ list = (0...length).map { |i| read_any_raw }
196
+ read_1
197
+ list
198
+ end
199
+
200
+ def read_bin
201
+ fail("Invalid Type, not an erlang binary") unless read_1 == BIN
202
+ length = read_4
203
+ read_string(length)
204
+ end
205
+
206
+ def fail(str)
207
+ raise DecodeError, str
65
208
  end
66
209
  end
67
-
68
- def peek_1
69
- peek(1).unpack("C").first
70
- end
71
-
72
- def peek_2
73
- peek(2).unpack("n").first
74
- end
75
-
76
- def read_1
77
- read(1).unpack("C").first
78
- end
79
-
80
- def read_2
81
- read(2).unpack("n").first
82
- end
83
-
84
- def read_4
85
- read(4).unpack("N").first
86
- end
87
-
88
- def read_string(length)
89
- read(length)
90
- end
91
-
92
- def read_atom
93
- fail("Invalid Type, not an atom") unless read_1 == ATOM
94
- length = read_2
95
- read_string(length).to_sym
96
- end
97
-
98
- def read_small_int
99
- fail("Invalid Type, not a small int") unless read_1 == SMALL_INT
100
- read_1
101
- end
102
-
103
- def read_int
104
- fail("Invalid Type, not an int") unless read_1 == INT
105
- value = read_4
106
- negative = (value >> 31)[0] == 1
107
- value = (value - (1 << 32)) if negative
108
- value = Fixnum.induced_from(value)
109
- end
110
-
111
- def read_small_bignum
112
- fail("Invalid Type, not a small bignum") unless read_1 == SMALL_BIGNUM
113
- size = read_1
114
- sign = read_1
115
- bytes = read_string(size).unpack("C" * size)
116
- added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
117
- byte, index = *byte_index
118
- value = (byte * (256 ** index))
119
- sign != 0 ? (result - value) : (result + value)
120
- end
121
- Bignum.induced_from(added)
122
- end
123
-
124
- def read_large_bignum
125
- fail("Invalid Type, not a large bignum") unless read_1 == LARGE_BIGNUM
126
- size = read_4
127
- sign = read_1
128
- bytes = read_string(size).unpack("C" * size)
129
- added = bytes.zip((0..bytes.length).to_a).inject(0) do |result, byte_index|
130
- byte, index = *byte_index
131
- value = (byte * (256 ** index))
132
- sign != 0 ? (result - value) : (result + value)
133
- end
134
- Bignum.induced_from(added)
135
- end
136
-
137
- def read_float
138
- fail("Invalid Type, not a float") unless read_1 == FLOAT
139
- string_value = read_string(31)
140
- result = string_value.to_f
141
- end
142
-
143
- def read_new_reference
144
- fail("Invalid Type, not a new-style reference") unless read_1 == NEW_REF
145
- size = read_2
146
- node = read_atom
147
- creation = read_1
148
- id = (0...size).map{|i| read_4 }
149
- NewReference.new(node, creation, id)
150
- end
151
-
152
- def read_pid
153
- fail("Invalid Type, not a pid") unless read_1 == PID
154
- node = read_atom
155
- id = read_4
156
- serial = read_4
157
- creation = read_1
158
- Pid.new(node, id, serial, creation)
159
- end
160
-
161
- def read_small_tuple
162
- fail("Invalid Type, not a small tuple") unless read_1 == SMALL_TUPLE
163
- arity = read_1
164
-
165
- (0...arity).map{|i| read_any_raw }
166
- end
167
-
168
- def read_large_tuple
169
- fail("Invalid Type, not a small tuple") unless read_1 == LARGE_TUPLE
170
- arity = read_4
171
- (0...arity).map{|i| read_any_raw}
172
- end
173
-
174
- def read_nil
175
- fail("Invalid Type, not a nil list") unless read_1 == NIL
176
- []
177
- end
178
-
179
- def read_erl_string
180
- fail("Invalid Type, not an erlang string") unless read_1 == STRING
181
- length = read_2
182
- read_string(length).unpack('C' * length)
183
- end
184
-
185
- def read_list
186
- fail("Invalid Type, not an erlang list") unless read_1 == LIST
187
- length = read_4
188
- list = (0...length).map{|i| read_any_raw}
189
- read_1
190
- list
191
- end
192
-
193
- def read_bin
194
- fail("Invalid Type, not an erlang binary") unless read_1 == BIN
195
- length = read_4
196
- read_string(length)
197
- end
198
-
199
- def fail(str)
200
- raise DecodeError, str
201
- end
202
-
203
210
  end
204
- end