packable 1.3.8 → 1.3.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.rdoc +11 -10
- data/lib/packable.rb +2 -1
- data/lib/packable/extensions/integer.rb +1 -1
- data/lib/packable/extensions/io.rb +28 -10
- data/lib/packable/extensions/proc.rb +2 -2
- data/lib/packable/mixin.rb +7 -7
- data/lib/packable/packers.rb +6 -7
- data/lib/packable/version.rb +1 -1
- data/packable.gemspec +2 -0
- data/test/packing_doc_test.rb +13 -19
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 010ee6e2262aa7ea8e3bccde2fa58a80afd1ba4356678c0456a638091cc50f97
|
4
|
+
data.tar.gz: 357c44f3a0252815976df5c4ac2260dabe87fb4d907546d1d8380fb37f714ae5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34a8168ef33d77d73dfd27156db34a95e0787900611d5c88878c07953239d80a5e4ec57002057f044ca2fcfa3879295de67206f50e730395c264f84d0cbdcacd
|
7
|
+
data.tar.gz: 8619bf57c88279a4d5a852b6a3a7810495881665da8e160ffb7bbcd7b3fc7920e18ade6f50641d19ae341be5fd65447608f06260bc9f7543231a6e7f336d9289
|
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
*Note*: SciRuby are taking over maintenance for this library: https://github.com/SciRuby/packable
|
2
|
-
|
3
1
|
= Packable Library - Intro
|
4
2
|
|
3
|
+
*NOTE:* This library monkeypatches core classes and wa designed for Ruby 1.8 & 1.9. A redesign using refinements would be much better. Minimal support is provided.
|
4
|
+
|
5
5
|
If you need to do read and write binary data, there is of course <tt>Array::pack</tt> and <tt>String::unpack</tt>.
|
6
6
|
The packable library makes (un)packing nicer, smarter and more powerful.
|
7
7
|
In case you are wondering why on earth someone would want to do serious (un)packing when YAML & XML are built-in:
|
@@ -26,6 +26,7 @@ The method +each+ also accepts packing options:
|
|
26
26
|
StringIO.new("\000\001\000\002\000\003").each(:short).to_a ===> [1,2,3]
|
27
27
|
=== Custom classes
|
28
28
|
It's easy to make you own classes (un)packable. All the previous goodies are thus available:
|
29
|
+
|
29
30
|
File.open("great_flick.flv") do |f|
|
30
31
|
head = f.read(FLV::Header)
|
31
32
|
f.each(FLV::Tag) do |tag|
|
@@ -48,7 +49,7 @@ Add GitHub to your gem sources (if you haven't already):
|
|
48
49
|
Get the gem:
|
49
50
|
|
50
51
|
sudo gem install marcandre-packable
|
51
|
-
|
52
|
+
|
52
53
|
That's it! Simply <tt>require 'packable'</tt> in your code to use it.
|
53
54
|
|
54
55
|
== Compatibility
|
@@ -97,7 +98,7 @@ When unpacking, it is necessary to specify the class in addition to any option,
|
|
97
98
|
|
98
99
|
It's easy to add shortcuts for easier (un)packing:
|
99
100
|
|
100
|
-
|
101
|
+
String.packers.set :flv_signature, :bytes => 3, :fill => "FLV"
|
101
102
|
|
102
103
|
"x".pack(:flv_signature) ===> "xFL"
|
103
104
|
|
@@ -131,14 +132,14 @@ The following shortcuts and defaults are built-in the library:
|
|
131
132
|
:byte => :bytes=>1
|
132
133
|
:unsigned_long => :bytes=>4, :signed=>false
|
133
134
|
:unsigned_short => :bytes=>2, :signed=>false
|
134
|
-
|
135
|
-
=== Float
|
135
|
+
|
136
|
+
=== Float
|
136
137
|
:merge_all => :precision => :single, :endian => :big
|
137
138
|
:default => :float
|
138
139
|
:double => :precision => :double
|
139
140
|
:float => {}
|
140
|
-
|
141
|
-
=== String
|
141
|
+
|
142
|
+
=== String
|
142
143
|
:merge_all => :fill => " "
|
143
144
|
|
144
145
|
== Files and StringIO
|
@@ -180,7 +181,7 @@ and unpacking on +read_packed+. For example:
|
|
180
181
|
|
181
182
|
class MyHeader < Struct.new(:signature, :nb_blocks)
|
182
183
|
include Packable
|
183
|
-
|
184
|
+
|
184
185
|
def write_packed(packedio, options)
|
185
186
|
packedio << [signature, {:bytes=>3}] << [nb_blocks, :short]
|
186
187
|
end
|
@@ -238,7 +239,7 @@ A final note to say that packers are inherited in some way. For instance one cou
|
|
238
239
|
io.read(klass)
|
239
240
|
end
|
240
241
|
end
|
241
|
-
|
242
|
+
|
242
243
|
[42, MyHeader.new("Wow", 1)].pack(:with_class, :with_class).unpack(:with_class, :with_class) ===> [42, MyHeader.new("Wow", 1)]
|
243
244
|
|
244
245
|
= License
|
data/lib/packable.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "packable/version"
|
2
|
-
require 'backports'
|
2
|
+
require 'backports/tools/alias_method_chain'
|
3
|
+
require 'backports/rails/module'
|
3
4
|
require_relative 'packable/packers'
|
4
5
|
require_relative 'packable/mixin'
|
5
6
|
[Object, Array, String, Integer, Float, IO, Proc].each do |klass|
|
@@ -3,7 +3,7 @@ module Packable
|
|
3
3
|
module Integer #:nodoc:
|
4
4
|
NEEDS_REVERSAL = Hash.new{|h, endian| raise ArgumentError, "Endian #{endian} is not valid. It must be one of #{h.keys.join(', ')}"}.
|
5
5
|
merge!(:little => true, :big => false, :network => false, :native => "*\x00\x00\x00".unpack('L').first == 42).freeze
|
6
|
-
|
6
|
+
|
7
7
|
def self.included(base)
|
8
8
|
base.class_eval do
|
9
9
|
include Packable
|
@@ -9,7 +9,26 @@ module Packable
|
|
9
9
|
base.alias_method_chain :write, :packing
|
10
10
|
base.alias_method_chain :each, :packing
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
|
+
# Methods supported by seekable streams.
|
14
|
+
SEEKABLE_API = %i[pos pos= seek rewind].freeze
|
15
|
+
|
16
|
+
# Check whether can seek without errors.
|
17
|
+
def seekable?
|
18
|
+
if !defined?(@seekable)
|
19
|
+
@seekable =
|
20
|
+
# The IO class throws an exception at runtime if we try to change
|
21
|
+
# position on a non-regular file.
|
22
|
+
if respond_to?(:stat)
|
23
|
+
stat.file?
|
24
|
+
else
|
25
|
+
# Duck-type the rest of this.
|
26
|
+
SEEKABLE_API.all? { |m| respond_to?(m) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
@seekable
|
30
|
+
end
|
31
|
+
|
13
32
|
# Returns the change in io.pos caused by the block.
|
14
33
|
# Has nothing to do with packing, but quite helpful and so simple...
|
15
34
|
def pos_change(&block)
|
@@ -33,11 +52,11 @@ module Packable
|
|
33
52
|
r.stream = self
|
34
53
|
r >> options
|
35
54
|
end
|
36
|
-
|
55
|
+
|
37
56
|
# Returns (or yields) a modified IO object that will always pack/unpack when writing/reading.
|
38
57
|
def packed
|
39
58
|
packedio = clone
|
40
|
-
packedio.set_encoding("ascii-8bit") if packedio.respond_to? :set_encoding
|
59
|
+
packedio.set_encoding("ascii-8bit") if packedio.respond_to? :set_encoding
|
41
60
|
class << packedio
|
42
61
|
def << (arg)
|
43
62
|
arg = [arg, :default] unless arg.instance_of?(::Array)
|
@@ -53,17 +72,17 @@ module Packable
|
|
53
72
|
end
|
54
73
|
|
55
74
|
def each_with_packing(*options, &block)
|
56
|
-
return each_without_packing(*options, &block) if (Integer === options.first) || (String === options.first)
|
75
|
+
return each_without_packing(*options, &block) if options.empty? || (Integer === options.first) || (String === options.first) || !seekable?
|
57
76
|
return Enumerator.new(self, :each_with_packing, *options) unless block_given?
|
58
77
|
yield read(*options) until eof?
|
59
78
|
end
|
60
79
|
|
61
80
|
def write_with_packing(*arg)
|
62
|
-
(arg.length
|
81
|
+
(arg.length <= 1 || !seekable?) ? write_without_packing(*arg) : pack_and_write(*arg)
|
63
82
|
end
|
64
|
-
|
83
|
+
|
65
84
|
def read_with_packing(*arg)
|
66
|
-
return read_without_packing(*arg) if arg.
|
85
|
+
return read_without_packing(*arg) if arg.empty? || arg.first.nil? || arg.first.is_a?(Numeric) || !seekable?
|
67
86
|
values = Packable::Packers.to_class_option_list(*arg).map do |klass, options, original|
|
68
87
|
if options[:read_packed]
|
69
88
|
options[:read_packed].call(self)
|
@@ -73,7 +92,7 @@ module Packable
|
|
73
92
|
end
|
74
93
|
return values.size > 1 ? values : values.first
|
75
94
|
end
|
76
|
-
|
95
|
+
|
77
96
|
# returns a string of exactly n bytes, or else raises an EOFError
|
78
97
|
def read_exactly(n)
|
79
98
|
return "" if n.zero?
|
@@ -81,7 +100,7 @@ module Packable
|
|
81
100
|
raise EOFError if s.nil? || s.length < n
|
82
101
|
s
|
83
102
|
end
|
84
|
-
|
103
|
+
|
85
104
|
def pack_and_write(*arg)
|
86
105
|
original_pos = pos
|
87
106
|
Packable::Packers.to_object_option_list(*arg).each do |obj, options|
|
@@ -94,7 +113,6 @@ module Packable
|
|
94
113
|
pos - original_pos
|
95
114
|
end
|
96
115
|
|
97
|
-
|
98
116
|
end
|
99
117
|
end
|
100
118
|
end
|
data/lib/packable/mixin.rb
CHANGED
@@ -5,7 +5,7 @@ require 'stringio'
|
|
5
5
|
module Packable
|
6
6
|
def self.included(base) #:nodoc:
|
7
7
|
base.class_eval do
|
8
|
-
class << self
|
8
|
+
class << self
|
9
9
|
include PackersClassMethod
|
10
10
|
include ClassMethods
|
11
11
|
end
|
@@ -26,18 +26,18 @@ module Packable
|
|
26
26
|
#
|
27
27
|
def packers
|
28
28
|
yield packers if block_given?
|
29
|
-
Packers.for(self)
|
29
|
+
Packers.for(self)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
module ClassMethods
|
33
|
+
module ClassMethods
|
34
34
|
|
35
35
|
def unpack(s, options = :default)
|
36
36
|
return s.unpack(options).first if options.is_a? String
|
37
37
|
StringIO.new(s).packed.read(self, options)
|
38
38
|
end
|
39
|
-
|
40
|
-
# Default +read_packed+ calls either the instance method <tt>read_packed</tt> or the
|
39
|
+
|
40
|
+
# Default +read_packed+ calls either the instance method <tt>read_packed</tt> or the
|
41
41
|
# class method +unpack_string+. Choose:
|
42
42
|
# * define a class method +read_packed+ that returns the newly read object
|
43
43
|
# * define an instance method +read_packed+ which reads the io into +self+
|
@@ -57,5 +57,5 @@ module Packable
|
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
|
-
end
|
61
|
-
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/packable/packers.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Packable
|
2
|
-
|
2
|
+
|
3
3
|
# Packers for any packable class.
|
4
4
|
class Packers < Hash
|
5
5
|
SPECIAL = [:default, :merge_all].freeze
|
6
|
-
|
6
|
+
|
7
7
|
# Usage:
|
8
8
|
# PackableClass.packers.set :shortcut, :option => value, ...
|
9
9
|
# PackableClass.packers { |p| p.set...; p.set... }
|
@@ -42,7 +42,7 @@ module Packable
|
|
42
42
|
end
|
43
43
|
|
44
44
|
@@packers_for_class = Hash.new{|h, klass| h[klass] = Packers.new(klass)}
|
45
|
-
|
45
|
+
|
46
46
|
# Returns the configuration for the given +klass+.
|
47
47
|
def self.for(klass)
|
48
48
|
@@packers_for_class[klass]
|
@@ -59,7 +59,7 @@ module Packable
|
|
59
59
|
end
|
60
60
|
r
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
def self.to_object_option_list(*arg) #:nodoc:
|
64
64
|
r=[]
|
65
65
|
until arg.empty? do
|
@@ -85,7 +85,6 @@ module Packable
|
|
85
85
|
raise "Couldn't find packing option #{key}"
|
86
86
|
end
|
87
87
|
|
88
|
-
|
89
88
|
end
|
90
89
|
|
91
90
|
# Use to capture the blocks given to read/write
|
@@ -103,5 +102,5 @@ module Packable
|
|
103
102
|
options[:write_packed] = block.unbind
|
104
103
|
end
|
105
104
|
end
|
106
|
-
|
107
|
-
end
|
105
|
+
|
106
|
+
end
|
data/lib/packable/version.rb
CHANGED
data/packable.gemspec
CHANGED
@@ -6,6 +6,7 @@ require 'packable/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "packable"
|
8
8
|
gem.version = Packable::VERSION
|
9
|
+
gem.homepage = "https://github.com/marcandre/packable"
|
9
10
|
gem.authors = ["Marc-André Lafortune"]
|
10
11
|
gem.email = ["github@marc-andre.ca"]
|
11
12
|
gem.description = %q{If you need to do read and write binary data, there is of course <Array::pack and String::unpack\n The packable library makes (un)packing nicer, smarter and more powerful.\n}
|
@@ -16,6 +17,7 @@ Gem::Specification.new do |gem|
|
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
19
|
gem.require_paths = ["lib"]
|
20
|
+
gem.required_ruby_version = '>= 1.8.7'
|
19
21
|
gem.add_runtime_dependency 'backports'
|
20
22
|
gem.add_development_dependency 'minitest'
|
21
23
|
gem.add_development_dependency 'shoulda'
|
data/test/packing_doc_test.rb
CHANGED
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
|
2
2
|
# Warning: ugly...
|
3
3
|
class MyHeader < Struct.new(:signature, :nb_blocks)
|
4
4
|
include Packable
|
5
|
-
|
5
|
+
|
6
6
|
def write_packed(packedio, options)
|
7
7
|
packedio << [signature, {:bytes=>3}] << [nb_blocks, :short]
|
8
8
|
end
|
@@ -10,19 +10,17 @@ class MyHeader < Struct.new(:signature, :nb_blocks)
|
|
10
10
|
def read_packed(packedio, options)
|
11
11
|
self.signature, self.nb_blocks = packedio >> [String, {:bytes => 3}] >> :short
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def ohoh
|
15
15
|
:ahah
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
|
-
class PackableDocTest < Minitest::Test
|
19
|
+
class PackableDocTest < Minitest::Test
|
21
20
|
def test_doc
|
22
21
|
|
23
22
|
assert_equal [1,2,3], StringIO.new("\000\001\000\002\000\003").each(:short).to_a
|
24
23
|
|
25
|
-
|
26
24
|
String.packers.set :flv_signature, :bytes => 3, :fill => "FLV"
|
27
25
|
|
28
26
|
assert_equal "xFL", "x".pack(:flv_signature)
|
@@ -31,12 +29,12 @@ class PackableDocTest < Minitest::Test
|
|
31
29
|
p.set :merge_all, :fill => "*" # Unless explicitly specified, :fill will now be "*"
|
32
30
|
p.set :default, :bytes => 8 # If no option is given, this will act as default
|
33
31
|
end
|
34
|
-
|
32
|
+
|
35
33
|
assert_equal "ab******", "ab".pack
|
36
34
|
assert_equal "ab**", "ab".pack(:bytes=>4)
|
37
35
|
assert_equal "ab", "ab".pack(:fill => "!")
|
38
36
|
assert_equal "ab!!", "ab".pack(:fill => "!", :bytes => 4)
|
39
|
-
|
37
|
+
|
40
38
|
String.packers do |p|
|
41
39
|
p.set :creator, :bytes => 4
|
42
40
|
p.set :app_type, :creator
|
@@ -44,9 +42,9 @@ class PackableDocTest < Minitest::Test
|
|
44
42
|
p.set :merge_all, :fill => " "
|
45
43
|
p.set :eigth_bytes, :bytes => 8
|
46
44
|
end
|
47
|
-
|
45
|
+
|
48
46
|
assert_equal "hello".pack(:app_type), "hell"
|
49
|
-
|
47
|
+
|
50
48
|
assert_equal [["sig", 1, "hello, w"]]*4,
|
51
49
|
[
|
52
50
|
lambda { |io| io >> :flv_signature >> Integer >> [String, {:bytes => 8}] },
|
@@ -54,8 +52,7 @@ class PackableDocTest < Minitest::Test
|
|
54
52
|
lambda { |io| io.read(:flv_signature, Integer, String, {:bytes => 8}) },
|
55
53
|
lambda { |io| [io.read(:flv_signature), io.read(Integer), io.read(String, {:bytes => 8})] }
|
56
54
|
].map {|proc| proc.call(StringIO.new("sig\000\000\000\001hello, world"))}
|
57
|
-
|
58
|
-
|
55
|
+
|
59
56
|
ex = "xFL\000\000\000BHello "
|
60
57
|
[
|
61
58
|
lambda { |io| io << "x".pack(:flv_signature) << 66.pack << "Hello".pack(:bytes => 8)}, # returns io
|
@@ -68,31 +65,28 @@ class PackableDocTest < Minitest::Test
|
|
68
65
|
ios.rewind
|
69
66
|
assert_equal ex, ios.read, "With #{proc}"
|
70
67
|
end
|
71
|
-
|
68
|
+
|
72
69
|
#insure StringIO class is not affected
|
73
70
|
ios = StringIO.new
|
74
71
|
ios.packed
|
75
72
|
ios << 66
|
76
73
|
ios.rewind
|
77
74
|
assert_equal "66", ios.read
|
78
|
-
|
79
|
-
|
75
|
+
|
80
76
|
String.packers.set :length_encoded do |packer|
|
81
77
|
packer.write { |io| io << length << self }
|
82
78
|
packer.read { |io| io.read(io.read(Integer)) }
|
83
79
|
end
|
84
|
-
|
80
|
+
|
85
81
|
assert_equal "\000\000\000\006hello!", "hello!".pack(:length_encoded)
|
86
82
|
assert_equal ["this", "is", "great!"], ["this", "is", "great!"].pack(*[:length_encoded]*3).unpack(*[:length_encoded]*3)
|
87
|
-
|
83
|
+
|
88
84
|
h = MyHeader.new("FLV", 65)
|
89
85
|
assert_equal "FLV\000A", h.pack
|
90
86
|
h2, = StringIO.new("FLV\000A") >> MyHeader
|
91
87
|
assert_equal h, h2
|
92
88
|
assert_equal h.ohoh, h2.ohoh
|
93
89
|
|
94
|
-
|
95
|
-
|
96
90
|
Object.packers.set :with_class do |packer|
|
97
91
|
packer.write { |io| io << [self.class.name, :length_encoded] << self }
|
98
92
|
packer.read do |io|
|
@@ -103,4 +97,4 @@ class PackableDocTest < Minitest::Test
|
|
103
97
|
ar = [42, MyHeader.new("FLV", 65)]
|
104
98
|
assert_equal ar, ar.pack(:with_class, :with_class).unpack(:with_class, :with_class)
|
105
99
|
end
|
106
|
-
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: packable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marc-André Lafortune
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backports
|
@@ -107,15 +107,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 1.8.7
|
111
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
112
|
requirements:
|
113
113
|
- - ">="
|
114
114
|
- !ruby/object:Gem::Version
|
115
115
|
version: '0'
|
116
116
|
requirements: []
|
117
|
-
|
118
|
-
rubygems_version: 2.2.2
|
117
|
+
rubygems_version: 3.1.2
|
119
118
|
signing_key:
|
120
119
|
specification_version: 4
|
121
120
|
summary: Extensive packing and unpacking capabilities
|
@@ -123,4 +122,3 @@ test_files:
|
|
123
122
|
- test/packing_doc_test.rb
|
124
123
|
- test/packing_test.rb
|
125
124
|
- test/test_helper.rb
|
126
|
-
has_rdoc:
|