toycol 0.1.0 → 0.3.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
  SHA256:
3
- metadata.gz: 9eb79d412c62ad9b86922964fa583b4fa1ed64b941c74654874b93d385d5665e
4
- data.tar.gz: 8d915a6bfc7677096164b7fa773e32fad7669ff0b847eb2306be885899298362
3
+ metadata.gz: a9cd0de8dd5ec5a48e22a49c8198492a96162c9ff922a07c897e3af7cdfe779a
4
+ data.tar.gz: 938b7f68fef82731d61c1937c2ed53753c1bbb301c49434cc6d538edb6f862f9
5
5
  SHA512:
6
- metadata.gz: c988be01719a879b367ea0da7323bc00100d4373f3516ab504e03fb92d26b3c2f0e3fe9e4411f84d139b97100d2e3342cebb4eb2e344674dadcd62ecadbf06bb
7
- data.tar.gz: fc5ab9812a9f87006052c22614b6cdae7257db0919c3f95f0f30bba5e238b0787ba2c83ba08eb363089d06df41edd68ff5d048e31fb4116fc1a91bfe7823c872
6
+ metadata.gz: 53e62c64e3f418da9e0f7068482531bab41466e6d73d560e8bf74ae900cf2587a61d810365eea15f01f194592c70b563320ffe93f170810e4fa6f443f3a73837
7
+ data.tar.gz: 112910c06cdb28e5f8690fd18373a2d418a466b075cc0ff793816aed2475acb1ccf5657ae8cfb7b3c321cefc1bde44e5a34fd9b9024f3c20d14047938adf6121
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: "[BUG]"
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Describe the bug
11
+ A clear and concise description of what the bug is.
12
+
13
+ ## To Reproduce
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ ## Expected behavior
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ ## Environment
24
+ - Ruby Version:
25
+ - Rack Version:
26
+ - Puma Version:
27
+
28
+ ## Additional context
29
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: "[FEATURE]"
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Is your feature request related to a problem? Please describe.
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ ## Describe the solution you'd like
14
+ A clear and concise description of what you want to happen.
15
+
16
+ ## Describe alternatives you've considered
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ ## Additional context
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,9 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "06:00"
8
+ open-pull-requests-limit: 10
9
+ rebase-strategy: "disabled"
data/.rubocop.yml CHANGED
@@ -14,6 +14,10 @@ AllCops:
14
14
  Style/Documentation:
15
15
  Enabled: false
16
16
 
17
+ Style/StringConcatenation:
18
+ Exclude:
19
+ - lib/toycol/server.rb
20
+
17
21
  Style/StringLiterals:
18
22
  Enabled: true
19
23
  EnforcedStyle: double_quotes
@@ -25,9 +29,14 @@ Style/StringLiteralsInInterpolation:
25
29
  Layout/LineLength:
26
30
  Max: 120
27
31
 
32
+ Layout/HashAlignment:
33
+ Exclude:
34
+ - lib/toycol/server.rb
35
+
28
36
  Metrics:
29
37
  Exclude:
30
38
  - lib/toycol/proxy.rb
39
+ - lib/toycol/server.rb
31
40
 
32
41
  Metrics/AbcSize:
33
42
  Max: 30
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "toycol", path: "../../"
@@ -0,0 +1,13 @@
1
+ Toycol::Protocol.define do
2
+ request.path do |message|
3
+ %r{(?<path>\/\w*)}.match(message)[:path]
4
+ end
5
+
6
+ request.query do |message|
7
+ %r{\?(?<query>.+)}.match(message) { |m| m[:query] }
8
+ end
9
+
10
+ request.http_method do |message|
11
+ "GET"
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "toycol"
5
+
6
+ Toycol::Protocol.use
7
+
8
+ class App
9
+ def call(env)
10
+ case env["REQUEST_METHOD"]
11
+ when "GET"
12
+ [
13
+ 200,
14
+ { "Content-Type" => "text/html" },
15
+ ["This app has no protocol name\n"]
16
+ ]
17
+ end
18
+ end
19
+ end
20
+
21
+ run App.new
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  Toycol::Protocol.define(:duck) do
4
2
  custom_status_codes(
5
3
  600 => "I'm afraid you are not a duck..."
@@ -7,11 +5,11 @@ Toycol::Protocol.define(:duck) do
7
5
  additional_request_methods "OTHER"
8
6
 
9
7
  request.path do |message|
10
- %r{(?<path>/\w*)}.match(message)[:path]
8
+ %r{(?<path>\/\w*)}.match(message)[:path]
11
9
  end
12
10
 
13
11
  request.query do |message|
14
- /\?(?<query>.+)/.match(message) { |m| m[:query] }
12
+ %r{\<3(?<query>.+)}.match(message) { |m| m[:query] }
15
13
  end
16
14
 
17
15
  request.http_method do |message|
@@ -2,4 +2,5 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
+ gem "puma"
5
6
  gem "toycol", path: "../../"
@@ -4,6 +4,7 @@ source "https://rubygems.org"
4
4
 
5
5
  # git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
+ gem "puma"
7
8
  gem "sinatra"
8
9
  gem "sinatra-contrib"
9
10
  gem "toycol", path: "../../"
data/{bin → exe}/toycol RENAMED
File without changes
@@ -1,24 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rack"
3
4
  require "rack/handler"
4
- require "rack/handler/puma"
5
5
 
6
6
  module Rack
7
7
  module Handler
8
8
  class Toycol
9
- def self.run(app, options = {})
10
- if (child_pid = fork)
11
- environment = ENV["RACK_ENV"] || "development"
12
- default_host = environment == "development" ? "localhost" : "0.0.0.0"
13
-
14
- host = options.delete(:Host) || default_host
15
- port = options.delete(:Port) || "9292"
16
-
17
- ::Toycol::Proxy.new(host, port).start
18
- Process.waitpid(child_pid)
19
- else
20
- puts "Toycol starts Puma in single mode, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
21
- Rack::Handler::Puma.run(app, **{ Host: ::Toycol::UNIX_SOCKET_PATH, Silent: true })
9
+ class << self
10
+ attr_writer :preferred_background_server, :host, :port
11
+
12
+ def run(app, _ = {})
13
+ @app = app
14
+ @host ||= ::Toycol::DEFAULT_HOST
15
+ @port ||= "9292"
16
+
17
+ if (child_pid = fork)
18
+ ::Toycol::Proxy.new(@host, @port).start
19
+ Process.waitpid(child_pid)
20
+ else
21
+ run_background_server
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def select_background_server
28
+ case @preferred_background_server
29
+ when "puma"
30
+ return "puma" if puma_requireable?
31
+
32
+ raise LoadError, "Puma is not installed in your environment."
33
+ when nil
34
+ puma_requireable? ? "puma" : "build_in"
35
+ else
36
+ "build_in"
37
+ end
38
+ rescue LoadError
39
+ Process.kill(:INT, Process.ppid)
40
+ abort
41
+ end
42
+
43
+ def puma_requireable?
44
+ require "rack/handler/puma"
45
+ true
46
+ rescue LoadError
47
+ false
48
+ end
49
+
50
+ def run_background_server
51
+ case select_background_server
52
+ when "puma"
53
+ puts "Toycol starts Puma in single mode, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
54
+ Rack::Handler::Puma.run(@app, **{ Host: ::Toycol::UNIX_SOCKET_PATH, Silent: true })
55
+ else
56
+ puts "Toycol starts build-in server, listening on unix://#{::Toycol::UNIX_SOCKET_PATH}"
57
+ ::Toycol::Server.run(@app, **{ Path: ::Toycol::UNIX_SOCKET_PATH, Port: @port })
58
+ end
22
59
  end
23
60
  end
24
61
  end
data/lib/toycol.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "toycol/const"
6
6
  require_relative "toycol/helper"
7
7
  require_relative "toycol/protocol"
8
8
  require_relative "toycol/proxy"
9
+ require_relative "toycol/server"
9
10
  require_relative "rack/handler/toycol"
10
11
 
11
12
  Dir["#{FileUtils.pwd}/Protocolfile*"].sort.each { |f| load f }
@@ -2,76 +2,124 @@
2
2
 
3
3
  require "optparse"
4
4
  require_relative "./client"
5
+ require_relative "./template_generator"
5
6
 
6
7
  module Toycol
7
8
  class Command
8
9
  class Options
10
+ @options = {}
11
+
9
12
  class << self
10
13
  def parse!(argv)
11
- options = {}
12
- option_parser = create_option_parser
13
- sub_command_parser = create_sub_command_parser
14
+ option_parser = create_option_parser
15
+ sub_command_option_parser = create_sub_command_option_parser
14
16
 
15
17
  begin
16
18
  option_parser.order!(argv)
17
- options[:command] = argv.shift
18
- options[:request_message] = argv.shift if %w[client c].include? options[:command]
19
-
20
- sub_command_parser[options[:command]].parse!(argv)
19
+ @options[:command] = argv.shift
20
+ @options[:request_message] = argv.shift if request_message?(argv.first)
21
+ @options[:protocol_name] = argv.shift if protocol_name?(argv.first)
22
+ sub_command_option_parser[@options[:command]].parse!(argv)
21
23
  rescue OptionParser::MissingArgument, OptionParser::InvalidOption, ArgumentError => e
22
24
  abort e.message
23
25
  end
24
26
 
25
- options
27
+ @options
26
28
  end
27
29
 
28
30
  def create_option_parser
29
31
  OptionParser.new do |opt|
30
- opt.banner = "Usage: #{opt.program_name} [-h|--help] [-v|--version] <command> <args>"
31
- display_adding_summary(opt)
32
-
33
- opt.on_head("-h", "--help", "Show this message") do
34
- puts opt.help
35
- exit
36
- end
32
+ opt.banner = "Usage: #{opt.program_name} [-h|--help] [-v|--version] COMMAND [arg...]"
37
33
 
38
34
  opt.on_head("-v", "--version", "Show Toycol version") do
39
35
  opt.version = Toycol::VERSION
40
36
  puts opt.ver
41
37
  exit
42
38
  end
39
+ opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
40
+
41
+ opt.separator ""
42
+ opt.separator "Sub commands:"
43
+ sub_command_summaries.each do |command|
44
+ opt.separator [opt.summary_indent, command[:name].ljust(31), command[:summary]].join(" ")
45
+ end
43
46
  end
44
47
  end
45
48
 
46
- def create_sub_command_parser
49
+ def create_sub_command_option_parser
47
50
  sub_command_parser = Hash.new { |_k, v| raise ArgumentError, "'#{v}' is not sub command" }
48
- sub_command_parser["client"] = client_option_parser
49
- sub_command_parser["c"] = client_option_parser
51
+ sub_command_parser["client"] = client_option_parser
52
+ sub_command_parser["c"] = client_option_parser
53
+ sub_command_parser["server"] = server_option_parser
54
+ sub_command_parser["s"] = server_option_parser
55
+ sub_command_parser["generate"] = generator_option_parser
56
+ sub_command_parser["g"] = generator_option_parser
50
57
  sub_command_parser
51
58
  end
52
59
 
53
60
  private
54
61
 
62
+ def request_message?(arg)
63
+ %w[client c].include?(@options[:command]) \
64
+ && arg != "-p" \
65
+ && arg != "-h"
66
+ end
67
+
68
+ def protocol_name?(arg)
69
+ %w[geberate g].include?(@options[:command]) \
70
+ && arg != "-t" \
71
+ && arg != "-h"
72
+ end
73
+
74
+ def sub_command_summaries
75
+ [
76
+ { name: "client REQUEST_MESSAGE -p PORT", summary: "Send request message to server" },
77
+ { name: "server -u SERVER_NAME", summary: "Start proxy and background server" },
78
+ { name: "generate NAME -t TYPE", summary: "Generate new protocol or Rack app" }
79
+ ]
80
+ end
81
+
55
82
  def client_option_parser
56
83
  OptionParser.new do |opt|
57
- opt.on("-p=PORT_NUMBER", "--port=PORT_NUMBER", "Set port number") do |n|
58
- ::Toycol::Client.port = n
84
+ opt.on("-p PORT_NUMBER", "--port PORT_NUMBER", "listen on PORT (default: 9292)") do |port|
85
+ ::Toycol::Client.port = port
59
86
  end
87
+
88
+ opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
60
89
  end
61
90
  end
62
91
 
63
- def display_adding_summary(opt)
64
- opt.separator ""
65
- opt.separator "Client command options:"
66
- client_command_help_messages.each do |command|
67
- opt.separator [opt.summary_indent, command[:name].ljust(31), command[:summary]].join(" ")
92
+ def server_option_parser
93
+ OptionParser.new do |opt|
94
+ opt.on("-o HOST", "--host HOST", "bind to HOST (default: localhost)") do |host|
95
+ ::Rack::Handler::Toycol.host = host
96
+ end
97
+
98
+ opt.on("-p PORT_NUMBER", "--port PORT_NUMBER", "listen on PORT (default: 9292)") do |port|
99
+ ::Rack::Handler::Toycol.port = port
100
+ end
101
+
102
+ opt.on("-u SERVER_NAME", "--use SERVER_NAME", "switch using SERVER(puma/build_in)") do |server_name|
103
+ ::Rack::Handler::Toycol.preferred_background_server = server_name
104
+ end
105
+
106
+ opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
68
107
  end
69
108
  end
70
109
 
71
- def client_command_help_messages
72
- [
73
- { name: "client -p=PORT_NUMBER", summary: "Send request to server" }
74
- ]
110
+ def generator_option_parser
111
+ OptionParser.new do |opt|
112
+ opt.on("-t TYPE", "--type TYPE", "generate TYPE of template (default: :all)") do |type|
113
+ @options[:template_type] = type
114
+ end
115
+
116
+ opt.on_head("-h", "--help", "Show this message") { help_command(opt) }
117
+ end
118
+ end
119
+
120
+ def help_command(parser)
121
+ puts parser.help
122
+ exit
75
123
  end
76
124
  end
77
125
  end
@@ -89,7 +137,14 @@ module Toycol
89
137
  command = options.delete(:command)
90
138
 
91
139
  case command
92
- when "client", "c" then ::Toycol::Client.execute!(options[:request_message])
140
+ when "client", "c"
141
+ ::Toycol::Client.execute!(options[:request_message])
142
+ when "server", "s"
143
+ ARGV.push("-q", "-s", "toycol")
144
+ Rack::Server.start
145
+ when "generate", "g"
146
+ type = options[:template_type] || "all"
147
+ ::Toycol::TemplateGenerator.generate!(type: type, name: options[:protocol_name])
93
148
  end
94
149
  end
95
150
  end
data/lib/toycol/const.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toycol
4
+ # For HTTP Protocol
4
5
  DEFAULT_HTTP_REQUEST_METHODS = %w[
5
6
  GET
6
7
  HEAD
@@ -77,5 +78,25 @@ module Toycol
77
78
  511 => "Network Authentication Required"
78
79
  }.freeze
79
80
 
81
+ # For environment
82
+ ENVIRONMENT = ENV["RACK_ENV"] || "development"
83
+ DEFAULT_HOST = ENVIRONMENT == "development" ? "localhost" : "0.0.0.0"
84
+
85
+ # For connection from proxy server to app server
80
86
  UNIX_SOCKET_PATH = ENV["TOYCOL_SOCKET_PATH"] || "/tmp/toycol.socket"
87
+
88
+ # Rack compartible environment
89
+ PATH_INFO = "PATH_INFO"
90
+ QUERY_STRING = "QUERY_STRING"
91
+ REQUEST_METHOD = "REQUEST_METHOD"
92
+ SERVER_NAME = "SERVER_NAME"
93
+ SERVER_PORT = "SERVER_PORT"
94
+ CONTENT_LENGTH = "CONTENT_LENGTH"
95
+ RACK_VERSION = "rack.version"
96
+ RACK_INPUT = "rack.input"
97
+ RACK_ERRORS = "rack.errors"
98
+ RACK_MULTITHREAD = "rack.multithread"
99
+ RACK_MULTIPROCESS = "rack.multiprocess"
100
+ RACK_RUN_ONCE = "rack.run_once"
101
+ RACK_URL_SCHEME = "rack.url_scheme"
81
102
  end
@@ -17,7 +17,7 @@ module Toycol
17
17
  end
18
18
 
19
19
  # For application which use the protocol
20
- def use(protocol_name)
20
+ def use(protocol_name = nil)
21
21
  @protocol_name = protocol_name
22
22
  end
23
23
 
data/lib/toycol/proxy.rb CHANGED
@@ -92,9 +92,11 @@ module Toycol
92
92
  response_message = response_message.join
93
93
  puts "[Toycol] Received response message from server: #{response_message.lines.first}"
94
94
 
95
- _, status_code, status_message = response_message.lines.first.split
95
+ response_line = response_message.lines.first
96
+ status_number = response_line[9..11]
97
+ status_message = response_line[12..].strip
96
98
 
97
- if (custom_message = @protocol.status_message(status_code.to_i)) != status_message
99
+ if (custom_message = @protocol.status_message(status_number.to_i)) != status_message
98
100
  response_message = response_message.sub(status_message, custom_message)
99
101
  puts "[Toycol] Status message has been translated to custom status message: #{custom_message}"
100
102
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+
5
+ module Toycol
6
+ class Server
7
+ BACKLOG = 1024
8
+ CHUNK_SIZE = 1024 * 16
9
+
10
+ class << self
11
+ def run(app, **options)
12
+ new(app, **options).run
13
+ end
14
+ end
15
+
16
+ def initialize(app, **options)
17
+ @app = app
18
+ @path = options[:Path]
19
+ @port = options[:Port]
20
+ @env = default_env
21
+ @returned_status = nil
22
+ @returned_headers = nil
23
+ @returned_body = nil
24
+ end
25
+
26
+ def run
27
+ verify_file_path!
28
+ server = UNIXServer.new @path
29
+ server.listen BACKLOG
30
+
31
+ loop do
32
+ trap(:INT) { exit }
33
+
34
+ socket = server.accept
35
+
36
+ request_message = []
37
+ request_message << socket.readpartial(CHUNK_SIZE) until socket.eof?
38
+ request_message = request_message.join
39
+ assign_parsed_attributes!(request_message)
40
+
41
+ @returned_status, @returned_headers, @returned_body = @app.call(@env)
42
+
43
+ socket.puts response_message
44
+ socket.close_write
45
+ socket.close
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def default_env
52
+ {
53
+ ::Toycol::PATH_INFO => "",
54
+ ::Toycol::QUERY_STRING => "",
55
+ ::Toycol::REQUEST_METHOD => "",
56
+ ::Toycol::SERVER_NAME => "toycol_server",
57
+ ::Toycol::SERVER_PORT => @port.to_s,
58
+ ::Toycol::CONTENT_LENGTH => "0",
59
+ ::Toycol::RACK_VERSION => Rack::VERSION,
60
+ ::Toycol::RACK_INPUT => stringio(""),
61
+ ::Toycol::RACK_ERRORS => $stderr,
62
+ ::Toycol::RACK_MULTITHREAD => false,
63
+ ::Toycol::RACK_MULTIPROCESS => false,
64
+ ::Toycol::RACK_RUN_ONCE => false,
65
+ ::Toycol::RACK_URL_SCHEME => "http"
66
+ }
67
+ end
68
+
69
+ def response_message
70
+ "#{response_status_code}#{response_headers}\r\n#{response_body}"
71
+ end
72
+
73
+ def response_status_code
74
+ "HTTP/1.1 #{@returned_status} #{::Toycol::DEFAULT_HTTP_STATUS_CODES[@returned_status.to_i] || "CUSTOM"}\r\n"
75
+ end
76
+
77
+ def response_headers
78
+ @returned_headers["Content-Length"] = response_body.size unless @returned_headers["Content-Length"]
79
+
80
+ @returned_headers.map { |k, v| "#{k}: #{v}" }.join("\r\n") + "\r\n"
81
+ end
82
+
83
+ def response_body
84
+ res = []
85
+ @returned_body.each { |body| res << body }
86
+ res.join
87
+ end
88
+
89
+ def stringio(body = "")
90
+ StringIO.new(body).set_encoding("ASCII-8BIT")
91
+ end
92
+
93
+ def verify_file_path!
94
+ return unless File.exist? @path
95
+
96
+ begin
97
+ bound_file = UNIXSocket.new @path
98
+ rescue SystemCallError, IOError
99
+ File.unlink @path
100
+ else
101
+ bound_file.close
102
+ raise "[Toycol] Address already in use: #{@path}"
103
+ end
104
+ end
105
+
106
+ def assign_parsed_attributes!(request_message)
107
+ request_line, *request_headers, request_body = request_message.split("\r\n").reject(&:empty?)
108
+ request_method, request_path, = request_line.split
109
+ request_path, query_string = request_path.split("?")
110
+
111
+ @env[::Toycol::REQUEST_METHOD] = request_method
112
+ @env[::Toycol::PATH_INFO] = request_path
113
+ @env[::Toycol::QUERY_STRING] = query_string || ""
114
+ @env[::Toycol::CONTENT_LENGTH]
115
+
116
+ request_headers.each do |request_header|
117
+ k, v = request_header.split(":").map(&:strip)
118
+ @env["::Toycol::#{k.tr("-", "_").upcase}"] = v
119
+ end
120
+
121
+ @env[::Toycol::RACK_INPUT] = stringio(request_body)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toycol
4
+ class TemplateGenerator
5
+ class << self
6
+ def generate!(type:, name:)
7
+ raise StandardError, "Unknown Type: This type of template can't be generated" unless valid? type
8
+
9
+ if type == "all"
10
+ new(name, "protocol").generate!
11
+ new(name, "app").generate!
12
+ else
13
+ new(name, type).generate!
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def valid?(type)
20
+ %w[all app protocol].include? type
21
+ end
22
+ end
23
+
24
+ def initialize(name, type)
25
+ @name = name
26
+ @type = type
27
+ end
28
+
29
+ def generate!
30
+ raise StandardError, "#{filename} already exists" unless Dir.glob(filename).empty?
31
+
32
+ File.open(filename, "w") { |f| f.print template_text_for_new }
33
+ puts "Generate #{filename} in #{FileUtils.pwd}"
34
+ end
35
+
36
+ private
37
+
38
+ def filename
39
+ @filename ||= case @type
40
+ when "protocol" then "Protocolfile#{@name ? ".#{@name}" : nil}"
41
+ when "app" then "config#{@name ? "_#{@name}" : nil}.ru"
42
+ end
43
+ end
44
+
45
+ def template_text_for_new
46
+ if @name
47
+ template_text.sub(":PROTOCOL_NAME", ":#{@name}")
48
+ else
49
+ template_text.sub("\(:PROTOCOL_NAME\)", "")
50
+ end
51
+ end
52
+
53
+ def template_text
54
+ case @type
55
+ when "protocol" then File.open("lib/toycol/templates/protocol.txt", "r", &:read)
56
+ when "app" then File.open("lib/toycol/templates/application.txt", "r", &:read)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ require "rack"
2
+ require "toycol"
3
+
4
+ # Specify which protocol to use
5
+ Toycol::Protocol.use(:PROTOCOL_NAME)
6
+
7
+ class App
8
+ def call(env)
9
+ # Define your app on request method, request path, request query etc
10
+ # For example:
11
+ # case env['REQUEST_METHOD']
12
+ # when 'GET'
13
+ # [
14
+ # 200,
15
+ # { 'Content-Type' => 'text/html' },
16
+ # ["Hello, This app is running by :#{name} protocol."]
17
+ # ]
18
+ # end
19
+ end
20
+ end
21
+
22
+ run App.new
@@ -0,0 +1,39 @@
1
+ Toycol::Protocol.define(:PROTOCOL_NAME) do
2
+ # For example
3
+ # client would send:
4
+ # quack, quack /posts<3user_id=1
5
+ # server would interpret client message:
6
+ # GET /posts?user_id=1
7
+
8
+
9
+ # [OPTIONAL] You can define your additional request methods:
10
+ # For example:
11
+ # additional_request_methods OTHER
12
+
13
+ # [OPTIONAL] You can define your own response status code:
14
+ # For example:
15
+ # define_status_codes(
16
+ # 600 => "I'm afraid you are not a duck..."
17
+ # )
18
+
19
+ # [REQUIRED] Define how you parse request path from request message
20
+ request.path do |message|
21
+ # For example:
22
+ # %r{(?<path>\/\w*)}.match(message)[:path]
23
+ end
24
+
25
+ # [REQUIRED] Define how you parse query from request message
26
+ request.query do |message|
27
+ # For example:
28
+ # %r{\<3(?<query>.+)}.match(message) { |m| m[:query] }
29
+ end
30
+
31
+ # [REQUIRED] Define how you parse query from request message
32
+ request.http_method do |message|
33
+ # For example:
34
+ # case message.scan(/quack/).size
35
+ # when 2 then "GET"
36
+ # else "OTHER"
37
+ # end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toycol
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/toycol.gemspec CHANGED
@@ -24,9 +24,8 @@ Gem::Specification.new do |spec|
24
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
25
  end
26
26
  spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.executables << "toycol"
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_dependency "puma"
31
30
  spec.add_dependency "rack", "~> 2.0"
32
31
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toycol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Misaki Shioi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-20 00:00:00.000000000 Z
11
+ date: 2021-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: puma
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: rack
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -41,10 +27,14 @@ dependencies:
41
27
  description: Toy Application Protocol framework
42
28
  email:
43
29
  - shioi.mm@gmail.com
44
- executables: []
30
+ executables:
31
+ - toycol
45
32
  extensions: []
46
33
  extra_rdoc_files: []
47
34
  files:
35
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
36
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
37
+ - ".github/dependabot.yml"
48
38
  - ".github/workflows/main.yml"
49
39
  - ".gitignore"
50
40
  - ".rubocop.yml"
@@ -56,7 +46,9 @@ files:
56
46
  - Rakefile
57
47
  - bin/console
58
48
  - bin/setup
59
- - bin/toycol
49
+ - examples/anonymous/Gemfile
50
+ - examples/anonymous/Protocolfile
51
+ - examples/anonymous/config.ru
60
52
  - examples/duck/Gemfile
61
53
  - examples/duck/Protocolfile.duck
62
54
  - examples/duck/config_duck.ru
@@ -74,6 +66,7 @@ files:
74
66
  - examples/unsafe_ruby/Gemfile
75
67
  - examples/unsafe_ruby/Protocolfile.unsafe_ruby
76
68
  - examples/unsafe_ruby/config_unsafe_ruby.ru
69
+ - exe/toycol
77
70
  - lib/rack/handler/toycol.rb
78
71
  - lib/toycol.rb
79
72
  - lib/toycol/client.rb
@@ -82,6 +75,10 @@ files:
82
75
  - lib/toycol/helper.rb
83
76
  - lib/toycol/protocol.rb
84
77
  - lib/toycol/proxy.rb
78
+ - lib/toycol/server.rb
79
+ - lib/toycol/template_generator.rb
80
+ - lib/toycol/templates/application.txt
81
+ - lib/toycol/templates/protocol.txt
85
82
  - lib/toycol/version.rb
86
83
  - toycol.gemspec
87
84
  homepage: https://github.com/shioimm/toycol