toycol 0.0.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  3. data/.github/dependabot.yml +9 -0
  4. data/.github/workflows/main.yml +4 -1
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +26 -1
  7. data/CHANGELOG.md +1 -1
  8. data/README.md +2 -4
  9. data/examples/duck/Gemfile +5 -0
  10. data/examples/duck/Protocolfile.duck +23 -0
  11. data/examples/duck/config_duck.ru +59 -0
  12. data/examples/rubylike/Gemfile +5 -0
  13. data/examples/rubylike/Protocolfile.rubylike +18 -0
  14. data/examples/rubylike/config_rubylike.ru +27 -0
  15. data/examples/safe_ruby/Gemfile +6 -0
  16. data/examples/safe_ruby/Protocolfile.safe_ruby +47 -0
  17. data/examples/safe_ruby/config_safe_ruby.ru +55 -0
  18. data/examples/safe_ruby_with_sinatra/Gemfile +10 -0
  19. data/examples/safe_ruby_with_sinatra/Protocolfile.safe_ruby_with_sinatra +44 -0
  20. data/examples/safe_ruby_with_sinatra/app.rb +28 -0
  21. data/examples/safe_ruby_with_sinatra/post.rb +34 -0
  22. data/examples/safe_ruby_with_sinatra/views/index.erb +32 -0
  23. data/examples/unsafe_ruby/Gemfile +5 -0
  24. data/examples/unsafe_ruby/Protocolfile.unsafe_ruby +15 -0
  25. data/examples/unsafe_ruby/config_unsafe_ruby.ru +24 -0
  26. data/exe/toycol +6 -0
  27. data/lib/rack/handler/toycol.rb +65 -0
  28. data/lib/toycol.rb +20 -1
  29. data/lib/toycol/client.rb +38 -0
  30. data/lib/toycol/command.rb +132 -0
  31. data/lib/toycol/const.rb +102 -0
  32. data/lib/toycol/helper.rb +27 -0
  33. data/lib/toycol/protocol.rb +126 -0
  34. data/lib/toycol/proxy.rb +116 -0
  35. data/lib/toycol/server.rb +124 -0
  36. data/lib/toycol/version.rb +1 -1
  37. data/toycol.gemspec +3 -7
  38. metadata +48 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e39fd4d4d223eef7eb8e86059fd061ebbb24259a1259b7f5b8bef228d6d9836
4
- data.tar.gz: 8d90ae7ed408d76a46da7726a280560d245e1372bca9d2a0857efe7bc22d6a3d
3
+ metadata.gz: 102026f42040d677807fb5fe1cd08797f564951096c15ac2c0150a757cc82926
4
+ data.tar.gz: d7b4312381d610821b197f688c871acbe26a6d70146c67441d23cbd52a0ed697
5
5
  SHA512:
6
- metadata.gz: 10d74fd171b7eec5618b1aeca4035a3f9bff05c12864285fb43fa83d1d53a97523276a19ac3a6185929e82854ce3701dd0f7d5963a55c38536cbe71828327809
7
- data.tar.gz: a73c3c5c7992ba1bebb34f8db9bfc8061a066a6c2b30ead33c8dfdc79a8b540092f540b793dd67c164dc277766dd7e6ce030b00b17d30148c28f5993fab4157e
6
+ metadata.gz: 11eb3611d926c804f1f0d632f40daf2a37b7f663bdb449c69c2353d608dba87161723f4380d5dedfa78a46f1d264dde625b9bdd30ba6dda6e64eed12964f83cf
7
+ data.tar.gz: 9c38815ef3861ad35ac635f713fb2f6b459ee6cbc64cbe9da56ec355381a5f21c30189e28ebe92f961406b99d9349631b52571729fd8c290d94cd61c0eefd4ea
@@ -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,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"
@@ -5,12 +5,15 @@ on: [push,pull_request]
5
5
  jobs:
6
6
  build:
7
7
  runs-on: ubuntu-latest
8
+ strategy:
9
+ matrix:
10
+ ruby: [2.6, 2.7, 3.0, head]
8
11
  steps:
9
12
  - uses: actions/checkout@v2
10
13
  - name: Set up Ruby
11
14
  uses: ruby/setup-ruby@v1
12
15
  with:
13
- ruby-version: 3.0.0
16
+ ruby-version: ${{ matrix.ruby }}
14
17
  bundler-cache: true
15
18
  - name: Run the default task
16
19
  run: bundle exec rake
data/.gitignore CHANGED
@@ -7,4 +7,5 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ .ruby-version
10
11
  Gemfile.lock
data/.rubocop.yml CHANGED
@@ -9,7 +9,14 @@ AllCops:
9
9
  - 'Rakefile'
10
10
  - 'toycol.gemspec'
11
11
  NewCops: enable
12
- TargetRubyVersion: 3.0
12
+ TargetRubyVersion: 2.6
13
+
14
+ Style/Documentation:
15
+ Enabled: false
16
+
17
+ Style/StringConcatenation:
18
+ Exclude:
19
+ - lib/toycol/server.rb
13
20
 
14
21
  Style/StringLiterals:
15
22
  Enabled: true
@@ -21,3 +28,21 @@ Style/StringLiteralsInInterpolation:
21
28
 
22
29
  Layout/LineLength:
23
30
  Max: 120
31
+
32
+ Layout/HashAlignment:
33
+ Exclude:
34
+ - lib/toycol/server.rb
35
+
36
+ Metrics:
37
+ Exclude:
38
+ - lib/toycol/proxy.rb
39
+ - lib/toycol/server.rb
40
+
41
+ Metrics/AbcSize:
42
+ Max: 30
43
+
44
+ Metrics/ClassLength:
45
+ Max: 400
46
+
47
+ Metrics/MethodLength:
48
+ Max: 15
data/CHANGELOG.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2021-07-12
3
+ ## [0.0.1] - 2021-07-12
4
4
 
5
5
  - Initial release
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # Toycol
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/toycol`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
3
+ Toy Application Protocol framework
6
4
 
7
5
  ## Installation
8
6
 
@@ -22,7 +20,7 @@ Or install it yourself as:
22
20
 
23
21
  ## Usage
24
22
 
25
- TODO: Write usage instructions here
23
+ WIP
26
24
 
27
25
  ## Development
28
26
 
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "toycol", path: "../../"
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Toycol::Protocol.define(:duck) do
4
+ custom_status_codes(
5
+ 600 => "I'm afraid you are not a duck..."
6
+ )
7
+ additional_request_methods "OTHER"
8
+
9
+ request.path do |message|
10
+ %r{(?<path>/\w*)}.match(message)[:path]
11
+ end
12
+
13
+ request.query do |message|
14
+ /\?(?<query>.+)/.match(message) { |m| m[:query] }
15
+ end
16
+
17
+ request.http_method do |message|
18
+ case message.scan(/quack/).size
19
+ when 2 then "GET"
20
+ else "OTHER"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "toycol"
5
+
6
+ Toycol::Protocol.use(:duck)
7
+
8
+ # Duck protocol application
9
+ class App
10
+ def call(env)
11
+ case env["REQUEST_METHOD"]
12
+ when "GET"
13
+ case env["PATH_INFO"]
14
+ when "/posts"
15
+ return app_for_get_with_query if env["QUERY_STRING"] == "user_id=1"
16
+
17
+ app_for_get
18
+ when "/" then app_for_get_to_root
19
+ end
20
+ when "OTHER" then app_for_other
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def app_for_get_with_query
27
+ [
28
+ 200,
29
+ { "Content-Type" => "text/html" },
30
+ ["quack quack, I am the No.1 duck\n"]
31
+ ]
32
+ end
33
+
34
+ def app_for_get
35
+ [
36
+ 200,
37
+ { "Content-Type" => "text/html" },
38
+ ["quack quack, quack quack, quack, quack\n", "quack quack, I am the No.1 duck\n"]
39
+ ]
40
+ end
41
+
42
+ def app_for_get_to_root
43
+ [
44
+ 200,
45
+ { "Content-Type" => "text/html" },
46
+ ["Hello, This app is running on Sample duck protocol.\n"]
47
+ ]
48
+ end
49
+
50
+ def app_for_other
51
+ [
52
+ 600,
53
+ { "Content-Type" => "text/html" },
54
+ ["Sorry, this application is only for ducks...\n"]
55
+ ]
56
+ end
57
+ end
58
+
59
+ run App.new
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "toycol", path: "../../"
@@ -0,0 +1,18 @@
1
+ Toycol::Protocol.define(:rubylike) do
2
+ custom_status_codes(
3
+ 700 => "Hmm...",
4
+ )
5
+
6
+ additional_request_methods "OTHER"
7
+
8
+ request.path do |message|
9
+ /['"](?<path>.+)['"]/.match(message)[:path]
10
+ end
11
+
12
+ request.http_method do |message|
13
+ case /\.(?<method>[A-z]+)/.match(message)&.captures&.first
14
+ when "get" then "GET"
15
+ else "OTHER"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "toycol"
5
+
6
+ Toycol::Protocol.use(:rubylike)
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
+ ["I love ruby!\n", "I love RubyKaigi!\n"]
16
+ ]
17
+ when "OTHER"
18
+ [
19
+ 700,
20
+ { "Content-Type" => "text/html" },
21
+ ["Sorry, but I'd like you to speak more like a Ruby programmer...\n"]
22
+ ]
23
+ end
24
+ end
25
+ end
26
+
27
+ run App.new
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "puma"
6
+ gem "toycol", path: "../../"
@@ -0,0 +1,47 @@
1
+ module SafeRuby
2
+ PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[A-z]+)/
3
+ QUERY_REGEX = /query?.*\{(?<query>.*)\}/
4
+ INPUT_REGEX = /input?.*\{(?<input>.*)\}/
5
+ end
6
+
7
+ Toycol::Protocol.define(:safe_ruby) do |message|
8
+ using Module.new {
9
+ refine String do
10
+ # Ex. '/posts'.get
11
+ # Ex. (with query string) '/posts'.get(query: { user_id: 2 })
12
+ def get(options = {})
13
+ Toycol::Protocol.request.query { options[:query] } if options[:query]
14
+ Toycol::Protocol.request.http_method { "GET" }
15
+ end
16
+
17
+ # Ex. '/posts'.post(input: { user_id: 1, body: 'This is a post request' })
18
+ def post(options = {})
19
+ Toycol::Protocol.request.input { options[:input] } if options[:input]
20
+ Toycol::Protocol.request.http_method { "POST" }
21
+ end
22
+
23
+ def parse_as_queries
24
+ split(",").map { |str| str.scan(/\w+/).join("=") }
25
+ end
26
+
27
+ def parse_as_inputs
28
+ split(",").map { |str| str.split(":").map { |s| s.strip! && s.gsub(/['"]/, "") }.join("=") }
29
+ end
30
+ end
31
+ }
32
+
33
+ path, method = SafeRuby::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }
34
+ query = SafeRuby::QUERY_REGEX.match(message) { |m| m[:query].parse_as_queries }&.join("&")
35
+ input = SafeRuby::INPUT_REGEX.match(message) { |m| m[:input].parse_as_inputs }&.join("&")
36
+ args = {}
37
+
38
+ request.path { path }
39
+
40
+ %i[query input].each do |k|
41
+ if v = binding.local_variable_get(k)
42
+ args[*k] = v
43
+ end
44
+ end
45
+
46
+ request_path.public_send(method, args)
47
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "toycol"
5
+
6
+ Toycol::Protocol.use(:safe_ruby)
7
+
8
+ class App
9
+ def call(env)
10
+ case env["REQUEST_METHOD"]
11
+ when "GET"
12
+ case env["PATH_INFO"]
13
+ when "/posts"
14
+ return app_for_get_with_query if env["QUERY_STRING"] == "user_id=2"
15
+
16
+ app_for_get
17
+ end
18
+ when "POST"
19
+ input = env["rack.input"].gets
20
+ created = input.split("&").map { |str| str.split("=") }.to_h
21
+
22
+ app_for_post(user_id: created["user_id"], body: created["body"])
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def app_for_get_with_query
29
+ [
30
+ 200,
31
+ { "Content-Type" => "text/html" },
32
+ ["User<2> I love RubyKaigi!\n"]
33
+ ]
34
+ end
35
+
36
+ def app_for_get
37
+ [
38
+ 200,
39
+ { "Content-Type" => "text/html" },
40
+ ["User<1> I love Ruby!\n", "User<2> I love RubyKaigi!\n"]
41
+ ]
42
+ end
43
+
44
+ def app_for_post(user_id:, body:)
45
+ [
46
+ 201,
47
+ { "Content-Type" => "text/html", "Location" => "/posts" },
48
+ ["User<1> I love Ruby!\n",
49
+ "User<2> I love RubyKaigi!\n",
50
+ "User<#{user_id}> #{body}\n"]
51
+ ]
52
+ end
53
+ end
54
+
55
+ run App.new
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ gem "puma"
8
+ gem "sinatra"
9
+ gem "sinatra-contrib"
10
+ gem "toycol", path: "../../"
@@ -0,0 +1,44 @@
1
+ module SafeRubyWithSinatra
2
+ PARSER_REGEX = /["'](?<path>\/.*)["']\.(?<method>[A-z]+)/
3
+ QUERY_REGEX = /query?.*\{(?<query>.*)\}/
4
+ INPUT_REGEX = /input?.*\{(?<input>.*)\}/
5
+ end
6
+
7
+ Toycol::Protocol.define(:safe_ruby_with_sinatra) do |message|
8
+ using Module.new {
9
+ refine String do
10
+ def get(options = {})
11
+ Toycol::Protocol.request.query { options[:query] } if options[:query]
12
+ Toycol::Protocol.request.http_method { "GET" }
13
+ end
14
+
15
+ def post(options = {})
16
+ Toycol::Protocol.request.input { options[:input] } if options[:input]
17
+ Toycol::Protocol.request.http_method { "POST" }
18
+ end
19
+
20
+ def parse_as_queries
21
+ split(",").map { |str| str.scan(/\w+/).join("=") }
22
+ end
23
+
24
+ def parse_as_inputs
25
+ split(",").map { |str| str.split(":").map { |s| s.strip! && s.gsub(/['"]/, "") }.join("=") }
26
+ end
27
+ end
28
+ }
29
+
30
+ path, method = SafeRubyWithSinatra::PARSER_REGEX.match(message) { |m| [m[:path], m[:method]] }
31
+ query = SafeRubyWithSinatra::QUERY_REGEX.match(message) { |m| m[:query].parse_as_queries }&.join("&")
32
+ input = SafeRubyWithSinatra::INPUT_REGEX.match(message) { |m| m[:input].parse_as_inputs }&.join("&")
33
+ args = {}
34
+
35
+ request.path { path }
36
+
37
+ %i[query input].each do |k|
38
+ if v = binding.local_variable_get(k)
39
+ args[*k] = v
40
+ end
41
+ end
42
+
43
+ request_path.public_send(method, args)
44
+ end