beambridge 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/History.txt +35 -0
  6. data/LICENSE +20 -0
  7. data/README.md +130 -0
  8. data/Rakefile +44 -0
  9. data/VERSION.yml +4 -0
  10. data/beambridge.gemspec +29 -0
  11. data/benchmarks/bench.rb +21 -0
  12. data/examples/echo/README.md +12 -0
  13. data/examples/echo/echo.erl +13 -0
  14. data/examples/echo/echo.rb +10 -0
  15. data/examples/gruff/gruff.erl +61 -0
  16. data/examples/gruff/gruff_provider.rb +30 -0
  17. data/examples/gruff/gruff_run.sh +19 -0
  18. data/examples/gruff/stat_run.sh +20 -0
  19. data/examples/gruff/stat_writer.erl +40 -0
  20. data/examples/simple/README.md +5 -0
  21. data/examples/simple/rerl.rb +110 -0
  22. data/examples/simple/rerl.sh +37 -0
  23. data/examples/tinderl/README.md +14 -0
  24. data/examples/tinderl/tinderl.erl +43 -0
  25. data/examples/tinderl/tinderl.rb +27 -0
  26. data/ext/decoder.c +398 -0
  27. data/ext/extconf.rb +11 -0
  28. data/lib/beambridge.rb +34 -0
  29. data/lib/beambridge/condition.rb +66 -0
  30. data/lib/beambridge/conditions/boolean.rb +11 -0
  31. data/lib/beambridge/conditions/hash.rb +13 -0
  32. data/lib/beambridge/conditions/static.rb +34 -0
  33. data/lib/beambridge/conditions/type.rb +17 -0
  34. data/lib/beambridge/constants.rb +36 -0
  35. data/lib/beambridge/decoder.rb +212 -0
  36. data/lib/beambridge/encoder.rb +164 -0
  37. data/lib/beambridge/errors/beambridge_error.rb +3 -0
  38. data/lib/beambridge/errors/decode_error.rb +3 -0
  39. data/lib/beambridge/errors/encode_error.rb +3 -0
  40. data/lib/beambridge/matcher.rb +21 -0
  41. data/lib/beambridge/port.rb +48 -0
  42. data/lib/beambridge/receiver.rb +69 -0
  43. data/lib/beambridge/types/function.rb +3 -0
  44. data/lib/beambridge/types/list.rb +3 -0
  45. data/lib/beambridge/types/new_function.rb +3 -0
  46. data/lib/beambridge/types/new_reference.rb +3 -0
  47. data/lib/beambridge/types/pid.rb +3 -0
  48. data/lib/beambridge/types/reference.rb +3 -0
  49. data/lib/beambridge/version.rb +3 -0
  50. data/spec/condition_spec.rb +72 -0
  51. data/spec/decode_spec.rb +143 -0
  52. data/spec/encode_spec.rb +152 -0
  53. data/spec/matcher_spec.rb +81 -0
  54. data/spec/port_spec.rb +34 -0
  55. data/spec/receiver_spec.rb +103 -0
  56. data/spec/spec_helper.rb +47 -0
  57. metadata +153 -0
@@ -0,0 +1,3 @@
1
+ class BeambridgeError < StandardError
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class DecodeError < BeambridgeError
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ class EncodeError < BeambridgeError
2
+
3
+ end
@@ -0,0 +1,21 @@
1
+ module Beambridge
2
+ class Matcher
3
+ attr_accessor :condition, :block
4
+ attr_accessor :receiver
5
+
6
+ def initialize(parent, condition, block)
7
+ self.receiver = parent
8
+ @block = block
9
+ @condition = Condition.for(condition)
10
+ end
11
+
12
+ def run(arg)
13
+ args = @condition.binding_for(arg)
14
+ block.call(*args)
15
+ end
16
+
17
+ def matches?(arg)
18
+ @condition.satisfies?(arg)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ module Beambridge
2
+ class Port
3
+ attr_reader :input, :output
4
+ attr_reader :skipped
5
+ attr_reader :queue
6
+
7
+ def initialize(input=STDIN, output=STDOUT)
8
+ @input = input
9
+ @output = output
10
+
11
+ input.sync = true
12
+ output.sync = true
13
+
14
+ output.set_encoding("ASCII-8BIT")
15
+
16
+ @encoder = Beambridge::Encoder.new(nil)
17
+ @skipped = []
18
+ @queue = []
19
+ end
20
+
21
+ def receive
22
+ queue.empty? ? read_from_input : queue.shift
23
+ end
24
+
25
+ def send(term)
26
+ @encoder.out = StringIO.new('', 'w')
27
+ @encoder.write_any(term)
28
+ data = @encoder.out.string
29
+ output.write([data.length].pack("N"))
30
+ output.write(data)
31
+ end
32
+
33
+ def restore_skipped
34
+ @queue = self.skipped + self.queue
35
+ end
36
+
37
+ private
38
+
39
+ def read_from_input
40
+ raw = input.read(4)
41
+ return nil unless raw
42
+
43
+ packet_length = raw.unpack('N').first
44
+ data = input.read(packet_length)
45
+ Beambridge::Decoder.decode(data)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,69 @@
1
+ module Beambridge
2
+ class Receiver
3
+ attr_accessor :port
4
+ attr_accessor :parent
5
+ attr_accessor :matchers
6
+
7
+ RECEIVE_LOOP = Object.new
8
+ NO_MATCH = Object.new
9
+
10
+ def initialize(port, parent = nil, &block)
11
+ @port = port
12
+ @parent = parent
13
+ @matchers = []
14
+ block.call(self) if block
15
+ end
16
+
17
+ def process(arg)
18
+ matcher = @matchers.find { |r| r.matches?(arg) }
19
+
20
+ if matcher
21
+ port.restore_skipped
22
+ matcher.run(arg)
23
+ else
24
+ NO_MATCH
25
+ end
26
+ end
27
+
28
+ def when(arg, &block)
29
+ condition = Condition.for(arg)
30
+ @matchers << Matcher.new(self, condition, block)
31
+ end
32
+
33
+ def run
34
+ loop do
35
+ msg = port.receive
36
+ return if msg.nil?
37
+
38
+ case result = process(msg)
39
+ when RECEIVE_LOOP then next
40
+ when NO_MATCH
41
+ port.skipped << msg
42
+ next
43
+ else
44
+ break result
45
+ end
46
+ end
47
+ end
48
+
49
+ def receive(&block)
50
+ Receiver.new(port, self, &block).run
51
+ end
52
+
53
+ def receive_loop
54
+ RECEIVE_LOOP
55
+ end
56
+
57
+ def send!(term)
58
+ port.send(term)
59
+ end
60
+ end
61
+ end
62
+
63
+ module Kernel
64
+ def receive(input = nil, output = nil, &block)
65
+ input ||= IO.new(3)
66
+ output ||= IO.new(4)
67
+ Beambridge::Receiver.new(Beambridge::Port.new(input, output), nil, &block).run
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ Function = Struct.new :pid, :module, :index, :uniq, :free_vars
3
+ end
@@ -0,0 +1,3 @@
1
+ class Beambridge::List < Array
2
+
3
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ NewFunction = Struct.new :arity, :uniq, :index,:num_free, :module, :old_index, :old_uniq, :pid, :free_vars
3
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ NewReference = Struct.new :node, :creation, :id
3
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ Pid = Struct.new :node, :id, :serial, :creation
3
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ Reference = Struct.new :node, :id, :creator
3
+ end
@@ -0,0 +1,3 @@
1
+ module Beambridge
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ describe "Beambridge::StaticConditions" do
4
+ it "should satisfy on the same value" do
5
+ Beambridge::StaticCondition.new(:foo).satisfies?(:foo).should == true
6
+ Beambridge::StaticCondition.new([:foo]).satisfies?([:foo]).should == true
7
+ Beambridge::StaticCondition.new(3).satisfies?(3).should == true
8
+ end
9
+
10
+ it "should not satisfy on different values" do
11
+ Beambridge::StaticCondition.new(:foo).satisfies?("foo").should == false
12
+ Beambridge::StaticCondition.new([:foo]).satisfies?(:foo).should == false
13
+ Beambridge::StaticCondition.new(Object.new).satisfies?(Object.new).should == false
14
+ Beambridge::StaticCondition.new(3).satisfies?(3.0).should == false
15
+ end
16
+
17
+ it "should not produce any bindings" do
18
+ s = Beambridge::StaticCondition.new(:foo)
19
+ s.binding_for(:foo).should == nil
20
+ end
21
+ end
22
+
23
+ describe "Beambridge::TypeConditions" do
24
+ it "should be satisfied when the arg has the same class" do
25
+ Beambridge::TypeCondition.new(Symbol).satisfies?(:foo).should == true
26
+ Beambridge::TypeCondition.new(Symbol).satisfies?(:bar).should == true
27
+ Beambridge::TypeCondition.new(String).satisfies?("foo").should == true
28
+ Beambridge::TypeCondition.new(String).satisfies?("bar").should == true
29
+ Beambridge::TypeCondition.new(Array).satisfies?([]).should == true
30
+ Beambridge::TypeCondition.new(Fixnum).satisfies?(3).should == true
31
+ end
32
+
33
+ it "should be satisfied when the arg is of a descendent class" do
34
+ Beambridge::TypeCondition.new(Object).satisfies?(:foo).should == true
35
+ Beambridge::TypeCondition.new(Object).satisfies?("foo").should == true
36
+ Beambridge::TypeCondition.new(Object).satisfies?(3).should == true
37
+ end
38
+
39
+ it "should not be satisfied when the arg is of a different class" do
40
+ Beambridge::TypeCondition.new(String).satisfies?(:foo).should == false
41
+ Beambridge::TypeCondition.new(Symbol).satisfies?("foo").should == false
42
+ Beambridge::TypeCondition.new(Fixnum).satisfies?(3.0).should == false
43
+ end
44
+
45
+ it "should bind the arg with no transormations" do
46
+ s = Beambridge::TypeCondition.new(Symbol)
47
+ s.binding_for(:foo).should == :foo
48
+ s.binding_for(:bar).should == :bar
49
+ end
50
+ end
51
+
52
+ describe "Beambridge::HashConditions" do
53
+ it "should satisfy an args of the form [[key, value], [key, value]]" do
54
+ Beambridge::HashCondition.new.satisfies?([[:foo, 3], [:bar, Object.new]]).should == true
55
+ Beambridge::HashCondition.new.satisfies?([[:foo, 3]]).should == true
56
+ end
57
+
58
+ it "should satisfy on empty arrays" do
59
+ Beambridge::HashCondition.new.satisfies?([]).should == true
60
+ end
61
+
62
+ it "should nat satisfy other args" do
63
+ Beambridge::HashCondition.new.satisfies?(:foo).should == false
64
+ Beambridge::HashCondition.new.satisfies?("foo").should == false
65
+ Beambridge::HashCondition.new.satisfies?(3.0).should == false
66
+ end
67
+
68
+ it "should bind to a Hash" do
69
+ s = Beambridge::HashCondition.new()
70
+ s.binding_for([[:foo, 3], [:bar, [3,4,5]]]).should == {:foo => 3, :bar => [3,4,5] }
71
+ end
72
+ end
@@ -0,0 +1,143 @@
1
+ require "spec_helper"
2
+
3
+ describe "When unpacking from a binary stream" do
4
+
5
+ it "an erlang atom should decode to a ruby symbol" do
6
+ get("haha").should == :haha
7
+ end
8
+
9
+ it "an erlang number encoded as a small_int (< 255) should decode to a fixnum" do
10
+ get("0").should == 0
11
+ get("255").should == 255
12
+ end
13
+
14
+ it "an erlang number encoded as a int (signed 27-bit number) should decode to a fixnum" do
15
+ get("256").should == 256
16
+ get("#{(1 << 27) -1}").should == (1 << 27) -1
17
+ get("-1").should == -1
18
+ get("#{-(1 << 27)}").should == -(1 << 27)
19
+ end
20
+
21
+ it "an erlang number encoded as a small bignum (1 byte length) should decode to fixnum if it can" do
22
+ get("#{(1 << 27)}").should == (1 << 27)
23
+ get("#{-(1 << 27) - 1}").should == -(1 << 27) - 1
24
+ get("#{(1 << word_length) - 1}").should == (1 << word_length) - 1
25
+ get("#{-(1 << word_length)}").should == -(1 << word_length)
26
+ end
27
+
28
+ it "an erlang number encoded as a small bignum (1 byte length) should decode to bignum if it can't be a fixnum" do
29
+ get("#{(1 << word_length)}").should == (1 << word_length)
30
+ get("#{-(1 << word_length) - 1}").should == -(1 << word_length) - 1
31
+ get("#{(1 << (255 * 8)) - 1}").should == (1 << (255 * 8)) - 1
32
+ get("#{-((1 << (255 * 8)) - 1)}").should == -((1 << (255 * 8)) - 1)
33
+ end
34
+
35
+ it "an erlang number encoded as a big bignum (4 byte length) should decode to bignum" do
36
+ get("#{(1 << (255 * 8)) }").should == (1 << (255 * 8))
37
+ get("#{-(1 << (255 * 8))}").should == -(1 << (255 * 8))
38
+ get("#{(1 << (512 * 8)) }").should == (1 << (512 * 8))
39
+ get("#{-(1 << (512 * 8))}").should == -(1 << (512 * 8))
40
+ end
41
+
42
+ it "an erlang float should decode to a Float" do
43
+ get("#{1.0}").should == 1.0
44
+ get("#{-1.0}").should == -1.0
45
+ get("#{123.456}").should == 123.456
46
+ get("#{123.456789012345}").should == 123.456789012345
47
+ end
48
+
49
+ it "an erlang reference should decode to a Reference object" do
50
+ ref = get("make_ref()")
51
+ ref.should be_an_instance_of Beambridge::NewReference
52
+ ref.node.should be_an_instance_of Symbol
53
+ end
54
+
55
+ it "an erlang pid should decode to a Pid object" do
56
+ pid = get("spawn(fun() -> 3 end)")
57
+ pid.should be_an_instance_of Beambridge::Pid
58
+ pid.node.should be_an_instance_of Symbol
59
+ end
60
+
61
+ it "an erlang tuple encoded as a small tuple (1-byte length) should decode to an array" do
62
+ ref = get("{3}")
63
+ ref.length.should == 1
64
+ ref.first.should == 3
65
+
66
+ ref = get("{3, a, make_ref()}")
67
+ ref.length.should == 3
68
+ ref[0].should == 3
69
+ ref[1].should == :a
70
+ ref[2].class.should == Beambridge::NewReference
71
+
72
+ tuple_meat = (['3'] * 255).join(', ')
73
+ ref = get("{#{tuple_meat}}")
74
+ ref.length.should == 255
75
+ ref.each{|r| r.should == 3}
76
+ end
77
+
78
+ it "an erlang tuple encoded as a large tuple (4-byte length) should decode to an array" do
79
+ tuple_meat = (['3'] * 256).join(', ')
80
+ ref = get("{#{tuple_meat}}")
81
+ ref.length.should == 256
82
+ ref.each{|r| r.should == 3}
83
+
84
+ tuple_meat = (['3'] * 512).join(', ')
85
+ ref = get("{#{tuple_meat}}")
86
+ ref.length.should == 512
87
+ ref.each{|r| r.should == 3}
88
+ end
89
+
90
+ it "an empty erlang list encoded as a nil should decode to an array" do
91
+ get("[]").class.should == Erl::List
92
+ get("[]").should == []
93
+ end
94
+
95
+ it "an erlang list encoded as a string should decode to an array of bytes (less than ideal, but consistent)" do
96
+ get("\"asdasd\"").class.should == Erl::List
97
+ get("\"asdasd\"").should == "asdasd".bytes.to_a
98
+ get("\"#{'a' * 65534}\"").should == "a".bytes.to_a * 65534
99
+ end
100
+
101
+ it "an erlang list encoded as a list should decode to an erl::list" do
102
+ get("[3,4,256]").class.should == Erl::List
103
+ get("[3,4,256]").should == [3,4,256]
104
+ get("\"#{'a' * 65535 }\"").should == [97] * 65535
105
+ get("[3,4, foo, {3,4,5,bar}, 256]").should == [3,4, :foo, [3,4,5,:bar], 256]
106
+ end
107
+
108
+ it "an erlang binary should decode to a string" do
109
+ get("<< 3,4,255 >>").should == "\003\004\377"
110
+ get("<< \"whatup\" >>").should == "whatup"
111
+ get("<< 99,0,99 >>").should == "c\000c"
112
+ end
113
+
114
+ it "the empty atom should decode to the empty symbol" do
115
+ empty_symbol = get("''")
116
+ empty_symbol.should be_an_instance_of Symbol
117
+ empty_symbol.to_s.should == ""
118
+ end
119
+
120
+ it "erlang atomic booleans should decode to ruby booleans" do
121
+ get("true").should == true
122
+ get("false").should == false
123
+ get("falsereio").should == :falsereio
124
+ get("t").should == :t
125
+ get("f").should == :f
126
+ end
127
+
128
+ it "massive binaries should not overflow the stack" do
129
+ bin = [131,109,0,128,0,0].pack('c*') + ('a' * (8 * 1024 * 1024))
130
+ Beambridge::Decoder.decode(bin).size.should eq(8 * 1024 * 1024)
131
+ end
132
+
133
+ it "a good thing should be awesome" do
134
+ get(%Q-[{options,{struct,[{test,<<"I'm chargin' mah lazer">>}]}},{passage,<<"Why doesn't this work?">>}]-).should ==
135
+ [[:options, [:struct, [[:test, "I'm chargin' mah lazer"]]]], [:passage, "Why doesn't this work?"]]
136
+ end
137
+
138
+ def get(str)
139
+ x = "term_to_binary(#{str.gsub(/"/, '\\\"')})"
140
+ bin = run_erl(x)
141
+ Beambridge::Decoder.decode(bin)
142
+ end
143
+ end
@@ -0,0 +1,152 @@
1
+ # coding: utf-8
2
+ require "spec_helper"
3
+
4
+ describe "When packing to a binary stream" do
5
+
6
+ let(:encoder) { Beambridge::Encoder.new(StringIO.new('', 'w')) }
7
+
8
+ it "A symbol should be encoded to an erlang atom" do
9
+ get{encoder.write_symbol :haha}.should == get_erl("haha")
10
+ write_any(:haha).should == get_erl_with_magic("haha")
11
+ end
12
+
13
+ it "A boolean should be encoded to an erlang atom" do
14
+ get{encoder.write_boolean true}.should == get_erl("true")
15
+ get{encoder.write_boolean false}.should == get_erl("false")
16
+ write_any(true).should == get_erl_with_magic("true")
17
+ write_any(false).should == get_erl_with_magic("false")
18
+ end
19
+
20
+ it "A number should be encoded as an erlang number would be" do
21
+ #SMALL_INTS
22
+ get{encoder.write_fixnum 0}.should == get_erl("0")
23
+ get{encoder.write_fixnum 255}.should == get_erl("255")
24
+ write_any(0).should == get_erl_with_magic("0")
25
+ write_any(255).should == get_erl_with_magic("255")
26
+
27
+ #INTS
28
+ get{encoder.write_fixnum 256}.should == get_erl("256")
29
+ get{encoder.write_fixnum((1 << 27) - 1)}.should == get_erl("#{(1 << 27) - 1}")
30
+ get{encoder.write_fixnum(-1)}.should == get_erl("-1")
31
+ get{encoder.write_fixnum(-(1 << 27))}.should == get_erl("#{-(1 << 27)}")
32
+ write_any(256).should == get_erl_with_magic("256")
33
+ write_any((1 << 27) - 1).should == get_erl_with_magic("#{(1 << 27) - 1}")
34
+ write_any(-1).should == get_erl_with_magic("-1")
35
+ write_any(-(1 << 27)).should == get_erl_with_magic("#{-(1 << 27)}")
36
+
37
+ # #SMALL_BIGNUMS
38
+ get{encoder.write_fixnum(10_000_000_000_000_000_000)}.should == get_erl("10000000000000000000")
39
+ # get{encoder.write_fixnum(1254976067)}.should == get_erl("1254976067")
40
+ # get{encoder.write_fixnum(-1254976067)}.should == get_erl("-1254976067")
41
+ # get{encoder.write_fixnum((1 << word_length))}.should == get_erl("#{(1 << word_length)}")
42
+ # get{encoder.write_fixnum(-(1 << word_length) - 1)}.should == get_erl("#{-(1 << word_length) - 1}")
43
+ # get{encoder.write_fixnum((1 << (255 * 8)) - 1)}.should == get_erl("#{(1 << (255 * 8)) - 1}")
44
+ # get{encoder.write_fixnum(-((1 << (255 * 8)) - 1))}.should == get_erl("#{-((1 << (255 * 8)) - 1)}")
45
+ #
46
+ # write_any((1 << word_length)).should == get_erl_with_magic("#{(1 << word_length)}")
47
+ # write_any(-(1 << word_length) - 1).should == get_erl_with_magic("#{-(1 << word_length) - 1}")
48
+ # write_any((1 << (255 * 8)) - 1).should == get_erl_with_magic("#{(1 << (255 * 8)) - 1}")
49
+ # write_any(-((1 << (255 * 8)) - 1)).should == get_erl_with_magic("#{-((1 << (255 * 8)) - 1)}")
50
+ #
51
+ # #LARGE_BIGNUMS
52
+ x = 1254976067 ** 256
53
+ get{encoder.write_fixnum(x)}.should == get_erl("#{x}")
54
+ get{encoder.write_fixnum(-x)}.should == get_erl("-#{x}")
55
+ # get{encoder.write_fixnum((1 << (255 * 8)))}.should == get_erl("#{(1 << (255 * 8))}")
56
+ # get{encoder.write_fixnum(-(1 << (255 * 8))}.should == get_erl("#{-(1 << (255 * 8)}")
57
+ # get{encoder.write_fixnum((1 << (512 * 8))}.should == get_erl("#{(1 << (512 * 8))}")
58
+ # get{encoder.write_fixnum(-((1 << (512 * 8)) - 1))}.should == get_erl("#{-((1 << (512 * 8)) - 1)}")
59
+ #
60
+ # write_any((1 << (255 * 8))).should == get_erl_with_magic("#{(1 << (255 * 8))}")
61
+ # write_any(-(1 << (255 * 8)).should == get_erl_with_magic("#{-(1 << (255 * 8)}")
62
+ # write_any((1 << (512 * 8))).should == get_erl_with_magic("#{(1 << (512 * 8))}")
63
+ # write_any(-((1 << (512 * 8)) - 1)).should == get_erl_with_magic("#{-((1 << (512 * 8)) - 1)}")
64
+ end
65
+
66
+ # it "A float (that is within the truncated precision of ruby compared to erlang) should encode as erlang does" do
67
+ # get{encoder.write_float 1.0}.should == get_erl("1.0")
68
+ # get{encoder.write_float -1.0}.should == get_erl("-1.0")
69
+ # get{encoder.write_float 123.456}.should == get_erl("123.456")
70
+ # get{encoder.write_float 123.456789012345}.should == get_erl("123.456789012345")
71
+ # end
72
+
73
+ it "An Erlectiricity::NewReference should encode back to its original form" do
74
+ ref_bin = run_erl("term_to_binary(make_ref())")
75
+ ruby_ref = Beambridge::Decoder.decode(ref_bin)
76
+
77
+ get{encoder.write_new_reference(ruby_ref)}.should == ref_bin[1..-1].bytes.to_a
78
+ write_any(ruby_ref).should == ref_bin.bytes.to_a
79
+ end
80
+
81
+ it "An Erlectiricity::Pid should encode back to its original form" do
82
+ pid_bin = run_erl("term_to_binary(spawn(fun() -> 3 end))")
83
+ ruby_pid = Beambridge::Decoder.decode(pid_bin)
84
+
85
+ get{encoder.write_pid(ruby_pid)}.should == pid_bin[1..-1].bytes.to_a
86
+ write_any(ruby_pid).should == pid_bin.bytes.to_a
87
+ end
88
+
89
+ it "An array written with write_tuple should encode as erlang would a tuple" do
90
+ get{encoder.write_tuple [1,2,3]}.should == get_erl("{1,2,3}")
91
+ get{encoder.write_tuple [3] * 255}.should == get_erl("{#{([3] * 255).join(',')}}")
92
+ get{encoder.write_tuple [3] * 256}.should == get_erl("{#{([3] * 256).join(',')}}")
93
+ get{encoder.write_tuple [3] * 512}.should == get_erl("{#{([3] * 512).join(',')}}")
94
+ end
95
+
96
+ it "An array should by default be written as a tuple" do
97
+ write_any([1,2,3]).should == get_erl_with_magic("{1,2,3}")
98
+ write_any([3] * 255).should == get_erl_with_magic("{#{([3] * 255).join(',')}}")
99
+ write_any([3] * 256).should == get_erl_with_magic("{#{([3] * 256).join(',')}}")
100
+ write_any([3] * 512).should == get_erl_with_magic("{#{([3] * 512).join(',')}}")
101
+ end
102
+
103
+ it "An Beambridge::List should by default be written as a list" do
104
+ write_any(Erl::List.new([1,2,300])).should == get_erl_with_magic("[1,2,300]")
105
+ write_any(Erl::List.new([300] * 255)).should == get_erl_with_magic("[#{([300] * 255).join(',')}]")
106
+ write_any(Erl::List.new([300] * 256)).should == get_erl_with_magic("[#{([300] * 256).join(',')}]")
107
+ write_any(Erl::List.new([300] * 512)).should == get_erl_with_magic("[#{([300] * 512).join(',')}]")
108
+ end
109
+
110
+ it "An array written with write_list should encode as erlang would a list" do
111
+ get{encoder.write_list [1,2,300]}.should == get_erl("[1,2,300]")
112
+ get{encoder.write_list [300] * 255}.should == get_erl("[#{([300] * 255).join(',')}]")
113
+ get{encoder.write_list [300] * 256}.should == get_erl("[#{([300] * 256).join(',')}]")
114
+ get{encoder.write_list [300] * 256}.should == get_erl("[#{([300] * 256).join(',')}]")
115
+ end
116
+
117
+ it "a string should be encoded as a erlang binary would be" do
118
+ get{encoder.write_binary "hey who"}.should == get_erl("<< \"hey who\" >>")
119
+ get{encoder.write_binary ""}.should == get_erl("<< \"\" >>")
120
+ get{encoder.write_binary "c\000c"}.should == get_erl("<< 99,0,99 >>")
121
+
122
+ write_any("hey who").should == get_erl_with_magic("<< \"hey who\" >>")
123
+ write_any("").should == get_erl_with_magic("<< \"\" >>")
124
+ end
125
+
126
+ it "encodes a string with unicode characters with the correct length" do
127
+ write_any([:hi, "😸", :bye]).should == [
128
+ 131, 104, 3, 100, 0, 2, 104, 105, 109, 0, 0, 0, 4,
129
+ 240, 159, 152, 184, 100, 0, 3, 98, 121, 101
130
+ ]
131
+ end
132
+
133
+ def get
134
+ encoder.out = StringIO.new('', 'w')
135
+ yield
136
+ encoder.out.string.bytes.to_a
137
+ end
138
+
139
+ def write_any(term)
140
+ encoder.out = StringIO.new('', 'w')
141
+ encoder.write_any term
142
+ encoder.out.string.bytes.to_a
143
+ end
144
+
145
+ def get_erl(str)
146
+ get_erl_with_magic(str)[1..-1] #[1..-1] to chop off the magic number
147
+ end
148
+
149
+ def get_erl_with_magic(str)
150
+ run_erl("term_to_binary(#{str.gsub(/"/, '\\\"')})").bytes.to_a
151
+ end
152
+ end