lib/sidekiq/web/action.rb
# frozen_string_literal: true require "erb" module Sidekiq class Web ## # These instance methods are available to all executing ERB # templates. class Action attr_accessor :env, :block def initialize(env, block) @_erb = false @env = env @block = block end def config env[:web_config] end def request @request ||= ::Rack::Request.new(env) end def halt(res) throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]] end # external redirect def redirect_to(url) throw :halt, [302, {"location" => url}, []] end def header(key, value) env["response_headers"][key] = value.to_s end # internal redirect def redirect(location) throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []] end def reload_page current_location = request.referer.gsub(request.base_url, "") redirect current_location end # stuff after ? or form input # uses String keys, no Symbols! def url_params(key) warn { "URL parameter `#{key}` should be accessed via String, not Symbol (at #{caller(3..3).first})" } if key.is_a?(Symbol) request.params[key.to_s] end # variables embedded in path, `/metrics/:name` # uses Symbol keys, no Strings! def route_params(key) warn { "Route parameter `#{key}` should be accessed via Symbol, not String (at #{caller(3..3).first})" } if key.is_a?(String) env["rack.route_params"][key.to_sym] end def params warn { "Direct access to Rack parameters is discouraged, use `url_params` or `route_params` (at #{caller(3..3).first})" } request.params end def session env["rack.session"] end def logger Sidekiq.logger end # flash { "Some message to show on redirect" } def flash msg = yield logger.info msg session[:flash] = msg end def flash? session&.[](:flash) end def get_flash @flash ||= session.delete(:flash) end def erb(content, options = {}) if content.is_a? Symbol unless respond_to?(:"_erb_#{content}") views = options[:views] || Web.views filename = "#{views}/#{content}.erb" src = ERB.new(File.read(filename)).src # Need to use lineno less by 1 because erb generates a # comment before the source code. Action.class_eval <<-RUBY, filename, -1 # standard:disable Style/EvalWithLocation def _erb_#{content} #{src} end RUBY end end if @_erb _erb(content, options[:locals]) else @_erb = true content = _erb(content, options[:locals]) _render { content } end end def render(engine, content, options = {}) raise "Only erb templates are supported" if engine != :erb erb(content, options) end def json(payload) [200, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]] end private def warn Sidekiq.logger.warn yield end def _erb(file, locals) locals&.each { |k, v| define_singleton_method(k) { v } unless singleton_methods.include? k } if file.is_a?(String) ERB.new(file).result(binding) else send(:"_erb_#{file}") end end class_eval <<-RUBY, ::Sidekiq::Web::LAYOUT, -1 # standard:disable Style/EvalWithLocation def _render #{ERB.new(File.read(::Sidekiq::Web::LAYOUT)).src} end RUBY end end end