├── README.md ├── app ├── controllers │ └── tests_controller.rb ├── models │ └── test.rb └── views │ └── tests │ ├── index.html.erb │ └── list.html.erb ├── config.ru ├── config ├── database.yml ├── environment.rb └── routes.rb ├── db └── test_guru.sqlite └── lib ├── simpler.rb └── simpler ├── application.rb ├── controller.rb ├── router.rb ├── router └── route.rb └── view.rb /README.md: -------------------------------------------------------------------------------- 1 | # Simpler 2 | 3 | **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. 4 | 5 | ## The application overview 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /app/controllers/tests_controller.rb: -------------------------------------------------------------------------------- 1 | class TestsController < Simpler::Controller 2 | 3 | def index 4 | @time = Time.now 5 | end 6 | 7 | def create 8 | 9 | end 10 | 11 | end 12 | -------------------------------------------------------------------------------- /app/models/test.rb: -------------------------------------------------------------------------------- 1 | # Simpler.application.db.create_table(:tests) do 2 | # primary_key :id 3 | # String :title, null: false 4 | # Integer :level, default: 0 5 | # end 6 | class Test < Sequel::Model 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/views/tests/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |<%= @time %>
11 | 12 | -------------------------------------------------------------------------------- /app/views/tests/list.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |<%= @time %>
11 | 12 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require_relative 'config/environment' 2 | 3 | run Simpler.application 4 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | adapter: sqlite 2 | database: db/test_guru.sqlite 3 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | require_relative '../lib/simpler' 2 | 3 | Simpler.application.bootstrap! 4 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Simpler.application.routes do 2 | get '/tests', 'tests#index' 3 | post '/tests', 'tests#create' 4 | end 5 | -------------------------------------------------------------------------------- /db/test_guru.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/psylone/simpler/c3d0a7863d0ef1e6a21bae73fe055ff130a045fa/db/test_guru.sqlite -------------------------------------------------------------------------------- /lib/simpler.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require_relative 'simpler/application' 3 | 4 | module Simpler 5 | 6 | class << self 7 | def application 8 | Application.instance 9 | end 10 | 11 | def root 12 | Pathname.new(File.expand_path('..', __dir__)) 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/simpler/application.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'singleton' 3 | require 'sequel' 4 | require_relative 'router' 5 | require_relative 'controller' 6 | 7 | module Simpler 8 | class Application 9 | 10 | include Singleton 11 | 12 | attr_reader :db 13 | 14 | def initialize 15 | @router = Router.new 16 | @db = nil 17 | end 18 | 19 | def bootstrap! 20 | setup_database 21 | require_app 22 | require_routes 23 | end 24 | 25 | def routes(&block) 26 | @router.instance_eval(&block) 27 | end 28 | 29 | def call(env) 30 | route = @router.route_for(env) 31 | controller = route.controller.new(env) 32 | action = route.action 33 | 34 | make_response(controller, action) 35 | end 36 | 37 | private 38 | 39 | def require_app 40 | Dir["#{Simpler.root}/app/**/*.rb"].each { |file| require file } 41 | end 42 | 43 | def require_routes 44 | require Simpler.root.join('config/routes') 45 | end 46 | 47 | def setup_database 48 | database_config = YAML.load_file(Simpler.root.join('config/database.yml')) 49 | database_config['database'] = Simpler.root.join(database_config['database']) 50 | @db = Sequel.connect(database_config) 51 | end 52 | 53 | def make_response(controller, action) 54 | controller.make_response(action) 55 | end 56 | 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/simpler/controller.rb: -------------------------------------------------------------------------------- 1 | require_relative 'view' 2 | 3 | module Simpler 4 | class Controller 5 | 6 | attr_reader :name, :request, :response 7 | 8 | def initialize(env) 9 | @name = extract_name 10 | @request = Rack::Request.new(env) 11 | @response = Rack::Response.new 12 | end 13 | 14 | def make_response(action) 15 | @request.env['simpler.controller'] = self 16 | @request.env['simpler.action'] = action 17 | 18 | set_default_headers 19 | send(action) 20 | write_response 21 | 22 | @response.finish 23 | end 24 | 25 | private 26 | 27 | def extract_name 28 | self.class.name.match('(?