tamashii 0.1.0 → 0.2.0

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