jets 1.9.32 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/jets/application/defaults.rb +6 -0
- data/lib/jets/cli.rb +9 -0
- data/lib/jets/commands/help/generate.md +25 -19
- data/lib/jets/commands/main.rb +3 -3
- data/lib/jets/commands/rake_tasks.rb +3 -0
- data/lib/jets/commands/templates/skeleton/app/views/layouts/application.html.erb.tt +1 -0
- data/lib/jets/commands/templates/skeleton/config/application.rb.tt +4 -0
- data/lib/jets/commands/templates/webpacker/app/javascript/src/jets/crud.js +3 -0
- data/lib/jets/commands/upgrade.rb +42 -109
- data/lib/jets/commands/upgrade/version1.rb +136 -0
- data/lib/jets/controller/base.rb +16 -0
- data/lib/jets/controller/error.rb +4 -0
- data/lib/jets/controller/error/invalid_authenticity_token.rb +6 -0
- data/lib/jets/controller/forgery_protection.rb +43 -0
- data/lib/jets/controller/middleware/local.rb +3 -3
- data/lib/jets/controller/middleware/local/route_matcher.rb +1 -1
- data/lib/jets/controller/middleware/main.rb +7 -1
- data/lib/jets/controller/rack/adapter.rb +1 -1
- data/lib/jets/controller/rack/env.rb +1 -1
- data/lib/jets/controller/rendering/rack_renderer.rb +44 -37
- data/lib/jets/controller/stage.rb +2 -1
- data/lib/jets/generator.rb +72 -8
- data/lib/jets/generator/templates/active_job/job/templates/application_job.rb.tt +6 -0
- data/lib/jets/generator/templates/active_job/job/templates/job.rb.tt +8 -0
- data/lib/jets/generator/templates/erb/scaffold/_form.html.erb +11 -16
- data/lib/jets/generator/templates/erb/scaffold/edit.html.erb +2 -2
- data/lib/jets/generator/templates/erb/scaffold/index.html.erb +5 -5
- data/lib/jets/generator/templates/erb/scaffold/new.html.erb +1 -1
- data/lib/jets/generator/templates/erb/scaffold/show.html.erb +3 -3
- data/lib/jets/generator/templates/rails/scaffold_controller/controller.rb +5 -5
- data/lib/jets/internal/app/controllers/jets/rack_controller.rb +1 -0
- data/lib/jets/overrides/rails.rb +2 -1
- data/lib/jets/overrides/rails/action_controller.rb +12 -0
- data/lib/jets/overrides/rails/url_helper.rb +66 -5
- data/lib/jets/resource/api_gateway/rest_api/routes/change/base.rb +1 -1
- data/lib/jets/resource/api_gateway/rest_api/routes/collision.rb +1 -1
- data/lib/jets/router.rb +32 -46
- data/lib/jets/router/dsl.rb +136 -0
- data/lib/jets/router/error.rb +4 -0
- data/lib/jets/router/helpers.rb +4 -0
- data/lib/jets/router/helpers/core_helper.rb +17 -0
- data/lib/jets/router/helpers/named_routes_helper.rb +8 -0
- data/lib/jets/router/method_creator.rb +54 -0
- data/lib/jets/router/method_creator/code.rb +98 -0
- data/lib/jets/router/method_creator/edit.rb +7 -0
- data/lib/jets/router/method_creator/generic.rb +11 -0
- data/lib/jets/router/method_creator/index.rb +42 -0
- data/lib/jets/router/method_creator/new.rb +7 -0
- data/lib/jets/router/method_creator/root.rb +15 -0
- data/lib/jets/router/method_creator/show.rb +7 -0
- data/lib/jets/router/resources/base.rb +7 -0
- data/lib/jets/router/resources/filter.rb +15 -0
- data/lib/jets/router/resources/options.rb +13 -0
- data/lib/jets/router/route.rb +226 -0
- data/lib/jets/router/scope.rb +65 -4
- data/lib/jets/router/util.rb +38 -0
- data/lib/jets/turbo/project/config/application.rb +1 -0
- data/lib/jets/version.rb +1 -1
- metadata +26 -2
- data/lib/jets/route.rb +0 -166
data/lib/jets/controller/base.rb
CHANGED
@@ -10,6 +10,8 @@ class Jets::Controller
|
|
10
10
|
include Params
|
11
11
|
include Rendering
|
12
12
|
include ActiveSupport::Rescuable
|
13
|
+
include Jets::Router::Helpers
|
14
|
+
include ForgeryProtection
|
13
15
|
|
14
16
|
delegate :headers, to: :request
|
15
17
|
delegate :set_header, to: :response
|
@@ -78,6 +80,20 @@ class Jets::Controller
|
|
78
80
|
JSON.dump(data)
|
79
81
|
end
|
80
82
|
|
83
|
+
def controller_paths
|
84
|
+
paths = []
|
85
|
+
klass = self.class
|
86
|
+
while klass != Jets::Controller::Base
|
87
|
+
paths << klass.controller_path
|
88
|
+
klass = klass.superclass
|
89
|
+
end
|
90
|
+
paths
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.controller_path
|
94
|
+
name.sub(/Controller$/, "".freeze).underscore
|
95
|
+
end
|
96
|
+
|
81
97
|
def self.process(event, context={}, meth)
|
82
98
|
controller = new(event, context, meth)
|
83
99
|
# Using send because process! is private method in Jets::RackController so
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Jets::Controller
|
2
|
+
module ForgeryProtection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
config = Jets.config
|
7
|
+
default_protect_from_forgery = config.dig(:controllers, :default_protect_from_forgery)
|
8
|
+
if default_protect_from_forgery
|
9
|
+
protect_from_forgery
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class_methods do
|
14
|
+
def protect_from_forgery(options = {})
|
15
|
+
before_action :verify_authenticity_token, options
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip_forgery_protection
|
19
|
+
skip_before_action :verify_authenticity_token
|
20
|
+
end
|
21
|
+
|
22
|
+
def forgery_protection_enabled?
|
23
|
+
# Example:
|
24
|
+
#
|
25
|
+
# before_actions [[:verify_authenticity_token, {}], [:set_post, {:only=>[:show, :edit, :update, :delete]}
|
26
|
+
#
|
27
|
+
before_actions.map { |a| a[0] }.include?(:verify_authenticity_token)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Instance methods
|
32
|
+
def verify_authenticity_token
|
33
|
+
return true if ENV['TEST'] || request.get? || request.head?
|
34
|
+
|
35
|
+
token = session[:authenticity_token]
|
36
|
+
verified = !token.nil? && (token == params[:authenticity_token] || token == request.headers["x-csrf-token"])
|
37
|
+
|
38
|
+
unless verified
|
39
|
+
raise Error::InvalidAuthenticityToken
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'kramdown'
|
2
2
|
|
3
|
-
# Handles
|
3
|
+
# Handles mimicing of API Gateway to Lambda function call locally
|
4
4
|
module Jets::Controller::Middleware
|
5
5
|
class Local
|
6
6
|
extend Memoist
|
@@ -35,7 +35,7 @@ module Jets::Controller::Middleware
|
|
35
35
|
# This can only really get called with the local server.
|
36
36
|
run_polymophic_function
|
37
37
|
else # Normal Jets request
|
38
|
-
|
38
|
+
mimic_aws_lambda!(env, mimic.vars) unless on_aws?(env)
|
39
39
|
@app.call(env)
|
40
40
|
end
|
41
41
|
end
|
@@ -60,7 +60,7 @@ module Jets::Controller::Middleware
|
|
60
60
|
end
|
61
61
|
|
62
62
|
# Modifies env the in the same way real call from AWS lambda would modify env
|
63
|
-
def
|
63
|
+
def mimic_aws_lambda!(env, vars)
|
64
64
|
env.merge!(vars)
|
65
65
|
env
|
66
66
|
end
|
@@ -89,7 +89,7 @@ class Jets::Controller::Middleware::Local
|
|
89
89
|
# posts/:id/edit => posts\/(.*)\/edit
|
90
90
|
|
91
91
|
regexp_string = route_path.split('/').map do |s|
|
92
|
-
s.include?(':') ? Jets::Route::CAPTURE_REGEX : s
|
92
|
+
s.include?(':') ? Jets::Router::Route::CAPTURE_REGEX : s
|
93
93
|
end.join('\/')
|
94
94
|
# make sure beginning and end of the string matches
|
95
95
|
regexp_string = "^#{regexp_string}$"
|
@@ -18,6 +18,7 @@ module Jets::Controller::Middleware
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def call
|
21
|
+
ENV['JETS_HOST'] = jets_host # Dirty to use what's essentially a global variable but unsure how else to achieve
|
21
22
|
dup.call!
|
22
23
|
end
|
23
24
|
|
@@ -29,7 +30,7 @@ module Jets::Controller::Middleware
|
|
29
30
|
# Common setup logical at this point of middleware processing right before
|
30
31
|
# calling any controller actions.
|
31
32
|
def setup
|
32
|
-
# We already recreated a
|
33
|
+
# We already recreated a mimic rack env earlier as part of the very first
|
33
34
|
# middleware layer. However, by the time the rack env reaches the main middleware
|
34
35
|
# it could had been updated by other middlewares. We update the env here again.
|
35
36
|
@controller.request.set_env!(@env)
|
@@ -38,6 +39,11 @@ module Jets::Controller::Middleware
|
|
38
39
|
@controller.session = @env['rack.session'] || {}
|
39
40
|
end
|
40
41
|
|
42
|
+
def jets_host
|
43
|
+
default = "#{@env['rack.url_scheme']}://#{@env['HTTP_HOST']}"
|
44
|
+
Jets.config.helpers.host || default
|
45
|
+
end
|
46
|
+
|
41
47
|
def self.call(env)
|
42
48
|
instance = new(env)
|
43
49
|
instance.call
|
@@ -77,7 +77,7 @@ module Jets::Controller::Rack
|
|
77
77
|
#
|
78
78
|
# Passes a these special variables so we have access to them in the middleware.
|
79
79
|
# The controller instance is called in the Main middleware.
|
80
|
-
# The lambda.* info is used by the Rack::Local middleware to create a
|
80
|
+
# The lambda.* info is used by the Rack::Local middleware to create a mimiced
|
81
81
|
# controller for the local server.
|
82
82
|
#
|
83
83
|
def rack_vars(vars)
|
@@ -29,7 +29,7 @@ module Jets::Controller::Rack
|
|
29
29
|
'QUERY_STRING' => query_string,
|
30
30
|
'REMOTE_ADDR' => headers['X-Forwarded-For'],
|
31
31
|
'REMOTE_HOST' => headers['Host'],
|
32
|
-
'REQUEST_METHOD' => @event['httpMethod'],
|
32
|
+
'REQUEST_METHOD' => @event['httpMethod'] || 'GET', # useful to default to GET when testing with Lambda console
|
33
33
|
'REQUEST_PATH' => @event['path'],
|
34
34
|
'REQUEST_URI' => request_uri,
|
35
35
|
'SCRIPT_NAME' => "",
|
@@ -26,11 +26,12 @@ module Jets::Controller::Rendering
|
|
26
26
|
# x-jets-base64 to convert this Rack triplet to a API Gateway hash structure later
|
27
27
|
headers["x-jets-base64"] = base64 ? 'yes' : 'no' # headers values must be Strings
|
28
28
|
|
29
|
-
# Rails rendering does heavy lifting
|
30
29
|
if drop_content_info?(status)
|
31
30
|
body = StringIO.new
|
32
31
|
else
|
33
|
-
|
32
|
+
# Rails rendering does heavy lifting
|
33
|
+
# _prefixes provided by jets/overrides/rails/action_controller.rb
|
34
|
+
ActionController::Base._prefixes = @controller.controller_paths
|
34
35
|
renderer = ActionController::Base.renderer.new(renderer_options)
|
35
36
|
body = renderer.render(render_options)
|
36
37
|
body = StringIO.new(body)
|
@@ -39,16 +40,6 @@ module Jets::Controller::Rendering
|
|
39
40
|
[status, headers, body] # triplet
|
40
41
|
end
|
41
42
|
|
42
|
-
# Example: posts/index
|
43
|
-
def default_template_name
|
44
|
-
"#{template_namespace}/#{@controller.meth}"
|
45
|
-
end
|
46
|
-
|
47
|
-
# PostsController => "posts" is the namespace
|
48
|
-
def template_namespace
|
49
|
-
@controller.class.to_s.sub('Controller','').underscore.pluralize
|
50
|
-
end
|
51
|
-
|
52
43
|
# default options:
|
53
44
|
# https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/renderer.rb#L41-L47
|
54
45
|
def renderer_options
|
@@ -74,6 +65,41 @@ module Jets::Controller::Rendering
|
|
74
65
|
options
|
75
66
|
end
|
76
67
|
|
68
|
+
def render_options
|
69
|
+
# normalize the template option
|
70
|
+
template = @options[:template]
|
71
|
+
if template and !template.include?('/')
|
72
|
+
template = "#{template_namespace}/#{template}"
|
73
|
+
end
|
74
|
+
template ||= default_template_name
|
75
|
+
# ready to override @options[:template]
|
76
|
+
@options[:template] = template if @options[:template]
|
77
|
+
|
78
|
+
render_options = {
|
79
|
+
template: template, # weird: template needs to be set no matter because it
|
80
|
+
# sets the name which is used in lookup_context.rb:209:in `normalize_name'
|
81
|
+
layout: @options[:layout],
|
82
|
+
assigns: controller_instance_variables,
|
83
|
+
# prefixes: ["posts"],
|
84
|
+
}
|
85
|
+
types = %w[json inline plain file xml body action].map(&:to_sym)
|
86
|
+
types.each do |type|
|
87
|
+
render_options[type] = @options[type] if @options[type]
|
88
|
+
end
|
89
|
+
|
90
|
+
render_options
|
91
|
+
end
|
92
|
+
|
93
|
+
# Example: posts/index
|
94
|
+
def default_template_name
|
95
|
+
"#{template_namespace}/#{@controller.meth}"
|
96
|
+
end
|
97
|
+
|
98
|
+
# PostsController => "posts" is the namespace
|
99
|
+
def template_namespace
|
100
|
+
@controller.class.to_s.sub('Controller','').underscore.pluralize
|
101
|
+
end
|
102
|
+
|
77
103
|
# Takes headers and adds HTTP_ to front of the keys because that is what rack
|
78
104
|
# does to the headers passed from a request. This seems to be the standard
|
79
105
|
# when testing with curl and inspecting the headers in a Rack app. Example:
|
@@ -110,30 +136,7 @@ module Jets::Controller::Rendering
|
|
110
136
|
results
|
111
137
|
end
|
112
138
|
|
113
|
-
|
114
|
-
# nomralize the template option
|
115
|
-
template = @options[:template]
|
116
|
-
if template and !template.include?('/')
|
117
|
-
template = "#{template_namespace}/#{template}"
|
118
|
-
end
|
119
|
-
template ||= default_template_name
|
120
|
-
# ready to override @options[:template]
|
121
|
-
@options[:template] = template if @options[:template]
|
122
|
-
|
123
|
-
render_options = {
|
124
|
-
template: template, # weird: template needs to be set no matter because it
|
125
|
-
# sets the name which is used in lookup_context.rb:209:in `normalize_name'
|
126
|
-
layout: @options[:layout],
|
127
|
-
assigns: controller_instance_variables,
|
128
|
-
}
|
129
|
-
types = %w[json inline plain file xml body action].map(&:to_sym)
|
130
|
-
types.each do |type|
|
131
|
-
render_options[type] = @options[type] if @options[type]
|
132
|
-
end
|
133
|
-
|
134
|
-
render_options
|
135
|
-
end
|
136
|
-
|
139
|
+
# Pass controller instance variables from jets-based controller to ActionView scope
|
137
140
|
def controller_instance_variables
|
138
141
|
instance_vars = @controller.instance_variables.inject({}) do |vars, v|
|
139
142
|
k = v.to_s.sub(/^@/,'') # @var => var
|
@@ -141,6 +144,9 @@ module Jets::Controller::Rendering
|
|
141
144
|
vars
|
142
145
|
end
|
143
146
|
instance_vars[:event] = event
|
147
|
+
# jets internal variables
|
148
|
+
# So ActionView has access back to the jets controller
|
149
|
+
instance_vars[:_jets] = { controller: @controller }
|
144
150
|
instance_vars
|
145
151
|
end
|
146
152
|
|
@@ -199,7 +205,8 @@ module Jets::Controller::Rendering
|
|
199
205
|
# Assign local variable because scope in the `:action_view do` block changes
|
200
206
|
app_helper_classes = find_app_helper_classes
|
201
207
|
ActiveSupport.on_load :action_view do
|
202
|
-
include
|
208
|
+
include Jets::Router::Helpers # internal routes helpers
|
209
|
+
include ApplicationHelper # include first
|
203
210
|
app_helper_classes.each do |helper_class|
|
204
211
|
include helper_class
|
205
212
|
end
|
@@ -8,7 +8,8 @@ class Jets::Controller
|
|
8
8
|
return @url unless add_stage?
|
9
9
|
|
10
10
|
stage_name = Jets::Resource::ApiGateway::Deployment.stage_name
|
11
|
-
"/#{stage_name}#
|
11
|
+
stage_name_with_slashes = "/#{stage_name}/" # use to prevent stage name being added twice if url_for is called twice on the same string
|
12
|
+
@url.include?(stage_name_with_slashes) ? @url : "/#{stage_name}#{@url}"
|
12
13
|
end
|
13
14
|
|
14
15
|
def add_stage?
|
data/lib/jets/generator.rb
CHANGED
@@ -1,21 +1,85 @@
|
|
1
1
|
# Piggy back off of Rails Generators.
|
2
2
|
class Jets::Generator
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
class << self
|
4
|
+
def invoke(generator, *args)
|
5
|
+
new(generator, *args).run(:invoke)
|
6
|
+
end
|
7
|
+
|
8
|
+
def revoke(generator, *args)
|
9
|
+
new(generator, *args).run(:revoke)
|
10
|
+
end
|
11
|
+
|
12
|
+
def help(args=ARGV)
|
13
|
+
require_generators
|
14
|
+
|
15
|
+
# `jets generate -h` results in:
|
16
|
+
#
|
17
|
+
# args = ["generate", "-h"]
|
18
|
+
#
|
19
|
+
args = args[1..-1] || []
|
20
|
+
help_flags = Thor::HELP_MAPPINGS + ["help"]
|
21
|
+
args.pop if help_flags.include?(args.last)
|
22
|
+
subcommand = args[0]
|
23
|
+
|
24
|
+
out = capture_stdout do
|
25
|
+
if subcommand
|
26
|
+
# Using invoke because it ensure the generator is configured properly
|
27
|
+
invoke(subcommand) # sub-level: jets generate scaffold -h
|
28
|
+
else
|
29
|
+
puts Jets::Commands::Help.text(:generate) # to trigger the regular Thor help
|
30
|
+
# Note: How to call the original top-level help menu from Rails. Keeping around in case its useful later:
|
31
|
+
# Rails::Generators.help # top-level: jets generate -h
|
32
|
+
end
|
33
|
+
end
|
34
|
+
out.gsub('rails','jets').gsub('Rails','Jets')
|
35
|
+
end
|
6
36
|
|
7
|
-
|
8
|
-
|
37
|
+
def capture_stdout
|
38
|
+
stdout_old = $stdout
|
39
|
+
io = StringIO.new
|
40
|
+
$stdout = io
|
41
|
+
yield
|
42
|
+
$stdout = stdout_old
|
43
|
+
io.string
|
44
|
+
end
|
45
|
+
|
46
|
+
def require_generators
|
47
|
+
# lazy require so Rails const is only defined when using generators
|
48
|
+
require "rails/generators"
|
49
|
+
require "rails/configuration"
|
50
|
+
require_active_job_generator
|
51
|
+
end
|
52
|
+
|
53
|
+
def require_active_job_generator
|
54
|
+
require "active_job"
|
55
|
+
require "rails/generators/job/job_generator"
|
56
|
+
# Override the source_root
|
57
|
+
Rails::Generators::JobGenerator.class_eval do
|
58
|
+
def self.source_root
|
59
|
+
File.expand_path("../generator/templates/active_job/job/templates", __FILE__)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
9
63
|
end
|
10
64
|
|
11
65
|
def initialize(generator, *args)
|
12
66
|
@generator, @args = generator, args
|
67
|
+
@args << '--pretend' if noop?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Used to delegate noop option to Rails generator pretend option. Both work:
|
71
|
+
#
|
72
|
+
# jets generate scaffold user title:string --noop
|
73
|
+
# jets generate scaffold user title:string --pretend
|
74
|
+
#
|
75
|
+
# Grabbing directly from the ARGV because think its cleaner than passing options from
|
76
|
+
# Thor all the way down.
|
77
|
+
def noop?
|
78
|
+
ARGV.include?('--noop')
|
13
79
|
end
|
14
80
|
|
15
81
|
def run(behavior=:invoke)
|
16
|
-
|
17
|
-
require "rails/generators"
|
18
|
-
require "rails/configuration"
|
82
|
+
self.class.require_generators
|
19
83
|
Rails::Generators.configure!(config)
|
20
84
|
Rails::Generators.invoke(@generator, @args, behavior: behavior, destination_root: Jets.root)
|
21
85
|
end
|
@@ -1,17 +1,12 @@
|
|
1
|
-
|
2
|
-
<%% action = editing ? "/<%= plural_table_name %>/#{<%= singular_table_name %>.id}" : "/<%= plural_table_name %>" %>
|
3
|
-
<%%= form_tag(action) do %>
|
4
|
-
<%% if editing -%>
|
5
|
-
<input type="hidden" name="_method" value="put" />
|
6
|
-
<%% end -%>
|
1
|
+
<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %>
|
7
2
|
<%% if <%= singular_table_name %>.errors.any? %>
|
8
3
|
<div id="error_explanation">
|
9
4
|
<h2><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
|
10
5
|
|
11
6
|
<ul>
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
<%% <%= singular_table_name %>.errors.full_messages.each do |message| %>
|
8
|
+
<li><%%= message %></li>
|
9
|
+
<%% end %>
|
15
10
|
</ul>
|
16
11
|
</div>
|
17
12
|
<%% end %>
|
@@ -19,21 +14,21 @@
|
|
19
14
|
<% attributes.each do |attribute| -%>
|
20
15
|
<div class="field">
|
21
16
|
<% if attribute.password_digest? -%>
|
22
|
-
<%%=
|
23
|
-
<%%=
|
17
|
+
<%%= form.label :password %>
|
18
|
+
<%%= form.password_field :password %>
|
24
19
|
</div>
|
25
20
|
|
26
21
|
<div class="field">
|
27
|
-
<%%=
|
28
|
-
<%%=
|
22
|
+
<%%= form.label :password_confirmation %>
|
23
|
+
<%%= form.password_field :password_confirmation %>
|
29
24
|
<% else -%>
|
30
|
-
<%%=
|
31
|
-
<%%=
|
25
|
+
<%%= form.label :<%= attribute.column_name %> %>
|
26
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %> %>
|
32
27
|
<% end -%>
|
33
28
|
</div>
|
34
29
|
|
35
30
|
<% end -%>
|
36
31
|
<div class="actions">
|
37
|
-
<%%=
|
32
|
+
<%%= form.submit %>
|
38
33
|
</div>
|
39
34
|
<%% end %>
|