beambridge 0.9.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 (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