tamashii 0.1.0 → 0.2.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: dbdb5d53cc23a55b0ddb271ceddb7f74b2a0f855
4
- data.tar.gz: e3be1cc5132de83256d1f6d48640c7a5ce1d1744
3
+ metadata.gz: 971b0a636366074f7e051af483166d7056753fcf
4
+ data.tar.gz: c75d1df8a9e5c487b1c989170e7aaf09594e315b
5
5
  SHA512:
6
- metadata.gz: 3393a35f1faf7338f2d4229ecc06f6f2c71b5e212fa20fae3b8ff809908a951e60227078686f8cb9dcb43b596e452bf5a8f1eabf2f26540a77aeb172b31928da
7
- data.tar.gz: 149669983dd790e0a0d1c18b0aebdcaa3818c47053e3eee6dd757a8de708748b3c8ce2faa05e20381d0396ba4c53590e3e6a84894ae9292ac1428bda018e6bc3
6
+ metadata.gz: '08bd487845177364886ecf70e5cb745446a1801cc6264b3dc4c477cc717ddfa172e00ba4b4edc97a0aba8054d2f151599a2f58e79c6965bda2a7e0f2ba28c7d3'
7
+ data.tar.gz: 3098ada65a89ee03fcce8ae3c1a99572e20fc70fcd51f282ddb233df58ec7f4ea658c935cf612e56f39e340bc3e73692fb522428d8f967f29f938beed870f9ad
@@ -0,0 +1,31 @@
1
+ # base image
2
+ image: "ruby:2.4.1"
3
+
4
+ # build stages
5
+ stages:
6
+ - test
7
+
8
+ # cache gems in between builds
9
+ cache:
10
+ paths:
11
+ - vendor/ruby
12
+
13
+ # this is a basic example for a gem or script which doesn't use
14
+ # services such as redis or postgres
15
+ before_script:
16
+ - gem install bundler -v 1.13.7 --no-ri --no-rdoc # bundler is not installed with the image
17
+ - bundle install -j $(nproc) --path vendor # install dependencies into ./vendor/ruby
18
+
19
+ # jobs
20
+ rspec:
21
+ stage: test
22
+ script:
23
+ - bundle exec rspec -p
24
+
25
+ rubocop:
26
+ stage: test
27
+ services: []
28
+ before_script:
29
+ - gem install rubocop
30
+ script:
31
+ - rubocop
@@ -0,0 +1,11 @@
1
+ inherit_from: .rubocop_todo.yml
2
+ AllCops:
3
+ DisplayCopNames: true
4
+ TargetRubyVersion: 2.4
5
+ Exclude:
6
+ - vendor/ruby/**/*
7
+ - tamashii.gemspec
8
+ - bin/*
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - spec/**/*_spec.rb
@@ -0,0 +1,91 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2017-05-03 14:59:53 +0800 using RuboCop version 0.48.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 3
10
+ # Configuration parameters: CountComments, ExcludedMethods.
11
+ Metrics/BlockLength:
12
+ Max: 36
13
+
14
+ # Offense count: 8
15
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
16
+ # URISchemes: http, https
17
+ Metrics/LineLength:
18
+ Max: 99
19
+
20
+ # Offense count: 1
21
+ # Cop supports --auto-correct.
22
+ # Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
23
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining
24
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
25
+ # FunctionalMethods: let, let!, subject, watch
26
+ # IgnoredMethods: lambda, proc, it
27
+ Style/BlockDelimiters:
28
+ Exclude:
29
+ - 'spec/tamashii/server_spec.rb'
30
+
31
+ # Offense count: 1
32
+ # Cop supports --auto-correct.
33
+ # Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
34
+ Style/ExtraSpacing:
35
+ Exclude:
36
+ - 'tamashii.gemspec'
37
+
38
+ # Offense count: 1
39
+ # Cop supports --auto-correct.
40
+ # Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
41
+ # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
42
+ Style/HashSyntax:
43
+ Exclude:
44
+ - 'Rakefile'
45
+
46
+ # Offense count: 1
47
+ # Cop supports --auto-correct.
48
+ Style/MutableConstant:
49
+ Exclude:
50
+ - 'lib/tamashii/version.rb'
51
+
52
+ # Offense count: 3
53
+ # Cop supports --auto-correct.
54
+ # Configuration parameters: PreferredDelimiters.
55
+ Style/PercentLiteralDelimiters:
56
+ Exclude:
57
+ - 'Guardfile'
58
+ - 'tamashii.gemspec'
59
+
60
+ # Offense count: 1
61
+ # Cop supports --auto-correct.
62
+ # Configuration parameters: AllowForAlignment.
63
+ Style/SpaceAroundOperators:
64
+ Exclude:
65
+ - 'tamashii.gemspec'
66
+
67
+ # Offense count: 18
68
+ # Cop supports --auto-correct.
69
+ # Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
70
+ # SupportedStyles: single_quotes, double_quotes
71
+ Style/StringLiterals:
72
+ Exclude:
73
+ - 'Guardfile'
74
+ - 'Rakefile'
75
+ - 'bin/console'
76
+ - 'lib/tamashii/version.rb'
77
+ - 'tamashii.gemspec'
78
+
79
+ # Offense count: 1
80
+ # Cop supports --auto-correct.
81
+ # Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
82
+ # SupportedStylesForMultiline: comma, consistent_comma, no_comma
83
+ Style/TrailingCommaInLiteral:
84
+ Exclude:
85
+ - 'spec/tamashii/server_spec.rb'
86
+
87
+ # Offense count: 2
88
+ # Cop supports --auto-correct.
89
+ Style/UnneededPercentQ:
90
+ Exclude:
91
+ - 'tamashii.gemspec'
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in tamashii.gemspec
data/Guardfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # A sample Guardfile
2
4
  # More info at https://github.com/guard/guard#readme
3
5
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
@@ -1,5 +1,8 @@
1
- require "tamashii/version"
1
+ # frozen_string_literal: true
2
2
 
3
+ require 'tamashii/version'
4
+
5
+ # :nodoc:
3
6
  module Tamashii
4
- # Your code goes here...
7
+ autoload :Server, 'tamashii/server'
5
8
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'logger'
5
+ require 'logger/colors'
6
+ require 'websocket/driver'
7
+ require 'rack'
8
+ require 'nio'
9
+ require 'thread'
10
+ require 'redis'
11
+
12
+ module Tamashii
13
+ # :nodoc:
14
+ module Server
15
+ autoload :Rack, 'tamashii/server/rack'
16
+ autoload :Base, 'tamashii/server/base'
17
+ autoload :Response, 'tamashii/server/response'
18
+ autoload :Client, 'tamashii/server/client'
19
+ autoload :Connection, 'tamashii/server/connection'
20
+ autoload :Subscription, 'tamashii/server/subscription'
21
+
22
+ def self.logger
23
+ # TODO: Add config to set logger path
24
+ @logger ||= ::Logger.new(STDOUT)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ class Base
7
+ attr_reader :mutex
8
+
9
+ def initialize
10
+ @mutex = Monitor.new
11
+ mutex.synchronize { @instance ||= Rack.new(self, event_loop) }
12
+ end
13
+
14
+ def call(env)
15
+ @instance.call(env)
16
+ end
17
+
18
+ def pubsub
19
+ @pubsub || mutex.synchronize do
20
+ @pubsub ||= Subscription::Redis.new(self)
21
+ end
22
+ end
23
+
24
+ def event_loop
25
+ @event_loop || mutex.synchronize do
26
+ @event_loop ||= Connection::StreamEventLoop.new
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ class Client
7
+ class << self
8
+ def register(socket)
9
+ return false unless socket.is_a?(Connection::ClientSocket)
10
+ clients.add(socket)
11
+ end
12
+
13
+ def unregister(socket)
14
+ clients.delete(socket)
15
+ end
16
+
17
+ def clients
18
+ @clients ||= Set.new
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ module Connection
7
+ autoload :ClientSocket, 'tamashii/server/connection/client_socket'
8
+ autoload :StreamEventLoop, 'tamashii/server/connection/stream_event_loop'
9
+ autoload :Stream, 'tamashii/server/connection/stream'
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ module Connection
6
+ # :nodoc:
7
+ class ClientSocket
8
+ def self.determine_url(env)
9
+ scheme = secure_request?(env) ? 'wss:' : 'ws:'
10
+ "#{scheme}//#{env['HTTP_HOST']}#{env['REQUEST_URI']}"
11
+ end
12
+
13
+ def self.secure_request?(env)
14
+ return true if env['HTTPS'] == 'on'
15
+ return true if env['HTTP_X_FORWARDED_SSL'] == 'on'
16
+ return true if env['HTTP_X_FORWARDED_SCHEME'] == 'https'
17
+ return true if env['HTTP_X_FORWARDED_PROTO'] == 'https'
18
+ return true if env['rack.url_scheme'] == 'https'
19
+
20
+ false
21
+ end
22
+
23
+ attr_reader :env, :url
24
+ attr_accessor :id
25
+
26
+ # TODO: Support define protocols
27
+ def initialize(server, env, event_loop)
28
+ @server = server
29
+ @env = env
30
+ @event_loop = event_loop
31
+
32
+ @id ||= env['REMOTE_ADDR']
33
+
34
+ @url = ClientSocket.determine_url(@env)
35
+ @driver = setup_driver
36
+
37
+ @stream = Stream.new(@event_loop, self)
38
+ end
39
+
40
+ def start_driver
41
+ return if @driver.nil?
42
+ @stream.hijack_rack_socket
43
+
44
+ callback = @env['async.callback']
45
+ callback&.call([101, {}, @stream])
46
+
47
+ @driver.start
48
+ end
49
+
50
+ def rack_response
51
+ start_driver
52
+ Server.logger.info("Accept new websocket connection from #{env['REMOTE_ADDR']}")
53
+ Server::Response.new(message: 'WebSocket Connected')
54
+ end
55
+
56
+ def write(data)
57
+ @stream.write(data)
58
+ rescue => e
59
+ emit_error e.message
60
+ end
61
+
62
+ def transmit(message)
63
+ Server.logger.debug("Send to #{id} with data #{message}")
64
+ case message
65
+ when Numeric then @driver.text(message.to_s)
66
+ when String then @driver.text(message)
67
+ else
68
+ @driver.binary(message)
69
+ end
70
+ end
71
+
72
+ def close
73
+ # TODO: Define close reason / code
74
+ @driver.close('', 1000)
75
+ end
76
+
77
+ def parse(data)
78
+ @driver.parse(data)
79
+ end
80
+
81
+ def client_gone
82
+ finialize_close
83
+ end
84
+
85
+ def protocol
86
+ @driver.protocol
87
+ end
88
+
89
+ private
90
+
91
+ def setup_driver
92
+ driver = ::WebSocket::Driver.rack(self)
93
+
94
+ driver.on(:open) { |_| open }
95
+ driver.on(:message) { |e| receive_message(e.data) }
96
+ driver.on(:close) { |e| begin_close(e.reason, e.code) }
97
+ driver.on(:error) { |e| emit_error(e.message) }
98
+
99
+ driver
100
+ end
101
+
102
+ def open
103
+ Server::Client.register(self)
104
+ end
105
+
106
+ def receive_message(data)
107
+ @server.pubsub.broadcast(data)
108
+ end
109
+
110
+ def emit_error(message)
111
+ Server.logger.error("Client #{id} has some error: #{message}")
112
+ end
113
+
114
+ def begin_close(_reason, _code)
115
+ Server.logger.info("Close connection to #{id}")
116
+ Client.unregister(self)
117
+ finialize_close
118
+ end
119
+
120
+ def finialize_close
121
+ # TODO: Processing close
122
+ @stream.close
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ module Connection
6
+ # :nodoc:
7
+ class Stream
8
+ attr_reader :event_loop
9
+
10
+ def initialize(event_loop, socket)
11
+ @event_loop = event_loop
12
+ @socket = socket
13
+ @stream_send = socket.env['stream.send']
14
+
15
+ @rack_hijack_io = nil
16
+ @write_lock = Mutex.new
17
+
18
+ @write_head = nil
19
+ @write_buffer = Queue.new
20
+ end
21
+
22
+ def each(&callback)
23
+ @stream_send ||= callback
24
+ end
25
+
26
+ def close
27
+ shutdown
28
+ @socket.client_gone
29
+ end
30
+
31
+ def shutdown
32
+ clean_rack_hijack
33
+ end
34
+
35
+ def write(data)
36
+ return @stream_send.call(data) if @stream_send
37
+
38
+ return write_safe(data) if @write_lock.try_lock
39
+ write_buffer(data)
40
+ data.bytesize
41
+ rescue EOFError, Errno::ECONNRESET
42
+ @socket.client_gone
43
+ end
44
+
45
+ def flush_write_buffer
46
+ @write_lock.synchronize do
47
+ loop do
48
+ return true if @write_buffer.empty? && @write_head.nil?
49
+ @write_head = @write_buffer.pop if @write_head.nil?
50
+
51
+ return unless process_flush
52
+ end
53
+ end
54
+ end
55
+
56
+ def receive(data)
57
+ @socket.parse(data)
58
+ end
59
+
60
+ def hijack_rack_socket
61
+ return unless @socket.env['rack.hijack']
62
+
63
+ @socket.env['rack.hijack'].call
64
+ @rack_hijack_io = @socket.env['rack.hijack_io']
65
+
66
+ @event_loop.attach(@rack_hijack_io, self)
67
+ end
68
+
69
+ private
70
+
71
+ def write_safe(data)
72
+ return unless @write_head.nil? && @write_buffer.empty?
73
+ written = @rack_hijack_io.write_nonblock(data, exception: false)
74
+
75
+ case written
76
+ when :wait_writable then write_buffer(data)
77
+ when data.bytesize then data.bytesize
78
+ else
79
+ write_head data.byteslice(written, data.bytesize)
80
+ end
81
+ ensure
82
+ @write_lock.unlock
83
+ end
84
+
85
+ def write_buffer(data)
86
+ @write_buffer << data
87
+ @event_loop.writes_pending @rack_hijack_io
88
+ end
89
+
90
+ def write_head(head)
91
+ @write_head = head
92
+ @event_loop.writes_pending @rack_hijack_io
93
+ end
94
+
95
+ def process_flush
96
+ written = @rack_hijack_io.write_nonblock(@write_head, exception: false)
97
+ case written
98
+ when :wait_writable then return false
99
+ when @write_head.bytesize
100
+ @write_head = nil
101
+ return true
102
+ else
103
+ @write_head = @write_head.byteslice(written, @write_head.bytesize)
104
+ return false
105
+ end
106
+ end
107
+
108
+ def clean_rack_hijack
109
+ return unless @rack_hijack_io
110
+ @event_loop.detach(@rack_hijack_io, self)
111
+ @rack_hijack_io = nil
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ module Tamashii
6
+ module Server
7
+ module Connection
8
+ # :nodoc:
9
+ class StreamEventLoop
10
+ def initialize
11
+ @nio = @thread = nil
12
+ @stopping = false
13
+
14
+ @streams = {}
15
+
16
+ @todo = Queue.new
17
+
18
+ @spawn_mutex = Mutex.new
19
+ end
20
+
21
+ def attach(io, stream)
22
+ @todo << lambda do
23
+ @streams[io] = @nio.register(io, :r)
24
+ @streams[io].value = stream
25
+ end
26
+ wakeup
27
+ end
28
+
29
+ def detach(io, _)
30
+ @todo << lambda do
31
+ @nio.deregister io
32
+ @streams.delete io
33
+ io.close
34
+ end
35
+ wakeup
36
+ end
37
+
38
+ def writes_pending(io)
39
+ @todo << lambda do
40
+ monitor = @streams[io]
41
+ monitor&.interests = :rw
42
+ end
43
+ wakeup
44
+ end
45
+
46
+ def stop
47
+ @stopping = true
48
+ wakeup if @nio
49
+ end
50
+
51
+ def stopped?
52
+ @stopping
53
+ end
54
+
55
+ private
56
+
57
+ def spawn
58
+ return if @thread && @thread.status
59
+
60
+ @spawn_mutex.synchronize do
61
+ return if @thread && @thread.status
62
+
63
+ @nio ||= NIO::Selector.new
64
+ @thread = Thread.new { run }
65
+
66
+ return true
67
+ end
68
+ end
69
+
70
+ def wakeup
71
+ spawn || @nio.wakeup
72
+ end
73
+
74
+ def run
75
+ loop do
76
+ if stopped?
77
+ @nio.close
78
+ break
79
+ end
80
+
81
+ @todo.pop(true).call until @todo.empty?
82
+
83
+ monitors = @nio.select
84
+ next unless monitors
85
+ process(monitors)
86
+ end
87
+ end
88
+
89
+ def process(monitors)
90
+ monitors.each do |monitor|
91
+ io = monitor.io
92
+ stream = monitor.value
93
+
94
+ if monitor.writable?
95
+ monitor.interests = :r if stream.flush_write_buffer
96
+ next unless monitor.readable?
97
+ end
98
+
99
+ next unless read(io, stream)
100
+ end
101
+ end
102
+
103
+ def read(io, stream)
104
+ incoming = io.read_nonblock(4096, exception: false)
105
+ case incoming
106
+ when :wait_readable then false
107
+ when nil then stream.close
108
+ else
109
+ stream.receive incoming
110
+ end
111
+ rescue
112
+ try_close(io, stream)
113
+ end
114
+
115
+ def try_close(io, stream)
116
+ stream.close
117
+ rescue
118
+ @nio.deregister io
119
+ @streams.delete io
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ class Rack
7
+ def initialize(server, event_loop)
8
+ @server = server
9
+ @event_loop = event_loop
10
+
11
+ @server.pubsub.subscribe
12
+ end
13
+
14
+ def call(env)
15
+ return start_websocket(env) if ::WebSocket::Driver.websocket?(env)
16
+ start_http(env)
17
+ end
18
+
19
+ private
20
+
21
+ def start_websocket(env)
22
+ Connection::ClientSocket.new(@server, env, @event_loop).rack_response
23
+ end
24
+
25
+ def start_http(_)
26
+ # TODO: Supply API for query WebSocket status
27
+ Server::Response.new(message: 'Invalid protocol or api endpoint')
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ class Response < ::Rack::Response
7
+ def initialize(body = {}, status = 200, header = {}, &block)
8
+ body = process_body(body)
9
+ header = process_header(header, body.first)
10
+ super
11
+ end
12
+
13
+ def process_body(body)
14
+ [body.merge(version: Tamashii::VERSION).to_json]
15
+ end
16
+
17
+ def process_header(header, body)
18
+ header.merge(
19
+ 'Content-Type' => 'application/json',
20
+ 'Content-Length' => body.bytesize
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ # :nodoc:
6
+ module Subscription
7
+ autoload :Redis, 'tamashii/server/subscription/redis'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tamashii
4
+ module Server
5
+ module Subscription
6
+ # :nodoc:
7
+ class Redis
8
+ def initialize(server)
9
+ @server = server
10
+ end
11
+
12
+ def broadcast(payload)
13
+ Server.logger.info("Broadcasting: #{payload}")
14
+ broadcast_conn.publish('_tamashii_internal', pack(payload))
15
+ end
16
+
17
+ def subscribe
18
+ ensure_listener_running
19
+ end
20
+
21
+ def shutdown
22
+ subscription_conn.unsubscribe
23
+ end
24
+
25
+ def prepare
26
+ ensure_listener_running
27
+ end
28
+
29
+ def pack(data)
30
+ case data
31
+ when Numeric then "N:#{data}"
32
+ when String then "S:#{data}"
33
+ else
34
+ "B:#{data.pack('C*')}"
35
+ end
36
+ end
37
+
38
+ def unpack(data)
39
+ case data[0..1]
40
+ when 'N:' then data[2..-1].to_i
41
+ when 'S:' then data[2..-1]
42
+ else
43
+ data[2..-1].unpack('C*')
44
+ end
45
+ end
46
+
47
+ protected
48
+
49
+ def broadcast_conn
50
+ # TODO: Add config to support set server
51
+ @conn || @server.mutex.synchronize do
52
+ @conn ||= ::Redis.new
53
+ end
54
+ end
55
+
56
+ def subscription_conn
57
+ @subscription_conn ||= ::Redis.new
58
+ end
59
+
60
+ def listen
61
+ Server.logger.info('Starting subscribe redis #_tamashii_internal channel')
62
+ subscription_conn.without_reconnect do
63
+ # TODO: Add config to support set namespace
64
+ subscription_conn.subscribe('_tamashii_internal') do |on|
65
+ on.message { |_, message| process_message(message) }
66
+ end
67
+ end
68
+ end
69
+
70
+ def process_message(message)
71
+ Client.clients.each { |client| client.transmit(unpack(message)) }
72
+ end
73
+
74
+ def ensure_listener_running
75
+ @thread ||= Thread.new { listen }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tamashii
2
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
3
5
  end
@@ -35,6 +35,8 @@ Gem::Specification.new do |spec|
35
35
  spec.add_runtime_dependency "tamashii-common"
36
36
  spec.add_runtime_dependency "websocket-driver"
37
37
  spec.add_runtime_dependency "nio4r"
38
+ spec.add_runtime_dependency "redis"
39
+ spec.add_runtime_dependency "logger-colors"
38
40
 
39
41
  spec.add_development_dependency 'bundler', '~> 1.14'
40
42
  spec.add_development_dependency 'rake', '~> 10.0'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tamashii
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - 蒼時弦也
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-05-03 00:00:00.000000000 Z
13
+ date: 2017-05-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: concurrent-ruby
@@ -82,6 +82,34 @@ dependencies:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
84
  version: '0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: redis
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: logger-colors
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :runtime
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
85
113
  - !ruby/object:Gem::Dependency
86
114
  name: bundler
87
115
  requirement: !ruby/object:Gem::Requirement
@@ -190,7 +218,10 @@ extensions: []
190
218
  extra_rdoc_files: []
191
219
  files:
192
220
  - ".gitignore"
221
+ - ".gitlab-ci.yml"
193
222
  - ".rspec"
223
+ - ".rubocop.yml"
224
+ - ".rubocop_todo.yml"
194
225
  - ".travis.yml"
195
226
  - Gemfile
196
227
  - Guardfile
@@ -199,6 +230,17 @@ files:
199
230
  - bin/console
200
231
  - bin/setup
201
232
  - lib/tamashii.rb
233
+ - lib/tamashii/server.rb
234
+ - lib/tamashii/server/base.rb
235
+ - lib/tamashii/server/client.rb
236
+ - lib/tamashii/server/connection.rb
237
+ - lib/tamashii/server/connection/client_socket.rb
238
+ - lib/tamashii/server/connection/stream.rb
239
+ - lib/tamashii/server/connection/stream_event_loop.rb
240
+ - lib/tamashii/server/rack.rb
241
+ - lib/tamashii/server/response.rb
242
+ - lib/tamashii/server/subscription.rb
243
+ - lib/tamashii/server/subscription/redis.rb
202
244
  - lib/tamashii/version.rb
203
245
  - tamashii.gemspec
204
246
  homepage: https://github.com/5xRuby/tamashii