client_for_poslynx 0.9.0 → 1.0.0.pre
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.
- checksums.yaml +8 -8
- data/README.md +172 -147
- data/examples/em_protocol_example.rb +82 -0
- data/examples/em_session_basic_example.rb +85 -0
- data/examples/em_session_interrupt_example.rb +155 -0
- data/lib/client_for_poslynx/data/requests/abstract_request.rb +6 -0
- data/lib/client_for_poslynx/net.rb +2 -1
- data/lib/client_for_poslynx/net/em_connector.rb +344 -0
- data/lib/client_for_poslynx/net/em_connector/callback_map.rb +50 -0
- data/lib/client_for_poslynx/net/em_connector/connection_handler.rb +14 -0
- data/lib/client_for_poslynx/net/em_connector/connector_state.rb +24 -0
- data/lib/client_for_poslynx/net/em_connector/event_dispatcher.rb +66 -0
- data/lib/client_for_poslynx/net/em_connector/handles_connection.rb +57 -0
- data/lib/client_for_poslynx/net/em_connector/request_call.rb +27 -0
- data/lib/client_for_poslynx/net/em_protocol.rb +1 -1
- data/lib/client_for_poslynx/net/em_session.rb +250 -0
- data/lib/client_for_poslynx/version.rb +1 -1
- data/spec/client_for_poslynx/net/em_connector_spec.rb +624 -0
- data/spec/client_for_poslynx/net/em_session_spec.rb +313 -0
- metadata +19 -8
- data/lib/client_for_poslynx/net/structured_client.rb +0 -104
- data/lib/client_for_poslynx/net/structured_client/em_connection.rb +0 -72
- data/spec/client_for_poslynx/net/structured_client_spec.rb +0 -118
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2JjMGU1MmVkOGYwMWJhMzE2NmVmMzRlYjhjNWM4YTE4OTJiN2ZkMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YTJhNWNkYzRhYjM5NTJiZGZjYzFmODI1YmU2ODQxNmRmYTI2NTdlMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDNlOWZjMzQ2ZmUzZDhjM2EwMGM4ZDUxN2JjYjNiZTljOGZiYzRhZGVjMGI1
|
10
|
+
YzgyOTY0NjJhZjYzMTJjYjdmNzY3OTlmMDU0MGI3NTE4ODRmOTQ1NDgxZTcw
|
11
|
+
ZDk0MDhhODlhM2NiYmY4MGMyNWM3MThjM2YzMTYwODc5NGZhYzY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NDkxMGJiYzk1ZTBjYzliZGE1YWJjMjFlODUwOWUzMmViNWUyMWM0NTZhZjMz
|
14
|
+
ZDM2OWEzMzFkM2Y5MGJhMjc1ZjAyZjk4ZGE3MjE0YWM2M2RlYzUyOWJkMmRl
|
15
|
+
OGY5ZjQyNzU0NDk2Y2Q2ODk1Y2I1NDAyZDczZjRhZTY4MjMzMDU=
|
data/README.md
CHANGED
@@ -9,157 +9,181 @@ Technologies.
|
|
9
9
|
Features:
|
10
10
|
|
11
11
|
* Data models for requests and responses.
|
12
|
-
*
|
13
|
-
*
|
14
|
-
adapter.
|
12
|
+
* Signature image translation to SVG.
|
13
|
+
* Network interaction using via EventMachine.
|
15
14
|
* A fake POSLynx appliance + PIN Pad script.
|
16
15
|
|
17
16
|
## Overview
|
18
17
|
|
19
|
-
The `client_for_poslynx` gem provides network adapters
|
20
|
-
be used to send requests to
|
21
|
-
responses to those requests. The
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
18
|
+
The `client_for_poslynx` gem provides layers of network adapters
|
19
|
+
(operating via EventMachine) that can be used to send requests to
|
20
|
+
a POSLynx unit and receive the responses to those requests. The
|
21
|
+
gem also includes data models with XML
|
22
|
+
serialization/deserialization that are used by those network
|
23
|
+
adapters or may be used as part of another network adapter if you
|
24
|
+
prefer to write one of your own.
|
25
|
+
|
26
|
+
### Asynchronous Messaging
|
27
|
+
|
28
|
+
Message interaction with POSLynx is primarily synchronous, in
|
29
|
+
that the client application makes a request, and then waits for
|
30
|
+
for a response. In any realistic integrated POS solution,
|
31
|
+
however, there are cases in which it is necessary to devaite from
|
32
|
+
the typical synchronous flow.
|
33
|
+
|
34
|
+
Let's say that a customer starts an interaction with the
|
35
|
+
terminal, gets an emergency call, and runs off without completing
|
36
|
+
the interaction. The client application cannot do anything to
|
37
|
+
cancel the interaction because it's still blocked waiting for a
|
38
|
+
response.
|
39
|
+
|
40
|
+
### EventMachine
|
41
|
+
|
42
|
+
[EventMachine](http://rubyeventmachine.com/) is a Ruby library
|
43
|
+
that supports asynchronous network I/O using event callbacks.
|
44
|
+
`client_for_poslynx` provides an EventMachine protocol to allow
|
45
|
+
sending requests and receiving responses over an EventMachine
|
46
|
+
connection as well as adapter layers to simplify event-based
|
47
|
+
interaction via the EventMachine protocol.
|
48
|
+
|
49
|
+
One caveat to basing the library around EventMachine is that it
|
50
|
+
forces developers to deal with the EventMachine workflow in their
|
51
|
+
applications.
|
52
|
+
|
53
|
+
### Data Models
|
54
|
+
|
55
|
+
The `client_for_poslynx` gem includes data model classes for
|
56
|
+
kinds of requests that can be made to the POSLynx as well as for
|
57
|
+
responses that can be received from the POSLynx in response to
|
58
|
+
requests.
|
59
|
+
|
60
|
+
Models can be converted to/from XML-format messages to be sent or
|
61
|
+
receive over a network connection to the POSLynx.
|
62
|
+
|
63
|
+
These models can be used with the EventManager-based
|
64
|
+
communication adapters that exist within the gem, or you can use
|
65
|
+
them independently (e.g. if you perfer to communicate with the
|
66
|
+
POSLynx using Ruby's standard TCP/IP networking facilities.
|
67
|
+
|
68
|
+
Additionally, a `SignatureImage` model is provided that can be
|
69
|
+
used to parse or build POSLynx-compatible signature data strings,
|
70
|
+
along with a facility for converting signature images to SVG for
|
71
|
+
printing or display.
|
72
|
+
|
73
|
+
### Fake POS Terminal
|
74
|
+
|
75
|
+
The `client_for_poslynx` gem includes a script that acts as a
|
76
|
+
fake POSLynx + PIN Pad. This is useful when you are working
|
77
|
+
without access to an actual POSLynx and PIN Pad, and want to test
|
78
|
+
your client code and try out workflows.
|
45
79
|
|
46
80
|
## Usage
|
47
81
|
|
48
|
-
### Using the
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
the
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
# SSL session has been successfully started
|
154
|
-
# Sending the PIN Pad Display Message request
|
155
|
-
# Received response from POSLynx
|
156
|
-
# Result text: Success
|
157
|
-
# Selected button: Tails
|
158
|
-
# Closing the connection
|
159
|
-
# The connection has been closed
|
160
|
-
# Terminating the event loop
|
161
|
-
# The event loop has ended
|
162
|
-
# Bye
|
82
|
+
### Using the "Request" and "Response" Data Models
|
83
|
+
See
|
84
|
+
* [`abstract_data.rb`](lib/client_for_poslynx/data/abstract_data.rb)
|
85
|
+
* [`requests`](lib/client_for_poslynx/data/requests/)
|
86
|
+
* [`responses`](lib/client_for_poslynx/data/responses/)
|
87
|
+
|
88
|
+
### Using the `EM_Session` Adapter
|
89
|
+
|
90
|
+
[`EM_Session`](lib/client_for_poslynx/net/em_session.rb) is the
|
91
|
+
highest level adapter, and the one that you will most likely want
|
92
|
+
to use. It hides the significant complexity of the lower-level
|
93
|
+
event-driven interaction behind a synchronous API. Interruptions
|
94
|
+
to the flow are accommodated by allowing multiple sessions to be
|
95
|
+
active at the same time and for one to interrupt the other. This
|
96
|
+
"magic" is accomplished through the use of Ruby fibers.
|
97
|
+
|
98
|
+
Code examples:
|
99
|
+
* [`em_session_basic_example.rb`](examples/em_session_basic_example.rb)
|
100
|
+
* [`em_session_interrupt_example.rb`](examples/em_session_interrupt_example.rb)
|
101
|
+
|
102
|
+
It is important to note that a session's code block runs on
|
103
|
+
EventMachine's event handling thread, and one of the core
|
104
|
+
principles of EventMachine is to **never block that thread**. If
|
105
|
+
you need to perform any time-consuming or potentially blocking
|
106
|
+
operations in a session, you should run it in a
|
107
|
+
`Session#exec_dissociated` block (runs via `EventMachine.defer`).
|
108
|
+
If you need to pause for a specific amount of time, then you can
|
109
|
+
call the non-blocking `Session#sleep` (runs via
|
110
|
+
`EventMachine.add_timer`).
|
111
|
+
|
112
|
+
When one session is in progress, and a new session makes a
|
113
|
+
request, the new request will attempt to override any pending
|
114
|
+
request of the first session, and to "detach" the other session
|
115
|
+
so that any of its subsequent request attemtps will be rejected
|
116
|
+
and fail with an exception.
|
117
|
+
|
118
|
+
In order to avoid race conditions and to ensure that every
|
119
|
+
response that is received is returned to the session that made
|
120
|
+
the corresponding request, the following rules apply.
|
121
|
+
|
122
|
+
1. If one session has a pending request, a new session makes a
|
123
|
+
different kind of request, and then a response to the
|
124
|
+
**second** request is received, then the first session
|
125
|
+
receives an exception, and the second session will have the
|
126
|
+
response reurned.
|
127
|
+
2. If one session has a pending request, a new session makes a
|
128
|
+
different kind of request, and then a response to the
|
129
|
+
**first** request is received, then the first session will
|
130
|
+
have that response returned and will be detached. The second
|
131
|
+
session continues waiting for the subsequent response (to its
|
132
|
+
request).
|
133
|
+
2. If one session has a pending `PinPadReset` (`PPRESET`)
|
134
|
+
request, and a new session makes a `PinPadReset` request, then
|
135
|
+
the first session receives an exception, and the response is
|
136
|
+
returned to the second session.
|
137
|
+
4. If one session makes a request other than a `PinPadReset`,
|
138
|
+
and a new session makes a request of the same type, then the
|
139
|
+
new session's request fails immediately with an exception.
|
140
|
+
|
141
|
+
An important consequence of the rules above is that if the first
|
142
|
+
request made by a new session is a `PinPadReset`, then it will
|
143
|
+
always be able to successfully interrupt any existing session.
|
144
|
+
If it starts with any other kind of request, however, then it has
|
145
|
+
the potential to fail as described in rule #4.
|
146
|
+
|
147
|
+
For that reason, you should generally start any session with a
|
148
|
+
Pin Pad Reset request, and only do otherwise if you have
|
149
|
+
considered the consequences with respect to the rules above.
|
150
|
+
|
151
|
+
### Using the `EM_Connector` adapter
|
152
|
+
|
153
|
+
[`EM_Connector`](lib/client_for_poslynx/net/em_connector.rb) is
|
154
|
+
a low-level abstraction around an `EventMachine` connection with
|
155
|
+
the [`POSLynx`](lib/client_for_poslynx/net/em_protocol.rb)
|
156
|
+
protocol module included.
|
157
|
+
|
158
|
+
The jobs of `EM_Connector` are...
|
159
|
+
|
160
|
+
1. Simplify the task of making sure that a connection is open and
|
161
|
+
making a request via that connection.
|
162
|
+
2. Assist in keeping track of the state of pending requests.
|
163
|
+
3. Provide a simple API for specifying callbacks to specific
|
164
|
+
attempts to connection or send a request.
|
165
|
+
|
166
|
+
### The `POSLynx` protocol for EventManager
|
167
|
+
|
168
|
+
[`POSLynx`](lib/client_for_poslynx/net/em_protocol.rb) is an
|
169
|
+
`EventMachine` protocol for sending and receiving POSLynx
|
170
|
+
requests and responses.
|
171
|
+
|
172
|
+
This protocol is utilized in the same manner as other typical
|
173
|
+
`EventManager` protocols, by defining a connection handler class
|
174
|
+
that inherits from `EM::Connection` and includes the
|
175
|
+
`EM::Protocols::POSLynx` module, and then passing that class as
|
176
|
+
the handler argument to `EventMachine.connect`.
|
177
|
+
|
178
|
+
Code in the handler can call `#send_request` to send a request
|
179
|
+
(an instance of a subclass of
|
180
|
+
`ClientForPoslynx::Data::Requests::AbstractRequest`) and can
|
181
|
+
override the `#receive_response` method to receive responses
|
182
|
+
(instances of subclasses of
|
183
|
+
`ClientForPoslynx::Data::Response::AbstractResponse`)
|
184
|
+
|
185
|
+
Code example:
|
186
|
+
* [`em_protocol_example.rb`](examples/em_protocol_example.rb)
|
163
187
|
|
164
188
|
### Using the `fake_pos_terminal` script
|
165
189
|
|
@@ -180,8 +204,9 @@ To stop the script, send an interrupt signal by pressing Ctrl+C.
|
|
180
204
|
|
181
205
|
## Known Limitations
|
182
206
|
|
183
|
-
* Only a subset of the possible messages and elements is
|
184
|
-
__More
|
207
|
+
* Only a subset of the possible messages and elements is
|
208
|
+
supported. __More might be added. Contributions are welcome
|
209
|
+
and encouraged. :)__
|
185
210
|
|
186
211
|
## Installation
|
187
212
|
|
@@ -199,7 +224,7 @@ Or install it yourself as:
|
|
199
224
|
|
200
225
|
## Contributing
|
201
226
|
|
202
|
-
1. Fork it ( https://github.com/[my-github-username]/client_for_poslynx/fork )
|
227
|
+
1. Fork it ( `https://github.com/[my-github-username]/client_for_poslynx/fork` )
|
203
228
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
204
229
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
205
230
|
4. Push to the branch (`git push origin my-new-feature`)
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'client_for_poslynx'
|
2
|
+
|
3
|
+
SERVER_IP = '192.168.1.25'
|
4
|
+
SERVER_PORT = 12345
|
5
|
+
CLIENT_MAC = '000000000000'
|
6
|
+
USE_SSL = true
|
7
|
+
|
8
|
+
class ButtonSelectionDemo < EM::Connection
|
9
|
+
include EM::Protocols::POSLynx
|
10
|
+
|
11
|
+
def connection_completed
|
12
|
+
puts "IP connection has been opened"
|
13
|
+
if USE_SSL
|
14
|
+
puts "Starting SSL"
|
15
|
+
start_tls verify_peer: false
|
16
|
+
else
|
17
|
+
puts "Using raw connection. No SSL"
|
18
|
+
display_the_message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def ssl_handshake_completed
|
23
|
+
puts "SSL session has been successfully started"
|
24
|
+
display_the_message
|
25
|
+
end
|
26
|
+
|
27
|
+
def display_the_message
|
28
|
+
puts "Sending the PIN Pad Display Message request"
|
29
|
+
|
30
|
+
req = ClientForPoslynx::Data::Requests::PinPadDisplayMessage.new
|
31
|
+
req.client_mac = CLIENT_MAC
|
32
|
+
req.line_count = 2
|
33
|
+
req.text_lines = [
|
34
|
+
"Heads, we clean the basement",
|
35
|
+
"Tails, we go to the movies"
|
36
|
+
]
|
37
|
+
req.button_labels = %w[ Heads Tails ]
|
38
|
+
|
39
|
+
send_request req
|
40
|
+
end
|
41
|
+
|
42
|
+
def receive_response(response)
|
43
|
+
puts "Received response from POSLynx"
|
44
|
+
puts "Result text: #{response.result_text}"
|
45
|
+
puts "Selected button: #{response.button_response}"
|
46
|
+
|
47
|
+
puts "Closing the connection"
|
48
|
+
close_connection
|
49
|
+
end
|
50
|
+
|
51
|
+
def unbind
|
52
|
+
puts "The connection has been closed"
|
53
|
+
puts "Terminating the event loop"
|
54
|
+
EM.stop_event_loop
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
# This code will block until the event loop exits.
|
60
|
+
EM.run do
|
61
|
+
EM.connect SERVER_IP, SERVER_PORT, ButtonSelectionDemo
|
62
|
+
EM.error_handler do |e|
|
63
|
+
raise e
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
puts "The event loop has ended"
|
68
|
+
puts "Bye"
|
69
|
+
|
70
|
+
# == Sample output ==
|
71
|
+
# IP connection has been opened
|
72
|
+
# Starting SSL
|
73
|
+
# SSL session has been successfully started
|
74
|
+
# Sending the PIN Pad Display Message request
|
75
|
+
# Received response from POSLynx
|
76
|
+
# Result text: Success
|
77
|
+
# Selected button: Tails
|
78
|
+
# Closing the connection
|
79
|
+
# The connection has been closed
|
80
|
+
# Terminating the event loop
|
81
|
+
# The event loop has ended
|
82
|
+
# Bye
|