arpie 0.0.6 → 0.1.0

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