rage-rb 0.5.2 → 0.7.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.
data/lib/rage/rails.rb ADDED
@@ -0,0 +1,62 @@
1
+ if Gem::Version.new(Rails.version) < Gem::Version.new(6)
2
+ fail "Rage is only compatible with Rails 6+. Detected Rails version: #{Rails.version}."
3
+ end
4
+
5
+ # load the framework
6
+ require "rage/all"
7
+
8
+ # patch Rack
9
+ Iodine.patch_rack
10
+
11
+ # configure the framework
12
+ Rage.config.internal.rails_mode = true
13
+
14
+ # make sure log formatter is not used in console
15
+ Rails.application.console do
16
+ Rage.config.internal.rails_console = true
17
+ Rage.logger.level = Rage.logger.level if Rage.logger # trigger redefining log methods
18
+ end
19
+
20
+ # patch ActiveRecord's connection pool
21
+ if defined?(ActiveRecord)
22
+ Rails.configuration.after_initialize do
23
+ module ActiveRecord::ConnectionAdapters
24
+ class ConnectionPool
25
+ def connection_cache_key(_)
26
+ Fiber.current
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ # plug into Rails' Zeitwerk instance to reload the code
34
+ Rails.autoloaders.main.on_setup do
35
+ if Iodine.running?
36
+ Rage.code_loader.rails_mode_reload
37
+ end
38
+ end
39
+
40
+ # patch `ActionDispatch::Reloader` to synchronize `reload!` calls
41
+ Rails.configuration.after_initialize do
42
+ conditional_mutex = Module.new do
43
+ def call(env)
44
+ @mutex ||= Mutex.new
45
+ if Rails.application.reloader.check!
46
+ @mutex.synchronize { super }
47
+ else
48
+ super
49
+ end
50
+ end
51
+ end
52
+
53
+ ActionDispatch::Reloader.prepend(conditional_mutex)
54
+ end
55
+
56
+ # clone Rails logger
57
+ Rails.configuration.after_initialize do
58
+ if Rails.logger && !Rage.logger
59
+ rails_logdev = Rails.logger.instance_variable_get(:@logdev)
60
+ Rage.config.logger = Rage::Logger.new(rails_logdev) if rails_logdev.is_a?(Logger::LogDevice)
61
+ end
62
+ end
data/lib/rage/request.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
4
+
3
5
  class Rage::Request
4
6
  # @private
5
7
  def initialize(env)
@@ -14,6 +16,55 @@ class Rage::Request
14
16
  @headers ||= Headers.new(@env)
15
17
  end
16
18
 
19
+ # Check if the request is fresh.
20
+ # @param etag [String] The etag of the requested resource.
21
+ # @param last_modified [Time] The last modified time of the requested resource.
22
+ # @return [Boolean] True if the request is fresh, false otherwise.
23
+ # @example
24
+ # request.fresh?(etag: "123", last_modified: Time.utc(2023, 12, 15))
25
+ # request.fresh?(last_modified: Time.utc(2023, 12, 15))
26
+ # request.fresh?(etag: "123")
27
+ def fresh?(etag:, last_modified:)
28
+ # Always render response when no freshness information
29
+ # is provided in the request.
30
+ return false unless if_none_match || if_not_modified_since
31
+
32
+ etag_matches?(
33
+ requested_etags: if_none_match, response_etag: etag
34
+ ) && not_modified?(
35
+ request_not_modified_since: if_not_modified_since,
36
+ response_last_modified: last_modified
37
+ )
38
+ end
39
+
40
+ private
41
+
42
+ def if_none_match
43
+ headers["HTTP_IF_NONE_MATCH"]
44
+ end
45
+
46
+ def if_not_modified_since
47
+ headers["HTTP_IF_MODIFIED_SINCE"] ? Time.httpdate(headers["HTTP_IF_MODIFIED_SINCE"]) : nil
48
+ rescue ArgumentError
49
+ nil
50
+ end
51
+
52
+ def etag_matches?(requested_etags:, response_etag:)
53
+ requested_etags = requested_etags ? requested_etags.split(",").each(&:strip!) : []
54
+
55
+ return true if requested_etags.empty?
56
+ return false if response_etag.nil?
57
+
58
+ requested_etags.include?(response_etag) || requested_etags.include?("*")
59
+ end
60
+
61
+ def not_modified?(request_not_modified_since:, response_last_modified:)
62
+ return true if request_not_modified_since.nil?
63
+ return false if response_last_modified.nil?
64
+
65
+ request_not_modified_since >= response_last_modified
66
+ end
67
+
17
68
  # @private
18
69
  class Headers
19
70
  HTTP = "HTTP_"
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Rage::Response
4
+ # @private
5
+ def initialize(headers, body)
6
+ @headers = headers
7
+ @body = body
8
+ end
9
+
10
+ # Returns the content of the response as a string. This contains the contents of any calls to `render`.
11
+ # @return [String]
12
+ def body
13
+ @body[0]
14
+ end
15
+
16
+ # Returns the headers for the response.
17
+ # @return [Hash]
18
+ def headers
19
+ @headers
20
+ end
21
+ end
@@ -14,6 +14,11 @@ class Rage::Router::Backend
14
14
  @constrainer = Rage::Router::Constrainer.new({})
15
15
  end
16
16
 
17
+ def reset_routes
18
+ @routes = []
19
+ @trees = {}
20
+ end
21
+
17
22
  def mount(path, handler, methods)
18
23
  raise "Mount handler should respond to `call`" unless handler.respond_to?(:call)
19
24
 
@@ -79,6 +84,9 @@ class Rage::Router::Backend
79
84
  end
80
85
 
81
86
  __on(method, path, handler, constraints, defaults, meta)
87
+
88
+ rescue Rage::Errors::RouterError => e
89
+ raise e unless Rage.code_loader.reloading?
82
90
  end
83
91
 
84
92
  def lookup(env)
@@ -280,7 +288,7 @@ class Rage::Router::Backend
280
288
  if Object.const_defined?(klass)
281
289
  Object.const_get(klass)
282
290
  else
283
- raise "Routing error: could not find the #{klass} class"
291
+ raise Rage::Errors::RouterError, "Routing error: could not find the #{klass} class"
284
292
  end
285
293
  end
286
294
  end
@@ -7,6 +7,8 @@ class Rage::Router::DSL
7
7
 
8
8
  def draw(&block)
9
9
  Handler.new(@router).instance_eval(&block)
10
+ # propagate route definitions to Rails for `rails routes` to work
11
+ Rails.application.routes.draw(&block) if Rage.config.internal.rails_mode
10
12
  end
11
13
 
12
14
  ##
@@ -283,7 +285,7 @@ class Rage::Router::DSL
283
285
  end
284
286
 
285
287
  _module, _path, _only, _except, _param = opts.values_at(:module, :path, :only, :except, :param)
286
- raise ":param option can't contain colons" if _param&.include?(":")
288
+ raise ":param option can't contain colons" if _param.to_s.include?(":")
287
289
 
288
290
  _only = Array(_only) if _only
289
291
  _except = Array(_except) if _except
data/lib/rage/setup.rb CHANGED
@@ -2,22 +2,10 @@ Iodine.patch_rack
2
2
 
3
3
  require_relative "#{Rage.root}/config/environments/#{Rage.env}"
4
4
 
5
+ # Run application initializers
6
+ Dir["#{Rage.root}/config/initializers/**/*.rb"].each { |initializer| load(initializer) }
5
7
 
6
- # load application files
7
- app, bad = Dir["#{Rage.root}/app/**/*.rb"], []
8
-
9
- loop do
10
- path = app.shift
11
- break if path.nil?
12
-
13
- require_relative path
14
-
15
- # push the file to the end of the list in case it depends on another file that has not yet been required;
16
- # re-raise if only errored out files are left
17
- rescue NameError
18
- raise if (app - bad).empty?
19
- app << path
20
- bad << path
21
- end
8
+ # Load application classes
9
+ Rage.code_loader.setup
22
10
 
23
11
  require_relative "#{Rage.root}/config/routes"
@@ -7,4 +7,6 @@ Rage.configure do
7
7
 
8
8
  # Specify the logger
9
9
  config.logger = Rage::Logger.new(STDOUT)
10
+
11
+ config.middleware.use Rage::Reloader
10
12
  end
File without changes
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.2"
4
+ VERSION = "0.7.0"
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 ||= Rage::Env.new(ENV["RAGE_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
31
+ @__env ||= Rage::Env.new(ENV["RAGE_ENV"])
32
32
  end
33
33
 
34
34
  def self.groups
@@ -45,10 +45,32 @@ module Rage
45
45
 
46
46
  def self.load_middlewares(rack_builder)
47
47
  config.middleware.middlewares.each do |middleware, args, block|
48
+ # in Rails compatibility mode we first check if the middleware is a part of the Rails middleware stack;
49
+ # if it is - it is expected to be built using `ActionDispatch::MiddlewareStack::Middleware#build`, but Rack
50
+ # expects the middleware to respond to `#new`, so we wrap the middleware into a helper module
51
+ if Rage.config.internal.rails_mode
52
+ rails_middleware = Rails.application.config.middleware.middlewares.find { |m| m.name == middleware.name }
53
+ if rails_middleware
54
+ wrapper = Module.new do
55
+ extend self
56
+ attr_accessor :middleware
57
+ def new(app, *, &)
58
+ middleware.build(app)
59
+ end
60
+ end
61
+ wrapper.middleware = rails_middleware
62
+ middleware = wrapper
63
+ end
64
+ end
65
+
48
66
  rack_builder.use(middleware, *args, &block)
49
67
  end
50
68
  end
51
69
 
70
+ def self.code_loader
71
+ @code_loader ||= Rage::CodeLoader.new
72
+ end
73
+
52
74
  module Router
53
75
  module Strategies
54
76
  end
data/rage.gemspec CHANGED
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "thor", "~> 1.0"
31
31
  spec.add_dependency "rack", "~> 2.0"
32
32
  spec.add_dependency "rage-iodine", "~> 3.0"
33
+ spec.add_dependency "zeitwerk", "~> 2.6"
33
34
  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.2
4
+ version: 0.7.0
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-11 00:00:00.000000000 Z
11
+ date: 2024-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: zeitwerk
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.6'
55
69
  description:
56
70
  email:
57
71
  - rsamoi@icloud.com
@@ -74,17 +88,23 @@ files:
74
88
  - lib/rage/all.rb
75
89
  - lib/rage/application.rb
76
90
  - lib/rage/cli.rb
91
+ - lib/rage/code_loader.rb
77
92
  - lib/rage/configuration.rb
78
93
  - lib/rage/controller/api.rb
79
94
  - lib/rage/env.rb
80
95
  - lib/rage/errors.rb
81
96
  - lib/rage/fiber.rb
82
97
  - lib/rage/fiber_scheduler.rb
83
- - lib/rage/fiber_wrapper.rb
98
+ - lib/rage/logger/json_formatter.rb
84
99
  - lib/rage/logger/logger.rb
85
100
  - lib/rage/logger/text_formatter.rb
101
+ - lib/rage/middleware/cors.rb
102
+ - lib/rage/middleware/fiber_wrapper.rb
103
+ - lib/rage/middleware/reloader.rb
86
104
  - lib/rage/params_parser.rb
105
+ - lib/rage/rails.rb
87
106
  - lib/rage/request.rb
107
+ - lib/rage/response.rb
88
108
  - lib/rage/router/README.md
89
109
  - lib/rage/router/backend.rb
90
110
  - lib/rage/router/constrainer.rb
@@ -101,6 +121,7 @@ files:
101
121
  - lib/rage/templates/config-environments-development.rb
102
122
  - lib/rage/templates/config-environments-production.rb
103
123
  - lib/rage/templates/config-environments-test.rb
124
+ - lib/rage/templates/config-initializers-.keep
104
125
  - lib/rage/templates/config-routes.rb
105
126
  - lib/rage/templates/config.ru
106
127
  - lib/rage/templates/lib-.keep