glottis 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []