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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OGRhYTY1N2JmMWVkZmVlNzA3MzM3MTBmYjY5YzVhMTE2YTE5YjQ5Mg==
4
+ N2JjMGU1MmVkOGYwMWJhMzE2NmVmMzRlYjhjNWM4YTE4OTJiN2ZkMA==
5
5
  data.tar.gz: !binary |-
6
- N2YxY2IzMTUzODE1NjI2OGEwOWE3ZTFiYzVhMTMzY2IzOGExZTRhNQ==
6
+ YTJhNWNkYzRhYjM5NTJiZGZjYzFmODI1YmU2ODQxNmRmYTI2NTdlMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MGRlY2MwMTgxMWU3YWExNGIxODE4MDI3NThkZDZjMTkwMmVjM2Y2MDk1MjQ4
10
- MTRmODhhYjcxN2VmY2FkOWMwYTU5MjI4MTZmYTY4YTE1MzI3MzIzZDRkNmNm
11
- MDRmMGE5ZTZjZDM0YTQ0NzFiNmY0N2ZmMTFiZWFiMTY1MmYxOGI=
9
+ MDNlOWZjMzQ2ZmUzZDhjM2EwMGM4ZDUxN2JjYjNiZTljOGZiYzRhZGVjMGI1
10
+ YzgyOTY0NjJhZjYzMTJjYjdmNzY3OTlmMDU0MGI3NTE4ODRmOTQ1NDgxZTcw
11
+ ZDk0MDhhODlhM2NiYmY4MGMyNWM3MThjM2YzMTYwODc5NGZhYzY=
12
12
  data.tar.gz: !binary |-
13
- ZjVkODM0YThiMzZlZDFkM2QwZDhlMDY5MDAwMTk1YTUyYzEzNDIxMzQ1YzIw
14
- YWNhOWNkZWRiZWM0YjY2MGE3NzY2NmNhOWM1MzRjYTc5MTM1ZmFiMGJkZDVl
15
- MTYyMGU1OTliMWM2MDRlMGZhZDEzZjNiYzBhNDQ1NzhiZTU0NjI=
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
- * Network interaction using an EventMachine protocol.
13
- * Simplified network interaction using a structured client
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 that can
20
- be used to send requests to a POSLynx unit and receive the
21
- responses to those requests. The gem also includes data models
22
- with XML serialization/deserialization that are used by those
23
- network adapters or may be used as part of a different network
24
- adapter if you prefer to build your own.
25
-
26
- The first network adapter provided is in the form of a protocol
27
- for EventMachine. EventMachine is a gem for implementing
28
- event-driven communication clients and servers. Essentially,
29
- being event-driven means that requests are sent and responses are
30
- received asynchronously. The application receives a call-back
31
- when the server responds or the connection is lost, etc.
32
-
33
- The second network adapter this gem provides is a "structured"
34
- (as opposed to event-driven) API. This is primarily provided
35
- as a convenience for use in situations where the event-driven
36
- API is inconvenient, such as when experimenting with the gem
37
- from an irb command line session.
38
-
39
- In addition to the network client libraries, this gem includes
40
- a script that acts as a fake POSLynx + PIN Pad. This is useful
41
- when you are working without access to an actual POSLynx and
42
- PIN Pad, and want to test your client code and try out
43
- workflows. This script will probably be extracted into a
44
- separate gem soon.
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 structured client
49
-
50
- A quick and easy way to try out this gem is to use the structured
51
- client and example-request factory from an irb console.
52
-
53
- Assuming you have a POSLynx host running at 192.168.1.99 on port
54
- 1234 with SSL required, and if your lane has a registered client
55
- MAC value of 123456789ABC, then with the client_for_poslynx gem
56
- installed, you should be able to execute a sequence similar to
57
- the following.
58
-
59
- $ irb
60
- 1.9.3-p545 :001 > require 'client_for_poslynx'
61
- => true
62
- 1.9.3-p545 :002 > client = ClientForPoslynx::Net::StructuredClient.new('192.168.1.99', 1234, true)
63
- => #<ClientForPoslynx::Net::StructuredClient:0x007fefa2103aa8 @directive_queue=#<Queue:0x007fefa21039e0 @que=[], @waiting=[], @mutex=#<Mutex:0x007fefa2103968>>, @activity_queue=#<Queue:0x007fefa2103940 @que=[], @waiting=[], @mutex=#<Mutex:0x007fefa2103300>>, @em_thread=#<Thread:0x007fefa2103260 sleep>>
64
- 1.9.3-p545 :003 > reqf = ClientForPoslynx::ExampleRequestFactory.new('1234567890ABC')
65
- => #<ClientForPoslynx::ExampleRequestFactory:0x007fefa2036030 @client_mac="1234567890ABC">
66
- 1.9.3-p545 :004 > req = reqf.pin_pad_initialize_request
67
- => #<ClientForPoslynx::Data::Requests::PinPadInitialize:0x007fefa204f760 @client_mac="1234567890ABC", @idle_prompt="Example idle prompt">
68
- 1.9.3-p545 :005 > client.send_request req
69
- => nil
70
- 1.9.3-p545 :006 > client.get_response
71
- => #<ClientForPoslynx::Data::Responses::PinPadInitialize:0x007fefa21106e0 @result="Success", @result_text="PinPad Initialized", @error_code="1000", @source_data="<?xml version=\"1.0\" standalone=\"yes\" ?><PLResponse><Command>PPINIT</Command><Result>Success</Result><ResultText>PinPad Initialized</ResultText><ErrorCode>1000</ErrorCode></PLResponse>">
72
- 1.9.3-p545 :007 > client.end_session
73
- => nil
74
-
75
- ### Using the EventMachine protocol
76
-
77
- The following example code demonstrates how to write an
78
- event-driven client using EventMachine and the POSLynx protocol
79
- for EventMachine.
80
-
81
- require 'client_for_poslynx'
82
-
83
- HOST = '192.168.1.25'
84
- PORT = 12345
85
- CLIENT_MAC = '000000000000'
86
- USE_SSL = true
87
-
88
- class ButtonSelectionDemo < EM::Connection
89
- include EM::Protocols::POSLynx
90
-
91
- def connection_completed
92
- puts "IP connection has been opened"
93
- if USE_SSL
94
- puts "Starting SSL"
95
- start_tls verify_peer: false
96
- else
97
- puts "Using raw connection. No SSL"
98
- display_the_message
99
- end
100
- end
101
-
102
- def ssl_handshake_completed
103
- puts "SSL session has been successfully started"
104
- display_the_message
105
- end
106
-
107
- def display_the_message
108
- puts "Sending the PIN Pad Display Message request"
109
-
110
- req = ClientForPoslynx::Data::Requests::PinPadDisplayMessage.new
111
- req.client_mac = CLIENT_MAC
112
- req.line_count = 2
113
- req.text_lines = [
114
- "Heads, we clean the basement",
115
- "Tails, we go to the movies"
116
- ]
117
- req.button_labels = %w[ Heads Tails ]
118
-
119
- send_request req
120
- end
121
-
122
- def receive_response(response)
123
- puts "Received response from POSLynx"
124
- puts "Result text: #{response.result_text}"
125
- puts "Selected button: #{response.button_response}"
126
-
127
- puts "Closing the connection"
128
- close_connection
129
- end
130
-
131
- def unbind
132
- puts "The connection has been closed"
133
- puts "Terminating the event loop"
134
- EM.stop_event_loop
135
- end
136
-
137
- end
138
-
139
- # This code will block until the event loop exits.
140
- EM.run do
141
- EM.connect HOST, PORT, ButtonSelectionDemo
142
- EM.error_handler do |e|
143
- raise e
144
- end
145
- end
146
-
147
- puts "The event loop has ended"
148
- puts "Bye"
149
-
150
- # == Sample output ==
151
- # IP connection has been opened
152
- # Starting SSL
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 supported.
184
- __More will be added. Contributions are welcome and encouraged. :)__
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