Skip to content
This repository was archived by the owner on May 1, 2018. It is now read-only.
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
24 changes: 24 additions & 0 deletions hw1/fibonacci.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class Fibonacci
attr_reader :result_array
include Enumerable

def each
result_array.each{ |item| yield item }
self
end

private

def initialize(length)
raise TypeError, "Expected argument to be Integer" unless [Fixnum, Bignum].include? length.class
raise ArgumentError, "Expected argument >= 1. Got #{length}" if length < 1

previous_item = next_item = 1
@result_array = (1..length).inject([]) do |result|
result << previous_item
previous_item, next_item = next_item, previous_item
next_item += previous_item
result
end
end
end
2 changes: 2 additions & 0 deletions hw2/Gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true
source "https://rubygems.org"

gem "pry"
gem "rack"
gem "rspec"
gem "oj"
10 changes: 10 additions & 0 deletions hw2/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
GEM
remote: https://rubygems.org/
specs:
coderay (1.1.1)
diff-lcs (1.2.5)
method_source (0.8.2)
oj (2.17.5)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rack (2.0.1)
rspec (3.5.0)
rspec-core (~> 3.5.0)
Expand All @@ -16,11 +23,14 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
slop (3.6.0)

PLATFORMS
ruby

DEPENDENCIES
oj
pry
rack
rspec

Expand Down
14 changes: 12 additions & 2 deletions hw2/app/app.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
class TestsController < Controller
def show
response(:json, params)
end

def test
response(:text, "Required method #{request.request_method}")
end
end

Application = Router.new do
get '/test', ->(env) { [200, {}, ['get test']] }
post '/test', ->(env) { [200, {}, ['post test']] }
get '/post/:name/:other_one', 'tests#show'
get '/test', 'tests#test'
end
35 changes: 35 additions & 0 deletions hw2/lib/controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Controller
RESPONSE_TYPES = {
text: ['text/plain', -> (c) { c.to_s }],
json: ['application/json', -> (c) { Oj.dump(c) }],
}

def call(env)
@env = env
@request = Rack::Request.new(env)
@request.params.merge!(env['router.params'] || {})
send(@action_name)
[200, @response_headers, [@response_body]]
end

def self.action(action_name)
proc { |env| new(action_name).call(env)}
end

private
attr_reader :request

def initialize(action_name)
@action_name = action_name
end

def params
request.params
end

def response(type, content)
@response_headers ||= {}
@response_headers.merge!('Content-Type' => RESPONSE_TYPES[type][0] )
@response_body = RESPONSE_TYPES[type][1].call(content)
end
end
47 changes: 43 additions & 4 deletions hw2/lib/router.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
class Router
def call(env)
@routes[env['REQUEST_METHOD']][env['REQUEST_PATH']].call(env)
find_route(env).call(env)
end

private
attr_reader :routes

def initialize(&block)
@routes = {}
@routes = []
instance_exec(&block)
end

Expand All @@ -19,7 +20,45 @@ def post(path, rack_app)
end

def match(http_method, path, rack_app)
@routes[http_method] ||= {}
@routes[http_method][path] = rack_app
rack_app = get_controller_action(rack_app) if rack_app.is_a? String
routes << { pattern: path, app: rack_app, regexp: path_to_regexp(path), method: http_method}
end

def get_controller_action(str)
controller_name, action_name = str.split('#') # tests#show => ['tests', 'show']
controller_name = to_upper_camel_case(controller_name)
Kernel.const_get(controller_name).send(:action, action_name)
end

def to_upper_camel_case(str)
str # 'public_pages/tests' => PublicPages::TestsController
.split('/') # ['public_pages', 'test']
.map { |part| part.split('_').map(&:capitalize).join } # ['PublicPages', 'Test']
.join('::') + 'Controller'
end

def find_route(env)
routes.each do |route|
if env['REQUEST_METHOD'] == route[:method] && env['REQUEST_PATH'] =~ route[:regexp]
env['router.params'] = extract_params(route[:pattern], env['REQUEST_PATH'])
return route[:app]
end
end

->(_env) { [404, {}, ['not found']] }
end

def path_to_regexp(path)
Regexp.new('\A' + path.gsub(/:[\w-]+/, '[\w-]+') + '\Z')
end

# pattern: "post/:name", route: "post/about_ruby"
def extract_params(pattern, path)
pattern
.split('/') # ['post', ':name']
.zip(path.split('/')) # [['post', 'post'],[':name', 'post']]
.reject { |e| e.first == e.last } # [[':name', 'post']]
.map { |e| [e.first[1..-1], e.last] } # [['name', 'post']]
.to_h
end
end
4 changes: 4 additions & 0 deletions hw2/main.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
require 'pry'
require 'rack'
require 'oj'
require './lib/controller'
require './lib/router'
require './app/app'
89 changes: 89 additions & 0 deletions hw2/spec/lib/controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
require 'spec_helper'

RSpec.describe Controller do
let(:controller) do
Class.new(described_class) do
def test_action
response(:text, 'simple test')
end

def text_action
response(:text, params.inspect)
end

def json_action
response(:json, params)
end
end
end

describe '#action' do
it 'returns proc' do
expect(controller.action(:test)).to be_a(Proc)
end

it 'generated proc calls action' do
expect(controller.action(:test_action).call(Rack::MockRequest.env_for('/')))
.to eq([200, { 'Content-Type' => 'text/plain' }, ['simple test']])
end
end

describe 'request processing' do
subject do
controller
.action(action)
.call(Rack::MockRequest.env_for('/?a=b&e=r').merge!(router_params))
end

let(:router_params) { {} }

context 'when text response' do
context 'response with simple text' do
let(:action) { :test_action }

it 'successfully responds' do
expect(subject)
.to eq([200, {'Content-Type'=>'text/plain'}, ['simple test']])
end
end

context 'response with params' do
let(:action) { :text_action }

it 'successfully responds' do
expect(subject)
.to eq([200, {"Content-Type"=>"text/plain"}, ["{\"a\"=>\"b\", \"e\"=>\"r\"}"]])
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

end
end
let(:action) { :test_action }

it 'successfully responds' do
expect(subject)
.to eq([200, {"Content-Type"=>"text/plain"}, ["simple test"]])
end
end

context 'when json response' do
let(:action) { :json_action }

it 'successfully responds' do
expect(subject)
.to eq([200, {"Content-Type"=>"application/json"}, ["{\"a\":\"b\",\"e\":\"r\"}"]])
end
end

context 'when has router params' do
let(:router_params) { { 'router.params' => { 'param' => 'value' } } }
let(:action) { :json_action }

it 'successfully responds' do
expect(subject)
.to eq([
200,
{"Content-Type"=>"application/json"},
["{\"a\":\"b\",\"e\":\"r\",\"param\":\"value\"}"]]
)
end
end
end
end
Loading