erlang-etf 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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +49 -0
  8. data/Rakefile +6 -0
  9. data/erlang-etf.gemspec +30 -0
  10. data/lib/erlang/etf.rb +40 -0
  11. data/lib/erlang/etf/atom.rb +46 -0
  12. data/lib/erlang/etf/atom_utf8.rb +44 -0
  13. data/lib/erlang/etf/bert.rb +74 -0
  14. data/lib/erlang/etf/binary.rb +44 -0
  15. data/lib/erlang/etf/bit_binary.rb +47 -0
  16. data/lib/erlang/etf/export.rb +44 -0
  17. data/lib/erlang/etf/extensions.rb +157 -0
  18. data/lib/erlang/etf/extensions/array.rb +29 -0
  19. data/lib/erlang/etf/extensions/big_decimal.rb +22 -0
  20. data/lib/erlang/etf/extensions/erlang-export.rb +26 -0
  21. data/lib/erlang/etf/extensions/erlang-list.rb +31 -0
  22. data/lib/erlang/etf/extensions/erlang-nil.rb +22 -0
  23. data/lib/erlang/etf/extensions/erlang-pid.rb +22 -0
  24. data/lib/erlang/etf/extensions/erlang-string.rb +22 -0
  25. data/lib/erlang/etf/extensions/erlang-tuple.rb +31 -0
  26. data/lib/erlang/etf/extensions/false_class.rb +28 -0
  27. data/lib/erlang/etf/extensions/float.rb +20 -0
  28. data/lib/erlang/etf/extensions/hash.rb +32 -0
  29. data/lib/erlang/etf/extensions/integer.rb +48 -0
  30. data/lib/erlang/etf/extensions/nil_class.rb +29 -0
  31. data/lib/erlang/etf/extensions/object.rb +24 -0
  32. data/lib/erlang/etf/extensions/regexp.rb +34 -0
  33. data/lib/erlang/etf/extensions/string.rb +35 -0
  34. data/lib/erlang/etf/extensions/symbol.rb +45 -0
  35. data/lib/erlang/etf/extensions/time.rb +29 -0
  36. data/lib/erlang/etf/extensions/true_class.rb +28 -0
  37. data/lib/erlang/etf/float.rb +57 -0
  38. data/lib/erlang/etf/fun.rb +67 -0
  39. data/lib/erlang/etf/integer.rb +29 -0
  40. data/lib/erlang/etf/large_big.rb +53 -0
  41. data/lib/erlang/etf/large_tuple.rb +55 -0
  42. data/lib/erlang/etf/list.rb +50 -0
  43. data/lib/erlang/etf/new_float.rb +33 -0
  44. data/lib/erlang/etf/new_fun.rb +98 -0
  45. data/lib/erlang/etf/new_reference.rb +59 -0
  46. data/lib/erlang/etf/nil.rb +23 -0
  47. data/lib/erlang/etf/pid.rb +45 -0
  48. data/lib/erlang/etf/port.rb +34 -0
  49. data/lib/erlang/etf/reference.rb +41 -0
  50. data/lib/erlang/etf/small_atom.rb +48 -0
  51. data/lib/erlang/etf/small_atom_utf8.rb +44 -0
  52. data/lib/erlang/etf/small_big.rb +59 -0
  53. data/lib/erlang/etf/small_integer.rb +29 -0
  54. data/lib/erlang/etf/small_tuple.rb +56 -0
  55. data/lib/erlang/etf/string.rb +46 -0
  56. data/lib/erlang/etf/term.rb +101 -0
  57. data/lib/erlang/etf/terms.rb +105 -0
  58. data/lib/erlang/etf/version.rb +5 -0
  59. data/spec/erlang/etf/atom_spec.rb +90 -0
  60. data/spec/erlang/etf/atom_utf8_spec.rb +90 -0
  61. data/spec/erlang/etf/binary_spec.rb +90 -0
  62. data/spec/erlang/etf/bit_binary_spec.rb +99 -0
  63. data/spec/erlang/etf/export_spec.rb +58 -0
  64. data/spec/erlang/etf/extensions/array_spec.rb +40 -0
  65. data/spec/erlang/etf/extensions/big_decimal_spec.rb +26 -0
  66. data/spec/erlang/etf/extensions/erlang-export_spec.rb +32 -0
  67. data/spec/erlang/etf/extensions/erlang-list_spec.rb +76 -0
  68. data/spec/erlang/etf/extensions/erlang-nil_spec.rb +24 -0
  69. data/spec/erlang/etf/extensions/erlang-pid_spec.rb +33 -0
  70. data/spec/erlang/etf/extensions/erlang-string_spec.rb +26 -0
  71. data/spec/erlang/etf/extensions/erlang-tuple_spec.rb +56 -0
  72. data/spec/erlang/etf/extensions/false_class_spec.rb +29 -0
  73. data/spec/erlang/etf/extensions/float_spec.rb +24 -0
  74. data/spec/erlang/etf/extensions/hash_spec.rb +90 -0
  75. data/spec/erlang/etf/extensions/integer_spec.rb +259 -0
  76. data/spec/erlang/etf/extensions/nil_class_spec.rb +29 -0
  77. data/spec/erlang/etf/extensions/object_spec.rb +30 -0
  78. data/spec/erlang/etf/extensions/regexp_spec.rb +35 -0
  79. data/spec/erlang/etf/extensions/string_spec.rb +43 -0
  80. data/spec/erlang/etf/extensions/symbol_spec.rb +64 -0
  81. data/spec/erlang/etf/extensions/time_spec.rb +32 -0
  82. data/spec/erlang/etf/extensions/true_class_spec.rb +29 -0
  83. data/spec/erlang/etf/float_spec.rb +92 -0
  84. data/spec/erlang/etf/fun_spec.rb +132 -0
  85. data/spec/erlang/etf/integer_spec.rb +57 -0
  86. data/spec/erlang/etf/large_big_spec.rb +67 -0
  87. data/spec/erlang/etf/large_tuple_spec.rb +119 -0
  88. data/spec/erlang/etf/list_spec.rb +159 -0
  89. data/spec/erlang/etf/new_float_spec.rb +92 -0
  90. data/spec/erlang/etf/new_fun_spec.rb +146 -0
  91. data/spec/erlang/etf/new_reference_spec.rb +60 -0
  92. data/spec/erlang/etf/nil_spec.rb +50 -0
  93. data/spec/erlang/etf/pid_spec.rb +61 -0
  94. data/spec/erlang/etf/port_spec.rb +58 -0
  95. data/spec/erlang/etf/reference_spec.rb +58 -0
  96. data/spec/erlang/etf/small_atom_spec.rb +90 -0
  97. data/spec/erlang/etf/small_atom_utf8_spec.rb +90 -0
  98. data/spec/erlang/etf/small_big_spec.rb +67 -0
  99. data/spec/erlang/etf/small_integer_spec.rb +57 -0
  100. data/spec/erlang/etf/small_tuple_spec.rb +112 -0
  101. data/spec/erlang/etf/string_spec.rb +92 -0
  102. data/spec/erlang/etf/term_spec.rb +27 -0
  103. data/spec/erlang/etf/terms_spec.rb +23 -0
  104. data/spec/erlang/etf_spec.rb +23 -0
  105. data/spec/erlang_spec.rb +77 -0
  106. data/spec/spec_helper.rb +7 -0
  107. metadata +310 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 195a05d3181de002ad6dff10e6994d94f722e8b2
4
+ data.tar.gz: 18a71520747f118760e31724f9dc135ebdc38dde
5
+ SHA512:
6
+ metadata.gz: c97cd0e64ceaf45946b042117c7cab7cb4654dcb325df611e4f35fdc323970b13b42317901d074ae1daaafff8901959332446e4737e53d37cdd5d874fafe0c5f
7
+ data.tar.gz: 2723c6b9376a9e1b235a5e195f30329f777f53b0c841f19bd4ebb44d975d2190d91b6fe2ab61be68711819c8fc5b8fbfe8c1ad83ad703d17fb000b1635d10972
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in erlang-etf.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Andrew Bennett
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # Erlang::Etf
2
+
3
+ [![Build Status](https://travis-ci.org/potatosalad/erlang-etf.png)](https://travis-ci.org/potatosalad/erlang-etf)
4
+
5
+ Erlang [External Term Format](http://erlang.org/doc/apps/erts/erl_ext_dist.html) (ETF) for Ruby.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'erlang-etf', require: 'erlang/etf'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install erlang-etf
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### Erlang.term_to_binary(term)
30
+
31
+ ```ruby
32
+ Erlang.term_to_binary(:atom)
33
+ # => "\x83s\x04atom"
34
+ ```
35
+
36
+ ### Erlang.binary_to_term(binary)
37
+
38
+ ```ruby
39
+ Erlang.binary_to_term("\x83s\x04atom")
40
+ # => :atom
41
+ ```
42
+
43
+ ## Contributing
44
+
45
+ 1. Fork it
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
48
+ 4. Push to the branch (`git push origin my-new-feature`)
49
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => [:spec]
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'erlang/etf/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "erlang-etf"
8
+ spec.version = Erlang::ETF::VERSION
9
+ spec.authors = ["Andrew Bennett"]
10
+ spec.email = ["andrew@delorum.com"]
11
+ spec.description = %q{Erlang External Term Format (ETF) for Ruby}
12
+ spec.summary = %q{Erlang External Term Format (ETF) for Ruby}
13
+ spec.homepage = "https://github.com/potatosalad/erlang-etf"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "binary-protocol"
22
+ spec.add_dependency "erlang-terms"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency "pry"
26
+ spec.add_development_dependency "pry-doc"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "simplecov"
30
+ end
data/lib/erlang/etf.rb ADDED
@@ -0,0 +1,40 @@
1
+ require "binary/protocol"
2
+ require "erlang/terms"
3
+
4
+ require "erlang/etf/version"
5
+ require "erlang/etf/extensions"
6
+ require "erlang/etf/terms"
7
+
8
+ module Erlang
9
+ module ETF
10
+
11
+ extend self
12
+
13
+ ERLANG_MAGIC_BYTE = 131.chr.freeze
14
+
15
+ def encode(term, buffer = "")
16
+ buffer << ERLANG_MAGIC_BYTE
17
+ term.__erlang_dump__(buffer)
18
+ end
19
+
20
+ def decode(buffer)
21
+ magic = buffer.read(1)
22
+ if magic == ERLANG_MAGIC_BYTE
23
+ Terms.evolve(buffer)
24
+ else
25
+ raise NotImplementedError, "unknown Erlang magic byte #{magic.inspect} (should be #{ERLANG_MAGIC_BYTE.inspect})"
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ def self.binary_to_term(binary)
32
+ buffer = binary.respond_to?(:read) ? binary : StringIO.new(binary)
33
+ ETF.decode(buffer)
34
+ end
35
+
36
+ def self.term_to_binary(term, buffer = "")
37
+ ETF.encode(term, buffer)
38
+ end
39
+
40
+ end
@@ -0,0 +1,46 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 2 | Len
6
+ # --- | --- | --------
7
+ # 100 | Len | AtomName
8
+ #
9
+ # An atom is stored with a 2 byte unsigned length in big-endian
10
+ # order, followed by Len numbers of 8 bit Latin1 characters that
11
+ # forms the AtomName.
12
+ #
13
+ # Note: The maximum allowed value for Len is 255.
14
+ #
15
+ class Atom
16
+ include Term
17
+
18
+ uint8 :tag, always: Terms::ATOM_EXT
19
+
20
+ uint16be :len, default: 0 do
21
+ string :atom_name
22
+ end
23
+
24
+ undef deserialize_atom_name
25
+ def deserialize_atom_name(buffer)
26
+ self.atom_name = buffer.read(len).from_utf8_binary
27
+ end
28
+
29
+ undef serialize_atom_name
30
+ def serialize_atom_name(buffer)
31
+ buffer << atom_name.to_utf8_binary
32
+ end
33
+
34
+ finalize
35
+
36
+ def initialize(atom_name)
37
+ @atom_name = atom_name
38
+ @len = atom_name.to_s.bytesize
39
+ end
40
+
41
+ def __ruby_evolve__
42
+ atom_name.intern
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,44 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 2 | Len
6
+ # --- | --- | --------
7
+ # 118 | Len | AtomName
8
+ #
9
+ # An atom is stored with a 2 byte unsigned length in big-endian
10
+ # order, followed by Len bytes containing the AtomName encoded in
11
+ # UTF-8.
12
+ #
13
+ class AtomUTF8
14
+ include Term
15
+
16
+ uint8 :tag, always: Terms::ATOM_UTF8_EXT
17
+
18
+ uint16be :len, default: 0 do
19
+ string :atom_name
20
+ end
21
+
22
+ undef deserialize_atom_name
23
+ def deserialize_atom_name(buffer)
24
+ self.atom_name = buffer.read(len).from_utf8_binary
25
+ end
26
+
27
+ undef serialize_atom_name
28
+ def serialize_atom_name(buffer)
29
+ buffer << atom_name.to_utf8_binary
30
+ end
31
+
32
+ finalize
33
+
34
+ def initialize(atom_name)
35
+ @atom_name = atom_name
36
+ @len = atom_name.to_s.bytesize
37
+ end
38
+
39
+ def __ruby_evolve__
40
+ atom_name.intern
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,74 @@
1
+ module Erlang
2
+ module ETF
3
+ module BERT
4
+ extend self
5
+
6
+ def evolve(tuple)
7
+ if tuple.elements[1].respond_to?(:atom_name)
8
+ case tuple.elements[1].atom_name.to_s.intern
9
+ ## boolean ##
10
+ when :false
11
+ false
12
+ when :true
13
+ true
14
+
15
+ ## dict ##
16
+ when :dict
17
+ if tuple.elements[2].respond_to?(:__ruby_evolve__) and
18
+ ((list = tuple.elements[2].__ruby_evolve__).is_a?(::Erlang::List) or (list.respond_to?(:empty?) and list.empty?))
19
+ list.inject({}) do |hash, (key, value)|
20
+ hash[key] = value
21
+ hash
22
+ end
23
+ else
24
+ unknown(tuple)
25
+ end
26
+
27
+ ## nil ##
28
+ when :nil
29
+ nil
30
+
31
+ ## regex ##
32
+ when :regex
33
+ if tuple.elements[2].is_a?(Erlang::ETF::Binary) and
34
+ (tuple.elements[3].is_a?(Erlang::ETF::List) or tuple.elements[3].is_a?(Erlang::ETF::Nil))
35
+
36
+ source = tuple.elements[2].__ruby_evolve__
37
+ opts = tuple.elements[3].__ruby_evolve__
38
+
39
+ options = 0
40
+ options |= ::Regexp::EXTENDED if opts.include?(:extended)
41
+ options |= ::Regexp::IGNORECASE if opts.include?(:caseless)
42
+ options |= ::Regexp::MULTILINE if opts.include?(:multiline)
43
+ ::Regexp.new(source, options)
44
+ else
45
+ unknown(tuple)
46
+ end
47
+
48
+ ## time ##
49
+ when :time
50
+ if tuple.arity == 5
51
+ megaseconds = tuple.elements[2].__ruby_evolve__
52
+ seconds = tuple.elements[3].__ruby_evolve__
53
+ microseconds = tuple.elements[4].__ruby_evolve__
54
+
55
+ Time.at(megaseconds * 1_000_000 + seconds, microseconds)
56
+ else
57
+ unknown(tuple)
58
+ end
59
+
60
+ else
61
+ unknown(tuple)
62
+ end
63
+ else
64
+ unknown(tuple)
65
+ end
66
+ end
67
+
68
+ def unknown(tuple)
69
+ ::Erlang::Tuple[*tuple.elements.map(&:__ruby_evolve__)]
70
+ end
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,44 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 4 | Len
6
+ # --- | --- | ----
7
+ # 109 | Len | Data
8
+ #
9
+ # Binaries are generated with bit syntax expression or with
10
+ # list_to_binary/1, term_to_binary/1, or as input from binary
11
+ # ports.
12
+ # The Len length field is an unsigned 4 byte integer (big endian).
13
+ #
14
+ class Binary
15
+ include Term
16
+
17
+ uint8 :tag, always: Terms::BINARY_EXT
18
+
19
+ uint32be :len, default: 0 do
20
+ string :data
21
+ end
22
+
23
+ undef deserialize_data
24
+ def deserialize_data(buffer)
25
+ self.data = buffer.read(len).from_utf8_binary
26
+ end
27
+
28
+ undef serialize_data
29
+ def serialize_data(buffer)
30
+ buffer << data.to_utf8_binary
31
+ end
32
+
33
+ finalize
34
+
35
+ def initialize(data)
36
+ @data = data
37
+ end
38
+
39
+ def __ruby_evolve__
40
+ data
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module Erlang
2
+ module ETF
3
+
4
+ #
5
+ # 1 | 4 | 1 | Len
6
+ # -- | --- | ---- | ----
7
+ # 77 | Len | Bits | Data
8
+ #
9
+ # This term represents a bitstring whose length in bits is not a
10
+ # multiple of 8 (created using the bit syntax in R12B and later).
11
+ # The Len field is an unsigned 4 byte integer (big endian).
12
+ # The Bits field is the number of bits that are used in the last
13
+ # byte in the data field, counting from the most significant bit
14
+ # towards the least significant.
15
+ #
16
+ class BitBinary
17
+ include Term
18
+
19
+ uint8 :tag, always: Terms::BIT_BINARY_EXT
20
+
21
+ uint32be :len, always: -> { data.bytesize }
22
+
23
+ uint8 :bits
24
+
25
+ string :data, default: ""
26
+
27
+ undef serialize_data
28
+ def serialize_data(buffer)
29
+ buffer << data.to_utf8_binary
30
+ end
31
+
32
+ deserialize do |buffer|
33
+ len, = buffer.read(BYTES_32).unpack(UINT32BE_PACK)
34
+ deserialize_bits(buffer)
35
+ self.data = buffer.read(len).from_utf8_binary
36
+ self
37
+ end
38
+
39
+ finalize
40
+
41
+ def initialize(bits, data = "")
42
+ self.bits = bits
43
+ self.data = data
44
+ end
45
+ end
46
+ end
47
+ end