rage-rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Rage::Router
6
+ class Node
7
+ STATIC = 0
8
+ PARAMETRIC = 1
9
+ WILDCARD = 2
10
+
11
+ attr_reader :is_leaf_node, :handler_storage, :kind
12
+
13
+ def initialize
14
+ @is_leaf_node = false
15
+ @routes = nil
16
+ @handler_storage = nil
17
+ end
18
+
19
+ def add_route(route, constrainer)
20
+ @routes ||= []
21
+ @handler_storage ||= HandlerStorage.new
22
+
23
+ @is_leaf_node = true
24
+ @routes << route
25
+ @handler_storage.add_handler(constrainer, route)
26
+ end
27
+ end
28
+
29
+ class ParentNode < Node
30
+ attr_reader :static_children
31
+
32
+ def initialize
33
+ super
34
+ @static_children = {}
35
+ end
36
+
37
+ def find_static_matching_child(path, path_index)
38
+ static_child = @static_children[path[path_index]]
39
+
40
+ if !static_child || !static_child.match_prefix.call(path, path_index)
41
+ return nil
42
+ end
43
+
44
+ static_child
45
+ end
46
+
47
+ def create_static_child(path)
48
+ if path.length == 0
49
+ return self
50
+ end
51
+
52
+ static_child = @static_children[path[0]]
53
+ if static_child
54
+ i = 1
55
+ while i < static_child.prefix.length
56
+ if path[i] != static_child.prefix[i]
57
+ static_child = static_child.split(self, i)
58
+ break
59
+ end
60
+ i += 1
61
+ end
62
+
63
+ return static_child.create_static_child(path[i, path.length - i])
64
+ end
65
+
66
+ @static_children[path[0]] = StaticNode.new(path)
67
+ end
68
+ end
69
+
70
+ class StaticNode < ParentNode
71
+ attr_reader :prefix, :match_prefix
72
+
73
+ def initialize(prefix)
74
+ super()
75
+ @prefix = prefix
76
+ @wildcard_child = nil
77
+ @parametric_children = []
78
+ @kind = Node::STATIC
79
+
80
+ compile_prefix_match
81
+ end
82
+
83
+ def create_parametric_child(static_suffix, node_path)
84
+ parametric_child = @parametric_children[0]
85
+
86
+ if parametric_child
87
+ parametric_child.node_paths.add(node_path)
88
+ return parametric_child
89
+ end
90
+
91
+ parametric_child = ParametricNode.new(static_suffix, node_path)
92
+ @parametric_children << parametric_child
93
+ @parametric_children.sort! do |child1, child2|
94
+ if child1.static_suffix.nil?
95
+ 1
96
+ elsif child2.static_suffix.nil?
97
+ -1
98
+ elsif child2.static_suffix.end_with?(child1.static_suffix)
99
+ 1
100
+ elsif child1.static_suffix.end_with?(child2.static_suffix)
101
+ -1
102
+ else
103
+ 0
104
+ end
105
+ end
106
+
107
+ parametric_child
108
+ end
109
+
110
+ def create_wildcard_child
111
+ @wildcard_child ||= WildcardNode.new
112
+ end
113
+
114
+ def split(parent_node, length)
115
+ parent_prefix = @prefix[0, length]
116
+ child_prefix = @prefix[length, @prefix.length - length]
117
+
118
+ @prefix = child_prefix
119
+ compile_prefix_match
120
+
121
+ static_node = StaticNode.new(parent_prefix)
122
+ static_node.static_children[child_prefix[0]] = self
123
+ parent_node.static_children[parent_prefix[0]] = static_node
124
+
125
+ static_node
126
+ end
127
+
128
+ def get_next_node(path, path_index, node_stack, params_count)
129
+ node = find_static_matching_child(path, path_index)
130
+ parametric_brother_node_index = 0
131
+
132
+ unless node
133
+ return @wildcard_child if @parametric_children.empty?
134
+
135
+ node = @parametric_children[0]
136
+ parametric_brother_node_index = 1
137
+ end
138
+
139
+ if @wildcard_child
140
+ node_stack << {
141
+ params_count: params_count,
142
+ brother_path_index: path_index,
143
+ brother_node: @wildcard_child
144
+ }
145
+ end
146
+
147
+ i = @parametric_children.length - 1
148
+ while i >= parametric_brother_node_index
149
+ node_stack << {
150
+ params_count: params_count,
151
+ brother_path_index: path_index,
152
+ brother_node: @parametric_children[i]
153
+ }
154
+ i -= 1
155
+ end
156
+
157
+ node
158
+ end
159
+
160
+ private
161
+
162
+ def compile_prefix_match
163
+ if @prefix.length == 1
164
+ @match_prefix = ->(_, _) { true }
165
+ return
166
+ end
167
+
168
+ lines = (1...@prefix.length).map do |i|
169
+ "path[i + #{i}]&.ord == #{@prefix[i].ord}"
170
+ end
171
+
172
+ @match_prefix = eval("->(path, i) { #{lines.join(" && ")} }")
173
+ end
174
+ end
175
+
176
+ class ParametricNode < ParentNode
177
+ attr_reader :static_suffix, :node_paths
178
+
179
+ def initialize(static_suffix, node_path)
180
+ super()
181
+ @static_suffix = static_suffix
182
+ @kind = Node::PARAMETRIC
183
+
184
+ @node_paths = Set.new([node_path])
185
+ end
186
+
187
+ def get_next_node(path, path_index, _, _)
188
+ find_static_matching_child(path, path_index)
189
+ end
190
+ end
191
+
192
+ class WildcardNode < Node
193
+ def initialize
194
+ super
195
+ @kind = Node::WILDCARD
196
+ end
197
+
198
+ def get_next_node(*)
199
+ nil
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Rage::Router::Strategies::Host
4
+ attr_reader :name, :must_match_when_derived
5
+
6
+ def initialize
7
+ @name = "host"
8
+ @must_match_when_derived = false
9
+ end
10
+
11
+ def storage
12
+ HostStorage.new
13
+ end
14
+
15
+ def custom?
16
+ false
17
+ end
18
+
19
+ def validate(value)
20
+ if !value.is_a?(String) && !value.is_a?(Regexp)
21
+ raise "Host should be a string or a Regexp"
22
+ end
23
+ end
24
+
25
+ class HostStorage
26
+ def initialize
27
+ @hosts = {}
28
+ @regexp_hosts = []
29
+ end
30
+
31
+ def get(host)
32
+ exact = @hosts[host]
33
+ return exact if exact
34
+
35
+ @regexp_hosts.each do |regexp|
36
+ return regexp[:value] if regexp[:host] =~ host.to_s
37
+ end
38
+
39
+ nil
40
+ end
41
+
42
+ def set(host, value)
43
+ if host.is_a?(Regexp)
44
+ @regexp_hosts << { host: host, value: value }
45
+ else
46
+ @hosts[host] = value
47
+ end
48
+ end
49
+ end
50
+ end
data/lib/rage/setup.rb ADDED
@@ -0,0 +1,6 @@
1
+ Iodine.patch_rack
2
+
3
+ project_root = Pathname.new(".").expand_path
4
+
5
+ Dir["#{project_root}/app/**/*.rb"].each { |path| require_relative path }
6
+ require_relative "#{project_root}/config/routes"
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rage"
4
+
5
+ # Build JSON APIs with ease
6
+ # gem "alba"
@@ -0,0 +1,2 @@
1
+ class ApplicationController < RageController::API
2
+ end
@@ -0,0 +1,4 @@
1
+ require "bundler/setup"
2
+ Bundler.require(:default)
3
+
4
+ require "rage/setup"
@@ -0,0 +1,3 @@
1
+ Rage.routes.draw do
2
+ root to: ->(env) { [200, {}, "It works!"] }
3
+ end
@@ -0,0 +1,3 @@
1
+ require_relative "config/application"
2
+
3
+ run Rage.application
File without changes
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rage
4
+ VERSION = "0.1.0"
5
+ end
data/lib/rage.rb ADDED
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack"
4
+ require "json"
5
+ require "iodine"
6
+
7
+ module Rage
8
+ def self.application
9
+ Application.new(__router)
10
+ end
11
+
12
+ def self.routes
13
+ Rage::Router::DSL.new(__router)
14
+ end
15
+
16
+ def self.__router
17
+ @__router ||= Rage::Router::Backend.new
18
+ end
19
+
20
+ module Router
21
+ module Strategies
22
+ end
23
+ end
24
+ end
25
+
26
+ module RageController
27
+ end
28
+
29
+ require_relative "rage/application"
30
+ require_relative "rage/fiber"
31
+ require_relative "rage/fiber_scheduler"
32
+
33
+ require_relative "rage/router/strategies/host"
34
+ require_relative "rage/router/backend"
35
+ require_relative "rage/router/constrainer"
36
+ require_relative "rage/router/dsl"
37
+ require_relative "rage/router/handler_storage"
38
+ require_relative "rage/router/node"
39
+
40
+ require_relative "rage/controller/api"
data/rage.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/rage/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "rage-rb"
7
+ spec.version = Rage::VERSION
8
+ spec.authors = ["Roman Samoilov"]
9
+ spec.email = ["rsamoi@icloud.com"]
10
+
11
+ spec.summary = "Fast web framework compatible with Rails."
12
+ spec.homepage = "https://github.com/rage-rb/rage"
13
+ spec.license = "MIT"
14
+ spec.required_ruby_version = ">= 3.0.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/rage-rb/rage"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(__dir__) do
22
+ `git ls-files -z`.split("\x0").reject do |f|
23
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
24
+ end
25
+ end
26
+ spec.bindir = "exe"
27
+ spec.executables = ["rage"]
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "thor"
31
+ spec.add_dependency "rack", "~> 2.0"
32
+ spec.add_dependency "rage-iodine", "~> 1.0"
33
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rage-rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roman Samoilov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-09-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
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
+ - !ruby/object:Gem::Dependency
28
+ name: rack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rage-iodine
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description:
56
+ email:
57
+ - rsamoi@icloud.com
58
+ executables:
59
+ - rage
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".rspec"
64
+ - CHANGELOG.md
65
+ - CODE_OF_CONDUCT.md
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - exe/rage
71
+ - lib/rage.rb
72
+ - lib/rage/application.rb
73
+ - lib/rage/cli.rb
74
+ - lib/rage/controller/api.rb
75
+ - lib/rage/fiber.rb
76
+ - lib/rage/fiber_scheduler.rb
77
+ - lib/rage/router/README.md
78
+ - lib/rage/router/backend.rb
79
+ - lib/rage/router/constrainer.rb
80
+ - lib/rage/router/dsl.rb
81
+ - lib/rage/router/handler_storage.rb
82
+ - lib/rage/router/node.rb
83
+ - lib/rage/router/strategies/host.rb
84
+ - lib/rage/setup.rb
85
+ - lib/rage/templates/Gemfile
86
+ - lib/rage/templates/app-controllers-application_controller.rb
87
+ - lib/rage/templates/config-application.rb
88
+ - lib/rage/templates/config-routes.rb
89
+ - lib/rage/templates/config.ru
90
+ - lib/rage/templates/lib-.keep
91
+ - lib/rage/templates/log-.keep
92
+ - lib/rage/templates/public-.keep
93
+ - lib/rage/version.rb
94
+ - rage.gemspec
95
+ homepage: https://github.com/rage-rb/rage
96
+ licenses:
97
+ - MIT
98
+ metadata:
99
+ homepage_uri: https://github.com/rage-rb/rage
100
+ source_code_uri: https://github.com/rage-rb/rage
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 3.0.0
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubygems_version: 3.4.10
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Fast web framework compatible with Rails.
120
+ test_files: []