glottis 0.1.1

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13566b8129e3820451ffdbeceac479488ee0c7c5
4
+ data.tar.gz: c35a76b88803bcd3441f13dd67cd513e62fbd1ff
5
+ SHA512:
6
+ metadata.gz: 6c03142ff57480a928bb64ee71a0065dc9fbfcde3fd595111f6978c8633ad54fdccc2b38e146f2fdb6c65f59fc04bb9f077a049d91bbb99d1508ca05afb884f3
7
+ data.tar.gz: a49d9b2a97e3fd40764964c5ec6dba856235cdd499e22553f8909b7727a868a1c3bacd20c398dfa41d49d48f8a5e4702b90cc7877248b91e9f421444841a9bcf
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.log
11
+ *.gem
@@ -0,0 +1,5 @@
1
+ Metrics/LineLength:
2
+ Max: 99
3
+
4
+ Metrics/MethodLength:
5
+ Max: 20
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in glottis.gemspec
4
+ gemspec
@@ -0,0 +1,19 @@
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.
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'glottis'
5
+ require 'irb'
6
+
7
+ IRB.start
@@ -0,0 +1,7 @@
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
@@ -0,0 +1,22 @@
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
@@ -0,0 +1,30 @@
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
@@ -0,0 +1,2 @@
1
+ require 'glottis/version'
2
+ require 'glottis/client'
@@ -0,0 +1,44 @@
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_handler'
6
+
7
+ module Glottis
8
+ # Client class, used to interact with the host specified.
9
+ class Client
10
+ def self.logger
11
+ @logger ||= Logger.new(STDOUT)
12
+ end
13
+
14
+ def initialize(host, port, opts = {})
15
+ @host = host
16
+ @port = port
17
+ @outgoing = Queue.new
18
+ @incoming = Queue.new
19
+ Client.logger.level = opts[:verbose] ? Logger::DEBUG : Logger::WARN
20
+ end
21
+
22
+ def run
23
+ Client.logger.info('starting...')
24
+ Thread.abort_on_exception = true
25
+
26
+ @handlers = [
27
+ Handlers::ConsoleInputHandler.new(@outgoing),
28
+ Handlers::ConsoleOutputHandler.new(@incoming),
29
+ Handlers::RemoteHandler.new(@outgoing, @incoming, @host, @port)
30
+ ]
31
+
32
+ @handlers.each(&:join)
33
+ rescue Glottis::Exceptions::UserExitedException
34
+ Client.logger.info('shutting down...')
35
+ rescue Errno::ECONNREFUSED => conn_ref
36
+ Client.logger.error("failed to connect: #{conn_ref}")
37
+ rescue StandardError => ex
38
+ Client.logger.warn("shutting down: #{ex}")
39
+ ensure
40
+ @handlers.each(&:cleanup)
41
+ exit(0)
42
+ end # run
43
+ end # Client
44
+ end
@@ -0,0 +1,6 @@
1
+ module Glottis
2
+ module Exceptions
3
+ class GlottisException < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'glottis/exceptions/glottis_exception'
2
+
3
+ module Glottis
4
+ module Exceptions
5
+ class UserExitedException < GlottisException
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,21 @@
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
@@ -0,0 +1,123 @@
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 RemoteHandler < Thread
10
+ POLL_INTERVAL = 0.1
11
+ PROTOCOL = 'http'.freeze
12
+
13
+ REMOTE_PATHS = {
14
+ get_session: '/api/session',
15
+ get_messages: '/api/messages',
16
+ post_message: '/api/message'
17
+ }.freeze
18
+
19
+ def initialize(outgoing, incoming, host, port)
20
+ @outgoing = outgoing
21
+ @incoming = incoming
22
+ @host = host
23
+ @port = port
24
+ @received_count = 0
25
+
26
+ setup_http
27
+
28
+ super do
29
+ @http.start
30
+ request_session
31
+
32
+ loop do
33
+ request_messages
34
+ send_queued
35
+
36
+ sleep POLL_INTERVAL
37
+ end
38
+ end
39
+ end
40
+
41
+ def cleanup
42
+ @http.finish if @http.started?
43
+ end
44
+
45
+ private
46
+
47
+ def setup_http
48
+ @http = Net::HTTP.new(@host, @port)
49
+ # TODO: make these configurable somewhere
50
+ @http.open_timeout = 1
51
+ @http.read_timeout = 1
52
+ @http.continue_timeout = 1
53
+ end
54
+
55
+ def request_session
56
+ Client.logger.info('making initial request for session...')
57
+
58
+ session_req = @http.get(REMOTE_PATHS.fetch(:get_session))
59
+
60
+ if Integer(session_req.code) != 201
61
+ fail("failed to retrieve session: #{response}")
62
+ end
63
+
64
+ response = JSON.parse(session_req.body)
65
+
66
+ @sid = response['sid']
67
+ Client.logger.info("current session id: #{@sid}")
68
+ end
69
+
70
+ def build_messages_from
71
+ [
72
+ REMOTE_PATHS.fetch(:get_messages),
73
+ '?',
74
+ URI.encode_www_form([['from', @received_count.to_s]])
75
+ ].join
76
+ end
77
+
78
+ # given the result of a .get or .post, converts the response code,
79
+ # and executes the (side effecting) block if the response was not
80
+ # successful. calls the block with a message describing the error.
81
+ def if_not_ok(http_req, ok_code = 200, &b)
82
+ b.call(http_req.body) if Integer(http_req.code) != ok_code
83
+ end
84
+
85
+ def request_messages
86
+ Client.logger.debug("polling for messages with with path: #{build_messages_from}")
87
+ messages_req = @http.get(build_messages_from)
88
+
89
+ if_not_ok(messages_req) do |err|
90
+ Client.logger.error("failed to retrieve messages: #{err}")
91
+ return
92
+ end
93
+
94
+ process_new_messages(messages_req.body)
95
+ end
96
+
97
+ def process_new_messages(messages_data)
98
+ new_messages = JSON.parse(messages_data)
99
+ Client.logger.debug("received #{new_messages.size} new messages")
100
+ @received_count += new_messages.size
101
+ new_messages.each { |msg| @incoming.push(msg) }
102
+ end
103
+
104
+ def send_queued
105
+ while @outgoing.size > 0
106
+ Client.logger.info("processing message with #{@outgoing.size} messages outgoing")
107
+
108
+ message_data = {
109
+ from: @sid,
110
+ to: '*',
111
+ msg: @outgoing.pop
112
+ }.to_json
113
+
114
+ post_req = @http.post(REMOTE_PATHS.fetch(:post_message), message_data)
115
+
116
+ if_not_ok(post_req) do |err|
117
+ Client.logger.warn("failed to post message: #{err}")
118
+ end
119
+ end
120
+ end # send_queued
121
+ end # RemoteHandler
122
+ end # Handlers
123
+ end # Glottis
@@ -0,0 +1,4 @@
1
+ # Autogenerated version module.
2
+ module Glottis
3
+ VERSION = '0.1.1'
4
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glottis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Max DeLiso
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-13 00:00:00.000000000 Z
12
+ 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
+ - !ruby/object:Gem::Dependency
28
+ name: slop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.35.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.35.1
97
+ description:
98
+ email:
99
+ - maxdeliso@gmail.com
100
+ executables:
101
+ - glottis
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rubocop.yml"
107
+ - ".travis.yml"
108
+ - Gemfile
109
+ - README.md
110
+ - Rakefile
111
+ - bin/console
112
+ - bin/setup
113
+ - exe/glottis
114
+ - glottis.gemspec
115
+ - lib/glottis.rb
116
+ - lib/glottis/client.rb
117
+ - lib/glottis/exceptions/glottis_exception.rb
118
+ - lib/glottis/exceptions/user_exited_exception.rb
119
+ - lib/glottis/handlers/console_input_handler.rb
120
+ - lib/glottis/handlers/console_output_handler.rb
121
+ - lib/glottis/handlers/remote_handler.rb
122
+ - lib/glottis/version.rb
123
+ homepage: https://github.com/maxdeliso/glottis
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.4.5.1
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: simple http messaging client
147
+ test_files: []