bencodr 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|