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