glottis 0.1.2 → 0.3.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
- SHA1:
3
- metadata.gz: 2884f76bb99f4bdec3dd3f76388f9938767ae903
4
- data.tar.gz: 7c765d282c0826521fb4eb2bc9eb3e638676935c
2
+ SHA256:
3
+ metadata.gz: c4738a04eaf3d36ff79c3b8b18e9408d825729e40a2314f76049aeb848e9fb23
4
+ data.tar.gz: 3bc4629dfdb5dbc0b9d55b78369fb4f5be2dc9032a0ac1b5b2f922fdff4b7a6d
5
5
  SHA512:
6
- metadata.gz: 1c37f77138f8fd920f199023a2cc41091b55df36dabb27901491e0dfc14e9815d033af075c4dc378fcdc8b287bf9dfbdbce281e287cf2714b84a1367c13e7fd5
7
- data.tar.gz: 83e91d51ae13a21ab278c7e9697e5e55dffed2c488cb5c2a794830c266f873e581781a05ca42feaccdcf9e01da32d0f1bc762c05fb15b97f951d19d55993bd4a
6
+ metadata.gz: 8b36bbc01a00d869a1bd91f58ccc31348332244896e6bca5b6c2c2ba96ce8c1cdb8a16254a50fe4a4c005cc6b3d79a97f789bae8b4c01d6e084aa497f9bb4081
7
+ data.tar.gz: 74dcce5991937a677eddbe3ebd51de4f792cd0c47ecff50f4d606288fe12c880ef36b9e9c280de8c7f269bf1e82a50248b0d894e38c34b2be2924ad4de3b6070
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semmle.com/semver/1.0.0/).
7
+
8
+ ## [0.3.0] - 2026-07-24
9
+
10
+ ### Changed
11
+ - Bumped required Ruby version to `>= 3.0.0`.
12
+ - Removed `rb-readline` dependency in favor of Ruby standard library `reline`/`readline`.
13
+ - Updated `slop` runtime dependency to `~> 4.10`.
14
+ - Modernized gemspec metadata and file list configuration.
15
+ - Updated RuboCop configuration for Ruby 3.0+ compatibility (`Layout/LineLength`).
16
+ - Migrated CI from Travis CI to GitHub Actions (`.github/workflows/ci.yml`).
17
+
18
+ ### Added
19
+ - Comprehensive RSpec test suite covering core client and handlers.
data/README.md CHANGED
@@ -1,21 +1,48 @@
1
- # glottis
2
-
3
- A CLI for the [valyx](https://github.com/maxdeliso/valyx) message passing server.
4
-
5
- ## Installation
6
-
7
- $ gem install glottis
8
-
9
- ## Usage
10
-
11
- $ glottis -h
12
-
13
- ## Development
14
-
15
- To install this gem onto your local machine, run `bundle exec rake install`.
16
-
17
- ## Contributing
18
-
19
- Bug reports and pull requests are welcome on GitHub at https://github.com/maxdeliso/glottis.
20
-
21
- [![Gem Version](https://badge.fury.io/rb/glottis.svg)](https://badge.fury.io/rb/glottis)
1
+ # glottis
2
+
3
+ A CLI for the [valyx](https://github.com/maxdeliso/valyx) message passing server.
4
+
5
+ [![CI](https://github.com/maxdeliso/glottis/actions/workflows/ci.yml/badge.svg)](https://github.com/maxdeliso/glottis/actions/workflows/ci.yml)
6
+ [![Gem Version](https://badge.fury.io/rb/glottis.svg)](https://badge.fury.io/rb/glottis)
7
+
8
+ ## Requirements
9
+
10
+ - Ruby >= 3.0.0
11
+
12
+ ## Installation
13
+
14
+ Install the gem by executing:
15
+
16
+ $ gem install glottis
17
+
18
+ Or add it to your application's Gemfile:
19
+
20
+ $ gem 'glottis', '~> 0.3'
21
+
22
+ ## Usage
23
+
24
+ Run the `glottis` command-line executable:
25
+
26
+ $ glottis --host localhost --port 8080
27
+
28
+ For help and available command line flags:
29
+
30
+ $ glottis --help
31
+
32
+ ## Development
33
+
34
+ To run the test suite and linter:
35
+
36
+ $ bundle exec rake
37
+
38
+ To install this gem onto your local machine:
39
+
40
+ $ bundle exec rake install
41
+
42
+ ## Contributing
43
+
44
+ Bug reports and pull requests are welcome on GitHub at https://github.com/maxdeliso/glottis.
45
+
46
+ ## License
47
+
48
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -1 +1,11 @@
1
- require 'bundler/gem_tasks'
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rubocop/rake_task'
5
+ require 'rspec/core/rake_task'
6
+
7
+ RuboCop::RakeTask.new(:rubocop)
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task default: %i[rubocop spec]
11
+
data/exe/glottis CHANGED
@@ -1,22 +1,24 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'slop'
4
- require 'glottis'
5
-
6
- opts = Slop.parse do |o|
7
- o.string '--host', 'a hostname', default: 'localhost'
8
- o.integer '--port', 'TCP port', default: 8080
9
- o.bool '-v', '--verbose', 'enable verbose mode'
10
- o.on '-V', '--version', 'print the version' do
11
- puts "glottis #{Glottis::VERSION}"
12
- exit
13
- end
14
- o.on '-h', '--help' do
15
- puts o
16
- exit
17
- end
18
- end
19
-
20
- Glottis::Client.new(opts[:host],
21
- opts[:port],
22
- verbose: opts[:verbose]).run
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'slop'
5
+ require 'glottis'
6
+
7
+ opts = Slop.parse do |o|
8
+ o.string '--host', 'a hostname', default: 'localhost'
9
+ o.integer '--port', 'TCP port', default: 8080
10
+ o.bool '-v', '--verbose', 'enable verbose mode'
11
+ o.on '-V', '--version', 'print the version' do
12
+ puts "glottis #{Glottis::VERSION}"
13
+ exit
14
+ end
15
+ o.on '-h', '--help' do
16
+ puts o
17
+ exit
18
+ end
19
+ end
20
+
21
+ Glottis::Client.new(opts[:host],
22
+ opts[:port],
23
+ verbose: opts[:verbose]).run
24
+
data/glottis.gemspec CHANGED
@@ -1,30 +1,38 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'glottis/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'glottis'
8
- spec.version = Glottis::VERSION
9
- spec.authors = ['Max DeLiso']
10
- spec.email = ['maxdeliso@gmail.com']
11
- spec.licenses = ['MIT']
12
- spec.summary = 'simple http messaging client'
13
- spec.homepage = 'https://github.com/maxdeliso/glottis'
14
-
15
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
- f.match(%r{^(test|spec|features)/})
17
- end
18
-
19
- spec.bindir = 'exe'
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
- spec.require_paths = ['lib']
22
-
23
- spec.add_runtime_dependency 'rb-readline', '~> 0.5.3'
24
- spec.add_runtime_dependency 'slop', '~> 4.2'
25
-
26
- spec.add_development_dependency 'bundler', '~> 1.10'
27
- spec.add_development_dependency 'pry', '~> 0.10.3'
28
- spec.add_development_dependency 'rake', '~> 10.0'
29
- spec.add_development_dependency 'rubocop', '~> 0.35.1'
30
- end
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'glottis/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'glottis'
9
+ spec.version = Glottis::VERSION
10
+ spec.authors = ['Max DeLiso']
11
+ spec.email = ['maxdeliso@gmail.com']
12
+ spec.licenses = ['MIT']
13
+ spec.summary = 'simple http messaging client'
14
+ spec.homepage = 'https://github.com/maxdeliso/glottis'
15
+ spec.required_ruby_version = '>= 3.0.0'
16
+
17
+ spec.metadata['homepage_uri'] = spec.homepage
18
+ spec.metadata['source_code_uri'] = 'https://github.com/maxdeliso/glottis'
19
+ spec.metadata['changelog_uri'] = 'https://github.com/maxdeliso/glottis/blob/main/CHANGELOG.md'
20
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/maxdeliso/glottis/issues'
21
+ spec.metadata['rubygems_mfa_required'] = 'true'
22
+
23
+ spec.files = Dir.glob('{exe,lib}/**/*') + %w[Rakefile README.md CHANGELOG.md glottis.gemspec]
24
+ spec.files = spec.files.select { |f| File.file?(f) }
25
+
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_runtime_dependency 'slop', '~> 4.10'
31
+
32
+ spec.add_development_dependency 'bundler', '~> 2.0'
33
+ spec.add_development_dependency 'rake', '~> 13.0'
34
+ spec.add_development_dependency 'rspec', '~> 3.0'
35
+ spec.add_development_dependency 'rubocop', '~> 1.0'
36
+ end
37
+
38
+
@@ -1,46 +1,56 @@
1
- require 'thread'
2
- require 'logger'
3
- require 'glottis/handlers/console_input_handler'
4
- require 'glottis/handlers/console_output_handler'
5
- require 'glottis/handlers/remote_input_handler'
6
- require 'glottis/handlers/remote_output_handler'
7
-
8
- module Glottis
9
- # Client class, used to interact with the host specified.
10
- class Client
11
- def self.logger
12
- @logger ||= Logger.new(STDOUT)
13
- end
14
-
15
- def initialize(host, port, opts = {})
16
- @host = host
17
- @port = port
18
- @outgoing = Queue.new
19
- @incoming = Queue.new
20
- Client.logger.level = opts[:verbose] ? Logger::DEBUG : Logger::WARN
21
- end
22
-
23
- def run
24
- Client.logger.info('starting...')
25
- Thread.abort_on_exception = true
26
-
27
- @handlers = [
28
- Handlers::ConsoleInputHandler.new(@outgoing),
29
- Handlers::ConsoleOutputHandler.new(@incoming),
30
- Handlers::RemoteInputHandler.new(@incoming, @host, @port),
31
- Handlers::RemoteOutputHandler.new(@outgoing, @host, @port)
32
- ]
33
-
34
- @handlers.each(&:join)
35
- rescue Glottis::Exceptions::UserExitedException
36
- Client.logger.info('shutting down...')
37
- rescue Errno::ECONNREFUSED => conn_ref
38
- Client.logger.error("failed to connect: #{conn_ref}")
39
- rescue StandardError => ex
40
- Client.logger.warn("shutting down: #{ex}")
41
- ensure
42
- @handlers.each(&:cleanup)
43
- exit(0)
44
- end # run
45
- end # Client
46
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'glottis/handlers/console_input_handler'
5
+ require 'glottis/handlers/console_output_handler'
6
+ require 'glottis/handlers/remote_input_handler'
7
+ require 'glottis/handlers/remote_output_handler'
8
+
9
+ module Glottis
10
+ # Client class used to interact with the specified host and port.
11
+ class Client
12
+ def self.logger
13
+ @logger ||= Logger.new($stdout)
14
+ end
15
+
16
+ def initialize(host, port, opts = {})
17
+ @host = host
18
+ @port = port
19
+ @outgoing = Queue.new
20
+ @incoming = Queue.new
21
+ @handlers = []
22
+ Client.logger.level = opts[:verbose] ? Logger::DEBUG : Logger::WARN
23
+ end
24
+
25
+ def run
26
+ Client.logger.info('starting...')
27
+
28
+ @handlers = [
29
+ Handlers::ConsoleInputHandler.new(@outgoing),
30
+ Handlers::ConsoleOutputHandler.new(@incoming),
31
+ Handlers::RemoteInputHandler.new(@incoming, @host, @port),
32
+ Handlers::RemoteOutputHandler.new(@outgoing, @host, @port)
33
+ ]
34
+
35
+ @handlers.each(&:start)
36
+ @handlers.each(&:join)
37
+ rescue Glottis::Exceptions::UserExitedException
38
+ Client.logger.info('User exited.')
39
+ rescue Interrupt
40
+ Client.logger.info('Received interrupt signal. Exiting...')
41
+ rescue Errno::ECONNREFUSED => conn_ref
42
+ Client.logger.error("Failed to connect to host: #{conn_ref.message}")
43
+ rescue StandardError => ex
44
+ Client.logger.warn("Shutting down due to error: #{ex.message}")
45
+ ensure
46
+ stop_handlers
47
+ end
48
+
49
+ private
50
+
51
+ def stop_handlers
52
+ @handlers.each(&:cleanup)
53
+ end
54
+ end
55
+ end
56
+
@@ -1,6 +1,9 @@
1
- module Glottis
2
- module Exceptions
3
- class GlottisException < StandardError
4
- end
5
- end
6
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Glottis
4
+ module Exceptions
5
+ class GlottisException < StandardError
6
+ end
7
+ end
8
+ end
9
+
@@ -1,8 +1,11 @@
1
- require 'glottis/exceptions/glottis_exception'
2
-
3
- module Glottis
4
- module Exceptions
5
- class UserExitedException < GlottisException
6
- end
7
- end
8
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'glottis/exceptions/glottis_exception'
4
+
5
+ module Glottis
6
+ module Exceptions
7
+ class UserExitedException < GlottisException
8
+ end
9
+ end
10
+ end
11
+
@@ -1,32 +1,50 @@
1
- require 'glottis/exceptions/user_exited_exception'
2
- require 'readline'
3
-
4
- module Glottis
5
- module Handlers
6
- # This class holds a reference to the outgoing message queue,
7
- # and posts messages to it from the console as they are entered.
8
- class ConsoleInputHandler < Thread
9
- USER_PROMPT = '> '.freeze
10
-
11
- def initialize(outgoing)
12
- @outgoing = outgoing
13
- super do
14
- loop do
15
- user_input = Readline.readline(USER_PROMPT)
16
- Readline::HISTORY.push(user_input)
17
-
18
- # if user_input.nil?
19
- # raise Glottis::Exceptions::UserExitedException.new
20
- # end
21
-
22
- @outgoing.push(user_input)
23
- end
24
- end
25
- end
26
-
27
- def cleanup
28
- # do nothing
29
- end
30
- end # ConsoleInputHandler
31
- end # Handlers
32
- end # Glottis
1
+ # frozen_string_literal: true
2
+
3
+ require 'glottis/exceptions/user_exited_exception'
4
+ require 'readline'
5
+
6
+ module Glottis
7
+ module Handlers
8
+ # Holds a reference to the outgoing message queue and posts console input.
9
+ class ConsoleInputHandler
10
+ USER_PROMPT = '> '
11
+
12
+ def initialize(outgoing)
13
+ @outgoing = outgoing
14
+ @running = false
15
+ @thread = nil
16
+ end
17
+
18
+ def start
19
+ @running = true
20
+ @thread = Thread.new { run_loop }
21
+ self
22
+ end
23
+
24
+ def join
25
+ @thread&.join
26
+ end
27
+
28
+ def cleanup
29
+ @running = false
30
+ @thread&.kill if @thread&.alive?
31
+ end
32
+
33
+ private
34
+
35
+ def run_loop
36
+ while @running
37
+ user_input = Readline.readline(USER_PROMPT, true)
38
+
39
+ raise Glottis::Exceptions::UserExitedException, 'User exited readline stream' if user_input.nil?
40
+
41
+ @outgoing.push(user_input)
42
+ end
43
+ rescue Glottis::Exceptions::UserExitedException => ex
44
+ Client.logger.info("Console input ended: #{ex.message}")
45
+ raise ex
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -1,21 +1,41 @@
1
- module Glottis
2
- module Handlers
3
- # This class holds a reference to the incoming message queue,
4
- # and displays messages in the console as they are received.
5
- class ConsoleOutputHandler < Thread
6
- def initialize(incoming)
7
- @incoming = incoming
8
- super do
9
- loop do
10
- new_msg = @incoming.pop
11
- puts "#{new_msg['from']}, #{new_msg['to']}, #{new_msg['msg']}"
12
- end
13
- end
14
- end
15
-
16
- def cleanup
17
- puts '|'
18
- end
19
- end # ConsoleOutputHandler
20
- end # Handlers
21
- end # Glottis
1
+ # frozen_string_literal: true
2
+
3
+ module Glottis
4
+ module Handlers
5
+ # Holds a reference to incoming message queue and prints messages to stdout.
6
+ class ConsoleOutputHandler
7
+ def initialize(incoming)
8
+ @incoming = incoming
9
+ @running = false
10
+ @thread = nil
11
+ end
12
+
13
+ def start
14
+ @running = true
15
+ @thread = Thread.new { run_loop }
16
+ self
17
+ end
18
+
19
+ def join
20
+ @thread&.join
21
+ end
22
+
23
+ def cleanup
24
+ @running = false
25
+ @thread&.kill if @thread&.alive?
26
+ end
27
+
28
+ private
29
+
30
+ def run_loop
31
+ while @running
32
+ new_msg = @incoming.pop
33
+ break if new_msg.nil? || new_msg == :shutdown
34
+
35
+ puts "#{new_msg['from']}, #{new_msg['to']}, #{new_msg['msg']}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -1,53 +1,87 @@
1
- require 'logger'
2
- require 'uri'
3
- require 'net/http'
4
- require 'json'
5
-
6
- module Glottis
7
- module Handlers
8
- # This class manages a TCP connection with a valyx server.
9
- class RemoteInputHandler < Thread
10
- PROTOCOL = 'http'.freeze
11
- STREAM_DELIMITER = "\0".freeze
12
- READ_TIMEOUT = 3600 # this needs to be high
13
- REMOTE_PATHS = {
14
- get_message_stream: '/api/messages/stream'
15
- }.freeze
16
-
17
- def initialize(incoming, host, port)
18
- @incoming = incoming
19
- @host = host
20
- @port = port
21
-
22
- setup_http
23
-
24
- super do
25
- @http.start
26
- read_loop
27
- end
28
- end
29
-
30
- def cleanup
31
- @http.finish if @http.started?
32
- end
33
-
34
- private
35
-
36
- def setup_http
37
- @http = Net::HTTP.new(@host, @port)
38
- @http.open_timeout = 1
39
- @http.read_timeout = READ_TIMEOUT
40
- end
41
-
42
- def read_loop
43
- Client.logger.info('reading stream...')
44
- @http.get(REMOTE_PATHS.fetch(:get_message_stream)) do |chunk|
45
- Client.logger.debug("got chunk of length: #{chunk.length}")
46
- chunk.split(STREAM_DELIMITER).each do |msg_data|
47
- @incoming.push(JSON.parse(msg_data))
48
- end # each
49
- end # get
50
- end # read_loop
51
- end # RemoteInputHandler
52
- end # Handlers
53
- end # Glottis
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'uri'
5
+ require 'net/http'
6
+ require 'json'
7
+
8
+ module Glottis
9
+ module Handlers
10
+ # Manages stream input from the remote valyx server.
11
+ class RemoteInputHandler
12
+ PROTOCOL = 'http'
13
+ STREAM_DELIMITER = "\0"
14
+ READ_TIMEOUT = 3600
15
+ REMOTE_PATHS = {
16
+ get_message_stream: '/api/messages/stream'
17
+ }.freeze
18
+
19
+ def initialize(incoming, host, port)
20
+ @incoming = incoming
21
+ @host = host
22
+ @port = port
23
+ @running = false
24
+ @thread = nil
25
+ @buffer = String.new
26
+ setup_http
27
+ end
28
+
29
+ def start
30
+ @running = true
31
+ @thread = Thread.new do
32
+ @http.start
33
+ read_loop
34
+ end
35
+ self
36
+ end
37
+
38
+ def join
39
+ @thread&.join
40
+ end
41
+
42
+ def cleanup
43
+ @running = false
44
+ @http.finish if @http.started?
45
+ @thread&.kill if @thread&.alive?
46
+ end
47
+
48
+ private
49
+
50
+ def setup_http
51
+ @http = Net::HTTP.new(@host, @port)
52
+ @http.open_timeout = 1
53
+ @http.read_timeout = READ_TIMEOUT
54
+ end
55
+
56
+ def read_loop
57
+ Client.logger.info('reading stream...')
58
+ @http.get(REMOTE_PATHS.fetch(:get_message_stream)) do |chunk|
59
+ break unless @running
60
+
61
+ Client.logger.debug("got chunk of length: #{chunk.length}")
62
+ process_chunk(chunk)
63
+ end
64
+ rescue StandardError => ex
65
+ Client.logger.error("Error reading message stream: #{ex.message}") if @running
66
+ end
67
+
68
+ def process_chunk(chunk)
69
+ @buffer << chunk
70
+
71
+ while (delimiter_index = @buffer.index(STREAM_DELIMITER))
72
+ msg_raw = @buffer.slice!(0..delimiter_index)
73
+ msg_data = msg_raw.chomp(STREAM_DELIMITER)
74
+ next if msg_data.empty?
75
+
76
+ begin
77
+ parsed_msg = JSON.parse(msg_data)
78
+ @incoming.push(parsed_msg)
79
+ rescue JSON::ParserError => err
80
+ Client.logger.warn("Failed to parse JSON payload: #{err.message}")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
@@ -1,107 +1,100 @@
1
- require 'logger'
2
- require 'uri'
3
- require 'net/http'
4
- require 'json'
5
-
6
- module Glottis
7
- module Handlers
8
- # This class manages a TCP connection with a valyx server.
9
- class RemoteOutputHandler < Thread
10
- POLL_INTERVAL = 0.1
11
- PROTOCOL = 'http'.freeze
12
-
13
- REMOTE_PATHS = {
14
- get_session: '/api/session',
15
- post_message: '/api/message'
16
- }.freeze
17
-
18
- def initialize(outgoing, host, port)
19
- @outgoing = outgoing
20
- @host = host
21
- @port = port
22
-
23
- setup_http
24
-
25
- super do
26
- @http.start
27
- request_session
28
-
29
- loop do
30
- send_queued
31
- end
32
- end
33
- end
34
-
35
- def cleanup
36
- @http.finish if @http.started?
37
- end
38
-
39
- private
40
-
41
- def setup_http
42
- @http = Net::HTTP.new(@host, @port)
43
- # TODO: make these configurable somewhere
44
- @http.open_timeout = 1
45
- @http.read_timeout = 1
46
- end
47
-
48
- def request_session
49
- Client.logger.info('making initial request for session...')
50
-
51
- session_req = @http.get(REMOTE_PATHS.fetch(:get_session))
52
-
53
- if Integer(session_req.code) != 201
54
- fail("failed to retrieve session: #{response}")
55
- end
56
-
57
- response = JSON.parse(session_req.body)
58
-
59
- @sid = response['sid']
60
- Client.logger.info("current session id: #{@sid}")
61
- end
62
-
63
- def build_messages_from
64
- [
65
- REMOTE_PATHS.fetch(:get_messages),
66
- '?',
67
- URI.encode_www_form([['from', @received_count.to_s]])
68
- ].join
69
- end
70
-
71
- # given the result of a .get or .post, converts the response code,
72
- # and executes the (side effecting) block if the response was not
73
- # successful. calls the block with a message describing the error.
74
- def if_not_ok(http_req, ok_code = 200, &b)
75
- b.call(http_req.body) if Integer(http_req.code) != ok_code
76
- end
77
-
78
- def request_messages
79
- Client.logger.debug("polling for messages with with path: #{build_messages_from}")
80
- messages_req = @http.get(build_messages_from)
81
-
82
- if_not_ok(messages_req) do |err|
83
- Client.logger.error("failed to retrieve messages: #{err}")
84
- return
85
- end
86
-
87
- process_new_messages(messages_req.body)
88
- end
89
-
90
- def send_queued
91
- Client.logger.info("processing message with #{@outgoing.size} messages outgoing")
92
-
93
- message_data = {
94
- from: @sid,
95
- to: '*',
96
- msg: @outgoing.pop
97
- }.to_json
98
-
99
- post_req = @http.post(REMOTE_PATHS.fetch(:post_message), message_data)
100
-
101
- if_not_ok(post_req) do |err|
102
- Client.logger.warn("failed to post message: #{message_data} #{err}")
103
- end
104
- end # send_queued
105
- end # RemoteOutputHandler
106
- end # Handlers
107
- end # Glottis
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'uri'
5
+ require 'net/http'
6
+ require 'json'
7
+
8
+ module Glottis
9
+ module Handlers
10
+ # Manages posting outgoing messages to the remote valyx server.
11
+ class RemoteOutputHandler
12
+ POLL_INTERVAL = 0.1
13
+ PROTOCOL = 'http'
14
+
15
+ REMOTE_PATHS = {
16
+ get_session: '/api/session',
17
+ post_message: '/api/message'
18
+ }.freeze
19
+
20
+ def initialize(outgoing, host, port)
21
+ @outgoing = outgoing
22
+ @host = host
23
+ @port = port
24
+ @running = false
25
+ @thread = nil
26
+ @sid = nil
27
+ setup_http
28
+ end
29
+
30
+ def start
31
+ @running = true
32
+ @thread = Thread.new do
33
+ @http.start
34
+ request_session
35
+
36
+ while @running
37
+ send_queued
38
+ end
39
+ end
40
+ self
41
+ end
42
+
43
+ def join
44
+ @thread&.join
45
+ end
46
+
47
+ def cleanup
48
+ @running = false
49
+ @http.finish if @http.started?
50
+ @thread&.kill if @thread&.alive?
51
+ end
52
+
53
+ private
54
+
55
+ def setup_http
56
+ @http = Net::HTTP.new(@host, @port)
57
+ @http.open_timeout = 1
58
+ @http.read_timeout = 1
59
+ end
60
+
61
+ def request_session
62
+ Client.logger.info('making initial request for session...')
63
+
64
+ session_req = @http.get(REMOTE_PATHS.fetch(:get_session))
65
+ response = JSON.parse(session_req.body)
66
+
67
+ if Integer(session_req.code) != 201
68
+ raise "failed to retrieve session: #{response}"
69
+ end
70
+
71
+ @sid = response['sid']
72
+ Client.logger.info("current session id: #{@sid}")
73
+ end
74
+
75
+ def if_not_ok(http_req, ok_code = 200)
76
+ yield http_req.body if Integer(http_req.code) != ok_code
77
+ end
78
+
79
+ def send_queued
80
+ raw_msg = @outgoing.pop
81
+ return if raw_msg.nil? || raw_msg == :shutdown
82
+
83
+ Client.logger.info("processing message with #{@outgoing.size} messages outgoing")
84
+
85
+ message_data = {
86
+ from: @sid,
87
+ to: '*',
88
+ msg: raw_msg
89
+ }.to_json
90
+
91
+ post_req = @http.post(REMOTE_PATHS.fetch(:post_message), message_data)
92
+
93
+ if_not_ok(post_req) do |err|
94
+ Client.logger.warn("failed to post message: #{message_data} #{err}")
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
@@ -1,4 +1,5 @@
1
- # Autogenerated version module.
2
- module Glottis
3
- VERSION = '0.1.2'
4
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Glottis
4
+ VERSION = '0.3.0'
5
+ end
data/lib/glottis.rb CHANGED
@@ -1,2 +1,5 @@
1
- require 'glottis/version'
2
- require 'glottis/client'
1
+ # frozen_string_literal: true
2
+
3
+ require 'glottis/version'
4
+ require 'glottis/client'
5
+
metadata CHANGED
@@ -1,100 +1,84 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glottis
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Max DeLiso
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2015-12-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rb-readline
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 0.5.3
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 0.5.3
27
12
  - !ruby/object:Gem::Dependency
28
13
  name: slop
29
14
  requirement: !ruby/object:Gem::Requirement
30
15
  requirements:
31
16
  - - "~>"
32
17
  - !ruby/object:Gem::Version
33
- version: '4.2'
18
+ version: '4.10'
34
19
  type: :runtime
35
20
  prerelease: false
36
21
  version_requirements: !ruby/object:Gem::Requirement
37
22
  requirements:
38
23
  - - "~>"
39
24
  - !ruby/object:Gem::Version
40
- version: '4.2'
25
+ version: '4.10'
41
26
  - !ruby/object:Gem::Dependency
42
27
  name: bundler
43
28
  requirement: !ruby/object:Gem::Requirement
44
29
  requirements:
45
30
  - - "~>"
46
31
  - !ruby/object:Gem::Version
47
- version: '1.10'
32
+ version: '2.0'
48
33
  type: :development
49
34
  prerelease: false
50
35
  version_requirements: !ruby/object:Gem::Requirement
51
36
  requirements:
52
37
  - - "~>"
53
38
  - !ruby/object:Gem::Version
54
- version: '1.10'
39
+ version: '2.0'
55
40
  - !ruby/object:Gem::Dependency
56
- name: pry
41
+ name: rake
57
42
  requirement: !ruby/object:Gem::Requirement
58
43
  requirements:
59
44
  - - "~>"
60
45
  - !ruby/object:Gem::Version
61
- version: 0.10.3
46
+ version: '13.0'
62
47
  type: :development
63
48
  prerelease: false
64
49
  version_requirements: !ruby/object:Gem::Requirement
65
50
  requirements:
66
51
  - - "~>"
67
52
  - !ruby/object:Gem::Version
68
- version: 0.10.3
53
+ version: '13.0'
69
54
  - !ruby/object:Gem::Dependency
70
- name: rake
55
+ name: rspec
71
56
  requirement: !ruby/object:Gem::Requirement
72
57
  requirements:
73
58
  - - "~>"
74
59
  - !ruby/object:Gem::Version
75
- version: '10.0'
60
+ version: '3.0'
76
61
  type: :development
77
62
  prerelease: false
78
63
  version_requirements: !ruby/object:Gem::Requirement
79
64
  requirements:
80
65
  - - "~>"
81
66
  - !ruby/object:Gem::Version
82
- version: '10.0'
67
+ version: '3.0'
83
68
  - !ruby/object:Gem::Dependency
84
69
  name: rubocop
85
70
  requirement: !ruby/object:Gem::Requirement
86
71
  requirements:
87
72
  - - "~>"
88
73
  - !ruby/object:Gem::Version
89
- version: 0.35.1
74
+ version: '1.0'
90
75
  type: :development
91
76
  prerelease: false
92
77
  version_requirements: !ruby/object:Gem::Requirement
93
78
  requirements:
94
79
  - - "~>"
95
80
  - !ruby/object:Gem::Version
96
- version: 0.35.1
97
- description:
81
+ version: '1.0'
98
82
  email:
99
83
  - maxdeliso@gmail.com
100
84
  executables:
@@ -102,14 +86,9 @@ executables:
102
86
  extensions: []
103
87
  extra_rdoc_files: []
104
88
  files:
105
- - ".gitignore"
106
- - ".rubocop.yml"
107
- - ".travis.yml"
108
- - Gemfile
89
+ - CHANGELOG.md
109
90
  - README.md
110
91
  - Rakefile
111
- - bin/console
112
- - bin/setup
113
92
  - exe/glottis
114
93
  - glottis.gemspec
115
94
  - lib/glottis.rb
@@ -124,8 +103,12 @@ files:
124
103
  homepage: https://github.com/maxdeliso/glottis
125
104
  licenses:
126
105
  - MIT
127
- metadata: {}
128
- post_install_message:
106
+ metadata:
107
+ homepage_uri: https://github.com/maxdeliso/glottis
108
+ source_code_uri: https://github.com/maxdeliso/glottis
109
+ changelog_uri: https://github.com/maxdeliso/glottis/blob/main/CHANGELOG.md
110
+ bug_tracker_uri: https://github.com/maxdeliso/glottis/issues
111
+ rubygems_mfa_required: 'true'
129
112
  rdoc_options: []
130
113
  require_paths:
131
114
  - lib
@@ -133,16 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
133
116
  requirements:
134
117
  - - ">="
135
118
  - !ruby/object:Gem::Version
136
- version: '0'
119
+ version: 3.0.0
137
120
  required_rubygems_version: !ruby/object:Gem::Requirement
138
121
  requirements:
139
122
  - - ">="
140
123
  - !ruby/object:Gem::Version
141
124
  version: '0'
142
125
  requirements: []
143
- rubyforge_project:
144
- rubygems_version: 2.4.5.1
145
- signing_key:
126
+ rubygems_version: 3.6.7
146
127
  specification_version: 4
147
128
  summary: simple http messaging client
148
129
  test_files: []
data/.gitignore DELETED
@@ -1,11 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- *.log
11
- *.gem
data/.rubocop.yml DELETED
@@ -1,5 +0,0 @@
1
- Metrics/LineLength:
2
- Max: 99
3
-
4
- Metrics/MethodLength:
5
- Max: 20
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.1.5
4
- before_install: gem install bundler -v 1.10.6
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in glottis.gemspec
4
- gemspec
data/bin/console DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'bundler/setup'
4
- require 'glottis'
5
- require 'irb'
6
-
7
- IRB.start
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here