Skip to content

Web framework simpler homework #149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions app/controllers/tests_controller.rb
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions app/views/tests/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>Test: <%= @test.title %> </h1>
5 changes: 5 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -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
15 changes: 10 additions & 5 deletions lib/simpler/application.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'yaml'
require 'singleton'
require 'sequel'
Expand All @@ -6,7 +8,6 @@

module Simpler
class Application

include Singleton

attr_reader :db
Expand All @@ -28,16 +29,21 @@ 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

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
Expand All @@ -53,6 +59,5 @@ def setup_database
def make_response(controller, action)
controller.make_response(action)
end

end
end
38 changes: 32 additions & 6 deletions lib/simpler/controller.rb
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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('(?<name>.+)Controller')[:name].downcase
end
Expand All @@ -34,6 +51,7 @@ def set_default_headers

def write_response
body = render_body
set_appropriate_header

@response.write(body)
end
Expand All @@ -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
34 changes: 31 additions & 3 deletions lib/simpler/router/route.rb
Original file line number Diff line number Diff line change
@@ -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
24 changes: 19 additions & 5 deletions lib/simpler/view.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
# frozen_string_literal: true

require 'erb'
require 'json'

module Simpler
class View

VIEW_BASE_PATH = 'app/views'.freeze

def initialize(env)
@env = 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
Expand All @@ -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
28 changes: 28 additions & 0 deletions log/app.log
Original file line number Diff line number Diff line change
@@ -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
20 changes: 20 additions & 0 deletions middleware/log.rb
Original file line number Diff line number Diff line change
@@ -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