bencodr 1.2.0 → 2.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.
- data/.autotest +0 -0
- data/.document +0 -0
- data/.gitignore +3 -1
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/LICENSE +0 -0
- data/{README.rdoc → README.md} +88 -53
- data/Rakefile +4 -44
- data/bencodr.gemspec +18 -75
- data/lib/bencodr.rb +23 -40
- data/lib/bencodr/dictionary.rb +19 -47
- data/lib/bencodr/ext.rb +38 -0
- data/lib/bencodr/integer.rb +17 -47
- data/lib/bencodr/io.rb +21 -56
- data/lib/bencodr/list.rb +19 -45
- data/lib/bencodr/object.rb +36 -0
- data/lib/bencodr/parser.rb +0 -14
- data/lib/bencodr/string.rb +17 -46
- data/lib/bencodr/version.rb +3 -0
- data/spec/bencode_spec.rb +17 -22
- data/spec/bencodr/dictionary_spec.rb +1 -74
- data/spec/bencodr/ext_spec.rb +66 -0
- data/spec/bencodr/integer_spec.rb +1 -67
- data/spec/bencodr/io_spec.rb +28 -30
- data/spec/bencodr/list_spec.rb +1 -31
- data/spec/bencodr/object_spec.rb +9 -0
- data/spec/bencodr/parser_spec.rb +38 -173
- data/spec/bencodr/string_spec.rb +1 -68
- data/spec/custom_matchers.rb +87 -0
- data/spec/samples/bencode.rb.torrent +0 -0
- data/spec/samples/mini.bencode +0 -0
- data/spec/samples/python.torrent +0 -0
- data/spec/shared_examples.rb +91 -0
- data/spec/spec_helper.rb +8 -9
- metadata +55 -28
- data/VERSION +0 -1
- data/autotest/discover.rb +0 -3
- data/spec/spec.opts +0 -1
data/lib/bencodr/dictionary.rb
CHANGED
@@ -2,56 +2,28 @@
|
|
2
2
|
|
3
3
|
module BEncodr
|
4
4
|
module Dictionary
|
5
|
-
|
6
|
-
|
7
|
-
# Encodes an array into a bencoded dictionary. Bencoded dictionaries are encoded as a 'd' followed by a list of
|
8
|
-
# alternating keys and their corresponding values followed by an 'e'. Keys appear in sorted order (sorted as raw
|
9
|
-
# strings, not alphanumerics).
|
10
|
-
#
|
11
|
-
# {:cow => "moo", :seven => 7}.bencodr #=> "d3:cow3:moo5:seveni7ee"
|
12
|
-
#
|
13
|
-
# @return [::String] the bencoded dictionary
|
14
|
-
def bencode
|
15
|
-
(respond_to?(:to_h) ? to_h : to_hash).bencode
|
16
|
-
end
|
17
|
-
end
|
5
|
+
def bencode
|
6
|
+
Dictionary.bencode(self)
|
18
7
|
end
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# BEncodr::String.register MyClass
|
30
|
-
# my_class = MyClass.new
|
31
|
-
# my_class.bencodr #=> "d1:a1:a1:bi1ee"
|
32
|
-
#
|
33
|
-
# @param [Class#to_h, Class#to_hash] type the class to add the bencodr instance method to
|
34
|
-
def self.register(type)
|
35
|
-
type.send :include, Generic::InstanceMethods
|
8
|
+
|
9
|
+
def self.bencode(hashable)
|
10
|
+
hash = coerce(hashable)
|
11
|
+
|
12
|
+
hash.keys.sort{|a, b| a.to_s <=> b.to_s}.collect do |key|
|
13
|
+
BEncodr::String.bencode(key.to_s) + Object.bencode(hash[key])
|
14
|
+
end.unshift(:d).push(:e).join
|
36
15
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
def bencode
|
48
|
-
keys.sort{|a, b| a.to_s <=> b.to_s}.collect do |key|
|
49
|
-
key.to_s.bencode + self[key].bencode
|
50
|
-
end.unshift(:d).push(:e).join
|
51
|
-
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.coerce(hashable)
|
20
|
+
if hashable.respond_to?(:to_h)
|
21
|
+
hashable.to_h
|
22
|
+
elsif hashable.respond_to?(:to_hash)
|
23
|
+
hashable.to_hash
|
24
|
+
else
|
25
|
+
raise BEncodeError, "BEncodr::Dictionary.bencode can only be called on an object that provides a to_h or to_hash method."
|
52
26
|
end
|
53
|
-
|
54
|
-
::Hash.send :include, InstanceMethods
|
55
27
|
end
|
56
28
|
end
|
57
29
|
end
|
data/lib/bencodr/ext.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module BEncodr
|
2
|
+
module Ext
|
3
|
+
def self.include!
|
4
|
+
include_string!
|
5
|
+
include_integer!
|
6
|
+
include_list!
|
7
|
+
include_dictionary!
|
8
|
+
include_io!
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.include_string!
|
14
|
+
::String.send :include, Object
|
15
|
+
[::String, Symbol, URI::Generic].each do |stringable|
|
16
|
+
stringable.send :include, String
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.include_integer!
|
21
|
+
[Numeric, Time].each do |intable|
|
22
|
+
intable.send :include, Integer
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.include_list!
|
27
|
+
Array.send :include, List
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.include_dictionary!
|
31
|
+
Hash.send :include, Dictionary
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.include_io!
|
35
|
+
::IO.send :include, IO
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/bencodr/integer.rb
CHANGED
@@ -2,56 +2,26 @@
|
|
2
2
|
|
3
3
|
module BEncodr
|
4
4
|
module Integer
|
5
|
-
|
6
|
-
|
7
|
-
# Encodes object into a bencoded integer. BEncoded strings are length-prefixed base ten followed by a colon and
|
8
|
-
# the string. Object must implement to_i or to_int.
|
9
|
-
#
|
10
|
-
# 1.bencodr #=> "i1e"
|
11
|
-
#
|
12
|
-
# @return [::String] the bencoded integer
|
13
|
-
def bencode
|
14
|
-
(respond_to?(:to_i) ? to_i : to_int).bencode
|
15
|
-
end
|
16
|
-
end
|
5
|
+
def bencode
|
6
|
+
Integer.bencode(self)
|
17
7
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# def to_i
|
24
|
-
# 1
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# BEncodr::Integer.register MyClass
|
29
|
-
# my_class = MyClass.new
|
30
|
-
# my_class.bencodr #=> "i1e"
|
31
|
-
#
|
32
|
-
# @param [Class#to_i, Class#to_int] type the class to add the bencodr instance method to
|
33
|
-
def self.register(type)
|
34
|
-
type.send :include, Generic::InstanceMethods
|
8
|
+
|
9
|
+
def self.bencode(intable)
|
10
|
+
int = coerce(intable)
|
11
|
+
|
12
|
+
[:i, int, :e].join
|
35
13
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# -3.bencodr #=> "i-3e"
|
47
|
-
#
|
48
|
-
# @return [::String] the bencoded integer
|
49
|
-
def bencode
|
50
|
-
[:i, self, :e].join
|
51
|
-
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.coerce(intable)
|
18
|
+
if intable.respond_to?(:to_i)
|
19
|
+
intable.to_i
|
20
|
+
elsif intable.respond_to?(:to_int)
|
21
|
+
intable.to_int
|
22
|
+
else
|
23
|
+
raise BEncodeError, "BEncodr::Integer.bencode can only be called on an object that provides a to_i or to_int method."
|
52
24
|
end
|
53
|
-
|
54
|
-
::Integer.send :include, InstanceMethods
|
55
25
|
end
|
56
26
|
end
|
57
27
|
end
|
data/lib/bencodr/io.rb
CHANGED
@@ -1,60 +1,25 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# @param [Object] fd the file descriptor to use for output
|
12
|
-
# @param [Object] object the object to write
|
13
|
-
def bencode(fd, object)
|
14
|
-
open(fd, "wb") {|file| file.bencode object }
|
1
|
+
module BEncodr
|
2
|
+
module IO
|
3
|
+
module ClassMethods
|
4
|
+
def bencode(fd, object)
|
5
|
+
open(fd, "wb") {|file| file.bencode(object)}
|
6
|
+
end
|
7
|
+
|
8
|
+
def bdecode(fd)
|
9
|
+
open(fd, "rb") {|file| file.bdecode}
|
10
|
+
end
|
15
11
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
def bdecode(fd)
|
28
|
-
open(fd, "rb") {|file| file.bdecode}
|
12
|
+
|
13
|
+
def bencode(object)
|
14
|
+
write(Object.bencode(object))
|
15
|
+
end
|
16
|
+
|
17
|
+
def bdecode
|
18
|
+
Object.bdecode(read)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.extend ClassMethods
|
29
23
|
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# This method encodes the object and writes.
|
33
|
-
#
|
34
|
-
# # write to standard out
|
35
|
-
# $stdout.bencode("string") #=> "6:string" to stdout
|
36
|
-
#
|
37
|
-
# # write to file
|
38
|
-
# file = File.open("a.bencode", "wb")
|
39
|
-
# file.bencode("string") #=> "6:string" to a.bencode
|
40
|
-
#
|
41
|
-
# @param [Object] object the object to write
|
42
|
-
def bencode(object)
|
43
|
-
write object.bencode
|
44
|
-
end
|
45
|
-
|
46
|
-
# This method reads from the specified input and decodes the object.
|
47
|
-
#
|
48
|
-
# # read from standard in
|
49
|
-
# $stdin.bdecode #=> "string"
|
50
|
-
#
|
51
|
-
# # read from file
|
52
|
-
# file = File.open("a.bencode", "wb")
|
53
|
-
# file.bdecode #=> "string"
|
54
|
-
#
|
55
|
-
# @param [Object] fd the file descriptor to use for input
|
56
|
-
# @param [Object] object the object to write
|
57
|
-
def bdecode
|
58
|
-
read.bdecode
|
59
24
|
end
|
60
25
|
end
|
data/lib/bencodr/list.rb
CHANGED
@@ -2,54 +2,28 @@
|
|
2
2
|
|
3
3
|
module BEncodr
|
4
4
|
module List
|
5
|
-
|
6
|
-
|
7
|
-
# Encodes object into a bencoded list. BEncoded strings are length-prefixed base ten followed by a colon and
|
8
|
-
# the string. Object must implement to_a or to_ary.
|
9
|
-
#
|
10
|
-
# [].bencodr #=> "le"
|
11
|
-
#
|
12
|
-
# @return [::String] the bencoded list
|
13
|
-
def bencode
|
14
|
-
(respond_to?(:to_ary) ? to_ary : to_a).bencode
|
15
|
-
end
|
16
|
-
end
|
5
|
+
def bencode
|
6
|
+
List.bencode(self)
|
17
7
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# BEncodr::Integer.register MyClass
|
29
|
-
# my_class = MyClass.new
|
30
|
-
# my_class.bencodr #=> "li1e3:cate"
|
31
|
-
#
|
32
|
-
# @param [Class#to_a, Class#to_ary] type the class to add the bencodr instance method to
|
33
|
-
def self.register(type)
|
34
|
-
type.send :include, Generic::InstanceMethods
|
8
|
+
|
9
|
+
def self.bencode(arrayable)
|
10
|
+
ary = coerce(arrayable)
|
11
|
+
|
12
|
+
ary.collect do |element|
|
13
|
+
Object.bencode(element)
|
14
|
+
end.unshift(:l).push(:e).join
|
35
15
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
collect do |element|
|
47
|
-
element.bencode
|
48
|
-
end.unshift(:l).push(:e).join
|
49
|
-
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.coerce(arrayable)
|
20
|
+
if arrayable.respond_to?(:to_a)
|
21
|
+
arrayable.to_a
|
22
|
+
elsif arrayable.respond_to?(:to_ary)
|
23
|
+
arrayable.to_ary
|
24
|
+
else
|
25
|
+
raise BEncodeError, "BEncodr::List.bencode can only be called on an object that provides a to_a or to_ary method."
|
50
26
|
end
|
51
|
-
|
52
|
-
::Array.send :include, InstanceMethods
|
53
27
|
end
|
54
28
|
end
|
55
29
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module BEncodr
|
2
|
+
module Object
|
3
|
+
def self.bencode(object)
|
4
|
+
return object.bencode if object.respond_to?(:bencode)
|
5
|
+
|
6
|
+
case object
|
7
|
+
when ::String, Symbol, URI::Generic
|
8
|
+
return String.bencode(object)
|
9
|
+
when Numeric, Time
|
10
|
+
return Integer.bencode(object)
|
11
|
+
when Array
|
12
|
+
return List.bencode(object)
|
13
|
+
when Hash
|
14
|
+
return Dictionary.bencode(object)
|
15
|
+
else
|
16
|
+
[String, Integer, List, Dictionary].each do |type|
|
17
|
+
begin
|
18
|
+
return type.bencode(object)
|
19
|
+
rescue
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
raise BEncodeError, "BEncodr::Object.bencode was unable to infer the type of the object passed in."
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.bdecode(string)
|
28
|
+
object = Parser.parse_object(StringScanner.new(string))
|
29
|
+
object or raise BEncodeError, "BEncodr::Object.bdecode was unable to parse the string passed in."
|
30
|
+
end
|
31
|
+
|
32
|
+
def bdecode
|
33
|
+
Object.bdecode(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/bencodr/parser.rb
CHANGED
@@ -110,18 +110,4 @@ module BEncodr
|
|
110
110
|
private :parse_key_value
|
111
111
|
end
|
112
112
|
end
|
113
|
-
end
|
114
|
-
|
115
|
-
class String
|
116
|
-
# This method decodes a bencoded string into a ruby object.
|
117
|
-
#
|
118
|
-
# "6:string".bdecode #=> "string"
|
119
|
-
# "i-1e".bdecode #=> -1
|
120
|
-
# "le".bdecode #=> []
|
121
|
-
# "de".bdecode #=> {}
|
122
|
-
#
|
123
|
-
# @return [::String, ::Integer, ::Array, ::Hash] the decoded object
|
124
|
-
def bdecode
|
125
|
-
BEncodr::Parser.parse_object(StringScanner.new self) or raise BEncodr::BEncodeError, "Invalid bencoding"
|
126
|
-
end
|
127
113
|
end
|
data/lib/bencodr/string.rb
CHANGED
@@ -4,55 +4,26 @@ require 'uri'
|
|
4
4
|
|
5
5
|
module BEncodr
|
6
6
|
module String
|
7
|
-
|
8
|
-
|
9
|
-
# Encodes object into a bencoded string. BEncoded strings are length-prefixed base ten followed by a colon and
|
10
|
-
# the string.
|
11
|
-
#
|
12
|
-
# :symbol.bencodr #=> "6:symbol"
|
13
|
-
#
|
14
|
-
# @return [::String] the bencoded string
|
15
|
-
def bencode
|
16
|
-
(respond_to?(:to_s) ? to_s : to_str).bencode
|
17
|
-
end
|
18
|
-
end
|
7
|
+
def bencode
|
8
|
+
String.bencode(self)
|
19
9
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
# def to_s
|
26
|
-
# "string"
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# BEncode::String.register MyClass
|
31
|
-
# my_class = MyClass.new
|
32
|
-
# my_class.bencodr #=> "6:string"
|
33
|
-
#
|
34
|
-
# @param [Class#to_s, Class#to_str] type the class to add the bencodr instance method to
|
35
|
-
def self.register(type)
|
36
|
-
type.send :include, Generic::InstanceMethods
|
10
|
+
|
11
|
+
def self.bencode(stringable)
|
12
|
+
string = coerce(stringable)
|
13
|
+
|
14
|
+
[string.length, ':', string].join
|
37
15
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#
|
49
|
-
# @return [::String] the bencoded string
|
50
|
-
def bencode
|
51
|
-
[length, ':', self].join
|
52
|
-
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def self.coerce(stringable)
|
20
|
+
if stringable.respond_to?(:to_s)
|
21
|
+
stringable.to_s
|
22
|
+
elsif stringable.respond_to?(:to_str)
|
23
|
+
stringable.to_str
|
24
|
+
else
|
25
|
+
raise BEncodeError, "BEncodr::String.bencode can only be called on an object that provides a to_s or to_str method."
|
53
26
|
end
|
54
|
-
|
55
|
-
::String.send :include, InstanceMethods
|
56
27
|
end
|
57
28
|
end
|
58
29
|
end
|