Skip to content
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

task #169

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

task #169

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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
log/app.log
34 changes: 30 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
# Simpler
Создайте на гитхабе форк учебного проекта из скринкаста (ссылки в дополнительных материалах), в полученном новом репозитории, в новой ветке выполните следующие задания:

**Simpler** is a little web framework written in [Ruby](https://www.ruby-lang.org) language. It's compatible with [Rack](https://rack.github.io) interface and intended to **learn** how web frameworks work in general.
Реализуйте расширенные возможности метода render которые позволят возвращать ответ в других форматах, например:
render plain: "Plain text response"

Реализуйте возможность устанавливать в методе контроллера статус ответа, например:
status 201

Реализуйте возможность устанавливать в методе контроллера заголовки, например:
headers['Content-Type'] = 'text/plain'

Реализуйте механизм обработки исключения когда маршрут для запрашиваемого URL не был найден. В этом случае клиенту должен отдаваться ответ со статусом 404

## The application overview

Simpler application is a singleton instance of the `Simpler::Application` class. For convenience it can be obtained by calling `Simpler.application` method. This instance holds all the routes and responds to `call` method which is required by the Rack interface.
Напишите механизм разбора route-параметров. Например, при добавлении маршрута
get '/tests/:id', 'tests#show'

а) Маршрут должен корректно обрабатывать GET запрос
/tests/101

b) В методе show контроллера при вызове метода params должен быть доступен параметр :id со значением 101

Напишите middleware для логирования HTTP-запросов и ответов:
a) Лог должен записываться в файл log/app.log
b) Для запросов необходимо записывать HTTP-метод запроса, URL, контроллер и метод который будет обрабатывать запрос, хэш параметров который будет доступен при вызове метода params
c) Для ответов необходимо записывать код статуса ответа, тип тела ответа и название шаблона (если ответ рендерился с помощью шаблона представления)

Пример:

Request: GET /tests?category=Backend
Handler: TestsController#index
Parameters: {'category' => 'Backend'}
Response: 200 OK [text/html] tests/index.html.erb
5 changes: 5 additions & 0 deletions app/controllers/tests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ class TestsController < Simpler::Controller

def index
@time = Time.now
@tests = Test.all
end

def create
render plain: "Test create!"
end

def show
@id = params[:id]
end

end
7 changes: 6 additions & 1 deletion app/views/tests/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@
<h1>Simpler framework at work!</h1>

<p><%= @time %></p>
<ul>
<% @tests.each do |test| %>
<li><%= test.title %> (<%= test.level%>)</li>
<% end %>
</ul>
</body>
</html>
</html>
2 changes: 1 addition & 1 deletion app/views/tests/list.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@

<p><%= @time %></p>
</body>
</html>
</html>
11 changes: 11 additions & 0 deletions app/views/tests/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Show | Simpler application</title>
</head>
<body>
<h1>Simpler framework at work!</h1>
<p><%= @id %></p>
</body>
</html>
2 changes: 2 additions & 0 deletions config.ru
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative 'config/environment'
require_relative 'middleware/logger'

use AppLogger, logdev: File.expand_path('log/app.log', __dir__)
run Simpler.application
1 change: 1 addition & 0 deletions config/environment.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'rack'
require_relative '../lib/simpler'

Simpler.application.bootstrap!
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Simpler.application.routes do
get '/tests', 'tests#index'
get '/tests/:id', 'tests#show'
post '/tests', 'tests#create'
end
Binary file modified db/test_guru.sqlite
Binary file not shown.
18 changes: 15 additions & 3 deletions lib/simpler/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,26 @@ 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)
action = route.action

make_response(controller, action)
make_response(controller, action)
else
status404
end
end

private

def status404
[
404,
{ 'content-type' => 'text/plain' },
['404 page is not faund']
]
end

def require_app
Dir["#{Simpler.root}/app/**/*.rb"].each { |file| require file }
end
Expand Down
30 changes: 27 additions & 3 deletions lib/simpler/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def initialize(env)
def make_response(action)
@request.env['simpler.controller'] = self
@request.env['simpler.action'] = action
@request.env['simpler.handler'] = "#{self.class.name}##{action}"

set_default_headers
send(action)
Expand All @@ -22,14 +23,34 @@ def make_response(action)
@response.finish
end

def params
@params ||= route_info.merge(request_params)
end

def request_params
@request.params
end

def route_info
@request.env['simpler.route_info']
end

private

def headers(content_type, type)
@response[content_type] = type
end

def status(code)
@response.status = code
end

def extract_name
self.class.name.match('(?<name>.+)Controller')[:name].downcase
end

def set_default_headers
@response['Content-Type'] = 'text/html'
@response['Content-Type'] ||= 'text/html'
end

def write_response
Expand All @@ -42,11 +63,14 @@ def render_body
View.new(@request.env).render(binding)
end

def params
@request.params
def redirect_to(uri)
[302, { "Location" => uri }, []]
end

def render(template)
if Hash(template)[:plain]
@response['Content-Type'] = 'text/plain'
end
@request.env['simpler.template'] = template
end

Expand Down
19 changes: 17 additions & 2 deletions lib/simpler/router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,35 @@ def route_for(env)
method = env['REQUEST_METHOD'].downcase.to_sym
path = env['PATH_INFO']

@routes.find { |route| route.match?(method, path) }
end
route = @routes.find { |route| route.match?(method, path) }
env['simpler.route_info'] = route.route_info(env) if route
route
end

private

def add_route(method, path, route_point)

route_point = route_point.split('#')
controller = controller_from_string(route_point[0])
action = route_point[1]

route = Route.new(method, path, controller, action)

@routes.push(route)

end

def controller_name
"#{route_info[:resource].capitalize}Controller"
end

def controller_class
Object.const_get(controller_name)
rescue NameError
nil
end

def controller_from_string(controller_name)
Object.const_get("#{controller_name.capitalize}Controller")
end
Expand Down
43 changes: 42 additions & 1 deletion lib/simpler/router/route.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,53 @@ def initialize(method, path, controller, action)
@path = path
@controller = controller
@action = action
@path_regexp = make_path_regexp
end

def match?(method, path)
@method == method && path.match(@path)
@method == method && match_path(path)
end

def route_info(env)
path = env['PATH_INFO']
@route_info ||= begin
resource = path_fragments(path)[0] || "base"
id, action = find_id_and_action(path_fragments(path)[1])
{ resource: resource, action: action, id: id }
end
end

def find_id_and_action(fragment)
case fragment
when "new"
[nil, :new]
else
[fragment, :show]
end
end

def match_path(path)
path.match(@path_regexp)
end

def make_path_regexp
path_parts = @path.split('/')
path_parts.map! do |part|
if part[0] == ":"
part.delete!(':')
part = "(?<#{part}>\\w+)"
else
part
end

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

подтяни регулярные выражения. Пригодится

end
str_regexp = path_parts.join("\\/")
/#{str_regexp}$/
end

def path_fragments(path)
@fragments ||= path.split("/").reject { |s| s.empty? }
end

end
end
end
11 changes: 8 additions & 3 deletions lib/simpler/view.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ def initialize(env)
end

def render(binding)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

типов может быть очень много. Лучше тут применить табличный метод. Или хотябы для каждого тим сделать свой метод, а тут вызывать нужный метод

template = File.read(template_path)

ERB.new(template).result(binding)
plain = Hash(template)[:plain]
if plain
"#{plain}\n"
else
template = File.read(template_path)

ERB.new(template).result(binding)
end
end

private
Expand Down
31 changes: 31 additions & 0 deletions middleware/logger.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'logger'

class AppLogger

def initialize(app, **options)
@logger = Logger.new(options[:logdev] || STROUT)
@app = app
end

def call(env)
request_line = "\nRequest: #{env["REQUEST_METHOD"]} "
request_line << env["PATH_INFO"]
request_line << "/?#{env["QUERY_STRING"]}" unless env["QUERY_STRING"].empty?
@logger.info request_line

status, headers, body = @app.call(env)

if env['simpler.controller']
@logger.info "\nParameters: #{env['simpler.controller'].params}"
end
@logger.info "\nHandler: #{env['simpler.handler']}"

response_line = "\nResponse: #{status} "
response_line << "[#{headers['Content-Type']}]"
response_line << " #{env['simpler.template']}"
@logger.info response_line

[status, headers, body]
end

end