yokunai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.markdown +72 -0
- data/lib/yokunai.rb +16 -0
- data/lib/yokunai/abstract_controller.rb +79 -0
- data/lib/yokunai/application.rb +40 -0
- data/lib/yokunai/config.rb +41 -0
- data/lib/yokunai/errors_controller.rb +13 -0
- data/lib/yokunai/mime.rb +15 -0
- data/lib/yokunai/render_context.rb +31 -0
- data/lib/yokunai/static_controller.rb +19 -0
- data/lib/yokunai/template.rb +49 -0
- metadata +81 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c1274ce0c2f7dacdd99c1abc30bed8deab94fc7b
|
4
|
+
data.tar.gz: e7d4494b0ffd794ee6044c899b78cb3e5738ce48
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8c4886093c184821c2f726d584e6c56c3e76d7eafb7f5a276bef6d65363abe4bd71929e2c322a8426665a2dc0321d86931e194b25384dbac6d824526a99ef590
|
7
|
+
data.tar.gz: 8811bce56b21078bef89fb97dcdb7ac54557631635f38199a5b552700dd7065584443a988ed85d270eeb6b4031093cb31aa5014127f6864552518c5d778d9ad9
|
data/README.markdown
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# よくない
|
2
|
+
|
3
|
+
It's not very good.
|
4
|
+
|
5
|
+
Please don't actually use this.
|
6
|
+
|
7
|
+
## How to use it
|
8
|
+
|
9
|
+
Add the dependency:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# Gemfile
|
13
|
+
source "https://rubygems.org/"
|
14
|
+
|
15
|
+
gem "yokunai"
|
16
|
+
```
|
17
|
+
|
18
|
+
Run `bundle`, of course. Then make a controller:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
# lib/my_app.rb
|
22
|
+
require "yokunai"
|
23
|
+
|
24
|
+
ROUTES = {
|
25
|
+
%r{^/$} => {class: "HomeController", methods: ["GET"]}
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
class HomeController < Yokunai::AbstractController
|
29
|
+
def get
|
30
|
+
respond body: "It works!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
Add some configuration for where you want to keep things:
|
36
|
+
|
37
|
+
```yml
|
38
|
+
# config/development.yml
|
39
|
+
---
|
40
|
+
template_dir: web/views
|
41
|
+
asset_dir: web/assets
|
42
|
+
```
|
43
|
+
|
44
|
+
Now just make a rackup config so we can run something:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# config.ru
|
48
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
|
49
|
+
require "my_app"
|
50
|
+
|
51
|
+
run Yokunai::Application.new(
|
52
|
+
route_map: ROUTES,
|
53
|
+
base_dir: File.dirname(__FILE__)
|
54
|
+
)
|
55
|
+
```
|
56
|
+
|
57
|
+
Then just `bundle exec rackup` and you're gold, Ponyboy.
|
58
|
+
|
59
|
+
## Assets
|
60
|
+
|
61
|
+
You probably have frontend assets, if this is a web page. There's a controller
|
62
|
+
built-in for that if you want it. Just point a path with a capture called `name`
|
63
|
+
to `Yokunai::StaticController`.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
ROUTES = {
|
67
|
+
%r{^/assets/(?<name>.+)$} => {class: "Yokunai::StaticController", methods: ["GET"]}
|
68
|
+
}
|
69
|
+
```
|
70
|
+
|
71
|
+
Now assets will be served out of the directory you set as your `asset_dir` in
|
72
|
+
the YAML config.
|
data/lib/yokunai.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Dependencies
|
2
|
+
require "mimemagic"
|
3
|
+
require "ostruct"
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
# Core
|
7
|
+
require "yokunai/config"
|
8
|
+
require "yokunai/application"
|
9
|
+
require "yokunai/render_context"
|
10
|
+
require "yokunai/template"
|
11
|
+
require "yokunai/mime"
|
12
|
+
|
13
|
+
# Controllers
|
14
|
+
require "yokunai/abstract_controller"
|
15
|
+
require "yokunai/errors_controller"
|
16
|
+
require "yokunai/static_controller"
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Yokunai
|
2
|
+
class AbstractController
|
3
|
+
|
4
|
+
# --------------------------------------------------------------------------
|
5
|
+
# Constants
|
6
|
+
# --------------------------------------------------------------------------
|
7
|
+
|
8
|
+
DEFAULT_HEADERS = {
|
9
|
+
"Content-Type" => "text/html",
|
10
|
+
"Server" => "yokunai/1.0"
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
# --------------------------------------------------------------------------
|
15
|
+
# Instance Methods
|
16
|
+
# --------------------------------------------------------------------------
|
17
|
+
|
18
|
+
# @param env [Rack::Env] The Rack ENV
|
19
|
+
# @param captures [MatchData] The named captures from the route regex
|
20
|
+
def initialize(env, captures=nil)
|
21
|
+
@env = env
|
22
|
+
@captures = captures
|
23
|
+
@templates = Yokunai::Template.new
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# --------------------------------------------------------------------------
|
28
|
+
# Default HTTP method handlers
|
29
|
+
# --------------------------------------------------------------------------
|
30
|
+
|
31
|
+
def get
|
32
|
+
unsupported_method
|
33
|
+
end
|
34
|
+
|
35
|
+
def post
|
36
|
+
unsupported_method
|
37
|
+
end
|
38
|
+
|
39
|
+
def put
|
40
|
+
unsupported_method
|
41
|
+
end
|
42
|
+
|
43
|
+
def patch
|
44
|
+
unsupported_method
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete
|
48
|
+
unsupported_method
|
49
|
+
end
|
50
|
+
|
51
|
+
def options
|
52
|
+
unsupported_method
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def respond(code: 200, headers: {}, body: "", template: nil, context: {})
|
58
|
+
if template
|
59
|
+
return respond_error(:not_found) unless @templates.exist?(template)
|
60
|
+
body = @templates.render(template, context)
|
61
|
+
end
|
62
|
+
|
63
|
+
[
|
64
|
+
code,
|
65
|
+
DEFAULT_HEADERS.merge(headers),
|
66
|
+
[body]
|
67
|
+
]
|
68
|
+
end
|
69
|
+
|
70
|
+
def respond_error(meth)
|
71
|
+
Yokunai::ErrorsController.new(@env).public_send(meth)
|
72
|
+
end
|
73
|
+
|
74
|
+
def unsupported_method
|
75
|
+
[405, {}, ["Error 405: Method not supported on this resource.\n"]]
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Yokunai
|
2
|
+
# The Rack-compatible entrypoint class. Acts as the router, sending the
|
3
|
+
# request to the appropriate controller for handling.
|
4
|
+
class Application
|
5
|
+
|
6
|
+
# @param route_map [Hash] A hash with path regexes as keys, and hashes as values with the controller to send matching requests to.
|
7
|
+
# @param base_dir [String] The absolute base directory to use for various lookups.
|
8
|
+
def initialize(route_map:, base_dir:)
|
9
|
+
@routes = route_map
|
10
|
+
Yokunai::Config.base_dir = base_dir
|
11
|
+
Yokunai::Config.populate(ENV["YOKUNAI_ENV"] || "development")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Route a request to the correct controller based on the given data.
|
15
|
+
#
|
16
|
+
# @param path [String] the domain-relative path being requested.
|
17
|
+
# @param env [Rack::Env] the full Rack environment
|
18
|
+
# @return [Array] a Rack-compatible response array.
|
19
|
+
def call(env)
|
20
|
+
route = @routes.map do |exp, meta|
|
21
|
+
next unless matches = env["PATH_INFO"].match(exp)
|
22
|
+
meta.merge({captures: matches})
|
23
|
+
end.compact.first
|
24
|
+
|
25
|
+
unless route
|
26
|
+
return Yokunai::ErrorsController.new(env).not_found
|
27
|
+
end
|
28
|
+
|
29
|
+
request_method = env["REQUEST_METHOD"]
|
30
|
+
if route[:methods].include?(request_method)
|
31
|
+
Object.const_get(route[:class])
|
32
|
+
.new(env, route[:captures])
|
33
|
+
.public_send(request_method.downcase)
|
34
|
+
else
|
35
|
+
Yokunai::ErrorsController.new(env).unsupported_method
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Yokunai
|
2
|
+
# A centralized way to get configuration options. Reads values out of
|
3
|
+
# `config.yml` (or whatever name is passed to `populate`).
|
4
|
+
class Config
|
5
|
+
|
6
|
+
# Set the base directory path.
|
7
|
+
#
|
8
|
+
# @param base_dir [String]
|
9
|
+
# @return [String]
|
10
|
+
def self.base_dir=(dir)
|
11
|
+
@@base_dir = dir
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the base directory path.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def self.base_dir
|
18
|
+
@@base_dir
|
19
|
+
end
|
20
|
+
|
21
|
+
# Loads the config into memory from disk. Called automatically on the first
|
22
|
+
# cold-get, but can be called manually to warm up.
|
23
|
+
#
|
24
|
+
# @param name [String] the name of the confg file to load. If not provided,
|
25
|
+
# will use the value of YOKUNAI_ENV.
|
26
|
+
def self.populate(name = nil)
|
27
|
+
name ||= ENV["YOKUNAI_ENV"]
|
28
|
+
@@config = YAML.load_file(File.join(@@base_dir, "config", "#{name}.yml"))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Get the value of a config option
|
32
|
+
#
|
33
|
+
# @param key [String] the key to fetch
|
34
|
+
# @return [any] whatever key contains
|
35
|
+
def self.get(key)
|
36
|
+
populate unless class_variable_defined?(:@@config)
|
37
|
+
@@config[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Yokunai
|
2
|
+
class ErrorsController < AbstractController
|
3
|
+
|
4
|
+
def not_found
|
5
|
+
respond(code: 404, body: "Error 404: Could not locate resource.")
|
6
|
+
end
|
7
|
+
|
8
|
+
def unsupported_method
|
9
|
+
respond(code: 405, body: "Error 405: Unsupported HTTP method.")
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
data/lib/yokunai/mime.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Yokunai
|
2
|
+
# Provides a light wrapper around MimeMagic to make it a little easier to use.
|
3
|
+
class Mime
|
4
|
+
|
5
|
+
# Detect the mime type from the given path string.
|
6
|
+
#
|
7
|
+
# @param path [String] the path
|
8
|
+
# @return [String] the mime type string
|
9
|
+
def self.detect_from_path(path, fallback = "text/plain")
|
10
|
+
mime = MimeMagic.by_path(path)
|
11
|
+
mime ? mime.type : fallback
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Yokunai
|
2
|
+
# Provides a clean binding and basic API to add variables to it. Meant to be
|
3
|
+
# used only for template rendering.
|
4
|
+
class RenderContext
|
5
|
+
|
6
|
+
# Instantiate a new instance and create local variables for the key/value
|
7
|
+
# pairs in the given hash.
|
8
|
+
#
|
9
|
+
# @param context [Hash] variables to set on the local binding
|
10
|
+
# @return [Yokunai::RenderContext]
|
11
|
+
def initialize(context = {})
|
12
|
+
@binding = binding
|
13
|
+
|
14
|
+
context.each do |key, value|
|
15
|
+
@binding.local_variable_set(key, value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the binding of this class, with the variables that have been set.
|
20
|
+
def get_binding
|
21
|
+
@binding
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sort of a hack, since if a var doesn't exist it will try and call a method
|
25
|
+
# and so we'll end up here...
|
26
|
+
def method_missing(m, *args, &block)
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Yokunai
|
2
|
+
class StaticController < AbstractController
|
3
|
+
|
4
|
+
def get
|
5
|
+
asset_dir = File.join(Yokunai::Config.base_dir, Yokunai::Config.get("asset_dir"))
|
6
|
+
asset_file = File.join(asset_dir, @captures[:name])
|
7
|
+
|
8
|
+
if File.exist?(asset_file)
|
9
|
+
mime = Yokunai::Mime.detect_from_path(asset_file)
|
10
|
+
asset_body = File.read(asset_file)
|
11
|
+
|
12
|
+
respond(body: asset_body, headers: {"Content-Type" => mime})
|
13
|
+
else
|
14
|
+
respond_error(:not_found)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Yokunai
|
2
|
+
# Renders ERB templates.
|
3
|
+
class Template
|
4
|
+
|
5
|
+
FALLBACK_EMPTY_LAYOUT = "<%= partial %>".freeze
|
6
|
+
|
7
|
+
def initialize(template_path: nil)
|
8
|
+
@template_path = template_path || File.join(Yokunai::Config.base_dir, Yokunai::Config.get("template_dir"))
|
9
|
+
@raw_layout = get_layout
|
10
|
+
end
|
11
|
+
|
12
|
+
# Render an ERB template with the given name, and cache the result for
|
13
|
+
# subsequent calls.
|
14
|
+
#
|
15
|
+
# @param template [String] the name of a template
|
16
|
+
# @param context [Hash] key/value pairs of variables to bind the template
|
17
|
+
# @return [String] the ERB render result
|
18
|
+
def render(template, context = {})
|
19
|
+
return nil unless exist?(template)
|
20
|
+
|
21
|
+
path = File.join(@template_path, template + ".erb")
|
22
|
+
layout_context = context.merge({
|
23
|
+
partial: ERB.new(File.read(path)).result(Yokunai::RenderContext.new(context).get_binding)
|
24
|
+
})
|
25
|
+
|
26
|
+
ERB.new(@raw_layout).result(Yokunai::RenderContext.new(layout_context).get_binding)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Checks if a template exists. Useful for gating before rendering.
|
30
|
+
#
|
31
|
+
# @param template [String] the template name
|
32
|
+
# @return [Boolean]
|
33
|
+
def exist?(template)
|
34
|
+
File.exist?(File.join(@template_path, template + ".erb"))
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def get_layout
|
40
|
+
layout_path = File.join(@template_path, "layout.erb")
|
41
|
+
if File.exist?(layout_path)
|
42
|
+
File.read(layout_path)
|
43
|
+
else
|
44
|
+
FALLBACK_EMPTY_LAYOUT
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yokunai
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Blackie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mimemagic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
description: '["Alex Blackie"]'
|
42
|
+
email: alex@alexblackie.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- README.markdown
|
48
|
+
- lib/yokunai.rb
|
49
|
+
- lib/yokunai/abstract_controller.rb
|
50
|
+
- lib/yokunai/application.rb
|
51
|
+
- lib/yokunai/config.rb
|
52
|
+
- lib/yokunai/errors_controller.rb
|
53
|
+
- lib/yokunai/mime.rb
|
54
|
+
- lib/yokunai/render_context.rb
|
55
|
+
- lib/yokunai/static_controller.rb
|
56
|
+
- lib/yokunai/template.rb
|
57
|
+
homepage: https://github.com/alexblackie/yokunai
|
58
|
+
licenses:
|
59
|
+
- BSD-3-Clause
|
60
|
+
metadata: {}
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 2.6.11
|
78
|
+
signing_key:
|
79
|
+
specification_version: 4
|
80
|
+
summary: Helps you make really simple dynamic Internet Web Pages.
|
81
|
+
test_files: []
|