butler-mainframe 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4002cfeff0a68711d48ded02dabdcfb246dfd8f
4
- data.tar.gz: d48bb6bd6986aa9b12cb2fe1695de8a73639ccbc
3
+ metadata.gz: 2be056e6d5c2cd0b24cdd1ba7c27a92f2e11d295
4
+ data.tar.gz: 0265a490b0c3891b40e0c336c05e4d73563d9150
5
5
  SHA512:
6
- metadata.gz: efbafb8fc1ae9f9416294f4f2b8dfd9e863d49f2651d6b16771ed9bf593466a4688f673d888a679c298f4b3c0e62b8e28ab520be9d99f54b28a8f0db44cf3d0d
7
- data.tar.gz: 5e4043550886c4bfc8540c8044da940f326e98fe3c3f7160f415d180bdca608c0fb80e12146f584f5f27cadd7e380b6598af7ea723ef9eb5cc43ccd98aa83ce3
6
+ metadata.gz: 71d70a1deaf10b776b6e4b49a5eb57d608c5931b245f72010876f32d9946e05ac9aa1cee342c87df895719a63b79dd6ae2ebac3ab6b715269f173b454f6185f7
7
+ data.tar.gz: 721c158317b5282ca9e51df2988e1b081992420427176092a0a3001581ca2930792ae7392003ec6284a51a62f0e6b6247c07f97cdd872aa745a7d54092b5ee77
data/CHANGELOG.md CHANGED
@@ -1,8 +1,18 @@
1
+ 0.1.0 [☰](https://github.com/marcomd/butler-mainframe/compare/v0.0.6...v0.1.0) October 14th, 2015
2
+ ------------------------------
3
+ * Added IBM personal communication support (tested on 6.0.16), use :pcomm as host gateway in butler configuration
4
+ * Many documentation improvements with technical details and examples
5
+ * Little improvements in host base to manage ready? sub methods, a new check as well as created?
6
+ * Two new config parameters: timeout and session_tag to connect to the right session
7
+ * Write text now have an option parameter :check_protect which raise an error when on protect area
8
+ * Improved passport driver class: now it use a session screen @variable to optimize memory use
9
+ * Change include module methods to monkey patch to add ruby 1.8.7 support
10
+
1
11
  0.0.6 [☰](https://github.com/marcomd/butler-mainframe/compare/v0.0.5...v0.0.6) October 12th, 2015
2
12
  ------------------------------
3
13
  * Added a simple rake test
4
14
  * Improved documentation
5
- * Several little improvements
15
+ * Some little improvements
6
16
 
7
17
  0.0.5 October 7th, 2015
8
18
  ------------------------------
data/README.md CHANGED
@@ -20,17 +20,67 @@ Instal the gem from rubygems
20
20
  Then you have to install your favorite emulator.
21
21
  At the moment is supported only [Passport web to host by rocket software](http://www.rocketsoftware.com/products/rocket-passport-web-to-host)
22
22
 
23
+ ## Emulator
24
+
25
+ At the moment are managed two emulators both are commercial which must be purchased and installed on the machine.
26
+ Both have x days free trial.
27
+
28
+ * [Passport web to host by Rocket Software](http://www.rocketsoftware.com/resource/rocket-passport-web-host-overview)
29
+ * [Personal communication by IBM](http://www-03.ibm.com/software/products/en/pcomm)
30
+
31
+
32
+
33
+
34
+ ## Configuration
35
+
36
+ In the config folder there are two files:
37
+
38
+ * config.rb
39
+ * settings.yml
40
+
41
+ ### Emulator configuration
42
+
43
+ config.rb can be used for the configuration of the gem and the emulator
44
+
45
+ ```ruby
46
+ # Example to configura Personal communication
47
+
48
+ ButlerMainframe.configure do |config|
49
+ config.host_gateway = :pcomm
50
+ config.browser_path = "'C:/Program Files (x86)/IBM/Personal Communications/pcsws.exe'"
51
+ config.session_path = "'C:/Users/Marco/AppData/Roaming/IBM/Personal Communications/host3270.ws'"
52
+ config.session_tag = 'A'
53
+ config.timeout = 3000
54
+ end
55
+ ```
56
+
57
+ ```ruby
58
+ # Example to configura Passport web to host
59
+
60
+ ButlerMainframe.configure do |config|
61
+ config.host_gateway = :passport
62
+ config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
63
+ config.session_path = 'https://localhost/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
64
+ config.session_tag = 1
65
+ config.timeout = 3000
66
+ end
67
+ ```
68
+
69
+ ### Use configuration
70
+
71
+ settings.yml for the variables necessary to use the emulator like user, password, cics selection and everything else. It has one section for every environment in rails style.
72
+
73
+ foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
74
+ bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
75
+
76
+
23
77
  ## How to use
24
78
 
25
79
  In irb:
26
80
 
27
81
  C:\Ruby>irb
28
82
  irb(main):001:0> require 'butler-mainframe'
29
-
30
- Use ButlerMainframe::Host.new
31
- Please read the documentation on github for more information
32
83
  => true
33
- irb(main):002:0>
34
84
 
35
85
  You can start the emulator 3270 manually, the butler will lean on that and will let it open at the end.
36
86
  In this example, I dont start the session and immediately create a new instance:
@@ -113,9 +163,9 @@ then
113
163
 
114
164
  bundle install
115
165
 
116
- run generator to copy configuration files
166
+ run generator to copy configuration files suitable for the emulator you need
117
167
 
118
- rails g butler:install
168
+ rails g butler:install --emulator=passport
119
169
 
120
170
  ```ruby
121
171
  Class Invoice
@@ -163,6 +213,124 @@ For more informations:
163
213
  bundle exec rake -T
164
214
 
165
215
 
216
+ ## More informations about supported emulators
217
+
218
+ I hope this can help to support my work or yours if you need something different
219
+
220
+ ### Passport web to host
221
+
222
+ Documentation can be found [here](http://www.zephyrcorp.com/legacy-integration/Documentation/passport_host_integration_objects.htm) Rocket use old Zephyr documentation, unfortunately it's obsolete :disappointed:
223
+
224
+ ```ruby
225
+ require 'win32ole'
226
+ passport = WIN32OLE.new("PASSPORT.System")
227
+ ```
228
+
229
+ passport.ole_methods:
230
+
231
+ # QueryInterface # AddRef # Release # GetTypeInfoCount
232
+ # GetTypeInfo # GetIDsOfNames # Invoke # ActiveSession
233
+ # Application # DefaultFilePath # DefaultFilePath # FullName
234
+ # Name # Parent # Path # Sessions
235
+ # TimeoutValue # Version # Quit # ViewStatus
236
+ # GetTypeInfoCount # GetTypeInfo # GetIDsOfNames # Invoke
237
+ # TimeoutValue
238
+
239
+ passport.Sessions(1).ole_methods
240
+
241
+ # QueryInterface # AddRef # Release # GetTypeInfoCount # Toolbars
242
+ # GetTypeInfo # GetIDsOfNames # Invoke # Application # Visible
243
+ # ColorScheme # ColorScheme # Connected # Connected # Activate
244
+ # EditScheme # EditScheme # FileTransferHostOS # FileTransferHostOS # SaveAs
245
+ # FileTransferScheme # FileTransferScheme # FullName # Height
246
+ # Height # HotSpotScheme # HotSpotScheme # KeyboardLocked
247
+ # KeyboardLocked # KeyMap # KeyMap # Left
248
+ # Name # PageRecognitionTime # PageRecognitionTime # Parent
249
+ # Path # QuickPads # Saved # Screen
250
+ # Top # Top # Type # Visible
251
+ # Width # Width # WindowState # WindowState
252
+ # Close # NavigateTo # ReceiveFile # Save
253
+ # SendFile # FileTransferOptions # GetTypeInfoCount # GetTypeInfo
254
+ # GetIDsOfNames # Invoke
255
+
256
+ passport.Sessions(1).Screen.ole_methods:
257
+
258
+ # QueryInterface # AddRef # Release # GetTypeInfoCount # GetTypeInfo
259
+ # GetIDsOfNames # Invoke # Application # Col # Col
260
+ # Cols # Name # OIA # Parent # Row
261
+ # Row # Rows # Selection # Updated # Area
262
+ # Copy # Cut # Delete # GetString # MoveRelative
263
+ # MoveTo # Paste # PutString # Search # Select
264
+ # SelectAll # SendInput # SendKeys # WaitForCursor # WaitForCursorMove
265
+ # WaitForKeys # WaitForStream # WaitForString # WaitHostQuiet # CheckTimeInterval
266
+ # CheckTimeInterval # WaitAfterAIDKey # WaitAfterAIDKey # GetTypeInfoCount # GetTypeInfo
267
+ # GetIDsOfNames # Invoke
268
+
269
+ ### Personal communication
270
+
271
+ Documentation can be found [here](http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/com.ibm.pcomm.doc/books/html/host_access08.htm) Ibm did a good work!
272
+
273
+ ```ruby
274
+ require 'win32ole'
275
+ session = WIN32OLE.new("PComm.autECLSession")
276
+ session.SetConnectionByName 'A'
277
+ space = session.autECLPS
278
+ screen = session.autECLOIA
279
+ ```
280
+
281
+ session.ole_methods:
282
+
283
+ # QueryInterface # AddRef # Release
284
+ # GetTypeInfoCount # GetTypeInfo # autECLWinMetrics
285
+ # GetIDsOfNames # Invoke # Handle
286
+ # autECLXfer # autECLPS # Started
287
+ # autECLOIA # Name # Ready
288
+ # ConnType # CodePage
289
+ # CommStarted # APIEnabled
290
+ # StartCommunication # StopCommunication
291
+ # SetConnectionByName # SetConnectionByHandle
292
+ # RegisterSessionEvent # UnregisterSessionEvent
293
+ # RegisterCommEvent # UnregisterCommEvent
294
+ # autECLPageSettings # autECLPrinterSettings
295
+ # GetTypeInfoCount # GetTypeInfo
296
+ # GetIDsOfNames # Invoke
297
+
298
+ session.autECLPS.ole_methods (presentation space):
299
+
300
+ # QueryInterface # AddRef # Release # GetTypeInfoCount
301
+ # GetTypeInfo # GetIDsOfNames # Invoke # autECLFieldList
302
+ # NumRows # NumCols # CursorPosRow # CursorPosCol
303
+ # SetConnectionByName # SetConnectionByHandle # SetCursorPos # GetTextRect
304
+ # SendKeys # SearchText # GetText # WaitWhileCursor
305
+ # SetText # Wait # StartMacro # WaitWhileStringInRect
306
+ # WaitForCursor # WaitWhileString # WaitForString # WaitForScreen
307
+ # WaitForStringInRect # WaitWhileAttrib # WaitForAttrib # UnregisterPSEvent
308
+ # WaitWhileScreen # CancelWaits # RegisterPSEvent # Handle
309
+ # RegisterKeyEvent # UnregisterKeyEvent # RegisterCommEvent # CommStarted
310
+ # UnregisterCommEvent # SetTextRect # Name # StopCommunication
311
+ # ConnType # CodePage # Started # Invoke
312
+ # APIEnabled # Ready # StartCommunication
313
+ # GetTypeInfoCount # GetTypeInfo # GetIDsOfNames
314
+
315
+ session.autECLOIA.ole_methods (screen):
316
+
317
+ # QueryInterface # AddRef # Release # GetTypeInfoCount # UpperShift
318
+ # GetTypeInfo # GetIDsOfNames # Invoke # Alphanumeric # CommErrorReminder
319
+ # APL # Katakana # Hiragana # DBCS # ConnType
320
+ # NumLock # Numeric # CapsLock # InsertMode # Ready
321
+ # MessageWaiting # InputInhibited # Name # Handle # Invoke
322
+ # CodePage # Started # CommStarted # APIEnabled
323
+ # SetConnectionByName # SetConnectionByHandle # StartCommunication # StopCommunication
324
+ # WaitForInputReady # WaitForSystemAvailable # WaitForAppAvailable # WaitForTransition
325
+ # CancelWaits # RegisterCommEvent # UnregisterCommEvent # RegisterOIAEvent
326
+ # UnregisterOIAEvent # GetTypeInfoCount # GetTypeInfo # GetIDsOfNames
327
+
328
+
329
+ ## ToDo
330
+
331
+ * Improve unit test
332
+ * Improve static navigation
333
+
166
334
  ## License
167
335
 
168
336
  The GNU Lesser General Public License, version 3.0 (LGPL-3.0)
@@ -30,26 +30,30 @@ require "mainframe/#{ButlerMainframe.configuration.host_gateway.to_s.downcase}"
30
30
 
31
31
  ButlerMainframe::Settings.load!(File.join(ButlerMainframe.root,'lib','config','settings.yml'), :env => env)
32
32
 
33
- if ButlerMainframe::Settings.modules_included[:activerecord]
34
- require 'mainframe/customization/active_record'
35
- # puts "Extending Host class with #{Host3270::ActiveRecord}" if debug
36
- ButlerMainframe::Host.include Host3270::ActiveRecord
33
+ require 'mainframe/customization/active_record'
34
+ # puts "Extending Host class with #{Host3270::ActiveRecord}" if debug
35
+ # Use monkey patch for 1.8 compatibility
36
+ class ButlerMainframe::Host
37
+ include Host3270::ActiveRecord
37
38
  end
38
39
 
39
- if ButlerMainframe::Settings.modules_included[:generic_functions]
40
- require 'mainframe/customization/generic_functions'
41
- # puts "Extending Host class with #{Host3270::GenericFunctions}" if debug
42
- ButlerMainframe::Host.include Host3270::GenericFunctions
40
+ require 'mainframe/customization/generic_functions'
41
+ # puts "Extending Host class with #{Host3270::GenericFunctions}" if debug
42
+ class ButlerMainframe::Host
43
+ include Host3270::GenericFunctions
43
44
  end
44
45
 
45
- if ButlerMainframe::Settings.modules_included[:custom_functions] && defined?(Host3270::CustomFunctions)
46
+ if defined?(Host3270::CustomFunctions)
46
47
  # puts "Extending Host class with #{Host3270::CustomFunctions}" if debug
47
- ButlerMainframe::Host.include Host3270::CustomFunctions
48
+ class ButlerMainframe::Host
49
+ include Host3270::CustomFunctions
50
+ end
48
51
  end
49
52
 
50
53
  =begin
51
54
  # To test in irb
52
55
  require 'butler-mainframe'
53
- host=ButlerMainframe::Host.new
56
+ host=ButlerMainframe::Host.new(debug: :full)
54
57
  host.scan_page
58
+ host.navigate :next
55
59
  =end
data/lib/config/config.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  ButlerMainframe.configure do |config|
2
- # config.language = :it #NOT READY YET
3
2
  config.host_gateway = 'passport'
4
3
  config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
5
- config.session_path = 'https://ergmilict10/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
4
+ config.session_url = 'https://ergmilict10/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
5
+ config.session_tag = 1
6
+ config.timeout = 3000
6
7
  end
7
8
 
@@ -0,0 +1,8 @@
1
+ ButlerMainframe.configure do |config|
2
+ config.host_gateway = :passport
3
+ config.browser_path = 'c:/Program Files (x86)/Internet Explorer/iexplore.exe'
4
+ config.session_url = 'https://localhost/zephyr/Ecomes.zwh?sessionprofile=3270dsp/Sessions/host3270'
5
+ config.session_tag = 1
6
+ config.timeout = 3000
7
+ end
8
+
@@ -0,0 +1,7 @@
1
+ ButlerMainframe.configure do |config|
2
+ config.host_gateway = :pcomm
3
+ config.session_path = '"C:/Program Files (x86)/IBM/Personal Communications/pcsws.exe" "C:/Users/XXXXXXXXXX/AppData/Roaming/IBM/Personal Communications/host3270.ws"'
4
+ config.session_tag = 'A'
5
+ config.timeout = 6000
6
+ end
7
+
@@ -2,12 +2,9 @@ defaults: &defaults
2
2
  cics: 7
3
3
  user: "IBM0001"
4
4
  password: "password"
5
- life_user: "LIFE1"
6
- life_password: "password"
7
- modules_included:
8
- activerecord: yes
9
- generic_functions: yes
10
- custom_functions: yes
5
+ #foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
6
+ # bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
7
+
11
8
 
12
9
  development:
13
10
  <<: *defaults
@@ -0,0 +1,17 @@
1
+ defaults: &defaults
2
+ cics: 7
3
+ user: "IBM0001"
4
+ password: "password"
5
+ #foo: add every variable you need and use it with => ButlerMainframe::Settings.foo
6
+ # bar: sub variable are accessible with hash => ButlerMainframe::Settings.foo[:bar]
7
+
8
+
9
+ development:
10
+ <<: *defaults
11
+
12
+ test:
13
+ <<: *defaults
14
+
15
+ production:
16
+ <<: *defaults
17
+ cics: 4
@@ -2,13 +2,16 @@ module ButlerMainframe
2
2
  class Configuration
3
3
  attr_writer :allow_sign_up
4
4
 
5
- attr_accessor :language, :host_gateway, :browser_path, :session_path
5
+ attr_accessor :language, :host_gateway, :browser_path, :session_url, :session_path, :session_tag, :timeout
6
6
 
7
7
  def initialize
8
8
  @language = :en
9
9
  @host_gateway = nil
10
10
  @browser_path = ""
11
+ @session_url = ""
11
12
  @session_path = ""
13
+ @session_tag = nil
14
+ @timeout = 1000
12
15
  end
13
16
 
14
17
  end
@@ -1,16 +1,20 @@
1
1
  require 'rails/generators/base'
2
+ require File.join(File.dirname(__FILE__), '../butler_mainframe')
2
3
 
3
4
  module Butler
4
5
  module Generators
5
6
  class InstallGenerator < Rails::Generators::Base
7
+ include ::ButlerMainframe::Base
8
+
6
9
  desc "Installs ButlerMainframe configuration files"
7
10
  source_root File.expand_path('../../', __FILE__)
11
+ class_option :emulator, :type => :string, :default => 'passport', :desc => "Choose your favorite emulator [passport]"
8
12
 
9
13
  def copy_to_local
10
14
  copy_file "butler/templates/custom_functions.rb", "config/initializers/butler_custom_functions.rb"
11
15
  copy_file "../config/settings.yml", "config/butler.yml"
12
16
  file = "config/initializers/butler.rb"
13
- copy_file "../config/config.rb", file
17
+ copy_file "../config/config_EXAMPLE_#{options[:emulator]}.rb", file
14
18
  append_file file do
15
19
  <<-FILE.gsub(/^ /, '')
16
20
  ButlerMainframe::Settings.load!(File.join(Rails.root,'config','butler.yml'), :env => Rails.env)
@@ -156,4 +156,6 @@ module Host3270
156
156
  end
157
157
  end
158
158
 
159
- ButlerMainframe::Host.include Host3270::CustomFunctions
159
+ class ButlerMainframe::Host
160
+ include Host3270::CustomFunctions
161
+ end
@@ -10,6 +10,8 @@ module Host3270
10
10
 
11
11
  def do_quit; exec_command "CLEAR" end
12
12
 
13
+ def do_erase; exec_command "ERASE EOF" end
14
+
13
15
  def destination_list
14
16
  [
15
17
  :company_menu,
@@ -11,12 +11,14 @@ module ButlerMainframe
11
11
 
12
12
  def initialize options={}
13
13
  options = {
14
- :session => 1,
14
+ :session_tag => ButlerMainframe.configuration.session_tag,
15
15
  :wait => 0.01, #wait screen in seconds
16
16
  :wait_debug => 2, #wait time for debug purpose
17
17
  :debug => true,
18
18
  :browser_path => ButlerMainframe.configuration.browser_path,
19
+ :session_url => ButlerMainframe.configuration.session_url,
19
20
  :session_path => ButlerMainframe.configuration.session_path,
21
+ :timeout => ButlerMainframe.configuration.timeout,
20
22
  :close_session => :evaluate
21
23
  #:evaluate if the session is found will not be closed
22
24
  #:never never close the session
@@ -26,8 +28,9 @@ module ButlerMainframe
26
28
  @debug = options[:debug]
27
29
  @wait = options[:wait]
28
30
  @wait_debug = options[:wait_debug]
29
- @session = options[:session]
31
+ @session_tag = options[:session_tag]
30
32
  @close_session = options[:close_session]
33
+ @timeout = options[:timeout]
31
34
  @pid = nil
32
35
 
33
36
  create_object options
@@ -59,7 +62,6 @@ module ButlerMainframe
59
62
 
60
63
  # Execute keyboard command like PF1 or PA2 or ENTER ...
61
64
  def exec_command cmd
62
- cmd = "<#{cmd}>"
63
65
  puts "Command: #{cmd}" if @debug
64
66
  sub_exec_command cmd
65
67
  wait_session
@@ -96,17 +98,18 @@ module ButlerMainframe
96
98
  :clean_first_chars => nil
97
99
  }.merge(options)
98
100
 
99
- y=options[:y]
100
- x=options[:x]
101
+ y = options[:y]
102
+ x = options[:x]
103
+ hooked_rows = 2
101
104
  raise "Missing coordinates! y(row)=#{y} x(column)=#{x} " unless x && y
102
105
  raise "Sorry, cannot write null values" unless text
103
106
 
104
107
  bol_written = nil
105
108
  if options[:hook]
106
- (y-2..y+2).each do |y_riga|
107
- if /#{options[:hook]}/ === scan_row(y_riga, 1, MAX_TERMINAL_COLUMNS)
108
- puts "Change y from #{y} to #{y_riga} cause hook to:#{options[:hook]}" if y_riga != y && @debug
109
- bol_written = write_clean_text_on_map text, y_riga, x, options
109
+ (y-hooked_rows..y+hooked_rows).each do |row_number|
110
+ if /#{options[:hook]}/ === scan_row(row_number, 1, MAX_TERMINAL_COLUMNS)
111
+ puts "Change y from #{y} to #{row_number} cause hook to:#{options[:hook]}" if row_number != y && @debug
112
+ bol_written = write_clean_text_on_map text, row_number, x, options
110
113
  break
111
114
  end
112
115
  end
@@ -116,47 +119,76 @@ module ButlerMainframe
116
119
  bol_written
117
120
  end
118
121
 
119
- # It returns the coordinates of the cursor
122
+ # Return the coordinates of the cursor
120
123
  def get_cursor_axes
121
124
  sub_get_cursor_axes
122
125
  end
123
126
 
127
+ # Move the cursor at given coordinates
128
+ def set_cursor_axes y, x, options={}
129
+ sub_set_cursor_axes y, x, options
130
+ end
131
+
124
132
  private
125
133
 
126
134
  # It creates the object calling subclass method
135
+ # It depends on the emulator chosen but typically the object is present after starting the terminal session
127
136
  # These are the options with default values:
128
137
  # :session => 1,
129
138
  # :debug => true,
130
139
  # :browser_path => ButlerMainframe::Settings.browser_path,
131
140
  # :session_path => ButlerMainframe::Settings.session_path,
132
141
  def create_object options={}
133
- sub_create_object
142
+ connection_attempts = 8
143
+ seconds_between_attempts = 2
144
+
145
+ sub_create_object options
146
+
147
+ # if the terminal is not found then we start it
134
148
  unless sub_object_created?
135
- puts "Session not found, starting new..." if @debug
149
+ puts "Session #{@session_tag} not found, starting new..." if @debug
150
+
151
+ executable, args = if options[:browser_path] && !options[:browser_path].empty?
152
+ [options[:browser_path], options[:session_url]]
153
+ elsif options[:session_path] && !options[:session_path].empty?
154
+ [options[:session_path], nil]
155
+ else
156
+ [nil, nil]
157
+ end
158
+ raise "Specify an executable in the configuration file!" unless executable
136
159
 
137
160
  if /^1.8/ === RUBY_VERSION
138
- Thread.new {system "#{options[:browser_path]} #{options[:session_path]}"}
161
+ Thread.new {system "#{executable} #{args}"}
139
162
  @pid = $?.pid if $?
140
163
  else
141
164
  #It works only on ruby 1.9+
142
- @pid = Process.spawn "#{options[:browser_path]}", "#{options[:session_path]}"
165
+ @pid = Process.spawn *[executable, args].compact
143
166
  end
144
167
 
145
168
  puts "Starting session with process id #{@pid}, wait please..." if @debug
146
169
  sleep 2
147
- 5.times do
148
- puts "Wait please..." if @debug
170
+ connection_attempts.times do
171
+ puts "Detecting session #{@session_tag}, wait please..." if @debug
149
172
  sub_create_object
150
- sub_object_created? ? break : sleep(10)
173
+ sub_object_created? ? break : sleep(seconds_between_attempts)
151
174
  end
152
175
  @session_started_by_me = true
153
176
  end
154
177
 
155
- if sub_object_created?
178
+ raise "Session #{@session_tag} not started. Check the session #{options[:browser_path]} #{options[:session_path]}" unless sub_object_created?
179
+
180
+ unless sub_object_ready?
181
+ connection_attempts.times do
182
+ puts "Waiting for the session to be ready..." if @debug
183
+ sub_object_ready? ? break : sleep(seconds_between_attempts)
184
+ end
185
+ end
186
+
187
+ if sub_object_ready?
156
188
  puts "** Connection established with #{sub_name} **"
157
189
  puts "Session full name: #{sub_fullname}" if @debug == :full
158
190
  else
159
- raise "Connection refused. Check the session #{options[:session]} and it was on the initial screen."
191
+ raise "Connection refused. Check session #{@session_tag} with process id #{@pid}"
160
192
  end
161
193
 
162
194
  rescue
@@ -194,7 +226,7 @@ module ButlerMainframe
194
226
 
195
227
  # Write a text on the screen
196
228
  # It also contains the logic to control the successful writing
197
- def write_text_on_map(text, y, x, options={})
229
+ def write_text_on_map text, y, x, options={}
198
230
  options = {
199
231
  :check => true,
200
232
  :raise_error_on_check => true,
@@ -203,7 +235,7 @@ module ButlerMainframe
203
235
  raise "Impossible to write beyond row #{MAX_TERMINAL_ROWS}" if y > MAX_TERMINAL_ROWS
204
236
  raise "Impossible to write beyond column #{MAX_TERMINAL_COLUMNS}" if x > MAX_TERMINAL_COLUMNS
205
237
  raise "Impossible to write a null value" unless text
206
- sub_write_text text, y, x
238
+ sub_write_text text, y, x, :check_protect => options[:check]
207
239
  # It returns the function's result
208
240
  if options[:check]
209
241
  # It expects the string is present on the session at the specified coordinates
@@ -2,64 +2,98 @@ require 'win32ole'
2
2
  require 'mainframe/host_base'
3
3
 
4
4
  # This class use Rocket 3270 emulator API
5
- # http://www.rocketsoftware.com/products/rocket-passport-web-to-host
5
+ # http://www.zephyrcorp.com/legacy-integration/Documentation/passport_host_integration_objects.htm
6
6
  module ButlerMainframe
7
7
  class Host < HostBase
8
8
 
9
9
  private
10
10
 
11
- def sub_create_object
11
+ # Create objects from emulator library
12
+ def sub_create_object options={}
12
13
  str_obj = 'PASSPORT.System'
13
14
  puts "#{Time.now.strftime "%H:%M:%S"} Creating object #{str_obj}..." if @debug == :full
14
15
  @action = WIN32OLE.new(str_obj)
16
+ @screen = @action.Sessions(@session_tag).Screen if sub_object_created?
15
17
  end
16
18
 
19
+ # Check is session is started
17
20
  def sub_object_created?
18
- puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full
19
- @action.Sessions(@session)
21
+ res = @action && @action.Sessions(@session_tag)
22
+ puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full && res
23
+ res
24
+ end
25
+
26
+ # Check is session is operative
27
+ def sub_object_ready?
28
+ res = @action.Sessions(@session_tag).Connected == -1
29
+ puts "#{Time.now.strftime "%H:%M:%S"} Session ready" if @debug == :full && res
30
+ res
20
31
  end
21
32
 
22
33
  def sub_name
23
- @action.Sessions(@session).Name
34
+ "#{@action.Name} #{@action.Sessions(@session_tag).Name}"
24
35
  end
25
36
 
26
37
  def sub_fullname
27
- @action.Sessions(@session).FullName
38
+ "#{sub_name} #{@action.Sessions(@session_tag).FullName}"
28
39
  end
29
40
 
30
41
  #Ends the connection and closes the session
31
42
  def sub_close_session
32
- @action.Sessions(@session).Close
43
+ @action.Sessions(@session_tag).Close
33
44
  @action.Quit
34
45
  @action = nil
35
46
  end
36
47
 
37
48
  #Execute keyboard command like PF1 or PA2 or ENTER ...
38
- def sub_exec_command cmd
39
- @action.Sessions(@session).Screen.SendKeys cmd
40
- @action.Sessions(@session).Screen.WaitHostQuiet
49
+ def sub_exec_command cmd, options={}
50
+ # Cast cmd to_s cause it could be passed as label
51
+ @screen.SendKeys "<#{cmd}>"
52
+ @screen.WaitHostQuiet
41
53
  end
42
54
 
43
55
  #It reads one line part of the screen
44
56
  def sub_scan_row y, x, len
45
- @action.Sessions(@session).Screen.GetString(y, x, len)
57
+ @screen.GetString(y, x, len)
46
58
  end
47
59
 
48
60
  #It reads a rectangle on the screen
49
61
  def sub_scan_area y1, x1, y2, x2
50
- @action.Sessions(@session).Screen.Area(y1, x1, y2, x2).Value
62
+ @screen.Area(y1, x1, y2, x2).Value
51
63
  end
52
64
 
65
+ # Get cursor coordinates
53
66
  def sub_get_cursor_axes
54
- [@action.Sessions(@session).Screen.Col, @action.Sessions(@session).Screen.Row]
67
+ [@screen.Col, @screen.Row]
68
+ end
69
+
70
+ # Move cursor to given coordinates
71
+ def sub_set_cursor_axes y, x, options={}
72
+ options = {
73
+ :wait => true
74
+ }.merge(options)
75
+ @screen.MoveTo y, x
76
+ @screen.WaitForCursor(y, x) if options[:wait]
55
77
  end
56
78
 
57
- def sub_write_text text, y, x
58
- @action.Sessions(@session).Screen.PutString(text, y, x)
79
+ # Write text on the screen at given coordinates
80
+ # :check_protect => true add sensitivity to protected areas
81
+ def sub_write_text text, y, x, options={}
82
+ options = {
83
+ :check_protect => true
84
+ }.merge(options)
85
+ if options[:check_protect]
86
+ # This method is sensitive to protected areas
87
+ sub_set_cursor_axes y, x
88
+ @screen.SendKeys text
89
+ else
90
+ @screen.PutString(text, y, x)
91
+ end
59
92
  end
60
93
 
94
+ # Wait text at given coordinates and wait the session is available again
61
95
  def sub_wait_for_string text, y, x
62
- @action.Sessions(@session).Screen.WaitForString(text, y, x).Value == -1
96
+ @screen.WaitForString(text, y, x).Value == -1
63
97
  end
64
98
 
65
99
  end
@@ -0,0 +1,105 @@
1
+ require 'win32ole'
2
+ require 'mainframe/host_base'
3
+
4
+ # This class use IBM personal communication
5
+ # http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/welcome.html
6
+ # http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_6.0.0/com.ibm.pcomm.doc/books/html/host_access08.htm
7
+ module ButlerMainframe
8
+ class Host < HostBase
9
+
10
+ private
11
+
12
+ # Create objects from emulator library
13
+ def sub_create_object options={}
14
+ str_obj = 'PComm.autECLSession'
15
+ puts "#{Time.now.strftime "%H:%M:%S"} Creating object #{str_obj}..." if @debug == :full
16
+ @action = WIN32OLE.new(str_obj)
17
+ @action.SetConnectionByName @session_tag
18
+ @space = @action.autECLPS
19
+ @screen = @action.autECLOIA
20
+ end
21
+
22
+ # Check is session is started
23
+ def sub_object_created?
24
+ res = @action && @action.CommStarted
25
+ puts "#{Time.now.strftime "%H:%M:%S"} Terminal successfully detected" if @debug == :full && res
26
+ res
27
+ end
28
+
29
+ # Check is session is operative
30
+ def sub_object_ready?
31
+ res = @action.Ready
32
+ puts "#{Time.now.strftime "%H:%M:%S"} Session ready" if @debug == :full && res
33
+ res
34
+ end
35
+
36
+ def sub_name
37
+ "PComm #{@action.Name}"
38
+ end
39
+
40
+ def sub_fullname
41
+ "#{sub_name} #{@action.ConnType}"
42
+ end
43
+
44
+ #Ends the connection and closes the session
45
+ def sub_close_session
46
+ @action.StopCommunication
47
+ @action = nil
48
+ Process.kill 9, @pid if @pid
49
+ end
50
+
51
+ #Execute keyboard command like PF1 or PA2 or ENTER ...
52
+ def sub_exec_command cmd, options={}
53
+ # Cast cmd to_s cause it could be passed as label
54
+ @space.SendKeys "[#{cmd}]"
55
+ @screen.WaitForAppAvailable @timeout
56
+ @screen.WaitForInputReady @timeout
57
+ end
58
+
59
+ #It reads one line part of the screen
60
+ def sub_scan_row y, x, len
61
+ @space.GetText(y, x, len)
62
+ end
63
+
64
+ #It reads a rectangle on the screen
65
+ def sub_scan_area y1, x1, y2, x2
66
+ @space.GetTextRect(y1, x1, y2, x2)
67
+ end
68
+
69
+ # Get cursor coordinates
70
+ def sub_get_cursor_axes
71
+ [@space.CursorPosCol, @space.CursorPosRow]
72
+ end
73
+
74
+ # Move cursor to given coordinates
75
+ def sub_set_cursor_axes y, x, options={}
76
+ options = {
77
+ :wait => true
78
+ }.merge(options)
79
+ @space.SetCursorPos y, x
80
+ @space.WaitForCursor(y, x, @timeout) if options[:wait]
81
+ end
82
+
83
+ # Write text on the screen at given coordinates
84
+ # :check_protect => true add sensitivity to protected areas
85
+ def sub_write_text text, y, x, options={}
86
+ options = {
87
+ :check_protect => true
88
+ }.merge(options)
89
+ if options[:check_protect]
90
+ # This method is sensitive to protected areas
91
+ @space.SendKeys(text, y, x)
92
+ else
93
+ @space.SetText(text, y, x)
94
+ end
95
+ end
96
+
97
+ # Wait text at given coordinates and wait the session is available again
98
+ def sub_wait_for_string text, y, x
99
+ @space.WaitForString text, y, x, @timeout
100
+ @screen.WaitForInputReady @timeout
101
+ end
102
+
103
+ end
104
+ end
105
+
data/rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+
4
+ Dir["#{File.dirname(__FILE__)}/test/*.rake"].sort.each { |ext| load ext }
data/test/test.rake ADDED
@@ -0,0 +1,26 @@
1
+ require 'fileutils'
2
+
3
+ namespace :butler do
4
+ namespace :mainframe do
5
+
6
+ desc "Test butler mainframe gem"
7
+ task(:test) do |task_name, args|
8
+
9
+ begin
10
+ require 'butler-mainframe'
11
+ host = ButlerMainframe::Host.new
12
+ str_screen1 = host.scan_page
13
+ raise 'host.scan_page' if str_screen1.empty?
14
+ host.navigate :next
15
+ str_screen2 = host.scan_page
16
+ raise 'host.navigate :next' if str_screen1 == str_screen2
17
+ host.close_session
18
+
19
+ rescue
20
+ puts $!.message
21
+ exit(9)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: butler-mainframe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Mastrodonato
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-12 00:00:00.000000000 Z
11
+ date: 2015-10-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: This gem provides a virtual butler which can perform your custom tasks
14
14
  on a 3270 emulator. You just have to choose your emulator (atm only one choice)
@@ -25,7 +25,10 @@ files:
25
25
  - README.md
26
26
  - lib/butler-mainframe.rb
27
27
  - lib/config/config.rb
28
+ - lib/config/config_EXAMPLE_passport.rb
29
+ - lib/config/config_EXAMPLE_pcomm.rb
28
30
  - lib/config/settings.yml
31
+ - lib/config/settings_EXAMPLE.yml
29
32
  - lib/core/configuration.rb
30
33
  - lib/core/configuration_dynamic.rb
31
34
  - lib/generators/butler/USAGE
@@ -36,7 +39,10 @@ files:
36
39
  - lib/mainframe/customization/generic_functions.rb
37
40
  - lib/mainframe/host_base.rb
38
41
  - lib/mainframe/passport.rb
42
+ - lib/mainframe/pcomm.rb
39
43
  - lib/vendor/deep_symbolize.rb
44
+ - rakefile
45
+ - test/test.rake
40
46
  homepage: https://github.com/marcomd/butler-mainframe
41
47
  licenses:
42
48
  - LGPL-3.0
@@ -56,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
62
  - !ruby/object:Gem::Version
57
63
  version: '0'
58
64
  requirements:
59
- - So many patience
65
+ - Be awake and rested
60
66
  rubyforge_project:
61
67
  rubygems_version: 2.2.5
62
68
  signing_key: