diff --git a/app/controllers/tests_controller.rb b/app/controllers/tests_controller.rb index 1526a689..e221faf6 100644 --- a/app/controllers/tests_controller.rb +++ b/app/controllers/tests_controller.rb @@ -4,8 +4,17 @@ def index @time = Time.now end + def show + render plain: params['id'] + end + def create end + def render_plain + headers['Content-Type'] = 'text/plain' + render plain: "Plain text response" + end + end diff --git a/config.ru b/config.ru index 3060cc20..e065e105 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,5 @@ require_relative 'config/environment' +require_relative 'middleware/logger' +use Logger run Simpler.application diff --git a/config/routes.rb b/config/routes.rb index 4a751251..304b05d6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Simpler.application.routes do + get '/tests/:id', 'tests#show' + get '/plain', 'tests#render_plain' get '/tests', 'tests#index' post '/tests', 'tests#create' end diff --git a/lib/simpler/application.rb b/lib/simpler/application.rb index 711946a9..61d40be2 100644 --- a/lib/simpler/application.rb +++ b/lib/simpler/application.rb @@ -28,6 +28,8 @@ def routes(&block) def call(env) route = @router.route_for(env) + return @router.not_found(env) unless route + route.extract_params(env) controller = route.controller.new(env) action = route.action diff --git a/lib/simpler/controller.rb b/lib/simpler/controller.rb index 9383b035..6cedec00 100644 --- a/lib/simpler/controller.rb +++ b/lib/simpler/controller.rb @@ -8,7 +8,9 @@ class Controller def initialize(env) @name = extract_name @request = Rack::Request.new(env) + @request.params.merge!(env['params']) @response = Rack::Response.new + @already_rendered = false end def make_response(action) @@ -17,7 +19,7 @@ def make_response(action) set_default_headers send(action) - write_response + write_response unless @already_rendered @response.finish end @@ -36,18 +38,55 @@ def write_response body = render_body @response.write(body) + @already_rendered = true end def render_body View.new(@request.env).render(binding) end + def render_string(template) + @request.env['simpler.template'] = template + end + + def render_error(error_message) + status 500 + @response.write(error_message) + @response.finish + end + + def render_hash(params) + if params.key?(:plain) + @request.env['simpler.render'] = :plain + @response.write(params[:plain].to_s) + @response.finish + else + render_error("Cannot render template with #{params.keys} options") + end + end + + def status(status) + response.status = status + end + + def headers + @response.headers + end + def params @request.params end def render(template) - @request.env['simpler.template'] = template + case template + when String + render_string(template) + when Hash + render_hash(template) + else + render_error("Cannot render #{template.class} template") + end + @already_rendered = true end end diff --git a/lib/simpler/router.rb b/lib/simpler/router.rb index 14b3415c..cd6450ee 100644 --- a/lib/simpler/router.rb +++ b/lib/simpler/router.rb @@ -1,4 +1,4 @@ -require_relative 'router/route' + require_relative 'router/route' module Simpler class Router @@ -22,6 +22,16 @@ def route_for(env) @routes.find { |route| route.match?(method, path) } end + def not_found(env) + method = env['REQUEST_METHOD'].downcase.to_sym + path = env['PATH_INFO'] + Rack::Response.new.then do |response| + response.status = 404 + response.write("[#{method} #{path}] route for current application is not found\n") + response.finish + end + end + private def add_route(method, path, route_point) diff --git a/lib/simpler/router/route.rb b/lib/simpler/router/route.rb index 4c66b4b7..d1256726 100644 --- a/lib/simpler/router/route.rb +++ b/lib/simpler/router/route.rb @@ -13,6 +13,25 @@ def initialize(method, path, controller, action) def match?(method, path) @method == method && path.match(@path) + @method == method && path.match(to_regex(@path)) + end + + def extract_params(env) + request_path_items = env['PATH_INFO'].split('/') + path_items = @path.split('/').map { |item| item.sub(':', '') } + + params = Hash[path_items.zip(request_path_items)].delete_if { |k, v| k == v } + env['params'] = params + end + + private + + def to_regex(path) + path.split('/') + .map { |string| string.start_with?(':') ? '\d+' : string } + .join('/') + .concat('$') + .then { |regex| Regexp.new regex } end end diff --git a/logs/log.txt b/logs/log.txt new file mode 100644 index 00000000..9e48f88c --- /dev/null +++ b/logs/log.txt @@ -0,0 +1,21 @@ + + Request: GET + Handler: NilClass# + Parameters: + Response: 404 [] + + Request: GET + Handler: TestsController#render_plain + Parameters: {} + Response: 200 [text/plain] plain + + Request: GET + Handler: TestsController#index + Parameters: {} + Response: 200 [text/html] + + Request: GET + Handler: TestsController#show + Parameters: {"id"=>"125"} + Response: 200 [text/html] plain + \ No newline at end of file diff --git a/middleware/logger.rb b/middleware/logger.rb new file mode 100644 index 00000000..f5e78b7a --- /dev/null +++ b/middleware/logger.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class Logger + def initialize(app) + @app = app + end + + def call(env) + @env = env + status, headers, body = @app.call(env) + + log_content = collect_logs(status, headers, body) + logging(log_content) + + [status, headers, body] + end + + def logging(content) + filepath = File.expand_path('logs/log.txt') + write_mode = File.exist?(filepath) ? 'a' : 'w' + File.write(filepath, content, mode: write_mode) + end + + def collect_logs(status, headers, _body) + controller = @env['simpler.controller'].class.name + action = @env['simpler.action'] + template = @env['simpler.template'] || @env['simpler.render'] + parameters = @env['params'] + """ + Request: #{@env['REQUEST_METHOD']} + Handler: #{controller}##{action} + Parameters: #{parameters} + Response: #{status} [#{headers['Content-Type']}] #{template} + """ + end +end