rage-rb 0.2.0 → 0.3.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 +10 -0
- data/Gemfile +0 -3
- data/README.md +1 -1
- data/lib/rage/application.rb +1 -1
- data/lib/rage/cli.rb +72 -2
- data/lib/rage/controller/api.rb +68 -17
- data/lib/rage/router/backend.rb +6 -3
- data/lib/rage/setup.rb +20 -4
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +5 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7684c979952ee81bee165b9b667dea87199f3fa28dadadc4878b29a12211fdac
|
4
|
+
data.tar.gz: 2428443ee2456ef375990decfbf36a2952670ee517ac07cf4c994f5b9b7076f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f59da6b77e69fa2b89761b30a497b4c0fa8f02cbabeed65c953b0f5e807b349e5b305bf31564bcc3eaeed4957215766e8781af5a5b8bbd9754e0461fa18f7b87
|
7
|
+
data.tar.gz: 733a15ef91abcbfc079a3bbbb83f82686038059b3c518c1c0a279e8000ad885c65cc5019cc691e2731fa1e59ca1f62e8fe95c8f454837ed0bdc65e181157ffc7
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.3.0] - 2023-10-08
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- CLI `routes` task.
|
8
|
+
- CLI `console` task.
|
9
|
+
- `:if` and `:unless` options in `before_action`.
|
10
|
+
- Allow to set response headers.
|
11
|
+
- Block version of `before_action`.
|
12
|
+
|
3
13
|
## [0.2.0] - 2023-09-27
|
4
14
|
|
5
15
|
### Added
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -142,7 +142,7 @@ end
|
|
142
142
|
Version | Changes
|
143
143
|
------- |------------
|
144
144
|
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
|
-
0.3 | CLI updates:<br> • `routes` task;<br> • `console` task;<br>Support the `:if` and `:unless` options in `before_action`.<br>Allow to set response headers
|
145
|
+
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
146
|
0.4 | 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;<br> • support regexp constraints;
|
147
147
|
0.5 | Implement Iodine-based equivalent of `ActionController::Live`.<br>Use `ActionDispatch::RemoteIp`.
|
148
148
|
0.6 | Expose the `cookies` object.<br>Expose the `send_data` and `send_file` methods.<br>Support conditional get with `etag` and `last_modified`.
|
data/lib/rage/application.rb
CHANGED
@@ -19,7 +19,7 @@ class Rage::Application
|
|
19
19
|
end
|
20
20
|
|
21
21
|
rescue => e
|
22
|
-
[500, {}, ["#{e.class}:#{e.message}\n\n#{e.backtrace.join("\n")}"]]
|
22
|
+
[500, {}, ["#{e.class}:#{e.message}\n\n#{e.backtrace.join("\n")}"]]
|
23
23
|
|
24
24
|
ensure
|
25
25
|
# notify Iodine the request can now be served
|
data/lib/rage/cli.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require "thor"
|
4
3
|
require "rage"
|
4
|
+
require "irb"
|
5
5
|
|
6
6
|
module Rage
|
7
7
|
class CLI < Thor
|
@@ -25,11 +25,81 @@ module Rage
|
|
25
25
|
|
26
26
|
::Iodine.start
|
27
27
|
end
|
28
|
+
|
29
|
+
desc 'routes', 'List all routes.'
|
30
|
+
option :grep, aliases: "-g", desc: "Filter routes by pattern"
|
31
|
+
def routes
|
32
|
+
# the result would be something like this:
|
33
|
+
# Action Verb Path Controller#Action
|
34
|
+
# index GET / application#index
|
35
|
+
|
36
|
+
# load config/application.rb
|
37
|
+
environment
|
38
|
+
|
39
|
+
routes = Rage.__router.routes
|
40
|
+
|
41
|
+
pattern = options[:grep]
|
42
|
+
|
43
|
+
if pattern
|
44
|
+
routes = routes.select do |route|
|
45
|
+
route[:path].match?(pattern) || route[:raw_handler].to_s.match?(pattern) || route[:method].match?(pattern)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return puts 'Action Verb Path Controller#Action' if routes.empty?
|
50
|
+
|
51
|
+
# construct a table
|
52
|
+
table = []
|
53
|
+
|
54
|
+
# longest_path is either the length of the longest path or 5
|
55
|
+
longest_path = routes.map { |route| route[:path].length }.max + 3
|
56
|
+
longest_path = longest_path > 5 ? longest_path : 5
|
57
|
+
|
58
|
+
longest_verb = routes.map { |route| route[:method].length }.max + 3
|
59
|
+
longest_verb = longest_verb > 4 ? longest_verb : 7
|
60
|
+
|
61
|
+
# longest_handler is either the length of the longest handler or 7, since DELETE is the longest HTTP method
|
62
|
+
longest_handler = routes.map { |route| route[:raw_handler].is_a?(Proc) ? 7 : route[:raw_handler].split('#').last.length }.max + 3
|
63
|
+
longest_handler = longest_handler > 7 ? longest_handler : 7
|
64
|
+
|
65
|
+
# longest_controller is either the length of the longest controller or 12, since Controller#{length} is the longest controller
|
66
|
+
longest_controller = routes.map { |route| route[:raw_handler].is_a?(Proc) ? 7 : route[:raw_handler].to_s.length }.max + 3
|
67
|
+
longest_controller = longest_controller > 12 ? longest_controller : 12
|
68
|
+
|
69
|
+
routes.each do |route|
|
70
|
+
table << [
|
71
|
+
format("%- #{longest_handler}s", route[:raw_handler].is_a?(Proc) ? 'Lambda' : route[:raw_handler].split('#').last),
|
72
|
+
format("%- #{longest_verb}s", route[:method]),
|
73
|
+
format("%- #{longest_path}s", route[:path]),
|
74
|
+
format("%- #{longest_controller}s", route[:raw_handler].is_a?(Proc) ? 'Lambda' : route[:raw_handler])
|
75
|
+
]
|
76
|
+
end
|
77
|
+
|
78
|
+
table.unshift([format("%- #{longest_handler}s", 'Action'), format("%- #{longest_verb}s", 'Verb'), format("%- #{longest_path}s", 'Path'),
|
79
|
+
format("%- #{longest_path}s", "Controller#Action\n")])
|
80
|
+
# print the table
|
81
|
+
table.each do |row|
|
82
|
+
# this should be changed to use the main logger when added
|
83
|
+
puts row.join
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
desc "c", "Start the app console."
|
88
|
+
def console
|
89
|
+
environment
|
90
|
+
ARGV.clear
|
91
|
+
IRB.start
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def environment
|
97
|
+
require File.expand_path("config/application.rb", Dir.pwd)
|
98
|
+
end
|
28
99
|
end
|
29
100
|
|
30
101
|
class NewAppGenerator < Thor::Group
|
31
102
|
include Thor::Actions
|
32
|
-
|
33
103
|
argument :path, type: :string
|
34
104
|
|
35
105
|
def self.source_root
|
data/lib/rage/controller/api.rb
CHANGED
@@ -12,13 +12,21 @@ class RageController::API
|
|
12
12
|
|
13
13
|
before_actions_chunk = if @__before_actions
|
14
14
|
filtered_before_actions = @__before_actions.select do |h|
|
15
|
-
(h[:only]
|
16
|
-
(h[:except]
|
15
|
+
(!h[:only] || h[:only].include?(action)) &&
|
16
|
+
(!h[:except] || !h[:except].include?(action))
|
17
17
|
end
|
18
18
|
|
19
19
|
lines = filtered_before_actions.map do |h|
|
20
|
+
condition = if h[:if] && h[:unless]
|
21
|
+
"if #{h[:if]} && !#{h[:unless]}"
|
22
|
+
elsif h[:if]
|
23
|
+
"if #{h[:if]}"
|
24
|
+
elsif h[:unless]
|
25
|
+
"unless #{h[:unless]}"
|
26
|
+
end
|
27
|
+
|
20
28
|
<<-RUBY
|
21
|
-
#{h[:name]}
|
29
|
+
#{h[:name]} #{condition}
|
22
30
|
return [@__status, @__headers, @__body] if @__rendered
|
23
31
|
RUBY
|
24
32
|
end
|
@@ -65,6 +73,16 @@ class RageController::API
|
|
65
73
|
klass.__rescue_handlers = @__rescue_handlers.freeze
|
66
74
|
end
|
67
75
|
|
76
|
+
# @private
|
77
|
+
@@__tmp_name_seed = ("a".."i").to_a.permutation
|
78
|
+
|
79
|
+
# @private
|
80
|
+
# define temporary method based on a block
|
81
|
+
def define_tmp_method(block)
|
82
|
+
name = @@__tmp_name_seed.next.join
|
83
|
+
define_method("__rage_tmp_#{name}", block)
|
84
|
+
end
|
85
|
+
|
68
86
|
############
|
69
87
|
#
|
70
88
|
# PUBLIC API
|
@@ -89,8 +107,7 @@ class RageController::API
|
|
89
107
|
def rescue_from(*klasses, with: nil, &block)
|
90
108
|
unless with
|
91
109
|
if block_given?
|
92
|
-
|
93
|
-
with = define_method("__#{name}", &block)
|
110
|
+
with = define_tmp_method(block)
|
94
111
|
else
|
95
112
|
raise "No handler provided. Pass the `with` keyword argument or provide a block."
|
96
113
|
end
|
@@ -107,21 +124,53 @@ class RageController::API
|
|
107
124
|
|
108
125
|
# Register a new `before_action` hook. Calls with the same `action_name` will overwrite the previous ones.
|
109
126
|
#
|
110
|
-
# @param action_name [String] the name of the callback to add
|
111
|
-
# @param
|
112
|
-
# @
|
127
|
+
# @param action_name [String, nil] the name of the callback to add
|
128
|
+
# @param [Hash] opts action options
|
129
|
+
# @option opts [Symbol, Array<Symbol>] :only restrict the callback to run only for specific actions
|
130
|
+
# @option opts [Symbol, Array<Symbol>] :except restrict the callback to run for all actions except specified
|
131
|
+
# @option opts [Symbol, Proc] :if only run the callback if the condition is true
|
132
|
+
# @option opts [Symbol, Proc] :unless only run the callback if the condition is false
|
113
133
|
# @example
|
114
134
|
# before_action :find_photo, only: :show
|
115
135
|
#
|
116
136
|
# def find_photo
|
117
137
|
# Photo.first
|
118
138
|
# end
|
119
|
-
|
139
|
+
# @example
|
140
|
+
# before_action :require_user, unless: :logged_in?
|
141
|
+
# @example
|
142
|
+
# before_action :set_locale, if: -> { params[:locale] != "en-US" }
|
143
|
+
# @example
|
144
|
+
# before_action do
|
145
|
+
# unless logged_in? # would be `controller.send(:logged_in?)` in Rails
|
146
|
+
# head :unauthorized
|
147
|
+
# end
|
148
|
+
# end
|
149
|
+
# @note The block form doesn't receive an argument and is executed on the controller level as if it was a regular method.
|
150
|
+
def before_action(action_name = nil, **opts, &block)
|
151
|
+
if block_given?
|
152
|
+
action_name = define_tmp_method(block)
|
153
|
+
elsif action_name.nil?
|
154
|
+
raise "No handler provided. Pass the `action_name` parameter or provide a block."
|
155
|
+
end
|
156
|
+
|
157
|
+
_only, _except, _if, _unless = opts.values_at(:only, :except, :if, :unless)
|
158
|
+
|
120
159
|
if @__before_actions && @__before_actions.frozen?
|
121
160
|
@__before_actions = @__before_actions.dup
|
122
161
|
end
|
123
162
|
|
124
|
-
action = {
|
163
|
+
action = {
|
164
|
+
name: action_name,
|
165
|
+
only: _only && Array(_only),
|
166
|
+
except: _except && Array(_except),
|
167
|
+
if: _if,
|
168
|
+
unless: _unless
|
169
|
+
}
|
170
|
+
|
171
|
+
action[:if] = define_tmp_method(action[:if]) if action[:if].is_a?(Proc)
|
172
|
+
action[:unless] = define_tmp_method(action[:unless]) if action[:unless].is_a?(Proc)
|
173
|
+
|
125
174
|
if @__before_actions.nil?
|
126
175
|
@__before_actions = [action]
|
127
176
|
elsif i = @__before_actions.find_index { |a| a[:name] == action_name }
|
@@ -192,7 +241,7 @@ class RageController::API
|
|
192
241
|
@__body << if json
|
193
242
|
json.is_a?(String) ? json : json.to_json
|
194
243
|
else
|
195
|
-
|
244
|
+
headers["content-type"] = "text/plain; charset=utf-8"
|
196
245
|
plain.to_s
|
197
246
|
end
|
198
247
|
|
@@ -225,11 +274,13 @@ class RageController::API
|
|
225
274
|
end
|
226
275
|
end
|
227
276
|
|
228
|
-
|
229
|
-
|
230
|
-
#
|
231
|
-
|
232
|
-
|
233
|
-
|
277
|
+
# Set response headers.
|
278
|
+
#
|
279
|
+
# @example
|
280
|
+
# headers["Content-Type"] = "application/pdf"
|
281
|
+
def headers
|
282
|
+
# copy-on-write implementation for the headers object
|
283
|
+
@__headers = {}.merge!(@__headers) if DEFAULT_HEADERS.equal?(@__headers)
|
284
|
+
@__headers
|
234
285
|
end
|
235
286
|
end
|
data/lib/rage/router/backend.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "uri"
|
4
4
|
|
5
5
|
class Rage::Router::Backend
|
6
|
+
attr_reader :routes
|
7
|
+
|
6
8
|
OPTIONAL_PARAM_REGEXP = /\/?\(\/?(:\w+)\/?\)/
|
7
9
|
STRING_HANDLER_REGEXP = /^([a-z0-9_\/]+)#([a-z_]+)$/
|
8
10
|
|
@@ -13,6 +15,7 @@ class Rage::Router::Backend
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def on(method, path, handler, constraints: {}, defaults: nil)
|
18
|
+
raw_handler = handler
|
16
19
|
raise "Path could not be empty" if path&.empty?
|
17
20
|
|
18
21
|
if match_index = (path =~ OPTIONAL_PARAM_REGEXP)
|
@@ -42,7 +45,7 @@ class Rage::Router::Backend
|
|
42
45
|
handler = ->(env, _params) { orig_handler.call(env) }
|
43
46
|
end
|
44
47
|
|
45
|
-
__on(method, path, handler, constraints, defaults)
|
48
|
+
__on(method, path, handler, raw_handler, constraints, defaults)
|
46
49
|
end
|
47
50
|
|
48
51
|
def lookup(env)
|
@@ -52,7 +55,7 @@ class Rage::Router::Backend
|
|
52
55
|
|
53
56
|
private
|
54
57
|
|
55
|
-
def __on(method, path, handler, constraints, defaults)
|
58
|
+
def __on(method, path, handler, raw_handler, constraints, defaults)
|
56
59
|
@constrainer.validate_constraints(constraints)
|
57
60
|
# Let the constrainer know if any constraints are being used now
|
58
61
|
@constrainer.note_usage(constraints)
|
@@ -159,7 +162,7 @@ class Rage::Router::Backend
|
|
159
162
|
end
|
160
163
|
end
|
161
164
|
|
162
|
-
route = { method: method, path: path, pattern: pattern, params: params, constraints: constraints, handler: handler, defaults: defaults }
|
165
|
+
route = { method: method, path: path, pattern: pattern, params: params, constraints: constraints, handler: handler, raw_handler: raw_handler, defaults: defaults }
|
163
166
|
@routes << route
|
164
167
|
current_node.add_route(route, @constrainer)
|
165
168
|
end
|
data/lib/rage/setup.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
1
|
Iodine.patch_rack
|
2
2
|
|
3
|
-
|
3
|
+
require_relative "#{Rage.root}/config/environments/#{Rage.env}"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
|
6
|
+
# load application files
|
7
|
+
app, bad = Dir["#{Rage.root}/app/**/*.rb"], []
|
8
|
+
|
9
|
+
loop do
|
10
|
+
path = app.shift
|
11
|
+
break if path.nil?
|
12
|
+
|
13
|
+
require_relative path
|
14
|
+
|
15
|
+
# push the file to the end of the list in case it depends on another file that has not yet been required;
|
16
|
+
# re-raise if only errored out files are left
|
17
|
+
rescue NameError
|
18
|
+
raise if (app - bad).empty?
|
19
|
+
app << path
|
20
|
+
bad << path
|
21
|
+
end
|
22
|
+
|
23
|
+
require_relative "#{Rage.root}/config/routes"
|
data/lib/rage/version.rb
CHANGED
data/lib/rage-rb.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "rack"
|
4
4
|
require "json"
|
5
5
|
require "iodine"
|
6
|
+
require "pathname"
|
6
7
|
|
7
8
|
module Rage
|
8
9
|
def self.application
|
@@ -33,6 +34,10 @@ module Rage
|
|
33
34
|
[:default, Rage.env.to_sym]
|
34
35
|
end
|
35
36
|
|
37
|
+
def self.root
|
38
|
+
@root ||= Pathname.new(".").expand_path
|
39
|
+
end
|
40
|
+
|
36
41
|
module Router
|
37
42
|
module Strategies
|
38
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rage-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.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-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|