diff --git a/controuter/.gitignore b/controuter/.gitignore new file mode 100644 index 0000000..0cb6eeb --- /dev/null +++ b/controuter/.gitignore @@ -0,0 +1,9 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ diff --git a/controuter/.rspec b/controuter/.rspec new file mode 100644 index 0000000..8c18f1a --- /dev/null +++ b/controuter/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/controuter/Gemfile b/controuter/Gemfile new file mode 100644 index 0000000..a4fe9a1 --- /dev/null +++ b/controuter/Gemfile @@ -0,0 +1,4 @@ +source 'https://rubygems.org' + +# Specify your gem's dependencies in controuter.gemspec +gemspec diff --git a/controuter/LICENSE.txt b/controuter/LICENSE.txt new file mode 100644 index 0000000..6ef8aaf --- /dev/null +++ b/controuter/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Serg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/controuter/README.md b/controuter/README.md new file mode 100644 index 0000000..b225d1c --- /dev/null +++ b/controuter/README.md @@ -0,0 +1,41 @@ +# Controuter + +Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/controuter`. To experiment with that code, run `bin/console` for an interactive prompt. + +TODO: Delete this and the text above, and describe your gem + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'controuter' +``` + +And then execute: + + $ bundle + +Or install it yourself as: + + $ gem install controuter + +## Usage + +TODO: Write usage instructions here + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). + +## Contributing + +Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/controuter. + + +## License + +The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). + diff --git a/controuter/Rakefile b/controuter/Rakefile new file mode 100644 index 0000000..43022f7 --- /dev/null +++ b/controuter/Rakefile @@ -0,0 +1,2 @@ +require "bundler/gem_tasks" +task :default => :spec diff --git a/controuter/bin/console b/controuter/bin/console new file mode 100755 index 0000000..1e5f1f7 --- /dev/null +++ b/controuter/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "controuter" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start diff --git a/controuter/bin/setup b/controuter/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/controuter/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/controuter/controuter-0.2.4.gem b/controuter/controuter-0.2.4.gem new file mode 100644 index 0000000..0483e8b Binary files /dev/null and b/controuter/controuter-0.2.4.gem differ diff --git a/controuter/controuter.gemspec b/controuter/controuter.gemspec new file mode 100644 index 0000000..895cb38 --- /dev/null +++ b/controuter/controuter.gemspec @@ -0,0 +1,35 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'controuter/version' + +Gem::Specification.new do |spec| + spec.name = "controuter" + spec.version = Controuter::VERSION + spec.authors = ["Serg"] + spec.email = ["yelagins@gmail.com"] + spec.files = ["lib/controuter.rb"] + spec.summary = %q{gem.} + spec.homepage = "http://sadasdasd.com" + spec.license = "MIT" + + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' + # to allow pushing to a single host or delete this section to allow pushing to any host. + # if spec.respond_to?(:metadata) + # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserdsfver.com'" + # else + # raise "RubyGems 2.0 or newer is required to protect against " \ + # "public gem pushes." + # end + + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.13" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" +end diff --git a/controuter/lib/controuter.rb b/controuter/lib/controuter.rb new file mode 100644 index 0000000..a45101c --- /dev/null +++ b/controuter/lib/controuter.rb @@ -0,0 +1,87 @@ +require "controuter/version" + +module Controuter + + class Controller + RESPONSE_TYPES = { + text: ['text/html', ->(c) { c.to_s }], + json: ['application/json', ->(c) { Oj.dump(c) }] + }.freeze + + 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 + + class Router + def call(env) + find(env) + end + + private + + def initialize(&block) + @routes = {} + instance_exec(&block) + end + + def find(env) + current_env = ->(env) {[404, {}, ['404']]} + @routes[env['REQUEST_METHOD']].each { |(key,value)| + regular_value = Regexp.new('\A' + key.gsub(/:[\w-]+/, '[\w-]+') + '\Z') + current_env = get_controller_action(value) if regular_value=~env['REQUEST_PATH'] + } + current_env.call(env) + end + def get_controller_action(str) + controller_name, action_name = str.split('#') + 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 + .split('/') + .map { |part| part.split('_').map(&:capitalize).join } + .join('::') + 'Controller' + end + + def get(path, rack_app) + match('GET', path, rack_app) + end + + def post(path, rack_app) + match('POST', path, rack_app) + end + + def match(http_method, path, rack_app) + @routes[http_method] ||= {} + @routes[http_method][path] = rack_app + end + end +end diff --git a/controuter/lib/controuter/version.rb b/controuter/lib/controuter/version.rb new file mode 100644 index 0000000..be5448c --- /dev/null +++ b/controuter/lib/controuter/version.rb @@ -0,0 +1,3 @@ +module Controuter + VERSION = "0.2.4" +end diff --git a/controuter/spec/controuter_spec.rb b/controuter/spec/controuter_spec.rb new file mode 100644 index 0000000..fe273bf --- /dev/null +++ b/controuter/spec/controuter_spec.rb @@ -0,0 +1,41 @@ +require "spec_helper" +require "./lib/controuter" + +describe Controuter do + it "has a version number" do + expect(Controuter::VERSION).not_to be nil + end +end + +describe Controuter::Router do + subject do + Controuter::Router.new do + get '/test', 'tests#show' + end + end + + context "when page not found" do + let(:env) { { 'REQUEST_PATH' => '/wrong-page', 'REQUEST_METHOD' => 'GET'} } + it 'matches request' do + expect(subject.call(env)).to eq [404, {}, ['404']] + end + end +end + +describe Controuter::Router do + subject do + Controuter::Router.new do + get '/page', 'tests#show' + end + + TestsController = ->(_var){"show"} + end + + context "simulating controller action" do + let(:env) { { 'REQUEST_PATH' => '/page', 'REQUEST_METHOD' => 'GET'} } + + it do + expect(subject.call(env)).to eq "show" + end + end +end diff --git a/controuter/spec/spec_helper.rb b/controuter/spec/spec_helper.rb new file mode 100644 index 0000000..ab6277f --- /dev/null +++ b/controuter/spec/spec_helper.rb @@ -0,0 +1,2 @@ +$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) +require "controuter" diff --git a/hw1/fibonacci.rb b/hw1/fibonacci.rb index e69de29..ef851d7 100644 --- a/hw1/fibonacci.rb +++ b/hw1/fibonacci.rb @@ -0,0 +1,11 @@ +class Fibonacci + include Enumerable + def initialize(times) + @times,@a,@b,@c = times,1,1,1; + end + def each + @times.times do + yield @a;@a=@b;@b=@a+@c;@c=@a; + end + end +end diff --git a/hw2/app/app.rb b/hw2/app/app.rb index 2be87a8..e15700c 100644 --- a/hw2/app/app.rb +++ b/hw2/app/app.rb @@ -1,4 +1,7 @@ Application = Router.new do - get '/test', ->(env) { [200, {}, ['get test']] } - post '/test', ->(env) { [200, {}, ['post test']] } + get '/test1', ->(env) {[200, {}, ['Page 1']]} + get '/test2/test:id', ->(env) {[200, {}, ['Page 2']]} + get '/test3/:id', ->(env) {[200, {}, ['Page 3']]} + get '/test4/:id/test5', ->(env) {[200, {}, ['Page 4']]} + get '/test4/:id/test5/:id', ->(env) {[200, {}, ['Page 5']]} end diff --git a/hw2/lib/router.rb b/hw2/lib/router.rb index eb6e53d..811116a 100644 --- a/hw2/lib/router.rb +++ b/hw2/lib/router.rb @@ -1,6 +1,17 @@ class Router def call(env) - @routes[env['REQUEST_METHOD']][env['REQUEST_PATH']].call(env) + + unless env['REQUEST_PATH'] == "/favicon.ico" + current_env = ->(env) {[404, {}, ['404']]} + + @routes[env['REQUEST_METHOD']].each { |(key,value)| + key_to_reg_exp= key.gsub(/(?<=:)[^\/]+(?=($|\/))/,"[a-zA-Z0-9_]+").gsub(/\/:/,"/") + regular_value = Regexp.new /\A#{key_to_reg_exp}\Z/ + current_env = value if regular_value=~env['REQUEST_PATH'] + } + current_env.call(env) + end + end private diff --git a/hw2/spec/lib/router_spec.rb b/hw2/spec/lib/router_spec.rb index e53a275..0a0daa0 100644 --- a/hw2/spec/lib/router_spec.rb +++ b/hw2/spec/lib/router_spec.rb @@ -31,4 +31,28 @@ expect(subject.call(env)).to eq [200, {}, ['post test']] end end + + context 'when request is GET & PATH is /post/about_ruby' do + let(:env) {{ 'REQUEST_PATH' => '/post/about_ruby', 'REQUEST_METHOD' => 'GET'}} + + it 'matches request' do + expect(subject.call(env)).to eq [200, {}, ['post show page']] + end + end + + context 'when request is GET & PATH is /post/43' do + let(:env) {{ 'REQUEST_PATH' => '/post/43', 'REQUEST_METHOD' => 'GET'}} + + it 'matches request' do + expect(subject.call(env)).to eq [200, {}, ['post show page']] + end + end + + context 'when page not found' do + let(:env) {{ 'REQUEST_PATH' => '/post/about_ruby/43', 'REQUEST_METHOD' => 'GET'}} + + it 'matches request' do + expect(subject.call(env)).to eq [404, {}, ['404']] + end + end end diff --git a/hw3/.travis.yml b/hw3/.travis.yml new file mode 100644 index 0000000..8de0cce --- /dev/null +++ b/hw3/.travis.yml @@ -0,0 +1,10 @@ +language: ruby +rvm: +- 2.3.1 +script: +- bash before-deploy.sh +deploy: + provider: heroku + api_key: + secure: NNdlTVbKxcM0pdHxn79+1zRyDx3umKAa9d3Ka09TLUQkjfNQyINi6dtbwZtfmduOIYlWMTy0ZSfkOzZnAvcwGO9b4unmdnE8FIvxDiKzTH15S9I4BePfB+5Utpa4mbN0UnB8v0GcW1b9+fwnHNQsdwNdr08AvPENEoSMbIjyhAK2IvlQHlqwKR72JvDRfRxwwG7S1s6+pSpAHh9Hl6BBPzCwn5Uf4Lt+Vwwcb9MPQPwpWyXzQk+LF2QIAO23oqb0l6/jcCaEcSw8J2rDPh1gCZnBSDj2JuGtA8URxgemFMPrzaPNDkyo0NZUDmdzHjGFyeD89C/fZ+N5704pg/Osuxl8DP0J/+xavDO/eY9gdskQhfjq91kN2FiBu7/P35pm963YzCwDqlZysxA8MYniR3ST1C9D5q3eluNY0vFEWaHUhi90URuVy3tuLl1b+cOHQ+TKRwYuisyaOPhQF76ERnjHm+jr5k6NIqD+sbBJ5D+uk0dpc4/y4FOYyxnuZ5JGK/TrmMXfCPs1yKgk6KwBa0/RNolIAQ1dmSrjBZxnZNxS0kLXGPrtYAZCrP0xvcy+KY1Wi9KfCtQ1Xao/ASanPxW24/IXiTczXxdsxqxQowkLZRBNBIDLDsev8MKMD1Mg9yeH3wDa22ahTR0i7SR/PYpiURf13g54OsmUrElh998= + app: home-work-project diff --git a/hw3/Gemfile b/hw3/Gemfile new file mode 100644 index 0000000..b10cdf7 --- /dev/null +++ b/hw3/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true +source "https://rubygems.org" + +gem "rack" +gem "rspec" +gem "oj" +gem "controuter" #custom gem diff --git a/hw3/Gemfile.lock b/hw3/Gemfile.lock new file mode 100644 index 0000000..59fdc7c --- /dev/null +++ b/hw3/Gemfile.lock @@ -0,0 +1,32 @@ +GEM + remote: https://rubygems.org/ + specs: + controuter (0.2.4) + diff-lcs (1.2.5) + oj (2.17.5) + rack (2.0.1) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.4) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + controuter + oj + rack + rspec + +BUNDLED WITH + 1.13.6 diff --git a/hw3/app/app.rb b/hw3/app/app.rb new file mode 100644 index 0000000..f670299 --- /dev/null +++ b/hw3/app/app.rb @@ -0,0 +1,22 @@ +Controller = Controuter::Controller +Router = Controuter::Router + +class TestsController < Controller + def home + response(:text, "

Home Page


Page 1
Page 2") + end + + def show + response(:text, request.path_info) + end + + def test + response(:text, "Action 'test'") + end +end + +Application = Router.new do + get '/', 'tests#home' + get '/pages/1', 'tests#show' + get '/pages/2', 'tests#test' +end diff --git a/hw3/before-deploy.sh b/hw3/before-deploy.sh new file mode 100644 index 0000000..1bc4f5e --- /dev/null +++ b/hw3/before-deploy.sh @@ -0,0 +1 @@ +bundle install && bundle exec rspec diff --git a/hw3/config.ru b/hw3/config.ru new file mode 100644 index 0000000..c2e95d5 --- /dev/null +++ b/hw3/config.ru @@ -0,0 +1,3 @@ +require './main' + +run Application diff --git a/hw3/lib/controller.rb b/hw3/lib/controller.rb new file mode 100644 index 0000000..597e518 --- /dev/null +++ b/hw3/lib/controller.rb @@ -0,0 +1,35 @@ +class Controller + RESPONSE_TYPES = { + text: ['text/html', ->(c) { c.to_s }], + json: ['application/json', ->(c) { Oj.dump(c) }] + }.freeze + + 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 diff --git a/hw3/lib/router.rb b/hw3/lib/router.rb new file mode 100644 index 0000000..5496f20 --- /dev/null +++ b/hw3/lib/router.rb @@ -0,0 +1,47 @@ +class Router + def call(env) + find(env) + end + +private + + def initialize(&block) + @routes = {} + instance_exec(&block) + end + + def find(env) + current_env = ->(env) {[404, {}, ['404']]} + @routes[env['REQUEST_METHOD']].each { |(key,value)| + regular_value = Regexp.new('\A' + key.gsub(/:[\w-]+/, '[\w-]+') + '\Z') + current_env = get_controller_action(value) if regular_value=~env['REQUEST_PATH'] + } + current_env.call(env) + end + + def get_controller_action(str) + controller_name, action_name = str.split('#') + 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 + .split('/') + .map { |part| part.split('_').map(&:capitalize).join } + .join('::') + 'Controller' + end + + def get(path, rack_app) + match('GET', path, rack_app) + end + + def post(path, rack_app) + match('POST', path, rack_app) + end + + def match(http_method, path, rack_app) + @routes[http_method] ||= {} + @routes[http_method][path] = rack_app + end +end diff --git a/hw3/main.rb b/hw3/main.rb new file mode 100644 index 0000000..aee994b --- /dev/null +++ b/hw3/main.rb @@ -0,0 +1,4 @@ +require 'rack' +require 'controuter' +require './app/app' +require 'oj' diff --git a/hw3/spec/lib/router_spec.rb b/hw3/spec/lib/router_spec.rb new file mode 100644 index 0000000..ffd8604 --- /dev/null +++ b/hw3/spec/lib/router_spec.rb @@ -0,0 +1,28 @@ +require './main' + +RSpec.describe Router do + + context 'when page not found' do + let(:env) { { 'REQUEST_PATH' => '/wrong-page', 'REQUEST_METHOD' => 'GET'} } + + it 'matches request' do + expect(Application.call(env)).to eq [404, {}, ['404']] + end + end + + context 'when request is GET & PATH is home page' do + let(:env) {{ 'REQUEST_PATH' => '/', 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/'}} + + it 'matches request' do + expect(Application.call(env)).to eq [200, {"Content-Type"=>"text/html"}, ["

Home Page


Page 1
Page 2"]] + end + end + + context 'when request is GET & PATH is some page' do + let(:env) {{ 'REQUEST_PATH' => '/pages/1', 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/pages/1'}} + + it 'matches request' do + expect(Application.call(env)).to eq [200, {"Content-Type"=>"text/html"}, ["/pages/1"]] + end + end +end diff --git a/hw3/spec/spec_helper.rb b/hw3/spec/spec_helper.rb new file mode 100644 index 0000000..ef0fca6 --- /dev/null +++ b/hw3/spec/spec_helper.rb @@ -0,0 +1,28 @@ +require './lib/router' + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups + + config.filter_run_when_matching :focus + + config.disable_monkey_patching! + + config.warnings = true + + if config.files_to_run.one? + config.default_formatter = 'doc' + end + + config.profile_examples = 10 + + config.order = :random + Kernel.srand config.seed +end