class Jets::Controller::Rendering::RackRenderer
def controller_instance_variables
def controller_instance_variables instance_vars = @controller.instance_variables.inject({}) do |vars, v| k = v.to_s.sub(/^@/,'') # @var => var vars[k] = @controller.instance_variable_get(v) vars end instance_vars[:event] = event instance_vars end
def default_template_name
def default_template_name "#{template_namespace}/#{@controller.meth}" end
def drop_body?(status)
def drop_body?(status) DROP_BODY_RESPONSES.include?(status.to_i) end
def drop_content_info?(status)
def drop_content_info?(status) status.to_i / 100 == 1 or drop_body?(status) end
def find_app_helper_classes
def find_app_helper_classes internal_path = File.expand_path("../../internal", File.dirname(__FILE__)) internal_classes = find_app_helper_classes_from(internal_path) app_classes = find_app_helper_classes_from(Jets.root) (internal_classes + app_classes).uniq end
def find_app_helper_classes_from(project_root)
def find_app_helper_classes_from(project_root) klasses = [] expression = "#{project_root}/app/helpers/**/*" Dir.glob(expression).each do |path| next unless File.file?(path) class_name = path.sub("#{project_root}/app/helpers/","").sub(/\.rb/,'') unless class_name == "application_helper" klasses << class_name.camelize.constantize # autoload end end klasses end
def initialize(controller, options={})
def initialize(controller, options={}) @controller = controller @options = options end
def normalize_status_code(code)
:success => 200
:continue => 100
maps:
def normalize_status_code(code) status_code = if code.is_a?(Symbol) Rack::Utils::SYMBOL_TO_STATUS_CODE[code] else code end # API Gateway requires status to be String but local rack is okay with either # Note, ELB though requires status to be an Integer. We'll later in rack/adapter.rb # adjust status to an Integer if request is coming from an ELB. (status_code || 200).to_s end
def normalized_base64_option(options)
def normalized_base64_option(options) base64 = @options[:base64] if options.key?(:base64) base64 = @options[:isBase64Encoded] if options.key?(:isBase64Encoded) base64 end
def rackify_headers(headers)
"HTTP_X_AMZN_TRACE_ID"=>"Root=1-5bde5b19-61d0d4ab4659144f8f69e38f"}
"HTTP_VERSION"=>"HTTP/1.1",
"HTTP_ACCEPT"=>"*/*",
"HTTP_USER_AGENT"=>"curl/7.53.1",
{"HTTP_HOST"=>"localhost:8888",
Example output:
"x-amzn-trace-id"=>"Root=1-5bde5b19-61d0d4ab4659144f8f69e38f"}
"version"=>"HTTP/1.1",
"accept"=>"*/*",
"user-agent"=>"curl/7.53.1",
{"host"=>"localhost:8888",
Example input (from api gateway)
renderer_options are rack normalized headers.
ActionController::Base.renderer.new(renderer_options)
This is useful for:
https://gist.github.com/tongueroo/94f22f6c261c8999e4f4f776547e2ee3
when testing with curl and inspecting the headers in a Rack app. Example:
does to the headers passed from a request. This seems to be the standard
Takes headers and adds HTTP_ to front of the keys because that is what rack
def rackify_headers(headers) results = {} headers.each do |k,v| rack_key = 'HTTP_' + k.gsub('-','_').upcase results[rack_key] = v end results end
def render
[200, {"my-header" = > "value" }, "my body" ]
Example response:
def render # we do some normalization here status = normalize_status_code(@options[:status]) base64 = normalized_base64_option(@options) headers = @options[:headers] || {} set_content_type!(status, headers) # x-jets-base64 to convert this Rack triplet to a API Gateway hash structure later headers["x-jets-base64"] = base64 ? 'yes' : 'no' # headers values must be Strings # Rails rendering does heavy lifting if drop_content_info?(status) body = StringIO.new else renderer = ActionController::Base.renderer.new(renderer_options) body = renderer.render(render_options) body = StringIO.new(body) end [status, headers, body] # triplet end
def render_options
def render_options # nomralize the template option template = @options[:template] if template and !template.include?('/') template = "#{template_namespace}/#{template}" end template ||= default_template_name # ready to override @options[:template] @options[:template] = template if @options[:template] render_options = { template: template, # weird: template needs to be set no matter because it # sets the name which is used in lookup_context.rb:209:in `normalize_name' layout: @options[:layout], assigns: controller_instance_variables, } types = %w[json inline plain file xml body action].map(&:to_sym) types.each do |type| render_options[type] = @options[type] if @options[type] end render_options end
def renderer_options
default options:
def renderer_options options = { # script_name: "", # unfortunately doesnt seem to effect relative_url_root like desired # input: "" } origin = headers["origin"] if origin uri = URI.parse(origin) options[:https] = uri.scheme == "https" end # Important to not use rack_headers as local variable instead of headers. # headers is a method that gets deleted to controller.headers and using it # seems to cause issues. rack_headers = rackify_headers(headers) options.merge!(rack_headers) # Note @options[:method] uses @options vs options on purpose @options[:method] = event["httpMethod"].downcase if event["httpMethod"] options end
def set_content_type!(status, headers)
def set_content_type!(status, headers) if drop_content_info?(status) headers.delete "Content-Length" headers.delete "Content-Type" else headers["Content-Type"] = @options[:content_type] || headers['content-type'] || # Mega Mode (Rails) headers['Content-Type'] || # Just in case Jets::Controller::DEFAULT_CONTENT_TYPE end end
def setup!
def setup! require "action_controller" require "jets/overrides/rails" # Load helpers # Assign local variable because scope in the `:action_view do` block changes app_helper_classes = find_app_helper_classes ActiveSupport.on_load :action_view do include ApplicationHelper # include first app_helper_classes.each do |helper_class| include helper_class end end ActionController::Base.append_view_path("#{Jets.root}/app/views") ActionView::Resolver.caching = !Jets.env.development? setup_webpacker if Jets.webpacker? end
def setup_webpacker
def setup_webpacker require 'webpacker' require 'webpacker/helper' ActiveSupport.on_load :action_controller do ActionController::Base.helper Webpacker::Helper end ActiveSupport.on_load :action_view do include Webpacker::Helper end end
def template_namespace
def template_namespace @controller.class.to_s.sub('Controller','').underscore.pluralize end