rage-rb 1.6.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile +10 -7
  4. data/OVERVIEW.md +1 -1
  5. data/README.md +25 -9
  6. data/lib/rage/all.rb +1 -0
  7. data/lib/rage/cable/cable.rb +130 -0
  8. data/lib/rage/cable/channel.rb +452 -0
  9. data/lib/rage/cable/connection.rb +78 -0
  10. data/lib/rage/cable/protocol/actioncable_v1_json.rb +167 -0
  11. data/lib/rage/cable/router.rb +138 -0
  12. data/lib/rage/cli.rb +2 -1
  13. data/lib/rage/code_loader.rb +9 -0
  14. data/lib/rage/configuration.rb +53 -0
  15. data/lib/rage/controller/api.rb +51 -13
  16. data/lib/rage/cookies.rb +7 -9
  17. data/lib/rage/ext/active_record/connection_pool.rb +1 -1
  18. data/lib/rage/fiber.rb +3 -3
  19. data/lib/rage/fiber_scheduler.rb +1 -1
  20. data/lib/rage/logger/json_formatter.rb +1 -1
  21. data/lib/rage/logger/logger.rb +1 -1
  22. data/lib/rage/logger/text_formatter.rb +1 -1
  23. data/lib/rage/middleware/cors.rb +2 -2
  24. data/lib/rage/middleware/fiber_wrapper.rb +3 -1
  25. data/lib/rage/middleware/origin_validator.rb +38 -0
  26. data/lib/rage/middleware/reloader.rb +1 -1
  27. data/lib/rage/params_parser.rb +1 -1
  28. data/lib/rage/router/backend.rb +4 -6
  29. data/lib/rage/router/constrainer.rb +1 -1
  30. data/lib/rage/router/dsl.rb +7 -7
  31. data/lib/rage/router/dsl_plugins/legacy_hash_notation.rb +1 -1
  32. data/lib/rage/router/dsl_plugins/legacy_root_notation.rb +1 -1
  33. data/lib/rage/router/handler_storage.rb +1 -1
  34. data/lib/rage/session.rb +2 -2
  35. data/lib/rage/setup.rb +5 -1
  36. data/lib/rage/sidekiq_session.rb +1 -1
  37. data/lib/rage/version.rb +1 -1
  38. data/lib/rage-rb.rb +23 -15
  39. data/rage.gemspec +1 -1
  40. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39c84f49894f7ae6e8c53e78d5c84eef65885c15f3ad7d6469458a79dba6e691
4
- data.tar.gz: 3a8b37eb48370a2a63c1b78f09417013f60593cdbcf0385958dd495ce35e494c
3
+ metadata.gz: 4adc4048bfa84558a6b86b15cbcab8e6153858878425bde66db78a975fa46f46
4
+ data.tar.gz: 46bef9f42fdd681bdf593f037a73ca8ae2d435acad9eed5ddf4b3740bf61b601
5
5
  SHA512:
6
- metadata.gz: 8db258b1f23a58def4118ca0bdb1efcf6534ff03362ac82c45ded5276c90ab42809e007a9d7f72972dc8fd2597ac5eab2bbfaf27d6efb38a3857b1133fdbecde
7
- data.tar.gz: eb7a5910f52399ff67bc0bc4078fcc970e023fcc038dc65dd16b19e17afa902a95a7c1b62cca7f61a5f5e38ba45702dc2a314ef841868fd7cf40289a65a0fe0f
6
+ metadata.gz: 754b954d46d065020e0c2184c04f1a13d0c0a64ddf11d1b034062df4cc669460c259032a8bf40a61c876579f1774e50e9b57181d4d3e1301c4610577e11c6734
7
+ data.tar.gz: 4207556922f8fd2c82d07ba8664f53847dc1e5afb6bb5d38260ac0ccd428dd011fd8c6bf34f69aa75af97044a838df23c6c36b5baae1ae7cd3d4c08455fc699b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.8.0] - 2024-08-06
4
+
5
+ ### Added
6
+
7
+ - Support WebSockets (#88).
8
+
9
+ ## [1.7.0] - 2024-07-30
10
+
11
+ ### Added
12
+
13
+ - Support `wrap_parameters` by [@alex-rogachev](https://github.com/alex-rogachev) (#89).
14
+ - Unknown environment error handling by [@cuneyter](https://github.com/cuneyter) (#95).
15
+ - Allow `rescue_from` handlers to not accept arguments (#93).
16
+
3
17
  ## [1.6.0] - 2024-07-15
4
18
 
5
19
  ### Added
data/Gemfile CHANGED
@@ -8,12 +8,15 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
- gem "http"
12
11
  gem "yard"
12
+ gem "rubocop", "~> 1.65", require: false
13
13
 
14
- gem "pg"
15
- gem "mysql2"
16
- gem "connection_pool", "~> 2.0"
17
-
18
- gem "rbnacl"
19
- gem "domain_name"
14
+ group :test do
15
+ gem "http"
16
+ gem "pg"
17
+ gem "mysql2"
18
+ gem "connection_pool", "~> 2.0"
19
+ gem "rbnacl"
20
+ gem "domain_name"
21
+ gem "websocket-client-simple"
22
+ end
data/OVERVIEW.md CHANGED
@@ -31,7 +31,7 @@ class UsersController < RageController::API
31
31
  end
32
32
  ```
33
33
 
34
- Before processing requests to `UsersController#show`, Rage has to [register](https://github.com/rage-rb/rage/blob/master/lib/rage/controller/api.rb#L10) the show action. Registering means defining a new method that will look like this:
34
+ Before processing requests to `UsersController#show`, Rage has to [register](https://github.com/rage-rb/rage/blob/master/lib/rage/controller/api.rb#L11) the show action. Registering means defining a new method that will look like this:
35
35
 
36
36
  ```ruby
37
37
  class UsersController
data/README.md CHANGED
@@ -15,7 +15,7 @@ Inspired by [Deno](https://deno.com) and built on top of [Iodine](https://github
15
15
 
16
16
  * **API-only** - separation of concerns is one of the most fundamental principles in software development. Backend and frontend are very different layers with different goals and paths to those goals. Separating BE code from FE code results in a much more sustainable architecture compared with classic Rails monoliths.
17
17
 
18
- * **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on IO.
18
+ * **Acceptance of modern Ruby** - the framework includes a fiber scheduler, which means your code never blocks while waiting on I/O.
19
19
 
20
20
  ## Installation
21
21
 
@@ -44,7 +44,7 @@ Start coding!
44
44
 
45
45
  ## Getting Started
46
46
 
47
- This gem is designed to be a drop-in replacement for Rails in API mode. Public API is mostly expected to match Rails, however, sometimes it's a little bit more strict.
47
+ This gem is designed to be a drop-in replacement for Rails in API mode. Public API is expected to fully match Rails.
48
48
 
49
49
  Check out in-depth API docs for more information:
50
50
 
@@ -60,6 +60,8 @@ Also, see the following integration guides:
60
60
  - [Rails integration](https://github.com/rage-rb/rage/wiki/Rails-integration)
61
61
  - [RSpec integration](https://github.com/rage-rb/rage/wiki/RSpec-integration)
62
62
 
63
+ If you are a first-time contributor, make sure to check the [overview doc](https://github.com/rage-rb/rage/blob/master/OVERVIEW.md) that shows how Rage's core components interact with each other.
64
+
63
65
  ### Example
64
66
 
65
67
  A sample controller could look like this:
@@ -116,34 +118,48 @@ class PagesController < RageController::API
116
118
  end
117
119
  ```
118
120
 
119
- :information_source: **Note**: When using `Fiber.await`, it is important to wrap any instance of IO into a fiber using `Fiber.schedule`.
121
+ :information_source: **Note**: When using `Fiber.await`, it is important to wrap every argument into a fiber using `Fiber.schedule`.
120
122
 
121
123
  ## Benchmarks
122
124
 
123
- #### hello world
125
+ #### Hello World
124
126
 
125
127
  ```ruby
126
- class ArticlesController < ApplicationController
128
+ class BenchmarksController < ApplicationController
127
129
  def index
128
130
  render json: { hello: "world" }
129
131
  end
130
132
  end
131
133
  ```
132
- ![Requests per second](https://github.com/rage-rb/rage/assets/2270393/6c221903-e265-4c94-80e1-041f266c8f47)
133
134
 
134
- #### waiting on IO
135
+ ![Requests per second](https://github.com/user-attachments/assets/a7f864ae-0dfb-4628-a420-265a10d8591d)
136
+
137
+ #### Waiting on I/O
135
138
 
136
139
  ```ruby
137
140
  require "net/http"
138
141
 
139
- class ArticlesController < ApplicationController
142
+ class BenchmarksController < ApplicationController
140
143
  def index
141
144
  Net::HTTP.get(URI("<endpoint-that-responds-in-one-second>"))
142
145
  head :ok
143
146
  end
144
147
  end
145
148
  ```
146
- ![Time to complete 100 requests](https://github.com/rage-rb/rage/assets/2270393/007044e9-efe0-4675-9cab-8a4868154118)
149
+
150
+ ![Time to complete 100 requests](https://github.com/user-attachments/assets/4f4feda3-bd88-43d8-8999-268534c2f9de)
151
+
152
+ #### Using ActiveRecord
153
+
154
+ ```ruby
155
+ class BenchmarksController < ApplicationController
156
+ def show
157
+ render json: World.find(rand(10_000))
158
+ end
159
+ end
160
+ ```
161
+
162
+ ![Requests per second](https://github.com/user-attachments/assets/04678788-0034-4db4-9582-d0bc16fd9e28)
147
163
 
148
164
  ## Upcoming releases
149
165
 
data/lib/rage/all.rb CHANGED
@@ -26,6 +26,7 @@ require_relative "logger/text_formatter"
26
26
  require_relative "logger/json_formatter"
27
27
  require_relative "logger/logger"
28
28
 
29
+ require_relative "middleware/origin_validator"
29
30
  require_relative "middleware/fiber_wrapper"
30
31
  require_relative "middleware/cors"
31
32
  require_relative "middleware/reloader"
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rage::Cable
4
+ # Create a new Cable application.
5
+ #
6
+ # @example
7
+ # map "/cable" do
8
+ # run Rage.cable.application
9
+ # end
10
+ def self.application
11
+ protocol = Rage.config.cable.protocol
12
+ protocol.init(__router)
13
+
14
+ handler = __build_handler(protocol)
15
+ accept_response = [0, protocol.protocol_definition, []]
16
+
17
+ application = ->(env) do
18
+ if env["rack.upgrade?"] == :websocket
19
+ env["rack.upgrade"] = handler
20
+ accept_response
21
+ else
22
+ [426, { "Connection" => "Upgrade", "Upgrade" => "websocket" }, []]
23
+ end
24
+ end
25
+
26
+ Rage.with_middlewares(application, Rage.config.cable.middlewares)
27
+ end
28
+
29
+ # @private
30
+ def self.__router
31
+ @__router ||= Router.new
32
+ end
33
+
34
+ # @private
35
+ def self.__build_handler(protocol)
36
+ klass = Class.new do
37
+ def initialize(protocol)
38
+ Iodine.on_state(:on_start) do
39
+ unless Fiber.scheduler
40
+ Fiber.set_scheduler(Rage::FiberScheduler.new)
41
+ end
42
+ end
43
+
44
+ @protocol = protocol
45
+ end
46
+
47
+ def on_open(connection)
48
+ Fiber.schedule do
49
+ @protocol.on_open(connection)
50
+ rescue => e
51
+ log_error(e)
52
+ end
53
+ end
54
+
55
+ def on_message(connection, data)
56
+ Fiber.schedule do
57
+ @protocol.on_message(connection, data)
58
+ rescue => e
59
+ log_error(e)
60
+ end
61
+ end
62
+
63
+ if protocol.respond_to?(:on_close)
64
+ def on_close(connection)
65
+ return unless ::Iodine.running?
66
+
67
+ Fiber.schedule do
68
+ @protocol.on_close(connection)
69
+ rescue => e
70
+ log_error(e)
71
+ end
72
+ end
73
+ end
74
+
75
+ if protocol.respond_to?(:on_shutdown)
76
+ def on_shutdown(connection)
77
+ @protocol.on_shutdown(connection)
78
+ rescue => e
79
+ log_error(e)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def log_error(e)
86
+ Rage.logger.error("Unhandled exception has occured - #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
87
+ end
88
+ end
89
+
90
+ klass.new(protocol)
91
+ end
92
+
93
+ # Broadcast data directly to a named stream.
94
+ #
95
+ # @param stream [String] the name of the stream
96
+ # @param data [Object] the object to send to the clients. This will later be encoded according to the protocol used.
97
+ # @example
98
+ # Rage.cable.broadcast("chat", { message: "A new member has joined!" })
99
+ def self.broadcast(stream, data)
100
+ Rage.config.cable.protocol.broadcast(stream, data)
101
+ end
102
+
103
+ # @!parse [ruby]
104
+ # # @abstract
105
+ # class WebSocketConnection
106
+ # # Write data to the connection.
107
+ # #
108
+ # # @param data [String] the data to write
109
+ # def write(data)
110
+ # end
111
+ #
112
+ # # Subscribe to a channel.
113
+ # #
114
+ # # @param name [String] the channel name
115
+ # def subscribe(name)
116
+ # end
117
+ #
118
+ # # Close the connection.
119
+ # def close
120
+ # end
121
+ # end
122
+
123
+ module Protocol
124
+ end
125
+ end
126
+
127
+ require_relative "protocol/actioncable_v1_json"
128
+ require_relative "channel"
129
+ require_relative "connection"
130
+ require_relative "router"