antimony 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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDg0NTU1Yjg1YTRhZDY2MjY3YTNhNzc4MWFjMzM3MGFhZmZjOTljYQ==
5
+ data.tar.gz: !binary |-
6
+ YWJmZGJlYjVhMjViOTZkZTY2MTJiNmNmMzg5NDQ4NTUxNTc2Mzc3ZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NmU0MjI5ZTY1MGVmYzZjM2Q3YTQ0ODE3MjkyNTRlZDhiNTUwNWQwN2RkMzlh
10
+ MTU1YTMxNWJkYzM5ODY1N2I2YTUxOTk4YjQyZjFlZDI5Zjk1YTZmMWE5ZDE2
11
+ ZTQ1NGEyMTViNGE2N2QzODQyYTM4ZTc4NTk2NDhlODk5ODg4MWE=
12
+ data.tar.gz: !binary |-
13
+ YjM4MDVmYTc1MWE0OTk5NWFlYTFmOWUwNjY4MTUzZGIxNjEwMDYxYmM3MzVl
14
+ YjkwYzJhMzJlNGNhMWIxNmM4MWYyNjAyNDQyMzU2YTg2ZDI3NmZlOGFiMzIx
15
+ N2E3ZDM0ODUzYmU4MGQ3ZTVmNzRiN2JiOTUwMTcwZjQzOGJjYmE=
data/bin/sb ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib_path = File.expand_path('../../lib'.freeze, __FILE__)
4
+ ($LOAD_PATH.unshift lib_path) unless $LOAD_PATH.include? lib_path
5
+
6
+ require 'thor'
7
+ require 'logging'
8
+ require 'antimony'
9
+
10
+ class AntimonyCLI < Thor
11
+ desc 'formula FILE [--inputs my_input:hello other_input:goodbye]', 'run the specified formula'
12
+ method_option :inputs, type: :hash
13
+ def formula(file)
14
+ inputs = options[:inputs] || {}
15
+ formula = Antimony::Formula.new(file, inputs)
16
+ formula.run
17
+ end
18
+
19
+ default_task :formula
20
+ end
21
+
22
+ AntimonyCLI.start(ARGV)
@@ -0,0 +1,88 @@
1
+ # encoding 'ASCII-8bit'
2
+ lib_path = File.expand_path('../antimony', __FILE__)
3
+ ($LOAD_PATH.unshift lib_path) unless $LOAD_PATH.include? lib_path
4
+
5
+ require 'bundler'
6
+ Bundler.require(:default)
7
+
8
+ # Misc
9
+ require 'net/telnet'
10
+ require 'logging'
11
+
12
+ # Gem
13
+ require "#{lib_path}/config.rb"
14
+ require "#{lib_path}/formula.rb"
15
+ require "#{lib_path}/session.rb"
16
+
17
+ module Antimony
18
+ class << self
19
+ attr_writer :configuration
20
+ end
21
+
22
+ def self.configuration
23
+ @configuration ||= Antimony::Config.new.tap { |config| config.show_output = false }
24
+ end
25
+
26
+ def self.configure
27
+ yield configuration if block_given?
28
+ end
29
+
30
+ # Constants
31
+ ANSI_REGEX = /(\e\[.{1,2};?.{0,2}m|\e\[.{1}J|\e\[.{1,2}A|\e\[.{1,2}B|\e\[.{1,2}C|\e\[.{1,2}D|\e\[K|\e\[.{1,2};.{1,2}H|\e\[.{3}|.)/.freeze # rubocop:disable Metrics/LineLength
32
+ SCREEN_SEPARATOR = '################################################################################'.freeze
33
+ LOG_SEPARATOR = '================================================================================'.freeze
34
+
35
+ SPACE = ' '.freeze
36
+ EMPTY = ''.freeze
37
+ RECEIVE_OPTS = { 'Match' => /.{20}/, 'Timeout' => 1 }.freeze
38
+
39
+ ENTER = "\n".freeze
40
+ TAB = "\t".freeze
41
+ ESCAPE = "\e".freeze
42
+
43
+ H = 'H'.freeze
44
+
45
+ SEMICOLON = ';'.freeze
46
+
47
+ LINE = /.{80}/.freeze
48
+
49
+ class Session
50
+ KEYBOARD_METHODS = {
51
+ f1: "\x1B\x31".freeze,
52
+ f2: "\x1B\x32".freeze,
53
+ f3: "\x1B\x33".freeze,
54
+ f4: "\x1B\x34".freeze,
55
+ f5: "\x1B\x35".freeze,
56
+ f6: "\x1B\x36".freeze,
57
+ f7: "\x1B\x37".freeze,
58
+ f8: "\x1B\x38".freeze,
59
+ f9: "\x1B\x39".freeze,
60
+ f10: "\x1B\x30".freeze,
61
+ f11: "\x1B\x2D".freeze,
62
+ f12: "\x1B\x3D".freeze,
63
+ f13: "\x1B\x21".freeze,
64
+ f14: "\x1B\x40".freeze,
65
+ f15: "\x1B\x23".freeze,
66
+ f16: "\x1B\x24".freeze,
67
+ f17: "\x1B\x25".freeze,
68
+ f18: "\x1B\x5E".freeze,
69
+ f19: "\x1B\x26".freeze,
70
+ f20: "\x1B\x2A".freeze,
71
+ f21: "\x1B\x28".freeze,
72
+ f22: "\x1B\x29".freeze,
73
+ f23: "\x1B\x5F".freeze,
74
+ f24: "\x1B\x2B".freeze,
75
+ enter: "\n".freeze,
76
+ tab: "\t".freeze,
77
+ backspace: "\177".freeze,
78
+ arrow_up: "\e[A".freeze,
79
+ arrow_down: "\e[B".freeze,
80
+ arrow_right: "\e[C".freeze,
81
+ arrow_left: "\e[D".freeze
82
+ }
83
+
84
+ KEYBOARD_METHODS.each do |key, val|
85
+ define_method(key) { |count = 1| send_keys val, count, false }
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,5 @@
1
+ module Antimony
2
+ class Config
3
+ attr_accessor :show_output, :pretty
4
+ end
5
+ end
@@ -0,0 +1,62 @@
1
+ module Antimony
2
+ class Formula
3
+ LOG_PARENT_PATH = Dir.pwd + '/log'
4
+ LOG_PATH = "#{LOG_PARENT_PATH}/formulas"
5
+ FORMULAS_PATH = Dir.pwd + '/formulas'
6
+
7
+ attr_accessor :inputs, :outputs, :formula_log
8
+
9
+ def initialize(name, inputs = {})
10
+ ($LOAD_PATH.unshift FORMULAS_PATH) unless $LOAD_PATH.include? FORMULAS_PATH
11
+
12
+ @inputs = indifferent_hash(inputs)
13
+ @outputs = {}
14
+
15
+ init_log(name)
16
+
17
+ helper_path = "#{FORMULAS_PATH}/formula_helper.rb"
18
+
19
+ eval File.read(helper_path) if File.exist?(helper_path) # rubocop:disable Lint/Eval
20
+
21
+ @formula = File.read("#{FORMULAS_PATH}/#{name}.rb")
22
+ end
23
+
24
+ def run
25
+ eval(@formula) # rubocop:disable Lint/Eval
26
+ end
27
+
28
+ def session(host)
29
+ @connection = Antimony::Session.new(host)
30
+ yield if block_given?
31
+ @log.info @connection.session_log
32
+ @connection.close
33
+ @connection = nil
34
+ end
35
+
36
+ private
37
+
38
+ def init_log(name)
39
+ Dir.mkdir LOG_PARENT_PATH unless Dir.exist? LOG_PARENT_PATH
40
+ Dir.mkdir LOG_PATH unless Dir.exist? LOG_PATH
41
+ @log = Logging.logger[name]
42
+ layout = Logging.layouts.pattern(pattern: "%m\n")
43
+ @log.add_appenders(Logging.appenders.file("#{LOG_PATH}/#{name}.log", truncate: true,
44
+ layout: layout))
45
+ end
46
+
47
+ def method_missing(name, *args, &block)
48
+ fail 'No active connection!' unless @connection
49
+ @connection.send(name, *args, &block)
50
+ end
51
+
52
+ def indifferent_hash(hash)
53
+ {}.tap do |new_hash|
54
+ hash.each do |key, value|
55
+ value = indifferent_hash(value) if value.is_a?(Hash)
56
+ new_hash[key.to_sym] = value
57
+ new_hash[key.to_s] = value
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,120 @@
1
+ module Antimony
2
+ class Session
3
+ def initialize(host)
4
+ @cursor_position = 0
5
+ @screen_text = blank_screen
6
+ @session_log = []
7
+
8
+ @connection = Net::Telnet.new('Host' => host, 'Timeout' => 10)
9
+
10
+ update_screen
11
+ end
12
+
13
+ def close
14
+ @connection.close
15
+ end
16
+
17
+ def send_keys(keys, count = 1, text = true)
18
+ count.times { @connection.print keys }
19
+ update_screen
20
+ add_text(keys) if text
21
+ log_screen
22
+ print_screen if Antimony.configuration.show_output
23
+ end
24
+
25
+ def value_at(row, column, length)
26
+ start_index = ((row - 1) * 80) + (column - 1)
27
+ end_index = start_index + length - 1
28
+ @screen_text[start_index..end_index].join
29
+ end
30
+
31
+ def screen_text
32
+ @screen_text.join
33
+ end
34
+
35
+ def printable_screen_text
36
+ @screen_text.join.scan(LINE)
37
+ end
38
+
39
+ def print_screen
40
+ puts SCREEN_SEPARATOR
41
+ puts printable_screen_text
42
+ puts SCREEN_SEPARATOR
43
+ end
44
+
45
+ def session_log
46
+ txt = EMPTY.clone
47
+ @session_log.each_with_index do |entry, i|
48
+ txt += LOG_SEPARATOR + ENTER
49
+ txt += "#{i}: #{ENTER}"
50
+ txt += entry + ENTER
51
+ end
52
+ txt += LOG_SEPARATOR
53
+ txt
54
+ end
55
+
56
+ def log(text)
57
+ @session_log.push(text)
58
+ end
59
+
60
+ def log_screen
61
+ log(printable_screen_text.join("\n"))
62
+ end
63
+
64
+ private
65
+
66
+ def update_screen
67
+ @screen_data = receive_data
68
+ @screen_text = blank_screen if @screen_data.include?('[2J')
69
+ parse_ansi
70
+ end
71
+
72
+ def add_text(text)
73
+ text.chars.each do |char|
74
+ @screen_text[@cursor_position] = char
75
+ @cursor_position += 1
76
+ end
77
+ end
78
+
79
+ def receive_data
80
+ whole = []
81
+ whole.push(@chunk) while chunk
82
+ whole.join
83
+ end
84
+
85
+ def chunk
86
+ @chunk = @connection.waitfor RECEIVE_OPTS rescue nil
87
+ end
88
+
89
+ def cursor_position(esc)
90
+ esc_code = /\e\[/
91
+ pos = esc.gsub(esc_code, EMPTY).gsub(H, EMPTY).split(SEMICOLON)
92
+ row_index = pos[0].to_i - 1
93
+ col_index = pos[1].to_i - 1
94
+ (row_index * 80) + col_index
95
+ end
96
+
97
+ def parse_ansi
98
+ ansi = @screen_data.scan(ANSI_REGEX).map do |e|
99
+ {
100
+ value: e[0],
101
+ type: e[0].include?(ESCAPE) ? :esc : :chr
102
+ }
103
+ end
104
+
105
+ ansi.each do |e|
106
+ if (e[:type] == :esc) && (e[:value].end_with? H)
107
+ @cursor_position = cursor_position(e[:value])
108
+ elsif e[:type] == :chr
109
+ @screen_text[@cursor_position] = e[:value]
110
+ @cursor_position += 1
111
+ end
112
+ end
113
+ @screen_text
114
+ end
115
+
116
+ def blank_screen
117
+ (1..1920).to_a.map { |_i| SPACE }
118
+ end
119
+ end
120
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: antimony
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Terris
8
+ - Adrienne Hisbrook
9
+ - Lesley Dennison
10
+ - Ryan Rosenblum
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2015-05-15 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: logging
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: thor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: rake
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ~>
49
+ - !ruby/object:Gem::Version
50
+ version: '10.3'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: '10.3'
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ~>
63
+ - !ruby/object:Gem::Version
64
+ version: '3.0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ~>
70
+ - !ruby/object:Gem::Version
71
+ version: '3.0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: pry
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ~>
77
+ - !ruby/object:Gem::Version
78
+ version: 0.10.1
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 0.10.1
86
+ - !ruby/object:Gem::Dependency
87
+ name: rubocop
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ version: 0.31.0
93
+ type: :development
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ~>
98
+ - !ruby/object:Gem::Version
99
+ version: 0.31.0
100
+ description: DSL for writing scripts to drive TN5250 green-screen applications
101
+ email: ''
102
+ executables:
103
+ - sb
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - bin/sb
108
+ - lib/antimony.rb
109
+ - lib/antimony/config.rb
110
+ - lib/antimony/formula.rb
111
+ - lib/antimony/session.rb
112
+ homepage: https://github.com/manheim/antimony
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.4.6
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: TN5250/AS400 automation
136
+ test_files: []
137
+ has_rdoc: