arpie 0.0.6 → 0.1.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.
@@ -0,0 +1,4 @@
1
+ doc
2
+ pkg
3
+ .yardoc
4
+ Gemfile.lock
@@ -0,0 +1 @@
1
+ --no-private lib/**/*.rb - *.rdoc LICENCE
@@ -8,8 +8,8 @@ What a mouthful.
8
8
  Let's try with an example:
9
9
 
10
10
  class MyBinary < Arpie::Binary
11
- field :status, :uint8
12
- field :name, :string, :sizeof => :uint8
11
+ uint8 :status
12
+ string :name, :sizeof => :uint8
13
13
  end
14
14
 
15
15
  Looks simple enough, doesn't it?
@@ -27,6 +27,15 @@ This works both ways, of course:
27
27
  irb(main):006:0> a[0].to
28
28
  => "\001\005Arpie"
29
29
 
30
+ == Shorter notation for field definitions
31
+
32
+ Note that calling
33
+
34
+ field :name, :type
35
+
36
+ is equivalent to
37
+
38
+ type :name
30
39
 
31
40
  == Usage within Arpie::Protocol
32
41
 
@@ -83,19 +92,19 @@ You can actually specify a field or virtual that was defined before the currentl
83
92
  Example:
84
93
 
85
94
  class Inner < Arpie::Binary
86
- field :sz, :uint8
87
- field :ls, :list, :of => :uint8, :length => :sz
95
+ uint8 :sz
96
+ list :ls, :of => :uint8, :length => :sz
88
97
  end
89
98
 
90
99
  class Outer < Arpie::Binary
91
- field :totalsz, :uint8
100
+ uint8 :totalsz
92
101
 
93
- field :bytes, :bytes, :length => :totalsz do
94
- field :content, :list, :of => Inner,
102
+ bytes :bytes, :length => :totalsz do
103
+ list :content, :of => Inner,
95
104
  :length => :all
96
105
  end
97
106
 
98
- field :end, :uint8
107
+ uint8 :end
99
108
  end
100
109
 
101
110
  Note that fields defined this way will NOT update their "length referral" - you will have to
@@ -105,26 +114,34 @@ do that manually.
105
114
  This includes a prefixed "non-visible" field, which will be used to determine the
106
115
  actual expected length of the data. Example:
107
116
 
108
- field :blurbel, :bytes, :sizeof => :lint16
117
+ bytes :blurbel, :sizeof => :lint16
109
118
 
110
119
  Will expect a network-order short (16 bits), followed by the amout of bytes the short resolves to.
111
120
 
112
121
  If the field type given in :sizeof requires additional parameters, you can pass them with
113
122
  :sizeof_opts (just like with :list - :of).
114
123
 
124
+ === :mod
125
+ Certain packed types (namely, numerics), allow for :mod, which will apply
126
+ a fixed modificator to the read/written value. Example:
127
+
128
+ string :test, :sizeof => :uint8, :sizeof_opts => { :mod => -1 }
129
+
130
+ This will always cut off the last character.
131
+
115
132
  === :list
116
133
 
117
134
  A :list is an array of arbitary, same-type elements. The element type is given in the :list-specific
118
135
  :of parameter:
119
136
 
120
- field :my_list, :list, :of => :lint16
137
+ list :my_list, :of => :lint16
121
138
 
122
139
  This will complain of not being able to determine the size of the list - pass either a :sizeof,
123
140
  or a :length parameter, described as above.
124
141
 
125
142
  If your :of requires additional argument (a list of lists, for example), you can pass theses with :of_opts:
126
143
 
127
- field :my_list_2, :list, :sizeof => :uint8, :of => :string,
144
+ list :my_list_2, :sizeof => :uint8, :of => :string,
128
145
  :of_opts => { :sizeof, :nint16 }
129
146
 
130
147
  === :bitfield
@@ -132,9 +149,9 @@ If your :of requires additional argument (a list of lists, for example), you can
132
149
  The bitfield type unpacks one or more bytes into their bit values, for individual addressing:
133
150
 
134
151
  class TestClass < Arpie::Binary
135
- field :flags, :msb_bitfield, :length => 8 do
136
- field :bool_1, :bit
137
- field :compound, :bit, :length => 7
152
+ msg_bitfield :flags, :length => 8 do
153
+ bit :bool_1
154
+ bit :compound, :length => 7
138
155
  # Take care not to leave any bits unmanaged - weird things happen otherwise.
139
156
  end
140
157
  end
@@ -155,9 +172,15 @@ This is pretty much all that you can do with it, for now.
155
172
  The fixed type allows defining fixed strings that are always the same, both acting as a filler
156
173
  and a safeguard (it will complain if it does not match):
157
174
 
158
- field :blah, :fixed, :value => "FIXED"
175
+ fixed :blah, :value => "FIXED"
176
+
177
+ The alias Binary.static does this for you, but works slightly different:
178
+
179
+ static "aaa" # autogenerates a name with the assumption you don't want to access it
180
+ static :asdfg, "asdfg"
159
181
 
160
- The alias Binary.static does this for you.
182
+ Fields declared with the "static" alias have a :default value already set, whereas
183
+ fields of the type :fixed do not.
161
184
 
162
185
  == Nested Classes
163
186
 
@@ -165,11 +188,11 @@ Instead of pre-registered primitive data fiels you can pass in class names:
165
188
 
166
189
  class Outer < Arpie::Binary
167
190
  class Nested < Arpie::Binary
168
- field :a, :uint8
169
- field :b, :uint8
191
+ uint8 :a
192
+ uint8 :b
170
193
  end
171
194
 
172
- field :hah, :list, :of => Nested, :sizeof => :uint8
195
+ list :hah, :of => Nested, :sizeof => :uint8
173
196
  end
174
197
 
175
198
  == Inline Anonymous Classes
@@ -177,9 +200,9 @@ Instead of pre-registered primitive data fiels you can pass in class names:
177
200
  Also, you can specify anonymous nested classes, which can be used to split data of the same type more fine-grainedly:
178
201
 
179
202
  class TestClass < Arpie::Binary
180
- field :outer, :bytes, :length => 16 do
181
- field :key1, :bytes, :length => 8
182
- field :key2, :bytes, :length => 8
203
+ bytes :outer, :length => 16 do
204
+ bytes :key1, :length => 8
205
+ bytes :key2, :length => 8
183
206
  end
184
207
  end
185
208
 
@@ -205,24 +228,24 @@ A virtual is a field definition that is not actually part of the binary data.
205
228
  As you get to parse complex data structures, you might encounter the following case:
206
229
 
207
230
  class TestClass < Arpie::Binary
208
- field :len_a, :uint8
209
- field :len_b, :uint8
231
+ uint8 :len_a
232
+ uint8 :len_b
210
233
 
211
234
  field :middle, :something
212
235
 
213
- field :matrix, :list, :of => :uint8, :length => (value of :len_a * :len_b)
236
+ list :matrix, :of => :uint8, :length => (value of :len_a * :len_b)
214
237
  end
215
238
 
216
239
  In this case, you will need to use a virtual attribute:
217
240
 
218
241
  class TestClass < Arpie::Binary
219
- field :len_a, :uint8
220
- field :len_b, :uint8
242
+ uint8 :len_a
243
+ uint8 :len_b
221
244
 
222
245
  field :middle, :something
223
246
 
224
247
  virtual :v_len, :uint16 do |o| o.len_a * o.len_b end
225
- field :hah, :list, :of => Nested, :length => :v_len
248
+ list :hah, :of => Nested, :length => :v_len
226
249
 
227
250
  pre_to do |o|
228
251
  o.len_a = 4
@@ -235,6 +258,28 @@ virtual attributes are one-way - obviously they cannot be used to write out data
235
258
 
236
259
  That is what the pre_to is for - it recalculates len_a and len_b to your specifications.
237
260
 
261
+ == Self-documenting Arpie::Binary
262
+
263
+ Every Arpie::Binary is self-documenting, as is this example:
264
+
265
+ class Doc < Arpie::Binary
266
+ describe "a document"
267
+ string :author, :sizeof => :uint16,
268
+ :description => "The author"
269
+ string :text, :sizeof => :uint16,
270
+ :description => "The document text"
271
+ end
272
+
273
+ puts Doc.describe
274
+
275
+ Will produce output like this:
276
+
277
+ Binary: a document
278
+
279
+ Fields: NAME TYPE WIDTH OF DESCRIPTION
280
+ author string uint16 The author
281
+ text string uint16 The document text
282
+
238
283
  == hooks
239
284
 
240
285
  Binary provides several hooks that can be used to mangle data in the transformation process.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake', '>= 0.8.7'
6
+
7
+ gem 'rspec', :groups => [:development, :test]
8
+ gem 'yard', :groups => [:development]
data/LICENCE ADDED
@@ -0,0 +1,15 @@
1
+ Copyright Bernhard Stoeckner <le@e-ix.net> and contributors. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without modification, are
4
+ permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of
7
+ conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
10
+ of conditions and the following disclaimer in the documentation and/or other materials
11
+ provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
14
+ INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15
+ FOR A PARTICULAR PURPOSE.
@@ -0,0 +1,46 @@
1
+ == Writing custom Protocols
2
+
3
+ You can use arpies Protocol layer to write your custom protocol parser/emitters.
4
+ Consider the following, again very contrived, example. You have a linebased wire format,
5
+ which sends regular object updates in multiple lines, each holding a property to be updated.
6
+ What objects get updated is not relevant to this example.
7
+
8
+ For this example, we'll be using the SeparatorProtocol already contained in protocols.rb as
9
+ a base.
10
+
11
+ class AssembleExample < Arpie::Protocol
12
+
13
+ def from binary
14
+ # The wire format is simply a collection of lines
15
+ # where the first one is a number containing the
16
+ # # of lines to expect.
17
+ assemble! binary do |binaries, meta|
18
+ binaries.size >= 1 or incomplete!
19
+ binaries.size - 1 >= binaries[0].to_i or incomplete!
20
+
21
+ # Here, you can wrap all collected updates in
22
+ # whatever format you want it to be. We're just
23
+ # "joining" them to be a single array.
24
+ binaries.shift
25
+ binaries
26
+ end
27
+ end
28
+
29
+ def to object
30
+ yield object.size
31
+ object.each {|oo|
32
+ yield oo
33
+ }
34
+ end
35
+ end
36
+
37
+ p = Arpie::ProtocolChain.new(
38
+ AssembleExample.new,
39
+ Arpie::SeparatorProtocol.new
40
+ )
41
+ r, w = IO.pipe
42
+
43
+ p.write_message(w, %w{we want to be assembled})
44
+
45
+ p p.read_message(r)
46
+ # => ["we", "want", "to", "be", "assembled"]
@@ -0,0 +1,17 @@
1
+ = What's this?
2
+
3
+ Arpie is a toolkit for handling binary data, network protocols, file formats, and
4
+ similar.
5
+
6
+ It provides:
7
+
8
+ - a DSL-like syntax for describing file formats (see {file:BINARY})
9
+ - stackable protocols that can be used to abstract layers of on-wire data (see {file:PROTOCOLS})
10
+ - some bits and glue to put it all together (for example, with eventmachine)
11
+
12
+ == Getting started
13
+
14
+ arpie is packaged up as a gem - just do <tt>gem install arpie</tt>
15
+ to get the newest version.
16
+
17
+ The latest source is available through git[https://github.com/elven/arpie].
data/Rakefile CHANGED
@@ -1,116 +1,8 @@
1
- require "rake"
2
- require "rake/clean"
3
- require "rake/gempackagetask"
4
- begin
5
- require "hanna/rdoctask"
6
- rescue LoadError
7
- require "rake/rdoctask"
8
- end
9
- require "fileutils"
10
- include FileUtils
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ require 'yard'
11
5
 
12
- ##############################################################################
13
- # Configuration
14
- ##############################################################################
15
- NAME = "arpie"
16
- VERS = "0.0.6"
17
- CLEAN.include ["**/.*.sw?", "pkg", ".config", "rdoc", "coverage"]
18
- RDOC_OPTS = ["--quiet", "--line-numbers", "--inline-source", '--title', \
19
- "#{NAME}: A high-performing layered networking protocol framework. Simple to use, simple to extend.", \
20
- '--main', 'README']
6
+ YARD::Rake::YardocTask.new
21
7
 
22
- DOCS = ["README", "COPYING", "BINARY_SPEC"]
23
-
24
- Rake::RDocTask.new do |rdoc|
25
- rdoc.rdoc_dir = "rdoc"
26
- rdoc.options += RDOC_OPTS
27
- rdoc.rdoc_files.add DOCS + ["doc/*.rdoc", "lib/**/*.rb"]
28
- end
29
-
30
- desc "Packages up #{NAME}"
31
- task :package => [:clean]
32
-
33
- spec = Gem::Specification.new do |s|
34
- s.name = NAME
35
- s.rubyforge_project = "#{NAME}"
36
- s.version = VERS
37
- s.platform = Gem::Platform::RUBY
38
- s.has_rdoc = true
39
- s.extra_rdoc_files = DOCS + Dir["doc/*.rdoc"]
40
- s.rdoc_options += RDOC_OPTS + ["--exclude", "^(examples|extras)\/"]
41
- s.summary = "A high-performing layered networking protocol framework. Simple to use, simple to extend."
42
- s.description = s.summary
43
- s.author = "Bernhard Stoeckner"
44
- s.email = "elven@swordcoast.net"
45
- s.homepage = "http://#{NAME}.elv.es"
46
- s.executables = []
47
- s.required_ruby_version = ">= 1.8.4"
48
- s.files = %w(COPYING README Rakefile) + Dir.glob("{bin,doc,spec,lib,tools,scripts,data}/**/*")
49
- s.require_path = "lib"
50
- s.bindir = "bin"
51
- s.add_dependency('uuidtools', '>= 2.0.0')
52
- end
53
-
54
- Rake::GemPackageTask.new(spec) do |p|
55
- p.need_tar = true
56
- p.gem_spec = spec
57
- end
58
-
59
- desc "Install #{NAME} gem"
60
- task :install do
61
- sh %{rake package}
62
- sh %{sudo gem1.8 install pkg/#{NAME}-#{VERS}}
63
- end
64
-
65
- desc "Regenerate proto classes"
66
- task :protoc do
67
- sh %{rprotoc --out=lib/arpie arpie.proto}
68
- end
69
-
70
- desc "Install #{NAME} gem without docs"
71
- task :install_no_docs do
72
- sh %{rake package}
73
- sh %{sudo gem1.8 install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri}
74
- end
75
-
76
- desc "Uninstall #{NAME} gem"
77
- task :uninstall => [:clean] do
78
- sh %{sudo gem1.8 uninstall #{NAME}}
79
- end
80
-
81
- desc "Upload #{NAME} gem to rubyforge"
82
- task :release => [:package] do
83
- sh %{rubyforge login}
84
- sh %{rubyforge add_release #{NAME} #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.tgz}
85
- sh %{rubyforge add_file #{NAME} #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
86
- end
87
-
88
- require "spec/rake/spectask"
89
-
90
- desc "Run specs with coverage"
91
- Spec::Rake::SpecTask.new("spec") do |t|
92
- t.spec_files = FileList["spec/*_spec.rb"]
93
- t.spec_opts = File.read("spec/spec.opts").split("\n")
94
- t.rcov_opts = File.read("spec/rcov.opts").split("\n")
95
- t.rcov = true
96
- end
97
-
98
- desc "Run specs without coverage"
99
- task :default => [:spec_no_cov]
100
- Spec::Rake::SpecTask.new("spec_no_cov") do |t|
101
- t.spec_files = FileList["spec/*_spec.rb"]
102
- t.spec_opts = File.read("spec/spec.opts").split("\n")
103
- end
104
-
105
- desc "Run rcov only"
106
- Spec::Rake::SpecTask.new("rcov") do |t|
107
- t.rcov_opts = File.read("spec/rcov.opts").split("\n")
108
- t.spec_opts = File.read("spec/spec.opts").split("\n")
109
- t.spec_files = FileList["spec/*_spec.rb"]
110
- t.rcov = true
111
- end
112
-
113
- desc "check documentation coverage"
114
- task :dcov do
115
- sh "find lib -name '*.rb' | xargs dcov"
116
- end
8
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/arpie/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Bernhard Stoeckner"]
6
+ gem.email = ["le@e-ix.net"]
7
+ gem.description = %q{Toolkit for handling binary data, network protocols, file formats, and similar}
8
+ gem.summary = gem.description
9
+ gem.homepage = "https://github.com/elven/arpie"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "arpie"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Arpie::VERSION
17
+
18
+ gem.post_install_message = "
19
+ You have installed arpie 0.1.0 or greater. This breaks
20
+ compatibility with previous versions (0.0.x).
21
+
22
+ Specifically, it removes all client/server code, and
23
+ XMLRPC integration, since EventMachine and similar does
24
+ all that in a much cleaner and more efficient way.
25
+ "
26
+ end