rage-rb 0.5.1 → 0.5.2

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: 15d0d4682a307384b3e291b5b9d2f4f0323fc5ca74966b06a856a1f1367da01d
4
- data.tar.gz: 4a5d0f7ef64f5b70615e0b1f43a170ba7072b0b44d908858cf43d5dc4cc3fb2d
3
+ metadata.gz: abee0787faa5947d8ecb03f9c5851cb669295bc2bd52abe5513e7b10940ddfff
4
+ data.tar.gz: d167a4d6a48dbced809578f3c3a569c5ccdb913ed7bf51eba3e8d054b61e1c75
5
5
  SHA512:
6
- metadata.gz: d7c3a2cf837d4038618e925508af8b536040927cc9506beaed3f312f1176903dc5a0a1141c47321eb5b7f2ba20a21e4e9e635d54e098ed7b4e04ec93fba08da0
7
- data.tar.gz: e48c1bf19c83f09a21f5b0c8c731db109a24fe0c37de870454b2b155c5d85be98c890d555bec1b78a00c19f91e7a25a3cbc99df6937561d30a88e96027d10e4d
6
+ metadata.gz: eea4d03aa25b675fdf1f19836037dceaeb7f38e2227af9cafc1609d59b8f322d3e3fa49da7905b4c0dd783615d16ce6e592aa11f2fa934056d711f03aaafbb43
7
+ data.tar.gz: adf6eaaee9a88eef39631581ce52eaa311b5ac61797f481a95e28502ccabf166615a87e215b5f5b3260ebdfbb5c28f8c32e1e8342566d9d23836e56eb97b14d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.2] - 2023-12-11
4
+
5
+ ### Added
6
+
7
+ - Add env class (#43).
8
+
9
+ ### Changed
10
+
11
+ - Schedule request Fibers in a separate middleware (#48).
12
+
3
13
  ## [0.5.1] - 2023-12-01
4
14
 
5
15
  ### Fixed
data/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  [![Gem Version](https://badge.fury.io/rb/rage-rb.svg)](https://badge.fury.io/rb/rage-rb)
6
6
  ![Tests](https://github.com/rage-rb/rage/actions/workflows/main.yml/badge.svg)
7
+ ![Ruby Requirement](https://img.shields.io/badge/Ruby-3.1%2B-%23f40000)
8
+
7
9
 
8
10
  Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github.com/rage-rb/iodine), this is a Ruby web framework that is based on the following design principles:
9
11
 
@@ -11,7 +13,7 @@ Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github
11
13
 
12
14
  * **High performance** - some think performance is not a major metric for a framework, but it's not true. Poor performance is a risk, and in today's world, companies refuse to use risky technologies.
13
15
 
14
- * **API-only** - the only technology we should be using to create web UI is JavaScript. Using native technologies is always the most flexible, scalable, and simple solution in the long run. Check out [Vite](https://vitejs.dev) if you don't know where to start.
16
+ * **API-only** - separation of concerns is one of the most fundamental principles in software development. Backend and frontend are very different layers with different goals and paths to those goals. Separating BE code from FE code results in a much more sustainable architecture compared with classic Rails monoliths.
15
17
 
16
18
  * **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on IO.
17
19
 
data/lib/rage/all.rb CHANGED
@@ -9,6 +9,7 @@ require_relative "request"
9
9
  require_relative "uploaded_file"
10
10
  require_relative "errors"
11
11
  require_relative "params_parser"
12
+ require_relative "fiber_wrapper"
12
13
 
13
14
  require_relative "router/strategies/host"
14
15
  require_relative "router/backend"
@@ -2,41 +2,28 @@
2
2
 
3
3
  class Rage::Application
4
4
  def initialize(router)
5
- Iodine.on_state(:on_start) do
6
- Fiber.set_scheduler(Rage::FiberScheduler.new)
7
- end
8
5
  @router = router
9
6
  end
10
7
 
11
8
  def call(env)
12
- fiber = Fiber.schedule do
13
- init_logger
14
-
15
- handler = @router.lookup(env)
9
+ init_logger
16
10
 
17
- response = if handler
18
- params = Rage::ParamsParser.prepare(env, handler[:params])
19
- handler[:handler].call(env, params)
20
- else
21
- [404, {}, ["Not Found"]]
22
- end
11
+ handler = @router.lookup(env)
23
12
 
24
- rescue Exception => e
25
- exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
26
- Rage.logger.error(exception_str)
27
- response = [500, {}, [exception_str]]
28
-
29
- ensure
30
- finalize_logger(env, response, params)
31
- Iodine.publish(env["IODINE_REQUEST_ID"], "") # notify Iodine the request can now be served
32
- end
33
-
34
- # the fiber encountered blocking IO and yielded; instruct Iodine to pause the request;
35
- if fiber.alive?
36
- [:__http_defer__, fiber]
13
+ response = if handler
14
+ params = Rage::ParamsParser.prepare(env, handler[:params])
15
+ handler[:handler].call(env, params)
37
16
  else
38
- fiber.__get_result
17
+ [404, {}, ["Not Found"]]
39
18
  end
19
+
20
+ rescue Exception => e
21
+ exception_str = "#{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}"
22
+ Rage.logger.error(exception_str)
23
+ response = [500, {}, [exception_str]]
24
+
25
+ ensure
26
+ finalize_logger(env, response, params)
40
27
  end
41
28
 
42
29
  private
data/lib/rage/cli.rb CHANGED
@@ -20,6 +20,13 @@ module Rage
20
20
  app = ::Rack::Builder.parse_file("config.ru")
21
21
  app = app[0] if app.is_a?(Array)
22
22
 
23
+ unless app.is_a?(Rage::FiberWrapper)
24
+ raise <<-ERR
25
+ Couldn't find the default middleware. Make sure to add the following line to your config.ru file:
26
+ Rage.load_middlewares(self)
27
+ ERR
28
+ end
29
+
23
30
  ::Iodine.listen service: :http, handler: app, port: options[:port] || Rage.config.server.port
24
31
  ::Iodine.threads = Rage.config.server.threads_count
25
32
  ::Iodine.workers = Rage.config.server.workers_count
@@ -8,6 +8,10 @@ class Rage::Configuration
8
8
  @server ||= Server.new
9
9
  end
10
10
 
11
+ def middleware
12
+ @middleware ||= Middleware.new
13
+ end
14
+
11
15
  class Server
12
16
  attr_accessor :port, :workers_count
13
17
  attr_reader :threads_count
@@ -19,6 +23,43 @@ class Rage::Configuration
19
23
  end
20
24
  end
21
25
 
26
+ class Middleware
27
+ attr_reader :middlewares
28
+
29
+ def initialize
30
+ @middlewares = [[Rage::FiberWrapper]]
31
+ end
32
+
33
+ def use(new_middleware, *args, &block)
34
+ insert_after(@middlewares.length - 1, new_middleware, *args, &block)
35
+ end
36
+
37
+ def insert_before(existing_middleware, new_middleware, *args, &block)
38
+ index = find_middleware_index(existing_middleware)
39
+ @middlewares = (@middlewares[0...index] + [[new_middleware, args, block]] + @middlewares[index..]).uniq(&:first)
40
+ end
41
+
42
+ def insert_after(existing_middleware, new_middleware, *args, &block)
43
+ index = find_middleware_index(existing_middleware)
44
+ @middlewares = (@middlewares[0..index] + [[new_middleware, args, block]] + @middlewares[index + 1..]).uniq(&:first)
45
+ end
46
+
47
+ private
48
+
49
+ def find_middleware_index(middleware)
50
+ if middleware.is_a?(Integer)
51
+ if middleware < 0 || middleware >= @middlewares.length
52
+ raise ArgumentError, "Middleware index should be in the (0...#{@middlewares.length}) range"
53
+ end
54
+ middleware
55
+ else
56
+ @middlewares.index { |m, _, _| m == middleware }.tap do |i|
57
+ raise ArgumentError, "Couldn't find #{middleware} in the middleware stack" unless i
58
+ end
59
+ end
60
+ end
61
+ end
62
+
22
63
  def __finalize
23
64
  @logger ||= Rage::Logger.new(nil)
24
65
  @logger.formatter = @log_formatter if @logger && @log_formatter
data/lib/rage/env.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Rage::Env
4
+ STANDARD_ENVS = %w(development test staging production)
5
+
6
+ def initialize(env)
7
+ @env = env
8
+
9
+ STANDARD_ENVS.each do |standard_env|
10
+ self.class.define_method("#{standard_env}?") { false } if standard_env != @env
11
+ end
12
+ self.class.define_method("#{@env}?") { true }
13
+ end
14
+
15
+ def method_missing(method_name, *, &)
16
+ method_name.end_with?("?") ? false : super
17
+ end
18
+
19
+ def respond_to_missing?(method_name, include_private = false)
20
+ method_name.end_with?("?")
21
+ end
22
+
23
+ def ==(other)
24
+ @env == other
25
+ end
26
+
27
+ def to_sym
28
+ @env.to_sym
29
+ end
30
+
31
+ def to_s
32
+ @env
33
+ end
34
+ end
data/lib/rage/fiber.rb CHANGED
@@ -23,6 +23,16 @@ class Fiber
23
23
  @__err
24
24
  end
25
25
 
26
+ # @private
27
+ def __get_id
28
+ @__rage_id ||= object_id.to_s
29
+ end
30
+
31
+ # @private
32
+ def __yielded?
33
+ !@__rage_id.nil?
34
+ end
35
+
26
36
  # @private
27
37
  # pause a fiber and resume in the next iteration of the event loop
28
38
  def self.pause
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ ##
4
+ # The middleware wraps every request in a Fiber and implements the custom defer protocol with Iodine.
5
+ # Scheduling fibers in a middleware allows the framework to be compatibe with custom Rack middlewares.
6
+ #
7
+ class Rage::FiberWrapper
8
+ def initialize(app)
9
+ Iodine.on_state(:on_start) do
10
+ Fiber.set_scheduler(Rage::FiberScheduler.new)
11
+ end
12
+ @app = app
13
+ end
14
+
15
+ def call(env)
16
+ fiber = Fiber.schedule do
17
+ @app.call(env)
18
+ ensure
19
+ Iodine.publish(Fiber.current.__get_id, "", Iodine::PubSub::PROCESS) if Fiber.current.__yielded?
20
+ end
21
+
22
+ if fiber.alive?
23
+ [:__http_defer__, fiber]
24
+ else
25
+ fiber.__get_result
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,4 @@
1
1
  require_relative "config/application"
2
2
 
3
3
  run Rage.application
4
+ Rage.load_middlewares(self)
data/lib/rage/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rage
4
- VERSION = "0.5.1"
4
+ VERSION = "0.5.2"
5
5
  end
data/lib/rage-rb.rb CHANGED
@@ -28,7 +28,7 @@ module Rage
28
28
  end
29
29
 
30
30
  def self.env
31
- @__env ||= ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
31
+ @__env ||= Rage::Env.new(ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
32
32
  end
33
33
 
34
34
  def self.groups
@@ -43,6 +43,12 @@ module Rage
43
43
  @logger ||= config.logger
44
44
  end
45
45
 
46
+ def self.load_middlewares(rack_builder)
47
+ config.middleware.middlewares.each do |middleware, args, block|
48
+ rack_builder.use(middleware, *args, &block)
49
+ end
50
+ end
51
+
46
52
  module Router
47
53
  module Strategies
48
54
  end
@@ -51,3 +57,5 @@ end
51
57
 
52
58
  module RageController
53
59
  end
60
+
61
+ require_relative "rage/env"
data/rage.gemspec CHANGED
@@ -29,5 +29,5 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_dependency "thor", "~> 1.0"
31
31
  spec.add_dependency "rack", "~> 2.0"
32
- spec.add_dependency "rage-iodine", "~> 2.2"
32
+ spec.add_dependency "rage-iodine", "~> 3.0"
33
33
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Samoilov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-01 00:00:00.000000000 Z
11
+ date: 2023-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '2.2'
47
+ version: '3.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '2.2'
54
+ version: '3.0'
55
55
  description:
56
56
  email:
57
57
  - rsamoi@icloud.com
@@ -76,9 +76,11 @@ files:
76
76
  - lib/rage/cli.rb
77
77
  - lib/rage/configuration.rb
78
78
  - lib/rage/controller/api.rb
79
+ - lib/rage/env.rb
79
80
  - lib/rage/errors.rb
80
81
  - lib/rage/fiber.rb
81
82
  - lib/rage/fiber_scheduler.rb
83
+ - lib/rage/fiber_wrapper.rb
82
84
  - lib/rage/logger/logger.rb
83
85
  - lib/rage/logger/text_formatter.rb
84
86
  - lib/rage/params_parser.rb