erlectricity 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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