ruby_wolf 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 14d13bd7de3c5cca903738aefbe18c1d9e512a49
4
+ data.tar.gz: e92866e154a80c358571348ebedbcc3fa7c0038b
5
+ SHA512:
6
+ metadata.gz: 18c7b7288ab955e023beaf8de4a58fd763d68712fbe1b3589280b8c9899ee621a20bab20d1c6c666644597d1522428958a4397b373175c50a37586cf75ebb55b
7
+ data.tar.gz: 238eadfe11529cae062586465abf71ec3b72df78d4f5cf24a8822265663158c9d2e67d4b132c6682643a7b4309e536d1ff1d61194432809ca80602d84ec61b08
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.2
5
+ before_install: gem install bundler -v 1.13.6
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at nguyenquangminh0711@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruby_wolf.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Nguyễn Quang Minh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # RubyWolf
2
+
3
+ Ruby wolf is a tiny ruby web server for rack-based application. This server follows pre-forked and event driven with kqueue / epoll approach. Honestly, this web server is written for study and research purpose. I'm sure it could be used anywhere. So, don't use it in real world :)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ruby_wolf'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ruby_wolf
20
+
21
+ ## Usage
22
+
23
+ Start your rack-based application with the following command:
24
+
25
+ `ruby_wolf -p 3000`
26
+
27
+ To explore the provided options, please use `ruby_wolf --help`
28
+
29
+ ## Benchmark
30
+
31
+ Benchmark with some Hello world application:
32
+
33
+ ### RubyWolf
34
+
35
+ ```
36
+ 50% 7
37
+ 66% 11
38
+ 75% 14
39
+ 80% 15
40
+ 90% 19
41
+ 95% 24
42
+ 98% 31
43
+ 99% 33
44
+ 100% 51 (longest request)
45
+ ```
46
+
47
+ ### Puma
48
+
49
+ ```
50
+ 50% 25
51
+ 66% 27
52
+ 75% 28
53
+ 80% 29
54
+ 90% 31
55
+ 95% 32
56
+ 98% 35
57
+ 99% 37
58
+ 100% 39 (longest request)
59
+ ```
60
+
61
+ ### Thin
62
+
63
+ ```
64
+ 50% 22
65
+ 66% 23
66
+ 75% 24
67
+ 80% 24
68
+ 90% 28
69
+ 95% 30
70
+ 98% 34
71
+ 99% 36
72
+ 100% 227 (longest request)
73
+ ```
74
+
75
+ Note that Hello world application is not considered to be a real application. Thus this benchmark doesn't mean much
76
+
77
+ ## License
78
+
79
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
80
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/ruby_wolf ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (c) 2017 Nguyễn Quang Minh
4
+ #
5
+
6
+ require 'ruby_wolf'
7
+
8
+ cli = RubyWolf::CLI.new ARGV
9
+
10
+ cli.run
@@ -0,0 +1,56 @@
1
+ require 'optparse'
2
+
3
+ module RubyWolf
4
+ class CLI
5
+ attr_reader :app, :server, :configs
6
+
7
+ def initialize(args)
8
+ @args = args
9
+ @configs = RubyWolf::Configuration.new
10
+ @app_root = `pwd`.to_s.strip
11
+ end
12
+
13
+ def run
14
+ parse_options
15
+ raise 'Rack file not found' unless File.exist?(rack_file)
16
+
17
+ @server = RubyWolf::Server.new(rack_file, configs)
18
+ @server.start
19
+ end
20
+
21
+ def parse_options
22
+ opt_parser = OptionParser.new do |opts|
23
+ opts.banner = 'Usage: ruby_wolf [options]'
24
+
25
+ opts.on('-d', '--daemon', 'Demonize this web server to run background') do
26
+ @configs[:daemon] = true
27
+ end
28
+
29
+ opts.on('-h HOST', '--port=HOST', 'Binding host') do |arg|
30
+ @configs[:host] = arg
31
+ end
32
+
33
+ opts.on('-p PORT', '--port=PORT', 'Port of the program') do |arg|
34
+ @configs[:port] = arg.to_i
35
+ end
36
+
37
+ opts.on('-w WORKER', '--worker=WORKER', 'Number of worker processes') do |arg|
38
+ @configs[:worker] = arg.to_i
39
+ end
40
+
41
+ opts.on('-h', '--help', 'Show the usages') do
42
+ puts opts
43
+ exit
44
+ end
45
+ end
46
+
47
+ opt_parser.parse!(@args)
48
+ end
49
+
50
+ private
51
+
52
+ def rack_file
53
+ "#{@app_root}/config.ru"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ module RubyWolf
2
+ class Configuration < BasicObject
3
+ DEFAULT_DAEMON = false
4
+ DEFAULT_HOST = '0.0.0.0'.freeze
5
+ DEFAULT_PORT = 3000
6
+ DEFAULT_WORKER = 4
7
+
8
+ def initialize
9
+ @configs = {
10
+ daemon: DEFAULT_DAEMON,
11
+ worker: DEFAULT_WORKER,
12
+ host: DEFAULT_HOST,
13
+ port: DEFAULT_PORT
14
+ }
15
+ end
16
+
17
+ def []=(key, value)
18
+ @configs[key] = value
19
+ end
20
+
21
+ def [](key)
22
+ @configs[key]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,45 @@
1
+ module RubyWolf
2
+ class Connection
3
+ attr_reader :socket, :read_data, :write_data
4
+
5
+ def initialize(socket)
6
+ @socket = socket
7
+ @read_data = ''
8
+ @write_data = ''
9
+
10
+ @reading = true
11
+ end
12
+
13
+ def need_to_read?
14
+ @reading
15
+ end
16
+
17
+ def read
18
+ @read_data << socket.read_nonblock(RubyWolf::READ_SIZE)
19
+ @reading = false if @read_data.end_with?(RubyWolf::CRLF)
20
+ rescue EOFError
21
+ @reading = false
22
+ end
23
+
24
+ def enqueue_write(data)
25
+ @write_data += data
26
+ end
27
+
28
+ def write
29
+ writen = socket.write_nonblock(@write_data)
30
+ @write_data[0..writen] = ''
31
+ end
32
+
33
+ def need_to_write?
34
+ !@write_data.length.zero?
35
+ end
36
+
37
+ def to_io
38
+ @socket
39
+ end
40
+
41
+ def close
42
+ @socket.close
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,84 @@
1
+ require 'http/parser'
2
+
3
+ module RubyWolf
4
+ class Handler
5
+
6
+ attr_reader :app, :env, :connection, :response, :callback
7
+
8
+ def initialize(app, connection, &callback)
9
+ @app = app
10
+ @connection = connection
11
+ @callback = callback
12
+ @env = {}
13
+ @response = ''
14
+ end
15
+
16
+ def process
17
+ prepare_rack_env
18
+ parse_request
19
+ generate_response
20
+ callback.call(response) if callback
21
+ response
22
+ end
23
+
24
+ private
25
+
26
+ def prepare_rack_env
27
+ @env = ENV.to_h
28
+ @env.delete 'HTTP_CONTENT_LENGTH'
29
+ url_scheme = %w(yes on 1).include?(ENV[::Rack::HTTPS]) ? 'https' : 'http'
30
+ @env.update(
31
+ ::Rack::RACK_VERSION => ::Rack::VERSION,
32
+ ::Rack::RACK_INPUT => STDIN,
33
+ ::Rack::RACK_ERRORS => STDERR,
34
+ ::Rack::RACK_MULTITHREAD => false,
35
+ ::Rack::RACK_MULTIPROCESS => true,
36
+ ::Rack::RACK_RUNONCE => true,
37
+ ::Rack::RACK_URL_SCHEME => url_scheme,
38
+ ::Rack::SERVER_PROTOCOL => 'HTTP/1.1'
39
+ )
40
+ end
41
+
42
+ def parse_request
43
+ parser = Http::Parser.new
44
+ parser.on_headers_complete = proc do
45
+ env[::Rack::HTTP_VERSION] = parser.http_version
46
+ env[::Rack::REQUEST_METHOD] = parser.http_method
47
+
48
+ uri = URI.parse(parser.request_url)
49
+ env[::Rack::REQUEST_PATH] = uri.path
50
+ env[::Rack::QUERY_STRING] = uri.query
51
+ end
52
+ parser << connection.read_data
53
+ RubyWolf.log(
54
+ [
55
+ "HTTP/#{env[::Rack::HTTP_VERSION].join('.')}",
56
+ env[::Rack::REQUEST_METHOD],
57
+ "#{env[::Rack::REQUEST_PATH]}?#{env[::Rack::QUERY_STRING]}"
58
+ ].join(' ')
59
+ )
60
+ end
61
+
62
+ def generate_response
63
+ status, headers, body = app.call(env)
64
+ RubyWolf.log(
65
+ "Response #{env[::Rack::SERVER_PROTOCOL]} #{status}"
66
+ )
67
+ compose_response(status, headers, body)
68
+ end
69
+
70
+ def compose_response(status, headers, body)
71
+ @response += "#{env[::Rack::SERVER_PROTOCOL]} #{status} #{RubyWolf::CRLF}"
72
+ headers.each do |key, value|
73
+ @response += "#{key}: #{value}#{RubyWolf::CRLF}"
74
+ end
75
+
76
+ @response += RubyWolf::CRLF
77
+ body.each do |part|
78
+ @response += part
79
+ end
80
+ ensure
81
+ body.close if body.respond_to? :close
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,79 @@
1
+ require 'logger'
2
+
3
+ module RubyWolf
4
+ class Server
5
+ attr_reader :app, :configs, :socket, :workers
6
+
7
+ def initialize(rack_file, configs)
8
+ @rack_file = rack_file
9
+ @configs = configs
10
+ @workers = []
11
+ end
12
+
13
+ def start
14
+ trap_signal
15
+ setup_rack
16
+ setup_socket
17
+ configs[:worker].times do
18
+ workers << RubyWolf::Worker.new(self)
19
+ end
20
+ Process.detach if configs[:daemon]
21
+ workers.each(&:start)
22
+ handle_loop
23
+ end
24
+
25
+ private
26
+
27
+ def setup_rack
28
+ RubyWolf.log('~~~ Ruby Wolf ~~~')
29
+ RubyWolf.log('Loading Rack application')
30
+ @app, _rack_options = ::Rack::Builder.parse_file(@rack_file)
31
+ end
32
+
33
+ def setup_socket
34
+ @socket = TCPServer.new(configs[:host], configs[:port])
35
+ RubyWolf.log("Server is running on #{configs[:host]}:#{configs[:port]}")
36
+ RubyWolf.log("Process pid is #{Process.pid}")
37
+ RubyWolf.log("Number of worker: #{configs[:worker]}")
38
+ end
39
+
40
+ def handle_loop
41
+ while stopped_pid = Process.wait do
42
+ stopped_worker = workers.find { |w| w.pid == stopped_pid }
43
+ next unless stopped_worker
44
+
45
+ RubyWolf.log("Worker with pid #{stopped_pid} suddenly stopped", :error)
46
+
47
+ sleep(1)
48
+ worker = RubyWolf::Worker.new(self)
49
+ worker.start
50
+
51
+ workers << worker
52
+ end
53
+ end
54
+
55
+ def trap_signal
56
+ Signal.trap(:INT) do
57
+ if RubyWolf::MAIN_PID == Process.pid
58
+ puts "Stopping server\n"
59
+ else
60
+ puts "Stopping worker #{Process.pid} \n"
61
+ end
62
+ exit
63
+ end
64
+
65
+ Signal.trap(:TERM) do
66
+ if RubyWolf::MAIN_PID == Process.pid
67
+ puts "Stopping server\n"
68
+ workers.each do |w|
69
+ Process.kill(:TERM, w.pid)
70
+ end
71
+ else
72
+ puts "Stopping worker #{Process.pid} \n"
73
+ end
74
+ sleep 1
75
+ exit
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module RubyWolf
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,71 @@
1
+ module RubyWolf
2
+ class Worker
3
+ attr_reader :pid, :server, :app, :socket, :connections
4
+
5
+ def initialize(server)
6
+ @server = server
7
+ @app = server.app
8
+ @socket = server.socket
9
+ @connections = []
10
+ end
11
+
12
+ def start
13
+ @pid = fork do
14
+ RubyWolf.log('Worker is ready')
15
+ handle_loop
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def handle_loop
22
+ loop do
23
+ need_to_read = connections.select(&:need_to_read?)
24
+ need_to_write = connections.select(&:need_to_write?)
25
+
26
+ ready_to_read, ready_to_write, = IO.select(
27
+ need_to_read + [socket],
28
+ need_to_write
29
+ )
30
+
31
+ handle_read(ready_to_read)
32
+ handle_write(ready_to_write)
33
+ end
34
+ end
35
+
36
+ def handle_read(ready_to_read)
37
+ ready_to_read.each do |connection|
38
+ if connection == socket
39
+ accept_connection
40
+ else
41
+ connection.read
42
+ handle_request(connection) unless connection.need_to_read?
43
+ end
44
+ end
45
+ end
46
+
47
+ def handle_write(ready_to_write)
48
+ ready_to_write.each do |connection|
49
+ connection.write
50
+ close_connection(connection) unless connection.need_to_write?
51
+ end
52
+ end
53
+
54
+ def handle_request(connection)
55
+ handler = RubyWolf::Handler.new(app, connection) do |response|
56
+ connection.enqueue_write(response)
57
+ end
58
+ handler.process
59
+ end
60
+
61
+ def accept_connection
62
+ @connections << RubyWolf::Connection.new(socket.accept_nonblock)
63
+ rescue IO::WaitReadable, Errno::EINTR
64
+ end
65
+
66
+ def close_connection(connection)
67
+ connection.close
68
+ @connections.delete(connection)
69
+ end
70
+ end
71
+ end
data/lib/ruby_wolf.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'byebug'
2
+ require 'rack'
3
+
4
+ require 'ruby_wolf/version'
5
+ require 'ruby_wolf/configuration'
6
+ require 'ruby_wolf/connection'
7
+ require 'ruby_wolf/handler'
8
+ require 'ruby_wolf/server'
9
+ require 'ruby_wolf/worker'
10
+ require 'ruby_wolf/cli'
11
+
12
+ module RubyWolf
13
+ MAIN_PID = Process.pid
14
+ CRLF = "\r\n".freeze
15
+ READ_SIZE = 16 * 1024
16
+
17
+ def self.logger
18
+ @logger ||= Logger.new(STDOUT)
19
+ end
20
+
21
+ def self.log(content, mode = :info)
22
+ contents = content.to_s.split("\n")
23
+ object = Process.pid == MAIN_PID ? '[Main]' : "[Worker #{Process.pid}]"
24
+ contents.each do |line|
25
+ logger.send(mode, "#{object} #{line}")
26
+ end
27
+ end
28
+ end
data/ruby_wolf.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ruby_wolf/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'ruby_wolf'
8
+ spec.version = RubyWolf::VERSION
9
+ spec.authors = ['Nguyễn Quang Minh']
10
+ spec.email = ['nguyenquangminh0711@gmail.com']
11
+
12
+ spec.summary = 'A simple ruby web server using pre-fork and event loop'
13
+ spec.description = 'My simple implementation of Rack web server using pre-fork and event loop'
14
+ spec.homepage = 'https://github.com/nguyenquangminh0711/ruby_wolf'
15
+
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.bindir = 'bin'
20
+ spec.executables = ['ruby_wolf']
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'rspec', '~>3.5.0', '>=3.5.0'
24
+ spec.add_development_dependency 'byebug', '~>9.0.0', '>=8.0.0'
25
+
26
+ spec.add_runtime_dependency 'rack', '~>2.0.0', '>=2.0.0'
27
+ spec.add_runtime_dependency 'http_parser.rb', '~>0.6.0', '>=0.6.0'
28
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyWolf::CLI do
4
+ let(:cli) { RubyWolf::CLI.new(args) }
5
+
6
+ before { cli.parse_options }
7
+
8
+ describe '#run' do
9
+ let(:args) { ['-d'] }
10
+ let(:app_double) { double }
11
+
12
+ context 'Rack file found' do
13
+ before do
14
+ allow(File).to receive(:exist?).and_return(true)
15
+ allow(::Rack::Builder).to receive(:parse_file).and_return(app_double)
16
+ allow_any_instance_of(RubyWolf::Server).to receive(:start)
17
+ end
18
+
19
+ it 'creates Rack app from the rack file' do
20
+ cli.run
21
+ expect(cli.app).to eql(app_double)
22
+ end
23
+
24
+ it 'starts ruby wolf server' do
25
+ expect_any_instance_of(RubyWolf::Server).to receive(:start)
26
+ cli.run
27
+ expect(cli.server).to be_a(RubyWolf::Server)
28
+ expect(cli.server.app).to eql(app_double)
29
+ expect(cli.server.configs).to eq(cli.configs)
30
+ end
31
+ end
32
+
33
+ context 'Rack file not found' do
34
+ before do
35
+ allow(File).to receive(:exist?).and_return(false)
36
+ end
37
+
38
+ it 'raises exception' do
39
+ expect do
40
+ cli.run
41
+ end.to raise_error(/rack file not found/i)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#parse_options' do
47
+ describe 'daemon option' do
48
+ context 'default daemon' do
49
+ let(:args) { [] }
50
+ it { expect(cli.configs[:daemon]).to eql(false) }
51
+ end
52
+
53
+ context 'short form' do
54
+ let(:args) { ['-d'] }
55
+ it { expect(cli.configs[:daemon]).to eql(true) }
56
+ end
57
+
58
+ context 'full form' do
59
+ let(:args) { ['--daemon'] }
60
+ it { expect(cli.configs[:daemon]).to eql(true) }
61
+ end
62
+ end
63
+
64
+ describe 'host option' do
65
+ context 'default host' do
66
+ let(:args) { [] }
67
+ it { expect(cli.configs[:host]).to eql('0.0.0.0') }
68
+ end
69
+
70
+ context 'short form' do
71
+ let(:args) { ['-h localhost'] }
72
+ it { expect(cli.configs[:host]).to eql('localhost') }
73
+ end
74
+
75
+ context 'full form' do
76
+ let(:args) { ['--host=localhost'] }
77
+ it { expect(cli.configs[:host]).to eql('localhost') }
78
+ end
79
+ end
80
+
81
+ describe 'port option' do
82
+ context 'default port' do
83
+ let(:args) { [] }
84
+ it { expect(cli.configs[:port]).to eql(3000) }
85
+ end
86
+
87
+ context 'short form' do
88
+ let(:args) { ['-p 5000'] }
89
+ it { expect(cli.configs[:port]).to eql(5000) }
90
+ end
91
+
92
+ context 'full form' do
93
+ let(:args) { ['--port=5000'] }
94
+ it { expect(cli.configs[:port]).to eql(5000) }
95
+ end
96
+ end
97
+
98
+ describe 'worker option' do
99
+ context 'default worker' do
100
+ let(:args) { [] }
101
+ it { expect(cli.configs[:worker]).to eql(4) }
102
+ end
103
+
104
+ context 'short form' do
105
+ let(:args) { ['-w 20'] }
106
+ it { expect(cli.configs[:worker]).to eql(20) }
107
+ end
108
+
109
+ context 'full form' do
110
+ let(:args) { ['--worker=20'] }
111
+ it { expect(cli.configs[:port]).to eql(20) }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe RubyWolf::Configuration do
4
+ let(:configs) { RubyWolf::Configuration.new }
5
+
6
+ describe '[]=' do
7
+ context 'key not exists' do
8
+ before do
9
+ configs[:hihi] = 'Test'
10
+ end
11
+
12
+ it 'store the value under the key' do
13
+ expect(configs[:hihi]).to eql('Test')
14
+ end
15
+ end
16
+
17
+ context 'key exists' do
18
+ before do
19
+ configs[:hihi] = 'Test'
20
+ configs[:hihi] = 'or not to test'
21
+ end
22
+
23
+ it 'updates the value under the key' do
24
+ expect(configs[:hihi]).to eql('or not to test')
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '[]' do
30
+ context 'key exists' do
31
+ before do
32
+ configs[:hihi] = 'What'
33
+ end
34
+
35
+ it 'returns the value under the key' do
36
+ expect(configs[:hihi]).to eql('What')
37
+ end
38
+ end
39
+
40
+ context 'key not exists' do
41
+ it 'returns nil' do
42
+ expect(configs[:not_exist]).to eql(nil)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,7 @@
1
+ require "spec_helper"
2
+
3
+ describe RubyWolf do
4
+ it "has a version number" do
5
+ expect(RubyWolf::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "ruby_wolf"
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_wolf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Nguyễn Quang Minh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.5.0
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.5.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 3.5.0
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.5.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: byebug
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 9.0.0
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 8.0.0
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 9.0.0
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 8.0.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: rack
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: 2.0.0
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 2.0.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 2.0.0
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 2.0.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: http_parser.rb
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: 0.6.0
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 0.6.0
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.6.0
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 0.6.0
93
+ description: My simple implementation of Rack web server using pre-fork and event
94
+ loop
95
+ email:
96
+ - nguyenquangminh0711@gmail.com
97
+ executables:
98
+ - ruby_wolf
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - ".gitignore"
103
+ - ".rspec"
104
+ - ".travis.yml"
105
+ - CODE_OF_CONDUCT.md
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - bin/ruby_wolf
111
+ - lib/ruby_wolf.rb
112
+ - lib/ruby_wolf/cli.rb
113
+ - lib/ruby_wolf/configuration.rb
114
+ - lib/ruby_wolf/connection.rb
115
+ - lib/ruby_wolf/handler.rb
116
+ - lib/ruby_wolf/server.rb
117
+ - lib/ruby_wolf/version.rb
118
+ - lib/ruby_wolf/worker.rb
119
+ - ruby_wolf.gemspec
120
+ - spec/ruby_wolf/cli_spec.rb
121
+ - spec/ruby_wolf/configuration_spec.rb
122
+ - spec/ruby_wolf_spec.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/nguyenquangminh0711/ruby_wolf
125
+ licenses:
126
+ - MIT
127
+ metadata: {}
128
+ post_install_message:
129
+ rdoc_options: []
130
+ require_paths:
131
+ - lib
132
+ required_ruby_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project:
144
+ rubygems_version: 2.6.10
145
+ signing_key:
146
+ specification_version: 4
147
+ summary: A simple ruby web server using pre-fork and event loop
148
+ test_files: []