rage-rb 0.4.0 → 0.5.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 +4 -4
 - data/CHANGELOG.md +29 -10
 - data/Gemfile +2 -0
 - data/README.md +10 -8
 - data/lib/rage/all.rb +7 -0
 - data/lib/rage/application.rb +33 -5
 - data/lib/rage/cli.rb +16 -9
 - data/lib/rage/configuration.rb +22 -6
 - data/lib/rage/controller/api.rb +1 -1
 - data/lib/rage/fiber.rb +58 -10
 - data/lib/rage/fiber_scheduler.rb +59 -32
 - data/lib/rage/logger/logger.rb +164 -0
 - data/lib/rage/logger/text_formatter.rb +46 -0
 - data/lib/rage/router/backend.rb +37 -4
 - data/lib/rage/router/dsl.rb +14 -3
 - data/lib/rage/router/handler_storage.rb +7 -4
 - data/lib/rage/sidekiq_session.rb +72 -0
 - data/lib/rage/templates/Rakefile +1 -0
 - data/lib/rage/templates/config-application.rb +5 -0
 - data/lib/rage/templates/config-environments-development.rb +6 -3
 - data/lib/rage/templates/config-environments-production.rb +7 -3
 - data/lib/rage/templates/config-environments-test.rb +6 -3
 - data/lib/rage/version.rb +1 -1
 - data/lib/rage-rb.rb +7 -2
 - data/rage.gemspec +1 -1
 - metadata +8 -4
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 22f98c39badb0c2128b270ce3092d183f62db4bead34e8df627cf322a3697c71
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: ea49aeae3da7630f52e8c704bc042752a66b0fefc03a67d713ead33eec1fd006
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e9e57e1eafda6e41112b8af0cc8d23eac629e0964b441622c48173f936f45aac95d0482b3545c5feb047dee755889c30ac63d0f63332e59d36cdff5cb1800198
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 481b9e52062350a9923c3b0195aea555ca0b8cb0c95fa95e76d3e7cd45a27e21a817ad259aed6e1cf9d90c74c17e99b8398066e84deda7e422247a1ea59254b9
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,25 +1,44 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ## [Unreleased]
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## [0.5.0] - 2023-11-25
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ### Added
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - Add sessions for compatibility with `Sidekiq::Web` (#35).
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Add logger (#33).
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            ### Fixed
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            - Fixes for `FiberScheduler#io_wait` and `FiberScheduler#io_read` (#32).
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Correctly handle exceptions in inner fibers (#34).
         
     | 
| 
      
 14 
     | 
    
         
            +
            - Fixes for `FiberScheduler#kernel_sleep` (#36).
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            - Use config namespaces (#25).
         
     | 
| 
      
 19 
     | 
    
         
            +
            - Update `Fiber.await` signature (#36).
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       3 
21 
     | 
    
         
             
            ## [0.4.0] - 2023-10-31
         
     | 
| 
       4 
22 
     | 
    
         | 
| 
       5 
23 
     | 
    
         
             
            ### Added
         
     | 
| 
       6 
24 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            - Expose the `params` object.
         
     | 
| 
       8 
     | 
    
         
            -
            - Support header authentication with `authenticate_with_http_token 
     | 
| 
       9 
     | 
    
         
            -
            - Add the `resources`  
     | 
| 
       10 
     | 
    
         
            -
            - Add the ` 
     | 
| 
       11 
     | 
    
         
            -
            -  
     | 
| 
      
 25 
     | 
    
         
            +
            - Expose the `params` object (#23).
         
     | 
| 
      
 26 
     | 
    
         
            +
            - Support header authentication with `authenticate_with_http_token` (#21).
         
     | 
| 
      
 27 
     | 
    
         
            +
            - Add the `resources` route helper (#20).
         
     | 
| 
      
 28 
     | 
    
         
            +
            - Add the `namespace` route helper by [@arikarim](https://github.com/arikarim) (#17).
         
     | 
| 
      
 29 
     | 
    
         
            +
            - Add the `mount` and `match` route helpers by [@arikarim](https://github.com/arikarim) (#18) (#14).
         
     | 
| 
      
 30 
     | 
    
         
            +
            - Allow to access request headers by [@arikarim](https://github.com/arikarim) (#15).
         
     | 
| 
       12 
31 
     | 
    
         
             
            - Support custom ports when starting the app with `rage s`.
         
     | 
| 
       13 
32 
     | 
    
         | 
| 
       14 
33 
     | 
    
         
             
            ## [0.3.0] - 2023-10-08
         
     | 
| 
       15 
34 
     | 
    
         | 
| 
       16 
35 
     | 
    
         
             
            ### Added
         
     | 
| 
       17 
36 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
            - CLI `routes` task.
         
     | 
| 
       19 
     | 
    
         
            -
            - CLI `console` task.
         
     | 
| 
       20 
     | 
    
         
            -
            - `:if` and `:unless` options in `before_action 
     | 
| 
       21 
     | 
    
         
            -
            - Allow to set response headers.
         
     | 
| 
       22 
     | 
    
         
            -
            - Block version of `before_action 
     | 
| 
      
 37 
     | 
    
         
            +
            - CLI `routes` task by [@arikarim](https://github.com/arikarim) (#9).
         
     | 
| 
      
 38 
     | 
    
         
            +
            - CLI `console` task (#12).
         
     | 
| 
      
 39 
     | 
    
         
            +
            - `:if` and `:unless` options in `before_action` (#10).
         
     | 
| 
      
 40 
     | 
    
         
            +
            - Allow to set response headers (#11).
         
     | 
| 
      
 41 
     | 
    
         
            +
            - Block version of `before_action` by [@heysyam99](https://github.com/heysyam99) (#8).
         
     | 
| 
       23 
42 
     | 
    
         | 
| 
       24 
43 
     | 
    
         
             
            ## [0.2.0] - 2023-09-27
         
     | 
| 
       25 
44 
     | 
    
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -49,6 +49,7 @@ Check out in-depth API docs for more information: 
     | 
|
| 
       49 
49 
     | 
    
         
             
            - [Controller API](https://rage-rb.pages.dev/RageController/API)
         
     | 
| 
       50 
50 
     | 
    
         
             
            - [Routing API](https://rage-rb.pages.dev/Rage/Router/DSL/Handler)
         
     | 
| 
       51 
51 
     | 
    
         
             
            - [Fiber API](https://rage-rb.pages.dev/Fiber)
         
     | 
| 
      
 52 
     | 
    
         
            +
            - [Logger API](https://rage-rb.pages.dev/Rage/Logger)
         
     | 
| 
       52 
53 
     | 
    
         | 
| 
       53 
54 
     | 
    
         
             
            Also, see the [changelog](https://github.com/rage-rb/rage/blob/master/CHANGELOG.md) and [upcoming-releases](https://github.com/rage-rb/rage#upcoming-releases) for currently supported and planned features.
         
     | 
| 
       54 
55 
     | 
    
         | 
| 
         @@ -98,10 +99,10 @@ require "net/http" 
     | 
|
| 
       98 
99 
     | 
    
         | 
| 
       99 
100 
     | 
    
         
             
            class PagesController < RageController::API
         
     | 
| 
       100 
101 
     | 
    
         
             
              def index
         
     | 
| 
       101 
     | 
    
         
            -
                pages = Fiber.await(
         
     | 
| 
      
 102 
     | 
    
         
            +
                pages = Fiber.await([
         
     | 
| 
       102 
103 
     | 
    
         
             
                  Fiber.schedule { Net::HTTP.get(URI("https://httpbin.org/json")) },
         
     | 
| 
       103 
104 
     | 
    
         
             
                  Fiber.schedule { Net::HTTP.get(URI("https://httpbin.org/html")) },
         
     | 
| 
       104 
     | 
    
         
            -
                )
         
     | 
| 
      
 105 
     | 
    
         
            +
                ])
         
     | 
| 
       105 
106 
     | 
    
         | 
| 
       106 
107 
     | 
    
         
             
                render json: { pages: pages }
         
     | 
| 
       107 
108 
     | 
    
         
             
              end
         
     | 
| 
         @@ -143,12 +144,13 @@ Version | Changes 
     | 
|
| 
       143 
144 
     | 
    
         
             
            ------- |------------
         
     | 
| 
       144 
145 
     | 
    
         
             
            0.2 :white_check_mark: | ~~Gem configuration by env.<br>Add `skip_before_action`.<br>Add `rescue_from`.<br>Router updates:<br> • make the `root` helper work correctly with `scope`;<br> • support the `defaults` option;~~
         
     | 
| 
       145 
146 
     | 
    
         
             
            0.3 :white_check_mark: | ~~CLI updates:<br> • `routes` task;<br> • `console` task;<br>Support the `:if` and `:unless` options in `before_action`.<br>Allow to set response headers.~~
         
     | 
| 
       146 
     | 
    
         
            -
            0.4 :white_check_mark: | ~~Expose the `params` object.<br>Support header authentication with `authenticate_with_http_token`.<br>Router updates:<br> • add the `resources` route helper;<br> • add the `namespace` route helper 
     | 
| 
       147 
     | 
    
         
            -
            0.5 |  
     | 
| 
       148 
     | 
    
         
            -
            0.6 |  
     | 
| 
       149 
     | 
    
         
            -
            0.7 |  
     | 
| 
       150 
     | 
    
         
            -
            0.8 |  
     | 
| 
       151 
     | 
    
         
            -
            0.9 |  
     | 
| 
      
 147 
     | 
    
         
            +
            0.4 :white_check_mark: | ~~Expose the `params` object.<br>Support header authentication with `authenticate_with_http_token`.<br>Router updates:<br> • add the `resources` route helper;<br> • add the `namespace` route helper;~~
         
     | 
| 
      
 148 
     | 
    
         
            +
            0.5 :white_check_mark:  | ~~Add request logging.~~
         
     | 
| 
      
 149 
     | 
    
         
            +
            0.6 | Automatic code reloading in development with Zeitwerk.
         
     | 
| 
      
 150 
     | 
    
         
            +
            0.7 | Expose the `send_data` and `send_file` methods.
         
     | 
| 
      
 151 
     | 
    
         
            +
            0.8 | Support conditional get with `etag` and `last_modified`.
         
     | 
| 
      
 152 
     | 
    
         
            +
            0.9 | Expose the `cookies` and `session` objects.
         
     | 
| 
      
 153 
     | 
    
         
            +
            1.0 | Implement Iodine-based equivalent of Action Cable.
         
     | 
| 
       152 
154 
     | 
    
         | 
| 
       153 
155 
     | 
    
         
             
            ## Development
         
     | 
| 
       154 
156 
     | 
    
         | 
    
        data/lib/rage/all.rb
    CHANGED
    
    | 
         @@ -18,3 +18,10 @@ require_relative "router/handler_storage" 
     | 
|
| 
       18 
18 
     | 
    
         
             
            require_relative "router/node"
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            require_relative "controller/api"
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            require_relative "logger/text_formatter"
         
     | 
| 
      
 23 
     | 
    
         
            +
            require_relative "logger/logger"
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            if defined?(Sidekiq)
         
     | 
| 
      
 26 
     | 
    
         
            +
              require_relative "sidekiq_session"
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rage/application.rb
    CHANGED
    
    | 
         @@ -10,21 +10,25 @@ class Rage::Application 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
              def call(env)
         
     | 
| 
       12 
12 
     | 
    
         
             
                fiber = Fiber.schedule do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  init_logger
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       13 
15 
     | 
    
         
             
                  handler = @router.lookup(env)
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  if handler
         
     | 
| 
      
 17 
     | 
    
         
            +
                  response = if handler
         
     | 
| 
       16 
18 
     | 
    
         
             
                    params = Rage::ParamsParser.prepare(env, handler[:params])
         
     | 
| 
       17 
19 
     | 
    
         
             
                    handler[:handler].call(env, params)
         
     | 
| 
       18 
20 
     | 
    
         
             
                  else
         
     | 
| 
       19 
21 
     | 
    
         
             
                    [404, {}, ["Not Found"]]
         
     | 
| 
       20 
22 
     | 
    
         
             
                  end
         
     | 
| 
       21 
23 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                rescue => e
         
     | 
| 
       23 
     | 
    
         
            -
                   
     | 
| 
      
 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]]
         
     | 
| 
       24 
28 
     | 
    
         | 
| 
       25 
29 
     | 
    
         
             
                ensure
         
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
                  Iodine.publish(env["IODINE_REQUEST_ID"], "")
         
     | 
| 
      
 30 
     | 
    
         
            +
                  finalize_logger(env, response, params)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  Iodine.publish(env["IODINE_REQUEST_ID"], "") # notify Iodine the request can now be served
         
     | 
| 
       28 
32 
     | 
    
         
             
                end
         
     | 
| 
       29 
33 
     | 
    
         | 
| 
       30 
34 
     | 
    
         
             
                # the fiber encountered blocking IO and yielded; instruct Iodine to pause the request;
         
     | 
| 
         @@ -34,4 +38,28 @@ class Rage::Application 
     | 
|
| 
       34 
38 
     | 
    
         
             
                  fiber.__get_result
         
     | 
| 
       35 
39 
     | 
    
         
             
                end
         
     | 
| 
       36 
40 
     | 
    
         
             
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              private
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              DEFAULT_LOG_CONTEXT = {}.freeze
         
     | 
| 
      
 45 
     | 
    
         
            +
              private_constant :DEFAULT_LOG_CONTEXT
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def init_logger
         
     | 
| 
      
 48 
     | 
    
         
            +
                Thread.current[:rage_logger] = {
         
     | 
| 
      
 49 
     | 
    
         
            +
                  tags: [Iodine::Rack::Utils.gen_request_tag],
         
     | 
| 
      
 50 
     | 
    
         
            +
                  context: DEFAULT_LOG_CONTEXT,
         
     | 
| 
      
 51 
     | 
    
         
            +
                  request_start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
         
     | 
| 
      
 52 
     | 
    
         
            +
                }
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              def finalize_logger(env, response, params)
         
     | 
| 
      
 56 
     | 
    
         
            +
                logger = Thread.current[:rage_logger]
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                duration = (
         
     | 
| 
      
 59 
     | 
    
         
            +
                  (Process.clock_gettime(Process::CLOCK_MONOTONIC) - logger[:request_start]) * 1000
         
     | 
| 
      
 60 
     | 
    
         
            +
                ).round(2)
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                logger[:final] = { env:, params:, response:, duration: }
         
     | 
| 
      
 63 
     | 
    
         
            +
                Rage.logger.info("")
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
       37 
65 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rage/cli.rb
    CHANGED
    
    | 
         @@ -1,7 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            require "thor"
         
     | 
| 
       3 
     | 
    
         
            -
            require " 
     | 
| 
       4 
     | 
    
         
            -
            require "irb"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "rack"
         
     | 
| 
       5 
4 
     | 
    
         | 
| 
       6 
5 
     | 
    
         
             
            module Rage
         
     | 
| 
       7 
6 
     | 
    
         
             
              class CLI < Thor
         
     | 
| 
         @@ -11,6 +10,7 @@ module Rage 
     | 
|
| 
       11 
10 
     | 
    
         | 
| 
       12 
11 
     | 
    
         
             
                desc "new PATH", "Create a new application."
         
     | 
| 
       13 
12 
     | 
    
         
             
                def new(path)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  require "rage/all"
         
     | 
| 
       14 
14 
     | 
    
         
             
                  NewAppGenerator.start([path])
         
     | 
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
         @@ -20,9 +20,9 @@ module Rage 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  app = ::Rack::Builder.parse_file("config.ru")
         
     | 
| 
       21 
21 
     | 
    
         
             
                  app = app[0] if app.is_a?(Array)
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                  ::Iodine.listen service: :http, handler: app, port: options[:port] || Rage.config.port
         
     | 
| 
       24 
     | 
    
         
            -
                  ::Iodine.threads = Rage.config.threads_count
         
     | 
| 
       25 
     | 
    
         
            -
                  ::Iodine.workers = Rage.config.workers_count
         
     | 
| 
      
 23 
     | 
    
         
            +
                  ::Iodine.listen service: :http, handler: app, port: options[:port] || Rage.config.server.port
         
     | 
| 
      
 24 
     | 
    
         
            +
                  ::Iodine.threads = Rage.config.server.threads_count
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ::Iodine.workers = Rage.config.server.workers_count
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
                  ::Iodine.start
         
     | 
| 
       28 
28 
     | 
    
         
             
                end
         
     | 
| 
         @@ -39,14 +39,20 @@ module Rage 
     | 
|
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
                  routes = Rage.__router.routes
         
     | 
| 
       41 
41 
     | 
    
         
             
                  pattern = options[:grep]
         
     | 
| 
       42 
     | 
    
         
            -
                  routes.unshift({ method: "Verb", path: "Path", raw_handler: "Controller#Action" })
         
     | 
| 
      
 42 
     | 
    
         
            +
                  routes.unshift({ method: "Verb", path: "Path", meta: { raw_handler: "Controller#Action" } })
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
                  grouped_routes = routes.each_with_object({}) do |route, memo|
         
     | 
| 
       45 
45 
     | 
    
         
             
                    if pattern && !memo.empty?
         
     | 
| 
       46 
     | 
    
         
            -
                      next unless route[:path].match?(pattern) || route[:raw_handler].to_s.match?(pattern) || route[:method].match?(pattern)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      next unless route[:path].match?(pattern) || route[:meta][:raw_handler].to_s.match?(pattern) || route[:method].match?(pattern)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    key = [route[:path], route[:meta][:raw_handler]]
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    if route[:meta][:mount]
         
     | 
| 
      
 52 
     | 
    
         
            +
                      memo[key] = route.merge(method: "") unless route[:path].end_with?("*")
         
     | 
| 
      
 53 
     | 
    
         
            +
                      next
         
     | 
| 
       47 
54 
     | 
    
         
             
                    end
         
     | 
| 
       48 
55 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                    key = [route[:path], route[:raw_handler]]
         
     | 
| 
       50 
56 
     | 
    
         
             
                    if memo[key]
         
     | 
| 
       51 
57 
     | 
    
         
             
                      memo[key][:method] += "|#{route[:method]}"
         
     | 
| 
       52 
58 
     | 
    
         
             
                    else
         
     | 
| 
         @@ -68,7 +74,7 @@ module Rage 
     | 
|
| 
       68 
74 
     | 
    
         
             
                    meta = route[:constraints]
         
     | 
| 
       69 
75 
     | 
    
         
             
                    meta.merge!(route[:defaults]) if route[:defaults]
         
     | 
| 
       70 
76 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
                    handler = route[:raw_handler]
         
     | 
| 
      
 77 
     | 
    
         
            +
                    handler = route[:meta][:raw_handler]
         
     | 
| 
       72 
78 
     | 
    
         
             
                    handler = "#{handler} #{meta}" unless meta&.empty?
         
     | 
| 
       73 
79 
     | 
    
         | 
| 
       74 
80 
     | 
    
         
             
                    puts format("%-#{longest_method}s%-#{longest_path}s%s", route[:method], route[:path], handler)
         
     | 
| 
         @@ -78,6 +84,7 @@ module Rage 
     | 
|
| 
       78 
84 
     | 
    
         | 
| 
       79 
85 
     | 
    
         
             
                desc "c", "Start the app console."
         
     | 
| 
       80 
86 
     | 
    
         
             
                def console
         
     | 
| 
      
 87 
     | 
    
         
            +
                  require "irb"
         
     | 
| 
       81 
88 
     | 
    
         
             
                  environment
         
     | 
| 
       82 
89 
     | 
    
         
             
                  ARGV.clear
         
     | 
| 
       83 
90 
     | 
    
         
             
                  IRB.start
         
     | 
    
        data/lib/rage/configuration.rb
    CHANGED
    
    | 
         @@ -1,10 +1,26 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            class Rage::Configuration
         
     | 
| 
       2 
     | 
    
         
            -
              attr_accessor : 
     | 
| 
       3 
     | 
    
         
            -
              attr_reader :threads_count
         
     | 
| 
      
 2 
     | 
    
         
            +
              attr_accessor :logger, :log_formatter, :log_level
         
     | 
| 
       4 
3 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
               
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 4 
     | 
    
         
            +
              # used in DSL
         
     | 
| 
      
 5 
     | 
    
         
            +
              def config = self
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              def server
         
     | 
| 
      
 8 
     | 
    
         
            +
                @server ||= Server.new
         
     | 
| 
      
 9 
     | 
    
         
            +
              end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              class Server
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_accessor :port, :workers_count
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :threads_count
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                def initialize
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @threads_count = 1
         
     | 
| 
      
 17 
     | 
    
         
            +
                  @workers_count = -1
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @port = 3000
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def __finalize
         
     | 
| 
      
 23 
     | 
    
         
            +
                @logger.formatter = @log_formatter if @logger && @log_formatter
         
     | 
| 
      
 24 
     | 
    
         
            +
                @logger.level = @log_level if @logger && @log_level
         
     | 
| 
       9 
25 
     | 
    
         
             
              end
         
     | 
| 
       10 
26 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rage/controller/api.rb
    CHANGED
    
    
    
        data/lib/rage/fiber.rb
    CHANGED
    
    | 
         @@ -1,4 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            class Fiber
         
     | 
| 
      
 4 
     | 
    
         
            +
              AWAIT_ERROR_MESSAGE = "err"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       2 
6 
     | 
    
         
             
              # @private
         
     | 
| 
       3 
7 
     | 
    
         
             
              def __set_result(result)
         
     | 
| 
       4 
8 
     | 
    
         
             
                @__result = result
         
     | 
| 
         @@ -9,30 +13,74 @@ class Fiber 
     | 
|
| 
       9 
13 
     | 
    
         
             
                @__result
         
     | 
| 
       10 
14 
     | 
    
         
             
              end
         
     | 
| 
       11 
15 
     | 
    
         | 
| 
      
 16 
     | 
    
         
            +
              # @private
         
     | 
| 
      
 17 
     | 
    
         
            +
              def __set_err(err)
         
     | 
| 
      
 18 
     | 
    
         
            +
                @__err = err
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              # @private
         
     | 
| 
      
 22 
     | 
    
         
            +
              def __get_err
         
     | 
| 
      
 23 
     | 
    
         
            +
                @__err
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              # @private
         
     | 
| 
      
 27 
     | 
    
         
            +
              # pause a fiber and resume in the next iteration of the event loop
         
     | 
| 
      
 28 
     | 
    
         
            +
              def self.pause
         
     | 
| 
      
 29 
     | 
    
         
            +
                f = Fiber.current
         
     | 
| 
      
 30 
     | 
    
         
            +
                Iodine.defer { f.resume }
         
     | 
| 
      
 31 
     | 
    
         
            +
                Fiber.yield
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       12 
34 
     | 
    
         
             
              # Wait on several fibers at the same time. Calling this method will automatically pause the current fiber, allowing the
         
     | 
| 
       13 
35 
     | 
    
         
             
              #   server to process other requests. Once all fibers have completed, the current fiber will be automatically resumed.
         
     | 
| 
       14 
36 
     | 
    
         
             
              #
         
     | 
| 
       15 
37 
     | 
    
         
             
              # @param fibers [Fiber, Array<Fiber>] one or several fibers to wait on. The fibers must be created using the `Fiber.schedule` call.
         
     | 
| 
       16 
38 
     | 
    
         
             
              # @example
         
     | 
| 
       17 
     | 
    
         
            -
              #   Fiber.await(
         
     | 
| 
      
 39 
     | 
    
         
            +
              #   Fiber.await([
         
     | 
| 
       18 
40 
     | 
    
         
             
              #     Fiber.schedule { request_1 },
         
     | 
| 
       19 
41 
     | 
    
         
             
              #     Fiber.schedule { request_2 },
         
     | 
| 
       20 
     | 
    
         
            -
              #   )
         
     | 
| 
      
 42 
     | 
    
         
            +
              #   ])
         
     | 
| 
       21 
43 
     | 
    
         
             
              # @note This method should only be used when multiple fibers have to be processed in parallel. There's no need to use `Fiber.await` for single IO calls.
         
     | 
| 
       22 
     | 
    
         
            -
              def self.await( 
     | 
| 
       23 
     | 
    
         
            -
                f = Fiber.current
         
     | 
| 
      
 44 
     | 
    
         
            +
              def self.await(fibers)
         
     | 
| 
      
 45 
     | 
    
         
            +
                f, fibers = Fiber.current, Array(fibers)
         
     | 
| 
       24 
46 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                 
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
      
 47 
     | 
    
         
            +
                # check which fibers are alive (i.e. have yielded) and which have errored out
         
     | 
| 
      
 48 
     | 
    
         
            +
                i, err, num_wait_for = 0, nil, 0
         
     | 
| 
      
 49 
     | 
    
         
            +
                while i < fibers.length
         
     | 
| 
      
 50 
     | 
    
         
            +
                  if fibers[i].alive?
         
     | 
| 
      
 51 
     | 
    
         
            +
                    num_wait_for += 1
         
     | 
| 
      
 52 
     | 
    
         
            +
                  else
         
     | 
| 
      
 53 
     | 
    
         
            +
                    err = fibers[i].__get_err
         
     | 
| 
      
 54 
     | 
    
         
            +
                    break if err
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
                  i += 1
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                # raise if one of the fibers has errored out or return the result if none have yielded
         
     | 
| 
      
 60 
     | 
    
         
            +
                if err
         
     | 
| 
      
 61 
     | 
    
         
            +
                  raise err
         
     | 
| 
      
 62 
     | 
    
         
            +
                elsif num_wait_for == 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                  return fibers.map!(&:__get_result)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
       27 
65 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                 
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                   
     | 
| 
      
 66 
     | 
    
         
            +
                # wait on async fibers; resume right away if one of the fibers errors out
         
     | 
| 
      
 67 
     | 
    
         
            +
                Iodine.subscribe("await:#{f.object_id}") do |_, err|
         
     | 
| 
      
 68 
     | 
    
         
            +
                  if err == AWAIT_ERROR_MESSAGE
         
     | 
| 
      
 69 
     | 
    
         
            +
                    f.resume
         
     | 
| 
      
 70 
     | 
    
         
            +
                  else
         
     | 
| 
      
 71 
     | 
    
         
            +
                    num_wait_for -= 1
         
     | 
| 
      
 72 
     | 
    
         
            +
                    f.resume if num_wait_for == 0
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
       31 
74 
     | 
    
         
             
                end
         
     | 
| 
       32 
75 
     | 
    
         | 
| 
       33 
76 
     | 
    
         
             
                Fiber.yield
         
     | 
| 
       34 
77 
     | 
    
         
             
                Iodine.defer { Iodine.unsubscribe("await:#{f.object_id}") }
         
     | 
| 
       35 
78 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                 
     | 
| 
      
 79 
     | 
    
         
            +
                # if num_wait_for is not 0 means we exited prematurely because of an error
         
     | 
| 
      
 80 
     | 
    
         
            +
                if num_wait_for > 0
         
     | 
| 
      
 81 
     | 
    
         
            +
                  raise fibers.find(&:__get_err).__get_err
         
     | 
| 
      
 82 
     | 
    
         
            +
                else
         
     | 
| 
      
 83 
     | 
    
         
            +
                  fibers.map!(&:__get_result)
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
       37 
85 
     | 
    
         
             
              end
         
     | 
| 
       38 
86 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rage/fiber_scheduler.rb
    CHANGED
    
    | 
         @@ -3,41 +3,50 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require "resolv"
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            class Rage::FiberScheduler
         
     | 
| 
      
 6 
     | 
    
         
            +
              MAX_READ = 65536
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       6 
8 
     | 
    
         
             
              def initialize
         
     | 
| 
       7 
9 
     | 
    
         
             
                @root_fiber = Fiber.current
         
     | 
| 
       8 
10 
     | 
    
         
             
              end
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
       10 
12 
     | 
    
         
             
              def io_wait(io, events, timeout = nil)
         
     | 
| 
       11 
13 
     | 
    
         
             
                f = Fiber.current
         
     | 
| 
       12 
     | 
    
         
            -
                ::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil || 0) { f.resume }
         
     | 
| 
       13 
     | 
    
         
            -
                Fiber.yield
         
     | 
| 
      
 14 
     | 
    
         
            +
                ::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil || 0) { |err| f.resume(err) }
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                 
     | 
| 
      
 16 
     | 
    
         
            +
                err = Fiber.yield
         
     | 
| 
      
 17 
     | 
    
         
            +
                if err == Errno::ETIMEDOUT::Errno
         
     | 
| 
      
 18 
     | 
    
         
            +
                  0
         
     | 
| 
      
 19 
     | 
    
         
            +
                else
         
     | 
| 
      
 20 
     | 
    
         
            +
                  events
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
       16 
22 
     | 
    
         
             
              end
         
     | 
| 
       17 
23 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
              # TODO: this is more synchronous than asynchronous right now
         
     | 
| 
       19 
24 
     | 
    
         
             
              def io_read(io, buffer, length, offset = 0)
         
     | 
| 
       20 
     | 
    
         
            -
                 
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
      
 25 
     | 
    
         
            +
                length_to_read = if length == 0
         
     | 
| 
      
 26 
     | 
    
         
            +
                  buffer.size > MAX_READ ? MAX_READ : buffer.size
         
     | 
| 
      
 27 
     | 
    
         
            +
                else
         
     | 
| 
      
 28 
     | 
    
         
            +
                  length
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                while true
         
     | 
| 
      
 32 
     | 
    
         
            +
                  string = ::Iodine::Scheduler.read(io.fileno, length_to_read, offset)
         
     | 
| 
       22 
33 
     | 
    
         | 
| 
       23 
34 
     | 
    
         
             
                  if string.nil?
         
     | 
| 
       24 
35 
     | 
    
         
             
                    return offset
         
     | 
| 
       25 
36 
     | 
    
         
             
                  end
         
     | 
| 
       26 
37 
     | 
    
         | 
| 
       27 
38 
     | 
    
         
             
                  if string.empty?
         
     | 
| 
       28 
     | 
    
         
            -
                     
     | 
| 
       29 
     | 
    
         
            -
                    next
         
     | 
| 
      
 39 
     | 
    
         
            +
                    return -Errno::EAGAIN::Errno
         
     | 
| 
       30 
40 
     | 
    
         
             
                  end
         
     | 
| 
       31 
41 
     | 
    
         | 
| 
       32 
42 
     | 
    
         
             
                  buffer.set_string(string, offset)
         
     | 
| 
       33 
     | 
    
         
            -
                  offset += string.bytesize
         
     | 
| 
       34 
43 
     | 
    
         | 
| 
       35 
44 
     | 
    
         
             
                  size = string.bytesize
         
     | 
| 
       36 
     | 
    
         
            -
                   
     | 
| 
       37 
     | 
    
         
            -
                   
     | 
| 
       38 
     | 
    
         
            -
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  offset += size
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return offset if size < length_to_read || size >= buffer.size
         
     | 
| 
       39 
47 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
      
 48 
     | 
    
         
            +
                  Fiber.pause
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
       41 
50 
     | 
    
         
             
              end
         
     | 
| 
       42 
51 
     | 
    
         | 
| 
       43 
52 
     | 
    
         
             
              def io_write(io, buffer, length, offset = 0)
         
     | 
| 
         @@ -46,15 +55,11 @@ class Rage::FiberScheduler 
     | 
|
| 
       46 
55 
     | 
    
         | 
| 
       47 
56 
     | 
    
         
             
                ::Iodine::Scheduler.write(io.fileno, buffer.get_string, bytes_to_write, offset)
         
     | 
| 
       48 
57 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                 
     | 
| 
      
 58 
     | 
    
         
            +
                bytes_to_write - offset
         
     | 
| 
       50 
59 
     | 
    
         
             
              end
         
     | 
| 
       51 
60 
     | 
    
         | 
| 
       52 
61 
     | 
    
         
             
              def kernel_sleep(duration = nil)
         
     | 
| 
       53 
     | 
    
         
            -
                 
     | 
| 
       54 
     | 
    
         
            -
                  f = Fiber.current
         
     | 
| 
       55 
     | 
    
         
            -
                  ::Iodine.run_after((duration * 1000).to_i) { f.resume } 
         
     | 
| 
       56 
     | 
    
         
            -
                  Fiber.yield
         
     | 
| 
       57 
     | 
    
         
            -
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
                block(nil, duration || 0)
         
     | 
| 
       58 
63 
     | 
    
         
             
              end
         
     | 
| 
       59 
64 
     | 
    
         | 
| 
       60 
65 
     | 
    
         
             
              # TODO: GC works a little strange with this closure;
         
     | 
| 
         @@ -75,13 +80,22 @@ class Rage::FiberScheduler 
     | 
|
| 
       75 
80 
     | 
    
         
             
                Resolv.getaddresses(hostname)
         
     | 
| 
       76 
81 
     | 
    
         
             
              end
         
     | 
| 
       77 
82 
     | 
    
         | 
| 
       78 
     | 
    
         
            -
              def block( 
     | 
| 
       79 
     | 
    
         
            -
                f = Fiber.current
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                   
     | 
| 
      
 83 
     | 
    
         
            +
              def block(_blocker, timeout = nil)
         
     | 
| 
      
 84 
     | 
    
         
            +
                f, fulfilled, channel = Fiber.current, false, "unblock:#{Fiber.current.object_id}"
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                resume_fiber_block = proc do
         
     | 
| 
      
 87 
     | 
    
         
            +
                  unless fulfilled
         
     | 
| 
      
 88 
     | 
    
         
            +
                    fulfilled = true
         
     | 
| 
      
 89 
     | 
    
         
            +
                    ::Iodine.defer { ::Iodine.unsubscribe(channel) }
         
     | 
| 
      
 90 
     | 
    
         
            +
                    f.resume
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
       83 
92 
     | 
    
         
             
                end
         
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                ::Iodine.subscribe(channel, &resume_fiber_block)
         
     | 
| 
      
 95 
     | 
    
         
            +
                if timeout
         
     | 
| 
      
 96 
     | 
    
         
            +
                  ::Iodine.run_after((timeout * 1000).to_i, &resume_fiber_block)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
       85 
99 
     | 
    
         
             
                Fiber.yield
         
     | 
| 
       86 
100 
     | 
    
         
             
              end
         
     | 
| 
       87 
101 
     | 
    
         | 
| 
         @@ -90,15 +104,28 @@ class Rage::FiberScheduler 
     | 
|
| 
       90 
104 
     | 
    
         
             
              end
         
     | 
| 
       91 
105 
     | 
    
         | 
| 
       92 
106 
     | 
    
         
             
              def fiber(&block)
         
     | 
| 
       93 
     | 
    
         
            -
                 
     | 
| 
       94 
     | 
    
         
            -
                inner_schedule = f != @root_fiber
         
     | 
| 
      
 107 
     | 
    
         
            +
                parent = Fiber.current
         
     | 
| 
       95 
108 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
                fiber =  
     | 
| 
       97 
     | 
    
         
            -
                   
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                   
     | 
| 
      
 109 
     | 
    
         
            +
                fiber = if parent == @root_fiber
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # the fiber to wrap a request in
         
     | 
| 
      
 111 
     | 
    
         
            +
                  Fiber.new(blocking: false) do
         
     | 
| 
      
 112 
     | 
    
         
            +
                    Fiber.current.__set_result(block.call)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                else
         
     | 
| 
      
 115 
     | 
    
         
            +
                  # the fiber was created in the user code
         
     | 
| 
      
 116 
     | 
    
         
            +
                  logger = Thread.current[:rage_logger]
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  Fiber.new(blocking: false) do
         
     | 
| 
      
 119 
     | 
    
         
            +
                    Thread.current[:rage_logger] = logger
         
     | 
| 
      
 120 
     | 
    
         
            +
                    Fiber.current.__set_result(block.call)
         
     | 
| 
      
 121 
     | 
    
         
            +
                    # send a message for `Fiber.await` to work
         
     | 
| 
      
 122 
     | 
    
         
            +
                    Iodine.publish("await:#{parent.object_id}", "") if parent.alive?
         
     | 
| 
      
 123 
     | 
    
         
            +
                  rescue Exception => e
         
     | 
| 
      
 124 
     | 
    
         
            +
                    Fiber.current.__set_err(e)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    Iodine.publish("await:#{parent.object_id}", Fiber::AWAIT_ERROR_MESSAGE) if parent.alive?
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end
         
     | 
| 
       101 
127 
     | 
    
         
             
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
       102 
129 
     | 
    
         
             
                fiber.resume
         
     | 
| 
       103 
130 
     | 
    
         | 
| 
       104 
131 
     | 
    
         
             
                fiber
         
     | 
| 
         @@ -0,0 +1,164 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "logger"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ##
         
     | 
| 
      
 6 
     | 
    
         
            +
            # All logs in `rage` consist of two parts: keys and tags. A sample log entry might look like this:
         
     | 
| 
      
 7 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 8 
     | 
    
         
            +
            # [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=hello
         
     | 
| 
      
 9 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 10 
     | 
    
         
            +
            # In the log entry above, `timestamp`, `pid`, `level`, and `message` are keys, while `fecbba0735355738` is a tag.
         
     | 
| 
      
 11 
     | 
    
         
            +
            # 
         
     | 
| 
      
 12 
     | 
    
         
            +
            # Use {tagged} to add custom tags to an entry:
         
     | 
| 
      
 13 
     | 
    
         
            +
            # ```ruby
         
     | 
| 
      
 14 
     | 
    
         
            +
            # Rage.logger.tagged("ApiCall") do
         
     | 
| 
      
 15 
     | 
    
         
            +
            #   perform_api_call
         
     | 
| 
      
 16 
     | 
    
         
            +
            #   Rage.logger.info "success"
         
     | 
| 
      
 17 
     | 
    
         
            +
            # end
         
     | 
| 
      
 18 
     | 
    
         
            +
            # # => [fecbba0735355738][ApiCall] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info message=success
         
     | 
| 
      
 19 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 20 
     | 
    
         
            +
            #
         
     | 
| 
      
 21 
     | 
    
         
            +
            # {with_context} can be used to add custom keys:
         
     | 
| 
      
 22 
     | 
    
         
            +
            # ```ruby
         
     | 
| 
      
 23 
     | 
    
         
            +
            # cache_key = "mykey"
         
     | 
| 
      
 24 
     | 
    
         
            +
            # Rage.logger.with_context(cache_key: cache_key) do
         
     | 
| 
      
 25 
     | 
    
         
            +
            #   get_from_cache(cache_key)
         
     | 
| 
      
 26 
     | 
    
         
            +
            #   Rage.logger.info "cache miss"
         
     | 
| 
      
 27 
     | 
    
         
            +
            # end
         
     | 
| 
      
 28 
     | 
    
         
            +
            # # => [fecbba0735355738] timestamp=2023-10-19T11:12:56+00:00 pid=1825 level=info cache_key=mykey message=cache miss
         
     | 
| 
      
 29 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 30 
     | 
    
         
            +
            #
         
     | 
| 
      
 31 
     | 
    
         
            +
            # `Rage::Logger` also implements the interface of Ruby's native {https://ruby-doc.org/3.2.2/stdlibs/logger/Logger.html Logger}:
         
     | 
| 
      
 32 
     | 
    
         
            +
            # ```ruby
         
     | 
| 
      
 33 
     | 
    
         
            +
            # Rage.logger.info("Initializing")
         
     | 
| 
      
 34 
     | 
    
         
            +
            # Rage.logger.debug { "This is a " + potentially + " expensive operation" }
         
     | 
| 
      
 35 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 36 
     | 
    
         
            +
            class Rage::Logger
         
     | 
| 
      
 37 
     | 
    
         
            +
              METHODS_MAP = {
         
     | 
| 
      
 38 
     | 
    
         
            +
                "debug" => Logger::DEBUG,
         
     | 
| 
      
 39 
     | 
    
         
            +
                "info" => Logger::INFO,
         
     | 
| 
      
 40 
     | 
    
         
            +
                "warn" => Logger::WARN,
         
     | 
| 
      
 41 
     | 
    
         
            +
                "error" => Logger::ERROR,
         
     | 
| 
      
 42 
     | 
    
         
            +
                "fatal" => Logger::FATAL,
         
     | 
| 
      
 43 
     | 
    
         
            +
                "unknown" => Logger::UNKNOWN
         
     | 
| 
      
 44 
     | 
    
         
            +
              }
         
     | 
| 
      
 45 
     | 
    
         
            +
              private_constant :METHODS_MAP
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              attr_reader :level, :formatter
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
              # Create a new logger.
         
     | 
| 
      
 50 
     | 
    
         
            +
              #
         
     | 
| 
      
 51 
     | 
    
         
            +
              # @param log [Object] a filename (`String`), IO object (typically `STDOUT`, `STDERR`, or an open file), `nil` (it writes nothing) or `File::NULL` (same as `nil`)
         
     | 
| 
      
 52 
     | 
    
         
            +
              # @param level [Integer] logging severity threshold
         
     | 
| 
      
 53 
     | 
    
         
            +
              # @param formatter [#call] logging formatter
         
     | 
| 
      
 54 
     | 
    
         
            +
              # @param shift_age [Integer, String] number of old log files to keep, or frequency of rotation  (`"daily"`, `"weekly"` or `"monthly"`). Default value is `0`, which disables log file rotation
         
     | 
| 
      
 55 
     | 
    
         
            +
              # @param shift_size [Integer] maximum log file size in bytes (only applies when `shift_age` is a positive Integer)
         
     | 
| 
      
 56 
     | 
    
         
            +
              # @param shift_period_suffix [String] the log file suffix format for daily, weekly or monthly rotation
         
     | 
| 
      
 57 
     | 
    
         
            +
              # @param binmode sets whether the logger writes in binary mode
         
     | 
| 
      
 58 
     | 
    
         
            +
              def initialize(log, level: Logger::DEBUG, formatter: Rage::TextFormatter.new, shift_age: 0, shift_size: 104857600, shift_period_suffix: "%Y%m%d", binmode: false)
         
     | 
| 
      
 59 
     | 
    
         
            +
                if log && log != File::NULL
         
     | 
| 
      
 60 
     | 
    
         
            +
                  @logdev = Logger::LogDevice.new(log, shift_age:, shift_size:, shift_period_suffix:, binmode:)
         
     | 
| 
      
 61 
     | 
    
         
            +
                end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                @formatter = formatter
         
     | 
| 
      
 64 
     | 
    
         
            +
                @level = level
         
     | 
| 
      
 65 
     | 
    
         
            +
                define_log_methods
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def level=(level)
         
     | 
| 
      
 69 
     | 
    
         
            +
                @level = level
         
     | 
| 
      
 70 
     | 
    
         
            +
                define_log_methods
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
              def formatter=(formatter)
         
     | 
| 
      
 74 
     | 
    
         
            +
                @formatter = formatter
         
     | 
| 
      
 75 
     | 
    
         
            +
                define_log_methods
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
              # Add custom keys to an entry.
         
     | 
| 
      
 79 
     | 
    
         
            +
              #
         
     | 
| 
      
 80 
     | 
    
         
            +
              # @param context [Hash] a hash of custom keys
         
     | 
| 
      
 81 
     | 
    
         
            +
              # @example
         
     | 
| 
      
 82 
     | 
    
         
            +
              #   Rage.logger.with_context(key: "mykey") do
         
     | 
| 
      
 83 
     | 
    
         
            +
              #     Rage.logger.info "cache miss"
         
     | 
| 
      
 84 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 85 
     | 
    
         
            +
              def with_context(context)
         
     | 
| 
      
 86 
     | 
    
         
            +
                old_context = Thread.current[:rage_logger][:context]
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                if old_context.empty? # there's nothing in the context yet
         
     | 
| 
      
 89 
     | 
    
         
            +
                  Thread.current[:rage_logger][:context] = context
         
     | 
| 
      
 90 
     | 
    
         
            +
                else # it's not the first `with_context` call in the chain
         
     | 
| 
      
 91 
     | 
    
         
            +
                  Thread.current[:rage_logger][:context] = old_context.merge(context)
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                yield(self)
         
     | 
| 
      
 95 
     | 
    
         
            +
                true
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 98 
     | 
    
         
            +
                Thread.current[:rage_logger][:context] = old_context
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
              # Add a custom tag to an entry.
         
     | 
| 
      
 102 
     | 
    
         
            +
              #
         
     | 
| 
      
 103 
     | 
    
         
            +
              # @param tag [String] the tag to add to an entry
         
     | 
| 
      
 104 
     | 
    
         
            +
              # @example
         
     | 
| 
      
 105 
     | 
    
         
            +
              #   Rage.logger.tagged("ApiCall") do
         
     | 
| 
      
 106 
     | 
    
         
            +
              #     Rage.logger.info "success"
         
     | 
| 
      
 107 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 108 
     | 
    
         
            +
              def tagged(tag)
         
     | 
| 
      
 109 
     | 
    
         
            +
                Thread.current[:rage_logger][:tags] << tag
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                yield(self)
         
     | 
| 
      
 112 
     | 
    
         
            +
                true
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 115 
     | 
    
         
            +
                Thread.current[:rage_logger][:tags].pop
         
     | 
| 
      
 116 
     | 
    
         
            +
              end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
              alias_method :with_tag, :tagged
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
              private
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
              def define_log_methods
         
     | 
| 
      
 123 
     | 
    
         
            +
                methods = METHODS_MAP.map do |level_name, level_val|
         
     | 
| 
      
 124 
     | 
    
         
            +
                  if @logdev.nil? || level_val < @level
         
     | 
| 
      
 125 
     | 
    
         
            +
                    # logging is disabled or the log level is higher than the current one
         
     | 
| 
      
 126 
     | 
    
         
            +
                    <<-RUBY
         
     | 
| 
      
 127 
     | 
    
         
            +
                      def #{level_name}(msg = nil)
         
     | 
| 
      
 128 
     | 
    
         
            +
                        false
         
     | 
| 
      
 129 
     | 
    
         
            +
                      end
         
     | 
| 
      
 130 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 131 
     | 
    
         
            +
                  elsif defined?(IRB)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    # the call was made from IRB - don't use the formatter
         
     | 
| 
      
 133 
     | 
    
         
            +
                    <<-RUBY
         
     | 
| 
      
 134 
     | 
    
         
            +
                      def #{level_name}(msg = nil)
         
     | 
| 
      
 135 
     | 
    
         
            +
                        @logdev.write((msg || yield) + "\n")
         
     | 
| 
      
 136 
     | 
    
         
            +
                      end
         
     | 
| 
      
 137 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 138 
     | 
    
         
            +
                  elsif @formatter.class.name.start_with?("Rage::")
         
     | 
| 
      
 139 
     | 
    
         
            +
                    # the call was made from within the application and a built-in formatter is used;
         
     | 
| 
      
 140 
     | 
    
         
            +
                    # in such case we use the `gen_timestamp` method which is much faster than `Time.now.strftime`;
         
     | 
| 
      
 141 
     | 
    
         
            +
                    # it's not a standard approach however, so it's used with built-in formatters only
         
     | 
| 
      
 142 
     | 
    
         
            +
                    <<-RUBY
         
     | 
| 
      
 143 
     | 
    
         
            +
                      def #{level_name}(msg = nil)
         
     | 
| 
      
 144 
     | 
    
         
            +
                        @logdev.write(
         
     | 
| 
      
 145 
     | 
    
         
            +
                          @formatter.call("#{level_name}".freeze, Iodine::Rack::Utils.gen_timestamp, nil, msg || yield)
         
     | 
| 
      
 146 
     | 
    
         
            +
                        )
         
     | 
| 
      
 147 
     | 
    
         
            +
                      end
         
     | 
| 
      
 148 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 149 
     | 
    
         
            +
                  else
         
     | 
| 
      
 150 
     | 
    
         
            +
                    # the call was made from within the application and a custom formatter is used;
         
     | 
| 
      
 151 
     | 
    
         
            +
                    # stick to the standard approach of using one of the Log Level constants as sevetiry and `Time.now` as time
         
     | 
| 
      
 152 
     | 
    
         
            +
                    <<-RUBY
         
     | 
| 
      
 153 
     | 
    
         
            +
                      def #{level_name}(msg = nil)
         
     | 
| 
      
 154 
     | 
    
         
            +
                        @logdev.write(
         
     | 
| 
      
 155 
     | 
    
         
            +
                          @formatter.call(#{level_val}, Time.now, nil, msg || yield)
         
     | 
| 
      
 156 
     | 
    
         
            +
                        )
         
     | 
| 
      
 157 
     | 
    
         
            +
                      end
         
     | 
| 
      
 158 
     | 
    
         
            +
                    RUBY
         
     | 
| 
      
 159 
     | 
    
         
            +
                  end
         
     | 
| 
      
 160 
     | 
    
         
            +
                end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                self.class.class_eval(methods.join("\n"))
         
     | 
| 
      
 163 
     | 
    
         
            +
              end
         
     | 
| 
      
 164 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Rage::TextFormatter
         
     | 
| 
      
 2 
     | 
    
         
            +
              def initialize
         
     | 
| 
      
 3 
     | 
    
         
            +
                @pid = Process.pid
         
     | 
| 
      
 4 
     | 
    
         
            +
                Iodine.on_state(:on_start) do
         
     | 
| 
      
 5 
     | 
    
         
            +
                  @pid = Process.pid
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              def call(severity, timestamp, _, message)
         
     | 
| 
      
 10 
     | 
    
         
            +
                logger = Thread.current[:rage_logger]
         
     | 
| 
      
 11 
     | 
    
         
            +
                tags = logger[:tags]
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                if final = logger[:final]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  params, env = final[:params], final[:env]
         
     | 
| 
      
 15 
     | 
    
         
            +
                  if params
         
     | 
| 
      
 16 
     | 
    
         
            +
                    return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} controller=#{params[:controller]} action=#{params[:action]} status=#{final[:response][0]} duration=#{final[:duration]}\n"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # no controller/action keys are written if there are no params
         
     | 
| 
      
 19 
     | 
    
         
            +
                    return "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=info method=#{env["REQUEST_METHOD"]} path=#{env["PATH_INFO"]} status=#{final[:response][0]} duration=#{final[:duration]}\n"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                if tags.length == 1
         
     | 
| 
      
 24 
     | 
    
         
            +
                  tags_msg = "[#{tags[0]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
         
     | 
| 
      
 25 
     | 
    
         
            +
                elsif tags.length == 2
         
     | 
| 
      
 26 
     | 
    
         
            +
                  tags_msg = "[#{tags[0]}][#{tags[1]}] timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
         
     | 
| 
      
 27 
     | 
    
         
            +
                else
         
     | 
| 
      
 28 
     | 
    
         
            +
                  tags_msg = "[#{tags[0]}][#{tags[1]}]"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  i = 2
         
     | 
| 
      
 30 
     | 
    
         
            +
                  while i < tags.length
         
     | 
| 
      
 31 
     | 
    
         
            +
                    tags_msg << "[#{tags[i]}]"
         
     | 
| 
      
 32 
     | 
    
         
            +
                    i += 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  tags_msg << " timestamp=#{timestamp} pid=#{@pid} level=#{severity}"
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                context = logger[:context]
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                if !context.empty?
         
     | 
| 
      
 40 
     | 
    
         
            +
                  context_msg = ""
         
     | 
| 
      
 41 
     | 
    
         
            +
                  context.each { |k, v| context_msg << "#{k}=#{v} " }
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                "#{tags_msg} #{context_msg}message=#{message}\n"
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rage/router/backend.rb
    CHANGED
    
    | 
         @@ -14,8 +14,36 @@ class Rage::Router::Backend 
     | 
|
| 
       14 
14 
     | 
    
         
             
                @constrainer = Rage::Router::Constrainer.new({})
         
     | 
| 
       15 
15 
     | 
    
         
             
              end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
              def  
     | 
| 
      
 17 
     | 
    
         
            +
              def mount(path, handler, methods)
         
     | 
| 
      
 18 
     | 
    
         
            +
                raise "Mount handler should respond to `call`" unless handler.respond_to?(:call)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       18 
20 
     | 
    
         
             
                raw_handler = handler
         
     | 
| 
      
 21 
     | 
    
         
            +
                is_sidekiq = handler.respond_to?(:name) && handler.name == "Sidekiq::Web"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                handler = ->(env, _params) do
         
     | 
| 
      
 24 
     | 
    
         
            +
                  env["SCRIPT_NAME"] = path
         
     | 
| 
      
 25 
     | 
    
         
            +
                  sub_path = env["PATH_INFO"].delete_prefix!(path)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  env["PATH_INFO"] = "/" if sub_path == ""
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  if is_sidekiq
         
     | 
| 
      
 29 
     | 
    
         
            +
                    Rage::SidekiqSession.with_session(env) do
         
     | 
| 
      
 30 
     | 
    
         
            +
                      raw_handler.call(env)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  else
         
     | 
| 
      
 33 
     | 
    
         
            +
                    raw_handler.call(env)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 37 
     | 
    
         
            +
                  env["PATH_INFO"] = "#{env["SCRIPT_NAME"]}#{sub_path}"
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                methods.each do |method|
         
     | 
| 
      
 41 
     | 
    
         
            +
                  __on(method, path, handler, {}, {}, { raw_handler:, mount: true })
         
     | 
| 
      
 42 
     | 
    
         
            +
                  __on(method, "#{path}/*", handler, {}, {}, { raw_handler:, mount: true })
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def on(method, path, handler, constraints: {}, defaults: nil)
         
     | 
| 
       19 
47 
     | 
    
         
             
                raise "Path could not be empty" if path&.empty?
         
     | 
| 
       20 
48 
     | 
    
         | 
| 
       21 
49 
     | 
    
         
             
                if match_index = (path =~ OPTIONAL_PARAM_REGEXP)
         
     | 
| 
         @@ -29,12 +57,17 @@ class Rage::Router::Backend 
     | 
|
| 
       29 
57 
     | 
    
         
             
                  return
         
     | 
| 
       30 
58 
     | 
    
         
             
                end
         
     | 
| 
       31 
59 
     | 
    
         | 
| 
      
 60 
     | 
    
         
            +
                meta = { raw_handler: handler }
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
       32 
62 
     | 
    
         
             
                if handler.is_a?(String)
         
     | 
| 
       33 
63 
     | 
    
         
             
                  raise "Invalid route handler format, expected to match the 'controller#action' pattern" unless handler =~ STRING_HANDLER_REGEXP
         
     | 
| 
       34 
64 
     | 
    
         | 
| 
       35 
65 
     | 
    
         
             
                  controller, action = to_controller_class($1), $2
         
     | 
| 
       36 
66 
     | 
    
         
             
                  run_action_method_name = controller.__register_action(action.to_sym)
         
     | 
| 
       37 
67 
     | 
    
         | 
| 
      
 68 
     | 
    
         
            +
                  meta[:controller] = $1
         
     | 
| 
      
 69 
     | 
    
         
            +
                  meta[:action] = $2
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
       38 
71 
     | 
    
         
             
                  handler = eval("->(env, params) { #{controller}.new(env, params).#{run_action_method_name} }")
         
     | 
| 
       39 
72 
     | 
    
         
             
                else
         
     | 
| 
       40 
73 
     | 
    
         
             
                  raise "Non-string route handler should respond to `call`" unless handler.respond_to?(:call)
         
     | 
| 
         @@ -45,7 +78,7 @@ class Rage::Router::Backend 
     | 
|
| 
       45 
78 
     | 
    
         
             
                  handler = ->(env, _params) { orig_handler.call(env) }
         
     | 
| 
       46 
79 
     | 
    
         
             
                end
         
     | 
| 
       47 
80 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
                __on(method, path, handler,  
     | 
| 
      
 81 
     | 
    
         
            +
                __on(method, path, handler, constraints, defaults, meta)
         
     | 
| 
       49 
82 
     | 
    
         
             
              end
         
     | 
| 
       50 
83 
     | 
    
         | 
| 
       51 
84 
     | 
    
         
             
              def lookup(env)
         
     | 
| 
         @@ -55,7 +88,7 @@ class Rage::Router::Backend 
     | 
|
| 
       55 
88 
     | 
    
         | 
| 
       56 
89 
     | 
    
         
             
              private
         
     | 
| 
       57 
90 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
              def __on(method, path, handler,  
     | 
| 
      
 91 
     | 
    
         
            +
              def __on(method, path, handler, constraints, defaults, meta)
         
     | 
| 
       59 
92 
     | 
    
         
             
                @constrainer.validate_constraints(constraints)
         
     | 
| 
       60 
93 
     | 
    
         
             
                # Let the constrainer know if any constraints are being used now
         
     | 
| 
       61 
94 
     | 
    
         
             
                @constrainer.note_usage(constraints)
         
     | 
| 
         @@ -162,7 +195,7 @@ class Rage::Router::Backend 
     | 
|
| 
       162 
195 
     | 
    
         
             
                  end
         
     | 
| 
       163 
196 
     | 
    
         
             
                end
         
     | 
| 
       164 
197 
     | 
    
         | 
| 
       165 
     | 
    
         
            -
                route = { method 
     | 
| 
      
 198 
     | 
    
         
            +
                route = { method:, path:, pattern:, params:, constraints:, handler:, defaults:, meta: }
         
     | 
| 
       166 
199 
     | 
    
         
             
                @routes << route
         
     | 
| 
       167 
200 
     | 
    
         
             
                current_node.add_route(route, @constrainer)
         
     | 
| 
       168 
201 
     | 
    
         
             
              end
         
     | 
    
        data/lib/rage/router/dsl.rb
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ class Rage::Router::DSL 
     | 
|
| 
       12 
12 
     | 
    
         
             
              ##
         
     | 
| 
       13 
13 
     | 
    
         
             
              # This class implements routing logic for your application, providing API similar to Rails.
         
     | 
| 
       14 
14 
     | 
    
         
             
              #
         
     | 
| 
       15 
     | 
    
         
            -
              # Compared to Rails router, the most notable difference is that a wildcard segment can only be in the last section of the path and cannot be named.
         
     | 
| 
      
 15 
     | 
    
         
            +
              # Compared to the Rails router, the most notable difference is that a wildcard segment can only be in the last section of the path and cannot be named.
         
     | 
| 
       16 
16 
     | 
    
         
             
              # Example:
         
     | 
| 
       17 
17 
     | 
    
         
             
              # ```ruby
         
     | 
| 
       18 
18 
     | 
    
         
             
              # get "/photos/*"
         
     | 
| 
         @@ -327,8 +327,19 @@ class Rage::Router::DSL 
     | 
|
| 
       327 
327 
     | 
    
         
             
                    via = args[1][:via]
         
     | 
| 
       328 
328 
     | 
    
         
             
                  end
         
     | 
| 
       329 
329 
     | 
    
         | 
| 
       330 
     | 
    
         
            -
                   
     | 
| 
       331 
     | 
    
         
            -
                   
     | 
| 
      
 330 
     | 
    
         
            +
                  at = "/#{at}" unless at.start_with?("/")
         
     | 
| 
      
 331 
     | 
    
         
            +
                  at = at.delete_suffix("/") if at.end_with?("/")
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
                  http_methods = if via == :all || via.nil?
         
     | 
| 
      
 334 
     | 
    
         
            +
                    @default_match_methods.map { |method| method.to_s.upcase! }
         
     | 
| 
      
 335 
     | 
    
         
            +
                  else
         
     | 
| 
      
 336 
     | 
    
         
            +
                    Array(via).map! do |method|
         
     | 
| 
      
 337 
     | 
    
         
            +
                      raise ArgumentError, "Invalid HTTP method: #{method}" unless @default_match_methods.include?(method)
         
     | 
| 
      
 338 
     | 
    
         
            +
                      method.to_s.upcase!
         
     | 
| 
      
 339 
     | 
    
         
            +
                    end
         
     | 
| 
      
 340 
     | 
    
         
            +
                  end
         
     | 
| 
      
 341 
     | 
    
         
            +
             
     | 
| 
      
 342 
     | 
    
         
            +
                  @router.mount(at, app, http_methods)
         
     | 
| 
       332 
343 
     | 
    
         
             
                end
         
     | 
| 
       333 
344 
     | 
    
         | 
| 
       334 
345 
     | 
    
         
             
                private
         
     | 
| 
         @@ -22,7 +22,7 @@ class Rage::Router::HandlerStorage 
     | 
|
| 
       22 
22 
     | 
    
         
             
                  params: params,
         
     | 
| 
       23 
23 
     | 
    
         
             
                  constraints: constraints,
         
     | 
| 
       24 
24 
     | 
    
         
             
                  handler: route[:handler],
         
     | 
| 
       25 
     | 
    
         
            -
                  create_params_object: compile_create_params_object(params, route[:defaults])
         
     | 
| 
      
 25 
     | 
    
         
            +
                  create_params_object: compile_create_params_object(params, route[:defaults], route[:meta])
         
     | 
| 
       26 
26 
     | 
    
         
             
                }
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
                constraints_keys = constraints.keys
         
     | 
| 
         @@ -47,8 +47,11 @@ class Rage::Router::HandlerStorage 
     | 
|
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         
             
              private
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
              def compile_create_params_object(param_keys, defaults)
         
     | 
| 
       51 
     | 
    
         
            -
                lines = [ 
     | 
| 
      
 50 
     | 
    
         
            +
              def compile_create_params_object(param_keys, defaults, meta)
         
     | 
| 
      
 51 
     | 
    
         
            +
                lines = [
         
     | 
| 
      
 52 
     | 
    
         
            +
                  ":controller => '#{meta[:controller]}'.freeze",
         
     | 
| 
      
 53 
     | 
    
         
            +
                  ":action => '#{meta[:action]}'.freeze"
         
     | 
| 
      
 54 
     | 
    
         
            +
                ]
         
     | 
| 
       52 
55 
     | 
    
         | 
| 
       53 
56 
     | 
    
         
             
                param_keys.each_with_index do |key, i|
         
     | 
| 
       54 
57 
     | 
    
         
             
                  lines << ":#{key} => param_values[#{i}]"
         
     | 
| 
         @@ -56,7 +59,7 @@ class Rage::Router::HandlerStorage 
     | 
|
| 
       56 
59 
     | 
    
         | 
| 
       57 
60 
     | 
    
         
             
                if defaults
         
     | 
| 
       58 
61 
     | 
    
         
             
                  defaults.except(*param_keys.map(&:to_sym)).each do |key, value|
         
     | 
| 
       59 
     | 
    
         
            -
                    lines << ":#{key} => '#{value}'"
         
     | 
| 
      
 62 
     | 
    
         
            +
                    lines << ":#{key} => '#{value}'.freeze"
         
     | 
| 
       60 
63 
     | 
    
         
             
                  end
         
     | 
| 
       61 
64 
     | 
    
         
             
                end
         
     | 
| 
       62 
65 
     | 
    
         | 
| 
         @@ -0,0 +1,72 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "digest"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "base64"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            ##
         
     | 
| 
      
 7 
     | 
    
         
            +
            # Used **specifically** for compatibility with Sidekiq's Web interface.
         
     | 
| 
      
 8 
     | 
    
         
            +
            # Remove once we have real sessions or once Sidekiq's author decides they
         
     | 
| 
      
 9 
     | 
    
         
            +
            # don't need cookie sessions to protect against CSRF.
         
     | 
| 
      
 10 
     | 
    
         
            +
            #
         
     | 
| 
      
 11 
     | 
    
         
            +
            class Rage::SidekiqSession
         
     | 
| 
      
 12 
     | 
    
         
            +
              KEY = Digest::SHA2.hexdigest(ENV["SECRET_KEY_BASE"] || File.read("Gemfile.lock") + File.read("config/routes.rb"))
         
     | 
| 
      
 13 
     | 
    
         
            +
              SESSION_KEY = "rage.sidekiq.session"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              def self.with_session(env)
         
     | 
| 
      
 16 
     | 
    
         
            +
                env["rack.session"] = session = self.new(env)
         
     | 
| 
      
 17 
     | 
    
         
            +
                response = yield
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                if session.changed
         
     | 
| 
      
 20 
     | 
    
         
            +
                  Rack::Utils.set_cookie_header!(
         
     | 
| 
      
 21 
     | 
    
         
            +
                    response[1],
         
     | 
| 
      
 22 
     | 
    
         
            +
                    SESSION_KEY,
         
     | 
| 
      
 23 
     | 
    
         
            +
                    { path: env["SCRIPT_NAME"], httponly: true, same_site: true, value: session.dump }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  )
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                response
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              attr_reader :changed
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              def initialize(env)
         
     | 
| 
      
 33 
     | 
    
         
            +
                @env = env
         
     | 
| 
      
 34 
     | 
    
         
            +
                session = Rack::Utils.parse_cookies(@env)[SESSION_KEY]
         
     | 
| 
      
 35 
     | 
    
         
            +
                @data = decode_session(session)
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def [](key)
         
     | 
| 
      
 39 
     | 
    
         
            +
                @data[key]
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              def[]=(key, value)
         
     | 
| 
      
 43 
     | 
    
         
            +
                @changed = true
         
     | 
| 
      
 44 
     | 
    
         
            +
                @data[key] = value
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def to_hash
         
     | 
| 
      
 48 
     | 
    
         
            +
                @data
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              def dump
         
     | 
| 
      
 52 
     | 
    
         
            +
                encoded_data = Marshal.dump(@data)
         
     | 
| 
      
 53 
     | 
    
         
            +
                signature = OpenSSL::HMAC.hexdigest("SHA256", KEY, encoded_data)
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                Base64.urlsafe_encode64("#{encoded_data}--#{signature}")
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
              private
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              def decode_session(session)
         
     | 
| 
      
 61 
     | 
    
         
            +
                return {} unless session
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                encoded_data, signature = Base64.urlsafe_decode64(session).split("--")
         
     | 
| 
      
 64 
     | 
    
         
            +
                ref_signature = OpenSSL::HMAC.hexdigest("SHA256", KEY, encoded_data)
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                if Rack::Utils.secure_compare(signature, ref_signature)
         
     | 
| 
      
 67 
     | 
    
         
            +
                  Marshal.load(encoded_data)
         
     | 
| 
      
 68 
     | 
    
         
            +
                else
         
     | 
| 
      
 69 
     | 
    
         
            +
                  {}
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require_relative "config/application"
         
     | 
| 
         @@ -1,7 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            Rage.configure do 
     | 
| 
      
 1 
     | 
    
         
            +
            Rage.configure do
         
     | 
| 
       2 
2 
     | 
    
         
             
              # Specify the number of server processes to run. Defaults to number of CPU cores.
         
     | 
| 
       3 
     | 
    
         
            -
              config.workers_count = 1
         
     | 
| 
      
 3 
     | 
    
         
            +
              config.server.workers_count = 1
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
              # Specify the port the server will listen on.
         
     | 
| 
       6 
     | 
    
         
            -
              config.port = 3000
         
     | 
| 
      
 6 
     | 
    
         
            +
              config.server.port = 3000
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              # Specify the logger
         
     | 
| 
      
 9 
     | 
    
         
            +
              config.logger = Rage::Logger.new(STDOUT)
         
     | 
| 
       7 
10 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,7 +1,11 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            Rage.configure do 
     | 
| 
      
 1 
     | 
    
         
            +
            Rage.configure do
         
     | 
| 
       2 
2 
     | 
    
         
             
              # Specify the number of server processes to run. Defaults to number of CPU cores.
         
     | 
| 
       3 
     | 
    
         
            -
              # config.workers_count = ENV.fetch("WEB_CONCURRENCY", 1)
         
     | 
| 
      
 3 
     | 
    
         
            +
              # config.server.workers_count = ENV.fetch("WEB_CONCURRENCY", 1)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
              # Specify the port the server will listen on.
         
     | 
| 
       6 
     | 
    
         
            -
              config.port = 3000
         
     | 
| 
      
 6 
     | 
    
         
            +
              config.server.port = 3000
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              # Specify the logger
         
     | 
| 
      
 9 
     | 
    
         
            +
              config.logger = Rage::Logger.new("log/production.log")
         
     | 
| 
      
 10 
     | 
    
         
            +
              config.log_level = Logger::INFO
         
     | 
| 
       7 
11 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,7 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            Rage.configure do 
     | 
| 
      
 1 
     | 
    
         
            +
            Rage.configure do
         
     | 
| 
       2 
2 
     | 
    
         
             
              # Specify the number of server processes to run. Defaults to number of CPU cores.
         
     | 
| 
       3 
     | 
    
         
            -
              config.workers_count = 1
         
     | 
| 
      
 3 
     | 
    
         
            +
              config.server.workers_count = 1
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
              # Specify the port the server will listen on.
         
     | 
| 
       6 
     | 
    
         
            -
              config.port = 3000
         
     | 
| 
      
 6 
     | 
    
         
            +
              config.server.port = 3000
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              # Specify the logger
         
     | 
| 
      
 9 
     | 
    
         
            +
              config.logger = Rage::Logger.new("log/test.log")
         
     | 
| 
       7 
10 
     | 
    
         
             
            end
         
     | 
    
        data/lib/rage/version.rb
    CHANGED
    
    
    
        data/lib/rage-rb.rb
    CHANGED
    
    | 
         @@ -22,8 +22,9 @@ module Rage 
     | 
|
| 
       22 
22 
     | 
    
         
             
                @config ||= Rage::Configuration.new
         
     | 
| 
       23 
23 
     | 
    
         
             
              end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
              def self.configure
         
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
      
 25 
     | 
    
         
            +
              def self.configure(&)
         
     | 
| 
      
 26 
     | 
    
         
            +
                config.instance_eval(&)
         
     | 
| 
      
 27 
     | 
    
         
            +
                config.__finalize
         
     | 
| 
       27 
28 
     | 
    
         
             
              end
         
     | 
| 
       28 
29 
     | 
    
         | 
| 
       29 
30 
     | 
    
         
             
              def self.env
         
     | 
| 
         @@ -38,6 +39,10 @@ module Rage 
     | 
|
| 
       38 
39 
     | 
    
         
             
                @root ||= Pathname.new(".").expand_path
         
     | 
| 
       39 
40 
     | 
    
         
             
              end
         
     | 
| 
       40 
41 
     | 
    
         | 
| 
      
 42 
     | 
    
         
            +
              def self.logger
         
     | 
| 
      
 43 
     | 
    
         
            +
                @logger ||= config.logger
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       41 
46 
     | 
    
         
             
              module Router
         
     | 
| 
       42 
47 
     | 
    
         
             
                module Strategies
         
     | 
| 
       43 
48 
     | 
    
         
             
                end
         
     | 
    
        data/rage.gemspec
    CHANGED
    
    
    
        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. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.5.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- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2023-11-25 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. 
     | 
| 
      
 47 
     | 
    
         
            +
                    version: '2.2'
         
     | 
| 
       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. 
     | 
| 
      
 54 
     | 
    
         
            +
                    version: '2.2'
         
     | 
| 
       55 
55 
     | 
    
         
             
            description:
         
     | 
| 
       56 
56 
     | 
    
         
             
            email:
         
     | 
| 
       57 
57 
     | 
    
         
             
            - rsamoi@icloud.com
         
     | 
| 
         @@ -79,6 +79,8 @@ files: 
     | 
|
| 
       79 
79 
     | 
    
         
             
            - lib/rage/errors.rb
         
     | 
| 
       80 
80 
     | 
    
         
             
            - lib/rage/fiber.rb
         
     | 
| 
       81 
81 
     | 
    
         
             
            - lib/rage/fiber_scheduler.rb
         
     | 
| 
      
 82 
     | 
    
         
            +
            - lib/rage/logger/logger.rb
         
     | 
| 
      
 83 
     | 
    
         
            +
            - lib/rage/logger/text_formatter.rb
         
     | 
| 
       82 
84 
     | 
    
         
             
            - lib/rage/params_parser.rb
         
     | 
| 
       83 
85 
     | 
    
         
             
            - lib/rage/request.rb
         
     | 
| 
       84 
86 
     | 
    
         
             
            - lib/rage/router/README.md
         
     | 
| 
         @@ -89,7 +91,9 @@ files: 
     | 
|
| 
       89 
91 
     | 
    
         
             
            - lib/rage/router/node.rb
         
     | 
| 
       90 
92 
     | 
    
         
             
            - lib/rage/router/strategies/host.rb
         
     | 
| 
       91 
93 
     | 
    
         
             
            - lib/rage/setup.rb
         
     | 
| 
      
 94 
     | 
    
         
            +
            - lib/rage/sidekiq_session.rb
         
     | 
| 
       92 
95 
     | 
    
         
             
            - lib/rage/templates/Gemfile
         
     | 
| 
      
 96 
     | 
    
         
            +
            - lib/rage/templates/Rakefile
         
     | 
| 
       93 
97 
     | 
    
         
             
            - lib/rage/templates/app-controllers-application_controller.rb
         
     | 
| 
       94 
98 
     | 
    
         
             
            - lib/rage/templates/config-application.rb
         
     | 
| 
       95 
99 
     | 
    
         
             
            - lib/rage/templates/config-environments-development.rb
         
     |