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,18 @@
1
+ require 'rubygems'
2
+ require 'arpie'
3
+ require 'eventmachine'
4
+
5
+ EM::run {
6
+ EM::start_server "127.0.0.1", 51210, Arpie::EventMachine::ArpieProtocol,
7
+ Arpie::SeparatorProtocol.new do |c|
8
+ def c.receive message
9
+ puts message.reverse
10
+ EM::stop_event_loop
11
+ end
12
+ end
13
+
14
+ EM::connect "127.0.0.1", 51210, Arpie::EventMachine::ArpieProtocol,
15
+ Arpie::SeparatorProtocol.new do |c|
16
+ c.send "hi"
17
+ end
18
+ }
@@ -1,8 +1,6 @@
1
+ require 'arpie/version'
1
2
  require 'arpie/error'
2
3
  require 'arpie/protocol'
3
4
  require 'arpie/binary'
4
5
  require 'arpie/binary_types'
5
- require 'arpie/xmlrpc'
6
- require 'arpie/server'
7
- require 'arpie/client'
8
- require 'arpie/proxy'
6
+ require 'arpie/em'
@@ -50,7 +50,7 @@ module Arpie
50
50
  @@description ||= {}
51
51
  @@hooks ||= {}
52
52
 
53
- #:stopdoc:
53
+ # @private
54
54
  @@anonymous ||= {}
55
55
  def self.__anonymous
56
56
  @@anonymous[self]
@@ -58,7 +58,6 @@ module Arpie
58
58
  def self.__anonymous= x
59
59
  @@anonymous[self] = x
60
60
  end
61
- #:startdoc:
62
61
 
63
62
  def initialize
64
63
  @fields = {}
@@ -229,6 +228,17 @@ module Arpie
229
228
  end
230
229
  end
231
230
 
231
+ # You can use the field type as a class method to create new fields,
232
+ # as if you would call Binary.field with the appropriate parameters.
233
+ #
234
+ # Examples:
235
+ # uint8 :nameof, :default => 1
236
+ # fixed :fixedValue, :value => [1,2,3].pack("CCC")
237
+ # list :ls, :of => :uint8, :sizeof => :nameof
238
+ def self.method_missing meth, *va, &block
239
+ self.field(va.shift, meth, *va, &block)
240
+ end
241
+
232
242
  # Specify that this Binary has a field of type +type+.
233
243
  # See the class documentation for usage.
234
244
  def self.field name, type = nil, opts = {}, &block
@@ -331,7 +341,7 @@ module Arpie
331
341
  # a complete Binary.
332
342
  def self.from binary, opts = {}
333
343
  @@fields[self] ||= []
334
- binary = * self.call_hooks(:pre_from, binary)
344
+ binary, * = * self.call_hooks(:pre_from, binary)
335
345
 
336
346
  consumed_bytes = 0
337
347
  obj = new
@@ -373,7 +383,7 @@ module Arpie
373
383
  object.nil? and raise ArgumentError, "cannot #to nil"
374
384
  @@fields[self] ||= []
375
385
  r = []
376
- object = * self.call_hooks(:pre_to, object)
386
+ object, * = * self.call_hooks(:pre_to, object)
377
387
 
378
388
  @@fields[self].each {|field| # name, klass, kopts, inline_handler|
379
389
  field.opts[:object] = object
@@ -435,5 +445,9 @@ module Arpie
435
445
  def self.post_from &handler
436
446
  self.add_hook(:post_from, handler)
437
447
  end
448
+
449
+ private
450
+ def self.to_ary *a
451
+ end
438
452
  end
439
453
  end
@@ -29,7 +29,12 @@ module Arpie
29
29
  elsif opts[:length]
30
30
  len = case opts[:length]
31
31
  when :all
32
- binary.size
32
+ if @pack_string == "Z"
33
+ npos = binary.index("\000") or raise EIncomplete
34
+ npos + 1
35
+ else
36
+ binary.size
37
+ end
33
38
  when Symbol
34
39
  opts[:object].send(opts[:length])
35
40
  else
@@ -74,7 +79,7 @@ module Arpie
74
79
  Binary.register_type(BytesBinaryType.new("a", :length => 1), :char)
75
80
  Binary.register_type(BytesBinaryType.new("a"), :bytes)
76
81
  Binary.register_type(BytesBinaryType.new("A"), :string)
77
- Binary.register_type(BytesBinaryType.new("Z"), :nstring)
82
+ Binary.register_type(BytesBinaryType.new("Z", :length => :all), :nstring)
78
83
 
79
84
  Binary.register_type(BytesBinaryType.new("M"), :quoted_printable)
80
85
  Binary.register_type(BytesBinaryType.new("m"), :base64)
@@ -7,6 +7,7 @@ module Arpie
7
7
  def from binary, opts
8
8
  opts[:value] or raise ArgumentError, "Requires option :value"
9
9
  sz = opts[:value].size
10
+ binary.size >= sz or incomplete!
10
11
  existing = binary.unpack("a#{sz}")[0]
11
12
  existing == opts[:value] or bogon! nil, ":fixed did not match data in packet"
12
13
 
@@ -23,13 +24,19 @@ module Arpie
23
24
  class Binary
24
25
  # Create a static field.
25
26
  # This is an alias for:
26
- # field :name, :fixed, :value => content
27
+ # field :name, :fixed, :value => content, :default => content
27
28
  #
28
29
  # If no name is specified, it is assumed that the user will
29
30
  # never want to access it and a suitable one is autogenerated.
30
- def self.static content, name = nil
31
- name ||= "static-%x" % content.hash
32
- field name, :fixed, :value => content
31
+ def self.static name_or_content, content = nil
32
+ if content.nil?
33
+ name = "static-%x" % [name_or_content.hash]
34
+ content = name_or_content
35
+ else
36
+ name = name_or_content
37
+ end
38
+
39
+ field name, :fixed, :value => content, :default => content
33
40
  end
34
41
 
35
42
  # An alias for +static+.
@@ -67,6 +67,8 @@ module Arpie
67
67
  end
68
68
 
69
69
  else
70
+ raise StreamError, "length < 0" if length < 0
71
+
70
72
  for i in 0...length do
71
73
  cc, ate = type_of.from(binary[consumed .. -1], opts[:of_opts])
72
74
  list << cc
@@ -14,16 +14,26 @@ module Arpie
14
14
  count = 1 if count == 0
15
15
 
16
16
  length += case directive
17
- when 'A', 'a', 'C', 'c', 'Z', 'x' : count
18
- when 'B', 'b' : (count / 8.0).ceil
19
- when 'D', 'd', 'E', 'G' : count * 8
20
- when 'e', 'F', 'f', 'g' : count * 4
21
- when 'H', 'h' : (count / 2.0).ceil
22
- when 'I', 'i', 'L', 'l', 'N', 'V' : count * 4
23
- when 'n', 'S', 's', 'v' : count * 2
24
- when 'Q', 'q' : count * 8
25
- when 'X' : count * -1
26
- else raise ArgumentError, "#{directive} is not supported"
17
+ when 'A', 'a', 'C', 'c', 'Z', 'x'
18
+ count
19
+ when 'B', 'b'
20
+ (count / 8.0).ceil
21
+ when 'D', 'd', 'E', 'G'
22
+ count * 8
23
+ when 'e', 'F', 'f', 'g'
24
+ count * 4
25
+ when 'H', 'h'
26
+ (count / 2.0).ceil
27
+ when 'I', 'i', 'L', 'l', 'N', 'V'
28
+ count * 4
29
+ when 'n', 'S', 's', 'v'
30
+ count * 2
31
+ when 'Q', 'q'
32
+ count * 8
33
+ when 'X'
34
+ count * -1
35
+ else
36
+ raise ArgumentError, "#{directive} is not supported"
27
37
  end
28
38
  end
29
39
 
@@ -0,0 +1,48 @@
1
+ module Arpie
2
+ module EventMachine
3
+ # A EventMachine protocol implementing a simple protocol
4
+ # chain handler. To use, simply include it in your EM
5
+ # Connection moduleas you would any other EM protocol.
6
+ #
7
+ # The module expects a list of protocols given along with
8
+ # the module initializer:
9
+ # EM::start_server host, port, ArpieProtocol,
10
+ # Arpie::MarshalProtocol.new, Arpie::SizedProtocol.new
11
+ #
12
+ # To receive messages, override <tt>receive(message)</tt>, which will
13
+ # be called once for each message decoded with the given
14
+ # protocols.
15
+ #
16
+ # To send messages back over the same connection, simply call
17
+ # <tt>send(message)</tt>.
18
+ module ArpieProtocol
19
+ attr_reader :chain
20
+
21
+ def initialize *protocols
22
+ @chain = Arpie::ProtocolChain.new(*protocols)
23
+ end
24
+
25
+ # Receive a message. Override this in your implemention.
26
+ def receive message
27
+ end
28
+
29
+ def receive_data data
30
+ begin
31
+ for msg in @chain.from(data)
32
+ receive msg
33
+ end
34
+ rescue Arpie::EIncomplete
35
+ nil
36
+ end
37
+ end
38
+
39
+ # Send a message, encoding it with the given
40
+ # protocols.
41
+ def send message
42
+ for msg in @chain.to(message)
43
+ send_data(msg)
44
+ end
45
+ end
46
+ end
47
+ end # module EventMachine
48
+ end # module Arpie
@@ -6,18 +6,16 @@ module Arpie
6
6
  class StreamError < IOError ; end
7
7
  # Raised by arpie when a Protocol needs more data to parse a packet.
8
8
  # Usually only of relevance to the programmer when using Protocol#from directly.
9
- class EIncomplete < RuntimeError ; end
9
+ class EIncomplete < Errno::EAGAIN ; end
10
10
 
11
- # :stopdoc:
11
+ # @private
12
12
  # Used internally by arpie.
13
- class ETryAgain < RuntimeError ; end
14
13
  class YieldResult < RuntimeError
15
14
  attr_reader :result
16
15
  def initialize result
17
16
  @result = result
18
17
  end
19
18
  end
20
- # :startdoc:
21
19
 
22
20
  # Call this if you think the stream has been corrupted, or
23
21
  # non-protocol data arrived.
@@ -1,11 +1,10 @@
1
1
  require 'shellwords'
2
+ require 'psych' if RUBY_VERSION =~ /^1.9/
2
3
  require 'yaml'
3
4
  require 'zlib'
4
5
 
5
6
  module Arpie
6
7
  MTU = 1024
7
- # A RPC call. You need to wrap all calls sent over RPC protocols in this.
8
- class RPCall < Struct.new(:ns, :meth, :argv, :uuid); end
9
8
 
10
9
  # A ProtocolChain wraps one or more Protocols to provide a parser
11
10
  # list, into which io data can be fed and parsed packets received; and
@@ -22,10 +21,6 @@ module Arpie
22
21
  # A buffer holding all parsed, but unreturned messages.
23
22
  attr_reader :messages
24
23
 
25
- # The endpoint class of this Protocol.
26
- # Defaults to Arpie::Endpoint
27
- attr_accessor :endpoint_class
28
-
29
24
  # Create a new Chain. Supply an Array of Protocol
30
25
  # instances, where the leftmost is the innermost.
31
26
  #
@@ -39,8 +34,6 @@ module Arpie
39
34
  "The outermost protocol needs to be able to " +
40
35
  "separate messages in a stream (#{protocols.inspect} does not)."
41
36
 
42
- @endpoint_class = Arpie::Endpoint
43
-
44
37
  @chain = protocols
45
38
  @buffer = ""
46
39
  @messages = []
@@ -163,7 +156,7 @@ module Arpie
163
156
  # Write +message+ to +io+.
164
157
  def write_message io, *messages
165
158
  binary = messages.map {|m| to(m)}
166
- io.write(binary)
159
+ io.write(binary.flatten.join(""))
167
160
  end
168
161
 
169
162
  def reset
@@ -180,12 +173,12 @@ module Arpie
180
173
  # message separation within a stream.
181
174
  CAN_SEPARATE_MESSAGES = false
182
175
 
183
- # :stopdoc:
176
+ # @private
184
177
  # The stowbuffer hash used by assemble! No need to touch this, usually.
185
178
  attr_reader :stowbuffer
179
+ # @private
186
180
  # The meta-information hash used by assemble! No need to touch this, usually.
187
181
  attr_reader :metabuffer
188
- # :startdoc:
189
182
 
190
183
  # Convert obj to on-the-wire format.
191
184
  def to obj
@@ -212,12 +205,6 @@ module Arpie
212
205
  0
213
206
  end
214
207
 
215
- # Call this within Protocol#from to reparse the current
216
- # message.
217
- def again!
218
- raise ETryAgain
219
- end
220
-
221
208
  # Stow away a message in this protocols buffer for later reassembly.
222
209
  # Optional argument: a token if you are planning to reassemble multiple
223
210
  # interleaved/fragmented message streams.
@@ -338,16 +325,13 @@ module Arpie
338
325
  # A protocol which encodes objects into YAML representation.
339
326
  # Messages are arbitary yaml-encodable objects.
340
327
  class YAMLProtocol < Protocol
341
- CAN_SEPARATE_MESSAGES = true
342
-
343
328
  def to object
344
- yield YAML.dump(object) + "...\n"
329
+ yield YAML.dump(object)
345
330
  end
346
331
 
347
332
  def from binary
348
- index = binary =~ /^\.\.\.$/x or incomplete!
349
- yield YAML.load(binary[0, index])
350
- 4 + index
333
+ yield YAML.load(binary)
334
+ binary.size
351
335
  end
352
336
  end
353
337
 
@@ -0,0 +1,3 @@
1
+ module Arpie
2
+ VERSION = "0.1.0"
3
+ end
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(__FILE__), 'spec_helper')
2
2
 
3
- describe "Binary" do
3
+ describe Arpie::Binary do
4
4
 
5
5
  describe "empty:" do subject {
6
6
  [
@@ -9,7 +9,7 @@ describe "Binary" do
9
9
  ""
10
10
  ]
11
11
  }
12
- it_should_behave_like "Binary Tests"
12
+ include_examples "Binary Tests"
13
13
  end
14
14
 
15
15
  describe "basic sanity:" do subject {
@@ -24,7 +24,7 @@ describe "Binary" do
24
24
  ]
25
25
  }
26
26
 
27
- it_should_behave_like "Binary Tests with data"
27
+ include_examples "Binary Tests with data"
28
28
  end
29
29
 
30
30
  describe ".virtual:" do subject {
@@ -32,18 +32,26 @@ describe "Binary" do
32
32
  Class.new(Binary) do
33
33
  field :a, :uint8
34
34
  field :b, :uint8
35
+
35
36
  virtual :c, :uint8 do |o| o.a * o.b end
37
+
38
+ v :d, :uint8 do |o| 1 + o.c end
36
39
  end,
37
40
  [1, 2].pack("CC")
38
41
  ]
39
42
  }
40
43
 
41
- it_should_behave_like "Binary Tests with data"
42
-
43
44
  it "evaluates the block given" do
44
45
  b, co = @c.from(@d)
45
46
  b.c.should == b.a * b.b
46
47
  end
48
+
49
+ it "evaluates nested virtuals" do
50
+ b, co = @c.from(@d)
51
+ b.d.should == b.c + 1
52
+ end
53
+
54
+ include_examples "Binary Tests with data"
47
55
  end
48
56
 
49
57
  describe ".aliases:" do subject {
@@ -58,7 +66,7 @@ describe "Binary" do
58
66
  ]
59
67
  }
60
68
 
61
- it_should_behave_like "Binary Tests with data"
69
+ include_examples "Binary Tests with data"
62
70
  end
63
71
 
64
72
  describe ":default:" do subject {
@@ -72,8 +80,6 @@ describe "Binary" do
72
80
  ]
73
81
  }
74
82
 
75
- it_should_behave_like "Binary Tests with data"
76
-
77
83
  it "does not use the default when a value was read" do
78
84
  b, con = @c.from([1, 2].pack("CC"))
79
85
  b.a.should == 1
@@ -86,7 +92,106 @@ describe "Binary" do
86
92
  b.b.should == 5
87
93
  end
88
94
 
95
+ it "uses the default for new binaries" do
96
+ new = @c.new
97
+ new.b.should == 5
98
+ end
99
+
100
+ include_examples "Binary Tests with data"
89
101
  end
90
102
 
103
+ describe ":fixed:" do subject {
104
+ [
105
+ Class.new(Binary) do
106
+ field :a, :fixed, :value => "abc"
107
+ end,
108
+ ["abc"].pack("a*")
109
+ ]
110
+ }
111
+
112
+ it "does not use the default for new binaries when using :fixed" do
113
+ @c.new.a.should == nil
114
+ end
91
115
 
116
+ it "fails on parsing invalid values" do
117
+ proc { @c.from("abd") }.should raise_error Arpie::StreamError
118
+ end
119
+
120
+ it "fails on setting invalid values" do
121
+ proc {
122
+ c = @c.new
123
+ c.a = "abd"
124
+ c.to
125
+ }.should raise_error Arpie::StreamError
126
+ end
127
+
128
+ include_examples "Binary Tests with data"
129
+ end
130
+
131
+ describe "static:" do subject {
132
+ [
133
+ Class.new(Binary) do
134
+ static :a, "abc"
135
+ end,
136
+ ["abc"].pack("a*")
137
+ ]
138
+ }
139
+
140
+ it "uses the default for new binaries" do
141
+ new = @c.new.to.should == "abc"
142
+ end
143
+
144
+ it "fails on parsing invalid values" do
145
+ proc { @c.from("abd") }.should raise_error Arpie::StreamError
146
+ end
147
+
148
+ it "fails on setting invalid values" do
149
+ proc {
150
+ c = @c.new
151
+ c.a = "abd"
152
+ c.to
153
+ }.should raise_error Arpie::StreamError
154
+ end
155
+
156
+ include_examples "Binary Tests with data"
157
+ end
158
+
159
+ describe "mod:" do
160
+ specify {
161
+ klass = Class.new(Binary) do
162
+ uint8 :int, :mod => 1
163
+ string :test, :sizeof => :uint8, :sizeof_opts => { :mod => -1 }
164
+ end
165
+
166
+ b, read = klass.from("\x01\x05abcde")
167
+ b.int.should == 2
168
+ b.test.should == "abcd"
169
+ read.should == 6
170
+ }
171
+ end
172
+
173
+ describe "short notation" do
174
+ describe "invalid fields" do
175
+ specify do
176
+ proc {
177
+ Class.new(Binary) do
178
+ invalid :xy
179
+ end
180
+ }.should raise_error ArgumentError
181
+ end
182
+ end
183
+
184
+ describe "simple types" do
185
+ subject {
186
+ [
187
+ Class.new(Binary) do
188
+ bytes :a, :length => 5
189
+ end,
190
+ ["abc01"].pack("a5")
191
+ ]
192
+ }
193
+
194
+ include_examples "Binary Tests with data"
195
+ end
196
+ end
92
197
  end