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,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