diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..82b6728a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +/log/**/* +!/log/.gitkeep diff --git a/app/controllers/tests_controller.rb b/app/controllers/tests_controller.rb index 1526a689..a0568ec8 100644 --- a/app/controllers/tests_controller.rb +++ b/app/controllers/tests_controller.rb @@ -1,11 +1,11 @@ class TestsController < Simpler::Controller - def index @time = Time.now - end - - def create + headers['WEIRD_HEADER'] = '1234' + status(200) + render(plain: "Params sent: #{@request.params}") end + def create; end end diff --git a/app/models/test.rb b/app/models/test.rb index 86376668..98cea0cb 100644 --- a/app/models/test.rb +++ b/app/models/test.rb @@ -4,5 +4,4 @@ # Integer :level, default: 0 # end class Test < Sequel::Model - end diff --git a/config.ru b/config.ru index 3060cc20..2e044fa3 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,7 @@ require_relative 'config/environment' +require_relative 'middleware/logger' + +use Logger +use Rack::Reloader, 0 run Simpler.application diff --git a/config/routes.rb b/config/routes.rb index 4a751251..6898f509 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,4 @@ Simpler.application.routes do - get '/tests', 'tests#index' + get '/tests/:id', 'tests#index' post '/tests', 'tests#create' end diff --git a/lib/simpler.rb b/lib/simpler.rb index f9dfe3c4..d4d365c9 100644 --- a/lib/simpler.rb +++ b/lib/simpler.rb @@ -2,7 +2,6 @@ require_relative 'simpler/application' module Simpler - class << self def application Application.instance @@ -12,5 +11,4 @@ def root Pathname.new(File.expand_path('..', __dir__)) end end - end diff --git a/lib/simpler/application.rb b/lib/simpler/application.rb index 711946a9..d63147d6 100644 --- a/lib/simpler/application.rb +++ b/lib/simpler/application.rb @@ -6,7 +6,6 @@ module Simpler class Application - include Singleton attr_reader :db @@ -28,6 +27,10 @@ def routes(&block) def call(env) route = @router.route_for(env) + + return @router.not_found(env) unless route + + route.match_params(env) controller = route.controller.new(env) action = route.action @@ -53,6 +56,5 @@ def setup_database def make_response(controller, action) controller.make_response(action) end - end end diff --git a/lib/simpler/controller.rb b/lib/simpler/controller.rb index 9383b035..4b74953b 100644 --- a/lib/simpler/controller.rb +++ b/lib/simpler/controller.rb @@ -2,13 +2,14 @@ module Simpler class Controller - attr_reader :name, :request, :response def initialize(env) @name = extract_name @request = Rack::Request.new(env) + @request.params.merge!(env['matched.params']) @response = Rack::Response.new + @body_written = false end def make_response(action) @@ -17,11 +18,16 @@ def make_response(action) set_default_headers send(action) - write_response + + write_from_convention_template unless @body_written @response.finish end + def append_path_params(params) + @request.params.merge!(params) + end + private def extract_name @@ -32,10 +38,11 @@ def set_default_headers @response['Content-Type'] = 'text/html' end - def write_response + def write_from_convention_template body = render_body @response.write(body) + @body_written = true end def render_body @@ -47,8 +54,45 @@ def params end def render(template) + case template + when String + proceed_string_template(template) + when Hash + proceed_hash_options(template) + else + template_error("Template can be string or hash, but not #{template.class}.") + end + + @body_written = true + end + + def proceed_string_template(template) @request.env['simpler.template'] = template end + def proceed_hash_options(options) + if options.key?(:plain) + @request.env['simpler.render'] = :plain + @response.write(options[:plain].to_s) + else + template_error("Applying options #{options.keys} is not implemented yet") + end + end + + def template_error(message) + @response.status = 500 + @response.write(message) + @response.finish + end + + def status(status) + puts('Status has already been set before setting') if @response.status + + response.status = status + end + + def headers + @response.headers + end end end diff --git a/lib/simpler/router.rb b/lib/simpler/router.rb index 14b3415c..37cb18e5 100644 --- a/lib/simpler/router.rb +++ b/lib/simpler/router.rb @@ -2,7 +2,6 @@ module Simpler class Router - def initialize @routes = [] end @@ -22,6 +21,16 @@ def route_for(env) @routes.find { |route| route.match?(method, path) } end + def not_found(env) + response = Rack::Response.new + method = env['REQUEST_METHOD'].downcase.to_sym + path = env['PATH_INFO'] + + response.status = 404 + response.write("Cannot find route by path '#{path}' with method '#{method}'") + response.finish + end + private def add_route(method, path, route_point) @@ -36,6 +45,5 @@ def add_route(method, path, route_point) def controller_from_string(controller_name) Object.const_get("#{controller_name.capitalize}Controller") end - end end diff --git a/lib/simpler/router/route.rb b/lib/simpler/router/route.rb index 4c66b4b7..b4c3b48b 100644 --- a/lib/simpler/router/route.rb +++ b/lib/simpler/router/route.rb @@ -1,7 +1,6 @@ module Simpler class Router class Route - attr_reader :controller, :action def initialize(method, path, controller, action) @@ -12,9 +11,28 @@ def initialize(method, path, controller, action) end def match?(method, path) - @method == method && path.match(@path) + @method == method && path.match(path_to_regexp(@path)) + end + + def match_params(env) + params = {} + request_path_elements = env['PATH_INFO'].split('/') + @path.split('/').each_with_index do |param, index| + params[param.sub(':', '')] = request_path_elements[index] if param.start_with?(':') + end + env['matched.params'] = params end + private + + def path_to_regexp(path) + regexp_body = path + .split('/') + .map { |s| s.start_with?(':') ? '\d+' : s } + .join('/') + '(?:\/)?$' + + Regexp.new(regexp_body) + end end end end diff --git a/lib/simpler/view.rb b/lib/simpler/view.rb index 19a73b34..7d98297a 100644 --- a/lib/simpler/view.rb +++ b/lib/simpler/view.rb @@ -2,7 +2,6 @@ module Simpler class View - VIEW_BASE_PATH = 'app/views'.freeze def initialize(env) @@ -34,6 +33,5 @@ def template_path Simpler.root.join(VIEW_BASE_PATH, "#{path}.html.erb") end - end end diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/middleware/logger.rb b/middleware/logger.rb new file mode 100644 index 00000000..2ea2d39c --- /dev/null +++ b/middleware/logger.rb @@ -0,0 +1,40 @@ +class Logger + def initialize(app) + @app = app + end + + def call(env) + @env = env + status, headers, body = @app.call(env) + + log_entry = make_log_item(status, headers, body) + log(log_entry) + + [status, headers, body] + end + + def log(log_ingo) + filename = "#{Time.new.strftime('%d-%m-%Y')}.txt" + filepath = File.expand_path("log/#{filename}") + + File.write(filepath, log_ingo, mode: File.exist?(filepath) ? 'a' : 'w') + end + + def make_log_item(status, headers, _body) + response_info = collect_response_info + " + Request: #{@env['REQUEST_METHOD']} #{@env['REQUEST_METHOD']} + Handler: #{response_info[:controller]}##{response_info[:action]} + Parameters: #{query_string_parameters.merge(parameters)} + Response: #{status} [#{headers['Content-Type']}] #{response_info[:template]}" + end + + def collect_response_info + controller = env['simpler.controller'].class.name + action = env['simpler.action'] + parameters = env['matched.params'].merge(Rack::Utils.parse_query(env['QUERY_STRING'])) + template = env['simpler.template'] || env['simpler.render'] + + { controller: controller, action: action, parameters: parameters, template: template } + end +end