rmodbus 1.0.0-java

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,268 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2008-2011 Timin Aleksey
4
+ # Copyright (C) 2010 Kelley Reynolds
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ module ModBus
17
+ class Slave
18
+ include Errors
19
+ include Common
20
+ # Number of times to retry on read and read timeouts
21
+ attr_accessor :read_retries, :read_retry_timeout, :uid
22
+
23
+ Exceptions = {
24
+ 1 => IllegalFunction.new("The function code received in the query is not an allowable action for the server"),
25
+ 2 => IllegalDataAddress.new("The data address received in the query is not an allowable address for the server"),
26
+ 3 => IllegalDataValue.new("A value contained in the query data field is not an allowable value for server"),
27
+ 4 => SlaveDeviceFailure.new("An unrecoverable error occurred while the server was attempting to perform the requested action"),
28
+ 5 => Acknowledge.new("The server has accepted the request and is processing it, but a long duration of time will be required to do so"),
29
+ 6 => SlaveDeviceBus.new("The server is engaged in processing a long duration program command"),
30
+ 8 => MemoryParityError.new("The extended file area failed to pass a consistency check")
31
+ }
32
+ def initialize(uid, io)
33
+ @uid = uid
34
+ @read_retries = 10
35
+ @read_retry_timeout = 1
36
+ @io = io
37
+ end
38
+
39
+ # Returns a ModBus::ReadWriteProxy hash interface for coils
40
+ #
41
+ # @example
42
+ # coils[addr] => [1]
43
+ # coils[addr1..addr2] => [1, 0, ..]
44
+ # coils[addr] = 0 => [0]
45
+ # coils[addr1..addr2] = [1, 0, ..] => [1, 0, ..]
46
+ #
47
+ # @return [ReadWriteProxy] proxy object
48
+ def coils
49
+ ModBus::ReadWriteProxy.new(self, :coil)
50
+ end
51
+
52
+ # Read coils
53
+ #
54
+ # @example
55
+ # read_coils(addr, ncoils) => [1, 0, ..]
56
+ #
57
+ # @param [Integer] addr address first coil
58
+ # @param [Integer] ncoils number coils
59
+ # @return [Array] coils
60
+ def read_coils(addr, ncoils)
61
+ query("\x1" + addr.to_word + ncoils.to_word).unpack_bits[0..ncoils-1]
62
+ end
63
+ alias_method :read_coil, :read_coils
64
+
65
+ # Write a single coil
66
+ #
67
+ # @example
68
+ # write_single_coil(1, 0) => self
69
+ #
70
+ # @param [Integer] addr address coil
71
+ # @param [Integer] val value coil (0 or other)
72
+ # @return self
73
+ def write_single_coil(addr, val)
74
+ if val == 0
75
+ query("\x5" + addr.to_word + 0.to_word)
76
+ else
77
+ query("\x5" + addr.to_word + 0xff00.to_word)
78
+ end
79
+ self
80
+ end
81
+ alias_method :write_coil, :write_single_coil
82
+
83
+ # Write multiple coils
84
+ #
85
+ # @example
86
+ # write_multiple_coils(1, [0,1,0,1]) => self
87
+ #
88
+ # @param [Integer] addr address first coil
89
+ # @param [Array] vals written coils
90
+ def write_multiple_coils(addr, vals)
91
+ nbyte = ((vals.size-1) >> 3) + 1
92
+ sum = 0
93
+ (vals.size - 1).downto(0) do |i|
94
+ sum = sum << 1
95
+ sum |= 1 if vals[i] > 0
96
+ end
97
+
98
+ s_val = ""
99
+ nbyte.times do
100
+ s_val << (sum & 0xff).chr
101
+ sum >>= 8
102
+ end
103
+
104
+ query("\xf" + addr.to_word + vals.size.to_word + nbyte.chr + s_val)
105
+ self
106
+ end
107
+ alias_method :write_coils, :write_multiple_coils
108
+
109
+ # Returns a ModBus::ReadOnlyProxy hash interface for discrete inputs
110
+ #
111
+ # @example
112
+ # discrete_inputs[addr] => [1]
113
+ # discrete_inputs[addr1..addr2] => [1, 0, ..]
114
+ #
115
+ # @return [ReadOnlyProxy] proxy object
116
+ def discrete_inputs
117
+ ModBus::ReadOnlyProxy.new(self, :discrete_input)
118
+ end
119
+
120
+ # Read discrete inputs
121
+ #
122
+ # @example
123
+ # read_discrete_inputs(addr, ninputs) => [1, 0, ..]
124
+ #
125
+ # @param [Integer] addr address first input
126
+ # @param[Integer] ninputs number inputs
127
+ # @return [Array] inputs
128
+ def read_discrete_inputs(addr, ninputs)
129
+ query("\x2" + addr.to_word + ninputs.to_word).unpack_bits[0..ninputs-1]
130
+ end
131
+ alias_method :read_discrete_input, :read_discrete_inputs
132
+
133
+ # Returns a read/write ModBus::ReadOnlyProxy hash interface for coils
134
+ #
135
+ # @example
136
+ # input_registers[addr] => [1]
137
+ # input_registers[addr1..addr2] => [1, 0, ..]
138
+ #
139
+ # @return [ReadOnlyProxy] proxy object
140
+ def input_registers
141
+ ModBus::ReadOnlyProxy.new(self, :input_register)
142
+ end
143
+
144
+ # Read input registers
145
+ #
146
+ # @example
147
+ # read_input_registers(1, 5) => [1, 0, ..]
148
+ #
149
+ # @param [Integer] addr address first registers
150
+ # @param [Integer] nregs number registers
151
+ # @return [Array] registers
152
+ def read_input_registers(addr, nregs)
153
+ query("\x4" + addr.to_word + nregs.to_word).unpack('n*')
154
+ end
155
+ alias_method :read_input_register, :read_input_registers
156
+
157
+ # Returns a ModBus::ReadWriteProxy hash interface for holding registers
158
+ #
159
+ # @example
160
+ # holding_registers[addr] => [123]
161
+ # holding_registers[addr1..addr2] => [123, 234, ..]
162
+ # holding_registers[addr] = 123 => 123
163
+ # holding_registers[addr1..addr2] = [234, 345, ..] => [234, 345, ..]
164
+ #
165
+ # @return [ReadWriteProxy] proxy object
166
+ def holding_registers
167
+ ModBus::ReadWriteProxy.new(self, :holding_register)
168
+ end
169
+
170
+ # Read holding registers
171
+ #
172
+ # @example
173
+ # read_holding_registers(1, 5) => [1, 0, ..]
174
+ #
175
+ # @param [Integer] addr address first registers
176
+ # @param [Integer] nregs number registers
177
+ # @return [Array] registers
178
+ def read_holding_registers(addr, nregs)
179
+ query("\x3" + addr.to_word + nregs.to_word).unpack('n*')
180
+ end
181
+ alias_method :read_holding_register, :read_holding_registers
182
+
183
+ # Write a single holding register
184
+ #
185
+ # @example
186
+ # write_single_register(1, 0xaa) => self
187
+ #
188
+ # @param [Integer] addr address registers
189
+ # @param [Integer] val written to register
190
+ # @return self
191
+ def write_single_register(addr, val)
192
+ query("\x6" + addr.to_word + val.to_word)
193
+ self
194
+ end
195
+ alias_method :write_holding_register, :write_single_register
196
+
197
+
198
+ # Write multiple holding registers
199
+ #
200
+ # @example
201
+ # write_multiple_registers(1, [0xaa, 0]) => self
202
+ #
203
+ # @param [Integer] addr address first registers
204
+ # @param [Array] val written registers
205
+ # @return self
206
+ def write_multiple_registers(addr, vals)
207
+ s_val = ""
208
+ vals.each do |reg|
209
+ s_val << reg.to_word
210
+ end
211
+
212
+ query("\x10" + addr.to_word + vals.size.to_word + (vals.size * 2).chr + s_val)
213
+ self
214
+ end
215
+ alias_method :write_holding_registers, :write_multiple_registers
216
+
217
+ # Mask a holding register
218
+ #
219
+ # @example
220
+ # mask_write_register(1, 0xAAAA, 0x00FF) => self
221
+ # @param [Integer] addr address registers
222
+ # @param [Integer] and_mask mask for AND operation
223
+ # @param [Integer] or_mask mask for OR operation
224
+ def mask_write_register(addr, and_mask, or_mask)
225
+ query("\x16" + addr.to_word + and_mask.to_word + or_mask.to_word)
226
+ self
227
+ end
228
+
229
+ # Request pdu to slave device
230
+ #
231
+ # @param [String] pdu request to slave
232
+ # @return [String] received data
233
+ #
234
+ # @raise [ModBusTimeout] timed out during read attempt
235
+ # @raise [ModBusException] unknown error
236
+ # @raise [IllegalFunction] function code received in the query is not an allowable action for the server
237
+ # @raise [IllegalDataAddress] data address received in the query is not an allowable address for the server
238
+ # @raise [IllegalDataValue] value contained in the query data field is not an allowable value for server
239
+ # @raise [SlaveDeviceFailure] unrecoverable error occurred while the server was attempting to perform the requested action
240
+ # @raise [Acknowledge] server has accepted the request and is processing it, but a long duration of time will be required to do so
241
+ # @raise [SlaveDeviceBus] server is engaged in processing a long duration program command
242
+ # @raise [MemoryParityError] extended file area failed to pass a consistency check
243
+ def query(pdu)
244
+ tried = 0
245
+ begin
246
+ timeout(@read_retry_timeout, ModBusTimeout) do
247
+ send_pdu(pdu)
248
+ pdu = read_pdu
249
+ end
250
+ rescue ModBusTimeout => err
251
+ log "Timeout of read operation: (#{@read_retries - tried})"
252
+ tried += 1
253
+ retry unless tried >= @read_retries
254
+ raise ModBusTimeout.new, "Timed out during read attempt"
255
+ end
256
+
257
+ return nil if pdu.size == 0
258
+
259
+ if pdu.getbyte(0) >= 0x80
260
+ exc_id = pdu.getbyte(1)
261
+ raise Exceptions[exc_id] unless Exceptions[exc_id].nil?
262
+
263
+ raise ModBusException.new, "Unknown error"
264
+ end
265
+ pdu[2..-1]
266
+ end
267
+ end
268
+ end
data/lib/rmodbus/sp.rb ADDED
@@ -0,0 +1,45 @@
1
+ # RModBus - free implementation of ModBus protocol in Ruby.
2
+ #
3
+ # Copyright (C) 2011 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+
15
+ require 'serialport'
16
+
17
+ module ModBus
18
+ module SP
19
+ attr_reader :port, :baud, :data_bits, :stop_bits, :parity, :read_timeout
20
+ # Open serial port
21
+ # @param [String] port name serial ports ("/dev/ttyS0" POSIX, "com1" - Windows)
22
+ # @param [Integer] baud rate serial port (default 9600)
23
+ # @param [Hash] opts the options of serial port
24
+ #
25
+ # @option opts [Integer] :data_bits from 5 to 8
26
+ # @option opts [Integer] :stop_bits 1 or 2
27
+ # @option opts [Integer] :parity NONE, EVEN or ODD
28
+ # @option opts [Integer] :read_timeout default 100 ms
29
+ # @return [SerialPort] io serial port
30
+ def open_serial_port(port, baud, opts = {})
31
+ @port, @baud = port, baud
32
+
33
+ @data_bits, @stop_bits, @parity, @read_timeout = 8, 1, SerialPort::NONE, 100
34
+
35
+ @data_bits = opts[:data_bits] unless opts[:data_bits].nil?
36
+ @stop_bits = opts[:stop_bits] unless opts[:stop_bits].nil?
37
+ @parity = opts[:parity] unless opts[:parity].nil?
38
+ @read_timeout = options[:read_timeout] unless opts[:read_timeout].nil?
39
+
40
+ io = SerialPort.new(@port, @baud, @data_bits, @stop_bits, @parity)
41
+ io.read_timeout = @read_timeout
42
+ io
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2011 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ require 'socket'
15
+ require 'timeout'
16
+
17
+ module ModBus
18
+ module TCP
19
+ include Timeout
20
+ attr_reader :ipaddr, :port
21
+
22
+ private
23
+ # Open TCP socket
24
+ #
25
+ # @param [String] ipaddr IP address of remote server
26
+ # @param [Integer] port connection port
27
+ # @param [Hash] opts options of connection
28
+ # @option opts [Float, Integer] :connect_timeout seconds timeout for open socket
29
+ # @return [TCPSocket] socket
30
+ #
31
+ # @raise [ModBusTimeout] timed out attempting to create connection
32
+ def open_tcp_connection(ipaddr, port, opts = {})
33
+ @ipaddr, @port = ipaddr, port
34
+
35
+ opts[:connect_timeout] ||= 1
36
+
37
+ io = nil
38
+ begin
39
+ timeout(opts[:connect_timeout], ModBusTimeout) do
40
+ io = TCPSocket.new(@ipaddr, @port)
41
+ end
42
+ rescue ModBusTimeout => err
43
+ raise ModBusTimeout.new, 'Timed out attempting to create connection'
44
+ end
45
+
46
+ io
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,39 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2008-2011 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ module ModBus
15
+ # TCP client implementation
16
+ # @example
17
+ # TCPClient.connect('127.0.0.1', 502) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see TCPClient#open_connection
24
+ # @see Client#initialize
25
+ class TCPClient < Client
26
+ include TCP
27
+
28
+ protected
29
+ # Open TCP\IP connection
30
+ # @see TCP::open_connection
31
+ def open_connection(ipaddr, port = 502, opts = {})
32
+ open_tcp_connection(ipaddr, port, opts)
33
+ end
34
+
35
+ def get_slave(uid, io)
36
+ TCPSlave.new(uid, io)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,61 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2008 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ require 'gserver'
15
+
16
+ module ModBus
17
+ # TCP server implementation
18
+ # @example
19
+ # srv = TCPServer.new(10002, 1)
20
+ # srv.coils = [1,0,1,1]
21
+ # srv.discrete_inputs = [1,1,0,0]
22
+ # srv.holding_registers = [1,2,3,4]
23
+ # srv.input_registers = [1,2,3,4]
24
+ # srv.debug = true
25
+ # srv.start
26
+ class TCPServer < GServer
27
+ include Common
28
+ include Server
29
+
30
+ # Init server
31
+ # @param [Integer] port listen port
32
+ # @param [Integer] uid slave device
33
+ def initialize(port = 502, uid = 1)
34
+ @uid = uid
35
+ super(port)
36
+ end
37
+
38
+ # Serve requests
39
+ # @param [TCPSocket] io socket
40
+ def serve(io)
41
+ loop do
42
+ req = io.read(7)
43
+ if req[2,2] != "\x00\x00" or req.getbyte(6) != @uid
44
+ io.close
45
+ break
46
+ end
47
+
48
+ tr = req[0,2]
49
+ len = req[4,2].unpack('n')[0]
50
+ req = io.read(len - 1)
51
+ log "Server RX (#{req.size} bytes): #{logging_bytes(req)}"
52
+
53
+ pdu = exec_req(req, @coils, @discrete_inputs, @holding_registers, @input_registers)
54
+
55
+ resp = tr + "\0\0" + (pdu.size + 1).to_word + @uid.chr + pdu
56
+ log "Server TX (#{resp.size} bytes): #{logging_bytes(resp)}"
57
+ io.write resp
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,64 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2011 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ module ModBus
15
+ # TCP slave implementation
16
+ # @example
17
+ # TCP.connect('127.0.0.1', 10002) do |cl|
18
+ # cl.with_slave(uid) do |slave|
19
+ # slave.holding_registers[0..100]
20
+ # end
21
+ # end
22
+ #
23
+ # @see RTUViaTCPClient#open_connection
24
+ # @see Client#with_slave
25
+ # @see Slave
26
+ class TCPSlave < Slave
27
+ attr_reader :transaction
28
+
29
+ # @see Slave::initialize
30
+ def initialize(uid, io)
31
+ @transaction = 0
32
+ super(uid, io)
33
+ end
34
+
35
+ private
36
+ # overide method for RTU over TCP implamentaion
37
+ # @see Slave#query
38
+ def send_pdu(pdu)
39
+ @transaction = 0 if @transaction.next > 65535
40
+ @transaction += 1
41
+ msg = @transaction.to_word + "\0\0" + (pdu.size + 1).to_word + @uid.chr + pdu
42
+ @io.write msg
43
+
44
+ log "Tx (#{msg.size} bytes): " + logging_bytes(msg)
45
+ end
46
+
47
+ # overide method for RTU over TCP implamentaion
48
+ # @see Slave#query
49
+ def read_pdu
50
+ header = @io.read(7)
51
+ if header
52
+ tin = header[0,2].unpack('n')[0]
53
+ raise Errors::ModBusException.new("Transaction number mismatch") unless tin == @transaction
54
+ len = header[4,2].unpack('n')[0]
55
+ msg = @io.read(len-1)
56
+
57
+ log "Rx (#{(header + msg).size} bytes): " + logging_bytes(header + msg)
58
+ msg
59
+ else
60
+ raise Errors::ModBusException.new("Server did not respond")
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,17 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ #
3
+ # Copyright (C) 2011 Timin Aleksey
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ module ModBus
15
+ # Package version
16
+ VERSION = '1.0.0'
17
+ end
data/lib/rmodbus.rb ADDED
@@ -0,0 +1,35 @@
1
+ # RModBus - free implementation of ModBus protocol on Ruby.
2
+ # Copyright (C) 2008 - 2011 Timin Aleksey
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ require 'rmodbus/errors'
13
+ require 'rmodbus/ext'
14
+ require 'rmodbus/common'
15
+ require 'rmodbus/rtu'
16
+ require 'rmodbus/tcp'
17
+ require 'rmodbus/slave'
18
+ require 'rmodbus/client'
19
+ require 'rmodbus/server'
20
+ require 'rmodbus/tcp_slave'
21
+ require 'rmodbus/tcp_client'
22
+ require 'rmodbus/tcp_server'
23
+
24
+ # jruby not support serial RTU protocol yet
25
+ unless RUBY_PLATFORM == "java"
26
+ require 'rmodbus/sp'
27
+ require 'rmodbus/rtu_slave'
28
+ require 'rmodbus/rtu_client'
29
+ require 'rmodbus/rtu_server'
30
+ end
31
+
32
+ require 'rmodbus/rtu_via_tcp_slave'
33
+ require 'rmodbus/rtu_via_tcp_client'
34
+ require 'rmodbus/rtu_via_tcp_server'
35
+ require 'rmodbus/proxy'
@@ -0,0 +1,31 @@
1
+ require 'rmodbus'
2
+ include ModBus
3
+
4
+ describe Client do
5
+ before do
6
+ @cl = Client.new
7
+ end
8
+
9
+ it "should give object provider for slave" do
10
+ slave = @cl.with_slave(1)
11
+ slave.uid.should eq(1)
12
+ end
13
+
14
+ it "should give object provider for slave in block" do
15
+ @cl.with_slave(1) do |slave|
16
+ slave.uid.should eq(1)
17
+ end
18
+ end
19
+
20
+ it "should connect with TCP server" do
21
+ Client.connect do |cl|
22
+ cl.should be_instance_of(Client)
23
+ end
24
+ end
25
+
26
+ it ":new alias :connect" do
27
+ Client.new do |cl|
28
+ cl.should be_instance_of(Client)
29
+ end
30
+ end
31
+ end