From c9b6c4fb3c88b7966ea858d5433c21d907d74722 Mon Sep 17 00:00:00 2001 From: anastasiiastarostenko Date: Sat, 10 Sep 2022 16:00:41 +0300 Subject: [PATCH] Web framework simpler homework --- app/controllers/tests_controller.rb | 23 +++++++++++++++-- app/views/tests/show.html.erb | 1 + config.ru | 5 ++++ config/routes.rb | 5 ++++ lib/simpler/application.rb | 15 ++++++++---- lib/simpler/controller.rb | 38 ++++++++++++++++++++++++----- lib/simpler/router/route.rb | 34 +++++++++++++++++++++++--- lib/simpler/view.rb | 24 ++++++++++++++---- log/app.log | 28 +++++++++++++++++++++ middleware/log.rb | 20 +++++++++++++++ 10 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 app/views/tests/show.html.erb create mode 100644 log/app.log create mode 100644 middleware/log.rb diff --git a/app/controllers/tests_controller.rb b/app/controllers/tests_controller.rb index 1526a689..12afcc93 100644 --- a/app/controllers/tests_controller.rb +++ b/app/controllers/tests_controller.rb @@ -1,11 +1,30 @@ -class TestsController < Simpler::Controller +# frozen_string_literal: true +class TestsController < Simpler::Controller def index @time = Time.now + # render 'tests/list' end - def create + def no_content + status 204 + end + + def plain_text + render plain: 'Hello world' + end + def show + find_test end + def create + render json: { hi: 'hello' } + end + + private + + def find_test + @test = Test.find(params[:id]) + end end diff --git a/app/views/tests/show.html.erb b/app/views/tests/show.html.erb new file mode 100644 index 00000000..6fe77d45 --- /dev/null +++ b/app/views/tests/show.html.erb @@ -0,0 +1 @@ +

Test: <%= @test.title %>

diff --git a/config.ru b/config.ru index 3060cc20..526bd98e 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,8 @@ +# frozen_string_literal: true + require_relative 'config/environment' +require_relative 'middleware/log' + +use Log, log_path: File.expand_path('log/app.log', __dir__) run Simpler.application diff --git a/config/routes.rb b/config/routes.rb index 4a751251..75c3b286 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,9 @@ +# frozen_string_literal: true + Simpler.application.routes do get '/tests', 'tests#index' + get '/tests/no_content', 'tests#no_content' + get '/tests/plain_text', 'tests#plain_text' + get '/tests/:id', 'tests#show' post '/tests', 'tests#create' end diff --git a/lib/simpler/application.rb b/lib/simpler/application.rb index 711946a9..fa41a3b1 100644 --- a/lib/simpler/application.rb +++ b/lib/simpler/application.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'yaml' require 'singleton' require 'sequel' @@ -6,7 +8,6 @@ module Simpler class Application - include Singleton attr_reader :db @@ -28,8 +29,13 @@ def routes(&block) def call(env) route = @router.route_for(env) - controller = route.controller.new(env) - action = route.action + if route + controller = route.controller.new(env, route.params) + action = route.action + else + controller = Controller.new(env) + action = :not_found + end make_response(controller, action) end @@ -37,7 +43,7 @@ def call(env) private def require_app - Dir["#{Simpler.root}/app/**/*.rb"].each { |file| require file } + Dir["#{Simpler.root}/app/**/*.rb"].sort.each { |file| require file } end def require_routes @@ -53,6 +59,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..aa0e3dd1 100644 --- a/lib/simpler/controller.rb +++ b/lib/simpler/controller.rb @@ -1,19 +1,31 @@ +# frozen_string_literal: true + +require 'pry' + require_relative 'view' module Simpler class Controller + CONTENT_TYPE = { + plain: 'text/plain', + json: 'application/json', + default: 'text/html' + }.freeze - attr_reader :name, :request, :response + attr_reader :name, :request, :response, :params - def initialize(env) + def initialize(env, params = {}) @name = extract_name @request = Rack::Request.new(env) @response = Rack::Response.new + @params = params end def make_response(action) @request.env['simpler.controller'] = self @request.env['simpler.action'] = action + @request.env['simpler.format'] = :default + @request.env['simpler.template'] = "#{name}/#{action}" set_default_headers send(action) @@ -24,6 +36,11 @@ def make_response(action) private + def not_found + render plain: 'Oops! Something wrong' + status 404 + end + def extract_name self.class.name.match('(?.+)Controller')[:name].downcase end @@ -34,6 +51,7 @@ def set_default_headers def write_response body = render_body + set_appropriate_header @response.write(body) end @@ -42,13 +60,21 @@ def render_body View.new(@request.env).render(binding) end - def params - @request.params + def render(template) + return unless template.is_a? Hash + + @request.env['simpler.format'] = template.keys.first + @request.env['simpler.template'] = template.values.first end - def render(template) - @request.env['simpler.template'] = template + def status(response_code) + return unless Rack::Utils::HTTP_STATUS_CODES.keys.include? response_code + + @response.status = response_code end + def set_appropriate_header + @response['Content-Type'] = CONTENT_TYPE[@request.env['simpler.format']] + end end end diff --git a/lib/simpler/router/route.rb b/lib/simpler/router/route.rb index 4c66b4b7..40deec56 100644 --- a/lib/simpler/router/route.rb +++ b/lib/simpler/router/route.rb @@ -1,20 +1,48 @@ +# frozen_string_literal: true + module Simpler class Router class Route - - attr_reader :controller, :action + attr_reader :controller, :action, :params def initialize(method, path, controller, action) @method = method @path = path @controller = controller @action = action + @params = find_params end def match?(method, path) - @method == method && path.match(@path) + return false if @method != method + + path_match?(path) end + private + + def path_match?(path) + request_attr = split_path(path) + route_attr = split_path(@path) + + return false if request_attr.size != route_attr.size + + route_attr.zip(request_attr).all? do |route, request| + @params[route.delete_prefix(':').to_sym] = request if route[0] == ':' + request == route || route[0] == ':' + end + end + + def find_params + @params = {} + params = split_path(@path).select { |attr| attr[0] == ':' } + params.each { |attr| @params[attr.delete_prefix(':').to_sym] = nil } unless params.empty? + @params + end + + def split_path(path) + path.split('/')[1..-1] + end end end end diff --git a/lib/simpler/view.rb b/lib/simpler/view.rb index 19a73b34..61f1ec64 100644 --- a/lib/simpler/view.rb +++ b/lib/simpler/view.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + require 'erb' +require 'json' module Simpler class View - VIEW_BASE_PATH = 'app/views'.freeze def initialize(env) @@ -10,9 +12,18 @@ def initialize(env) end def render(binding) - template = File.read(template_path) - - ERB.new(template).result(binding) + case format + when :plain + "#{template}\n" + when :json + template.to_json + when :default + template = File.read(template_path) + + ERB.new(template).result(binding) + else + Controller.new(env).not_found + end end private @@ -29,11 +40,14 @@ def template @env['simpler.template'] end + def format + @env['simpler.format'] + end + def template_path path = template || [controller.name, action].join('/') Simpler.root.join(VIEW_BASE_PATH, "#{path}.html.erb") end - end end diff --git a/log/app.log b/log/app.log new file mode 100644 index 00000000..f243761c --- /dev/null +++ b/log/app.log @@ -0,0 +1,28 @@ +I, [2022-09-10T15:40:58.047779 #34083] INFO -- : Request: POST /tests +I, [2022-09-10T15:40:58.047902 #34083] INFO -- : Handler: tests#create +I, [2022-09-10T15:40:58.047929 #34083] INFO -- : Parameters: {} +I, [2022-09-10T15:40:58.047951 #34083] INFO -- : Response: 200 [application/json] +I, [2022-09-10T15:41:27.281397 #34083] INFO -- : Request: GET /tests/1 +I, [2022-09-10T15:41:27.281474 #34083] INFO -- : Handler: tests#show +I, [2022-09-10T15:41:27.281508 #34083] INFO -- : Parameters: {:id=>"1"} +I, [2022-09-10T15:41:27.281534 #34083] INFO -- : Response: 200 [text/html] tests/show +I, [2022-09-10T15:41:46.453167 #34083] INFO -- : Request: GET /tests +I, [2022-09-10T15:41:46.453231 #34083] INFO -- : Handler: tests#index +I, [2022-09-10T15:41:46.453258 #34083] INFO -- : Parameters: {} +I, [2022-09-10T15:41:46.453283 #34083] INFO -- : Response: 204 [] +I, [2022-09-10T15:44:06.595116 #40118] INFO -- : Request: GET /tests/plain_text +I, [2022-09-10T15:44:06.595201 #40118] INFO -- : Handler: tests#plain_text +I, [2022-09-10T15:44:06.595229 #40118] INFO -- : Parameters: {} +I, [2022-09-10T15:44:06.595252 #40118] INFO -- : Response: 200 [text/plain] +I, [2022-09-10T15:44:23.208996 #40118] INFO -- : Request: GET /tests +I, [2022-09-10T15:44:23.209063 #40118] INFO -- : Handler: tests#index +I, [2022-09-10T15:44:23.209090 #40118] INFO -- : Parameters: {} +I, [2022-09-10T15:44:23.209114 #40118] INFO -- : Response: 200 [text/html] tests/index +I, [2022-09-10T15:44:47.565818 #40118] INFO -- : Request: GET /abc +I, [2022-09-10T15:44:47.565897 #40118] INFO -- : Handler: simpler::#not_found +I, [2022-09-10T15:44:47.565928 #40118] INFO -- : Parameters: {} +I, [2022-09-10T15:44:47.565954 #40118] INFO -- : Response: 404 [text/plain] +I, [2022-09-10T15:55:42.581412 #52368] INFO -- : Request: GET /tests/1 +I, [2022-09-10T15:55:42.583421 #52368] INFO -- : Handler: tests#show +I, [2022-09-10T15:55:42.583469 #52368] INFO -- : Parameters: {:id=>"1"} +I, [2022-09-10T15:55:42.583500 #52368] INFO -- : Response: 200 [text/html] tests/show diff --git a/middleware/log.rb b/middleware/log.rb new file mode 100644 index 00000000..006cccf1 --- /dev/null +++ b/middleware/log.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true +require 'logger' + +class Log + + def initialize(app, **options) + @logger = Logger.new(options[:log_path] || STDOUT) + @app = app + end + + def call(env) + response = @app.call(env) + + @logger.info("Request: #{env['REQUEST_METHOD']} #{env['PATH_INFO']}") + @logger.info("Handler: #{env['simpler.controller'].name}##{env['simpler.action']}") + @logger.info("Parameters: #{env['simpler.controller'].params}") + @logger.info("Response: #{response[0]} [#{response[1]['Content-Type']}] #{env['simpler.template'] if ![:plain, :json].include?(env['simpler.format']) }") + response + end +end