toycol 0.1.0 → 0.3.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
  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