packable 1.3.8 → 1.3.14
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.
- 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:
|