jun 0.0.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +13 -0
- data/Gemfile +3 -1
- data/LICENSE.txt +1 -1
- data/README.md +18 -20
- data/Rakefile +13 -3
- data/bin/console +3 -9
- data/exe/jun +7 -0
- data/jun.gemspec +28 -18
- data/lib/jun/action_controller/base.rb +16 -0
- data/lib/jun/action_controller/callbacks.rb +46 -0
- data/lib/jun/action_controller/metal.rb +22 -0
- data/lib/jun/action_controller/redirecting.rb +17 -0
- data/lib/jun/action_controller/rendering.rb +84 -0
- data/lib/jun/action_dispatch/routing/mapper.rb +54 -0
- data/lib/jun/action_dispatch/routing/route_set.rb +112 -0
- data/lib/jun/action_dispatch/routing/welcome.html.erb +41 -0
- data/lib/jun/action_view/base.rb +26 -0
- data/lib/jun/action_view/helpers/url_helper.rb +13 -0
- data/lib/jun/action_view/helpers.rb +11 -0
- data/lib/jun/active_record/base.rb +52 -0
- data/lib/jun/active_record/migration.rb +76 -0
- data/lib/jun/active_record/migrator.rb +80 -0
- data/lib/jun/active_record/persistence.rb +60 -0
- data/lib/jun/active_record/relation.rb +42 -0
- data/lib/jun/active_support/core_ext/array/access.rb +27 -0
- data/lib/jun/active_support/core_ext/array/conversion.rb +10 -0
- data/lib/jun/active_support/core_ext/hash/transformation.rb +19 -0
- data/lib/jun/active_support/core_ext/string/access.rb +18 -0
- data/lib/jun/active_support/core_ext/string/inflector.rb +119 -0
- data/lib/jun/active_support/core_ext.rb +5 -0
- data/lib/jun/active_support/dependencies.rb +31 -0
- data/lib/jun/application.rb +45 -0
- data/lib/jun/cli/commands/base.rb +17 -0
- data/lib/jun/cli/commands/db/create.rb +35 -0
- data/lib/jun/cli/commands/db/drop.rb +18 -0
- data/lib/jun/cli/commands/db/migrate.rb +15 -0
- data/lib/jun/cli/commands/db/rollback.rb +15 -0
- data/lib/jun/cli/commands/db/schema/dump.rb +24 -0
- data/lib/jun/cli/commands/db/schema/load.rb +43 -0
- data/lib/jun/cli/commands/db/seed.rb +19 -0
- data/lib/jun/cli/commands/generate/migration.rb +27 -0
- data/lib/jun/cli/commands/new.rb +62 -0
- data/lib/jun/cli/commands/server.rb +17 -0
- data/lib/jun/cli/commands/version.rb +13 -0
- data/lib/jun/cli/generator_templates/migration.rb.erb +11 -0
- data/lib/jun/cli/generator_templates/new_app/Gemfile.erb +9 -0
- data/lib/jun/cli/generator_templates/new_app/README.md.erb +3 -0
- data/lib/jun/cli/generator_templates/new_app/app/controllers/application_controller.rb.erb +4 -0
- data/lib/jun/cli/generator_templates/new_app/app/helpers/application_helper.rb.erb +4 -0
- data/lib/jun/cli/generator_templates/new_app/app/models/application_record.rb.erb +4 -0
- data/lib/jun/cli/generator_templates/new_app/app/views/layouts/application.html.erb.erb +11 -0
- data/lib/jun/cli/generator_templates/new_app/bin/console.erb +8 -0
- data/lib/jun/cli/generator_templates/new_app/config/application.rb.erb +12 -0
- data/lib/jun/cli/generator_templates/new_app/config/environment.rb.erb +7 -0
- data/lib/jun/cli/generator_templates/new_app/config/routes.rb.erb +4 -0
- data/lib/jun/cli/generator_templates/new_app/config.ru.erb +6 -0
- data/lib/jun/cli/generator_templates/new_app/db/seeds.rb.erb +9 -0
- data/lib/jun/cli.rb +33 -0
- data/lib/jun/connection_adapters/sqlite_adapter.rb +19 -0
- data/lib/jun/version.rb +3 -1
- data/lib/jun.rb +50 -2
- metadata +129 -24
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 70a404db6997b3aa8f1a682cfaac1d401e5a78aa2c6344645aed5819f9a7c617
|
4
|
+
data.tar.gz: 6ef4729b48e1f780a2004515c00a2ee6efbea77cade3522d942d05f602b509b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa512bde19df4fb617e5d49f6336433cff2ed31ba3dcfba942ed4473b8136a943ff92afcf0fb0a9dcf3f9ebc795f796393e0340a23bbfec183f7987cab90b841
|
7
|
+
data.tar.gz: 5bf62a8d2e2cfaedfce5435b44bc54388f2792b48459b028436c4baf40c8971a9bb6a53c3e63eda0126087142da5742cee470cb69890c50c2d23af228f883cad
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,37 +1,35 @@
|
|
1
1
|
# Jun
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/jun.svg)](https://badge.fury.io/rb/jun)
|
4
4
|
|
5
|
-
|
5
|
+
Jun is a simple, [Rails](https://github.com/rails/rails)-inspired web application framework. This is a rough implementation, built with the goal of learning more about Rails internals. Not meant for production use.
|
6
6
|
|
7
|
-
|
7
|
+
## Getting Started
|
8
8
|
|
9
|
-
|
10
|
-
gem 'jun'
|
11
|
-
```
|
9
|
+
Install the gem:
|
12
10
|
|
13
|
-
|
11
|
+
```
|
12
|
+
$ gem install jun
|
13
|
+
```
|
14
14
|
|
15
|
-
|
15
|
+
Then, create a new Jun application:
|
16
16
|
|
17
|
-
|
17
|
+
```
|
18
|
+
$ jun new my_app
|
19
|
+
```
|
18
20
|
|
19
|
-
|
21
|
+
Change directory into `my_app` and start up the server:
|
20
22
|
|
21
|
-
|
23
|
+
```
|
24
|
+
$ cd my_app
|
25
|
+
$ bin/jun server
|
26
|
+
```
|
22
27
|
|
23
|
-
|
28
|
+
Visit `http://localhost:6291` to view your app.
|
24
29
|
|
25
30
|
## Development
|
26
31
|
|
27
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
28
|
-
|
29
|
-
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).
|
30
|
-
|
31
|
-
## Contributing
|
32
|
-
|
33
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/jun.
|
34
|
-
|
32
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can run `bin/console` for an interactive prompt that will allow you to experiment.
|
35
33
|
|
36
34
|
## License
|
37
35
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "bundler/gem_tasks"
|
2
|
-
require "
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
require "rubocop/rake_task"
|
3
13
|
|
4
|
-
|
14
|
+
RuboCop::RakeTask.new
|
5
15
|
|
6
|
-
task :
|
16
|
+
task default: %i[test rubocop]
|
data/bin/console
CHANGED
@@ -1,14 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require "bundler/setup"
|
5
|
+
require "pry"
|
4
6
|
require "jun"
|
5
7
|
|
6
|
-
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|
8
|
+
Pry.start
|
data/exe/jun
ADDED
data/jun.gemspec
CHANGED
@@ -1,25 +1,35 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
require 'jun/version'
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/jun/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name
|
8
|
-
spec.version
|
9
|
-
spec.authors
|
10
|
-
spec.email
|
6
|
+
spec.name = "jun"
|
7
|
+
spec.version = Jun::VERSION
|
8
|
+
spec.authors = ["Zoran"]
|
9
|
+
spec.email = ["zspesic@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "A simple Ruby web framework."
|
12
|
+
spec.description = "A simple web framework inspired by Rails. Not meant for production use."
|
13
|
+
spec.homepage = "https://github.com/zokioki/jun"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
11
16
|
|
12
|
-
spec.
|
13
|
-
|
14
|
-
|
15
|
-
|
17
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
20
|
+
end
|
21
|
+
end
|
16
22
|
|
17
|
-
spec.
|
18
|
-
spec.
|
19
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
20
25
|
spec.require_paths = ["lib"]
|
21
26
|
|
22
|
-
spec.
|
23
|
-
spec.
|
24
|
-
spec.
|
27
|
+
spec.add_runtime_dependency "rack", "~> 2.2"
|
28
|
+
spec.add_runtime_dependency "tilt", "~> 2.0"
|
29
|
+
spec.add_runtime_dependency "sqlite3", "~> 1.4"
|
30
|
+
|
31
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
32
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
33
|
+
spec.add_development_dependency "rubocop", "~> 1.21"
|
34
|
+
spec.add_development_dependency "pry", "~> 0.14"
|
25
35
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "metal"
|
4
|
+
require_relative "callbacks"
|
5
|
+
require_relative "rendering"
|
6
|
+
require_relative "redirecting"
|
7
|
+
|
8
|
+
module Jun
|
9
|
+
module ActionController
|
10
|
+
class Base < Metal
|
11
|
+
include Jun::ActionController::Callbacks
|
12
|
+
include Jun::ActionController::Rendering
|
13
|
+
include Jun::ActionController::Redirecting
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActionController
|
5
|
+
module Callbacks
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def handle_response(action)
|
11
|
+
self.class.before_actions.each do |callback|
|
12
|
+
callback.call(self) if callback.match?(action)
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def before_action(method_name, options = {})
|
20
|
+
before_actions << Callback.new(method_name, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def before_actions
|
24
|
+
@before_actions ||= []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Callback
|
29
|
+
def initialize(method_name, options)
|
30
|
+
@method_name = method_name
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
def match?(action)
|
35
|
+
return true unless @options[:only]&.any?
|
36
|
+
|
37
|
+
@options[:only].include?(action.to_sym)
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(controller)
|
41
|
+
controller.send(@method_name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActionController
|
5
|
+
class Metal
|
6
|
+
attr_accessor :request, :response
|
7
|
+
|
8
|
+
def handle_response(action)
|
9
|
+
public_send(action)
|
10
|
+
render(action) unless response_rendered?
|
11
|
+
|
12
|
+
response
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def response_rendered?
|
18
|
+
!!@_response_rendered
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActionController
|
5
|
+
module Redirecting
|
6
|
+
def redirect_to(location, options = {})
|
7
|
+
return if response_rendered?
|
8
|
+
|
9
|
+
response.location = location
|
10
|
+
response.status = options[:status] || 302
|
11
|
+
response.write("<html><body>You are being <a href=\"#{response.location}\">redirected</a>.</body></html>")
|
12
|
+
|
13
|
+
@_response_rendered = true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tilt/erb"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
require_relative "../action_view/base"
|
7
|
+
|
8
|
+
module Jun
|
9
|
+
module ActionController
|
10
|
+
module Rendering
|
11
|
+
def render(options, extra_options = {})
|
12
|
+
return if response_rendered?
|
13
|
+
|
14
|
+
template_name = nil
|
15
|
+
if options.is_a?(Symbol) || options.is_a?(String)
|
16
|
+
template_name = options
|
17
|
+
options = extra_options
|
18
|
+
else
|
19
|
+
template_name = options[:template]
|
20
|
+
end
|
21
|
+
|
22
|
+
options[:layout] = true unless options.key?(:layout)
|
23
|
+
|
24
|
+
if content_type = options[:content_type]
|
25
|
+
response.content_type = content_type.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
if status = options[:status]
|
29
|
+
response.status = status
|
30
|
+
end
|
31
|
+
|
32
|
+
if template_name
|
33
|
+
filepath = views_path.join("#{template_name}.html.erb")
|
34
|
+
template = Tilt::ERBTemplate.new(filepath)
|
35
|
+
context = Jun::ActionView::Base.new(self)
|
36
|
+
body = template.render(context, options[:locals])
|
37
|
+
|
38
|
+
if options[:layout]
|
39
|
+
layout_name = options[:layout].is_a?(String) ? options[:layout] : "application"
|
40
|
+
layout_filepath = layouts_path.join("#{layout_name}.html.erb")
|
41
|
+
layout_template = Tilt::ERBTemplate.new(layout_filepath)
|
42
|
+
|
43
|
+
body = layout_template.render(context) { body }
|
44
|
+
end
|
45
|
+
|
46
|
+
response.write(body)
|
47
|
+
response.content_type ||= "text/html"
|
48
|
+
elsif options[:text]
|
49
|
+
response.write(options[:text])
|
50
|
+
response.content_type ||= "text/plain"
|
51
|
+
elsif options[:json]
|
52
|
+
json = options[:json].is_a?(String) ? options[:json] : JSON.generate(options[:json])
|
53
|
+
response.write(json)
|
54
|
+
response.content_type ||= "application/json"
|
55
|
+
elsif options[:nothing]
|
56
|
+
response.write("")
|
57
|
+
response.content_type ||= "text/plain"
|
58
|
+
end
|
59
|
+
|
60
|
+
@_response_rendered = true
|
61
|
+
end
|
62
|
+
|
63
|
+
def view_assigns
|
64
|
+
reserved_variables = %i[@request @response @_response_rendered]
|
65
|
+
variables = instance_variables - reserved_variables
|
66
|
+
|
67
|
+
variables.reduce({}) do |object, name|
|
68
|
+
object.merge(name[1..-1] => instance_variable_get(name))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def views_path
|
75
|
+
dirname = self.class.name.sub(/Controller\z/, "").underscore
|
76
|
+
Jun.root.join("app/views/#{dirname}")
|
77
|
+
end
|
78
|
+
|
79
|
+
def layouts_path
|
80
|
+
Jun.root.join("app/views/layouts")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActionDispatch
|
5
|
+
module Routing
|
6
|
+
class Mapper
|
7
|
+
def initialize(route_set)
|
8
|
+
@route_set = route_set
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(path, to:, as: nil)
|
12
|
+
add_route(:get, path, to: to, as: as)
|
13
|
+
end
|
14
|
+
|
15
|
+
def post(path, to:, as: nil)
|
16
|
+
add_route(:post, path, to: to, as: as)
|
17
|
+
end
|
18
|
+
|
19
|
+
def patch(path, to:, as: nil)
|
20
|
+
add_route(:patch, path, to: to, as: as)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(path, to:, as: nil)
|
24
|
+
add_route(:delete, path, to: to, as: as)
|
25
|
+
end
|
26
|
+
|
27
|
+
def root(to:)
|
28
|
+
get "/", to: to, as: "root"
|
29
|
+
end
|
30
|
+
|
31
|
+
def resources(plural_name)
|
32
|
+
get "/#{plural_name}", to: "#{plural_name}#index", as: plural_name.to_s
|
33
|
+
get "/#{plural_name}/new", to: "#{plural_name}#new", as: "new_#{plural_name.to_s.singularize}"
|
34
|
+
post "/#{plural_name}", to: "#{plural_name}#create"
|
35
|
+
get "/#{plural_name}/:id", to: "#{plural_name}#show", as: plural_name.to_s.singularize
|
36
|
+
get "/#{plural_name}/:id/edit", to: "#{plural_name}#edit", as: "edit_#{plural_name.to_s.singularize}"
|
37
|
+
patch "/#{plural_name}/:id", to: "#{plural_name}#update"
|
38
|
+
delete "/#{plural_name}/:id", to: "#{plural_name}#destroy"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def add_route(method, path, to:, as: nil)
|
44
|
+
method = method.to_s.upcase
|
45
|
+
controller, action = to.split("#")
|
46
|
+
path = path.to_s.start_with?("/") ? path.to_s : "/#{path}"
|
47
|
+
as ||= path.sub("/", "")
|
48
|
+
|
49
|
+
@route_set.add_route(method, path, controller, action, as)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "mapper"
|
4
|
+
|
5
|
+
module Jun
|
6
|
+
module ActionDispatch
|
7
|
+
module Routing
|
8
|
+
class Route < Struct.new(:method, :path, :controller, :action, :name)
|
9
|
+
def match?(request)
|
10
|
+
request.request_method == method && path_regex.match?(request.path_info)
|
11
|
+
end
|
12
|
+
|
13
|
+
def dispatch(request)
|
14
|
+
controller = controller_class.new
|
15
|
+
controller.request = request
|
16
|
+
controller.response = Rack::Response.new
|
17
|
+
controller.handle_response(action)
|
18
|
+
controller.response.finish
|
19
|
+
end
|
20
|
+
|
21
|
+
def controller_class
|
22
|
+
class_name = "#{controller.camelize}Controller"
|
23
|
+
Object.const_get(class_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def path_regex
|
27
|
+
path_string_for_regex = path.gsub(/:\w+/) { |match| "(?<#{match.delete(":")}>\\w+)" }
|
28
|
+
Regexp.new("^#{path_string_for_regex}\/?$")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class RouteSet
|
33
|
+
def initialize
|
34
|
+
@routes = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(env)
|
38
|
+
return welcome_response if @routes.none?
|
39
|
+
|
40
|
+
request = Rack::Request.new(env)
|
41
|
+
|
42
|
+
if route = find_route(request)
|
43
|
+
route.dispatch(request)
|
44
|
+
else
|
45
|
+
not_found_response
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_route(*args)
|
50
|
+
route = Route.new(*args)
|
51
|
+
@routes.push(route)
|
52
|
+
define_url_helper(route)
|
53
|
+
|
54
|
+
route
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_route(request)
|
58
|
+
@routes.detect { |route| route.match?(request) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def draw(&block)
|
62
|
+
mapper = Jun::ActionDispatch::Routing::Mapper.new(self)
|
63
|
+
mapper.instance_eval(&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def url_helpers
|
67
|
+
@url_helpers ||= Module.new.extend(url_helpers_module).include(url_helpers_module)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def url_helpers_module
|
73
|
+
@url_helpers_module ||= Module.new
|
74
|
+
end
|
75
|
+
|
76
|
+
def define_url_helper(route)
|
77
|
+
path_name = "#{route.name}_path"
|
78
|
+
|
79
|
+
url_helpers_module.define_method(path_name) do |*args|
|
80
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
81
|
+
path = route.path
|
82
|
+
path_tokens = path.scan(/:\w+/)
|
83
|
+
|
84
|
+
path_tokens.each.with_index do |token, index|
|
85
|
+
path.sub!(token, args[index])
|
86
|
+
end
|
87
|
+
|
88
|
+
path
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def not_found_response
|
93
|
+
response = Rack::Response.new
|
94
|
+
response.content_type = "text/plain"
|
95
|
+
response.status = 404
|
96
|
+
response.write("Not found")
|
97
|
+
response.finish
|
98
|
+
end
|
99
|
+
|
100
|
+
def welcome_response
|
101
|
+
template_filepath = File.expand_path("welcome.html.erb", __dir__)
|
102
|
+
template = Tilt::ERBTemplate.new(template_filepath)
|
103
|
+
|
104
|
+
response = Rack::Response.new
|
105
|
+
response.content_type = "text/html"
|
106
|
+
response.write(template.render)
|
107
|
+
response.finish
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<title>Welcome to Jun!</title>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
|
8
|
+
<style>
|
9
|
+
body {
|
10
|
+
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
11
|
+
background-color: #fff3f3;
|
12
|
+
color: #7d3737;
|
13
|
+
}
|
14
|
+
|
15
|
+
::selection {
|
16
|
+
background-color: #ffbdb6;
|
17
|
+
}
|
18
|
+
|
19
|
+
.root {
|
20
|
+
margin-top: 4rem;
|
21
|
+
text-align: center;
|
22
|
+
}
|
23
|
+
|
24
|
+
.sunspot {
|
25
|
+
width: 100px;
|
26
|
+
height: 100px;
|
27
|
+
border-radius: 50%;
|
28
|
+
background-color: #fc7e70;
|
29
|
+
margin: 2rem auto;
|
30
|
+
}
|
31
|
+
</style>
|
32
|
+
</head>
|
33
|
+
|
34
|
+
<body>
|
35
|
+
<div class="root">
|
36
|
+
<div class="sunspot"></div>
|
37
|
+
<h1>Welcome to Jun!</h1>
|
38
|
+
<small>Jun v<%= Jun::VERSION %> | Ruby v<%= RUBY_VERSION %> (<%= RUBY_PLATFORM %>)</small>
|
39
|
+
</div>
|
40
|
+
</body>
|
41
|
+
</html>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "helpers"
|
4
|
+
|
5
|
+
module Jun
|
6
|
+
module ActionView
|
7
|
+
class Base
|
8
|
+
include Jun::ActionView::Helpers
|
9
|
+
|
10
|
+
attr_reader :controller
|
11
|
+
|
12
|
+
def initialize(controller)
|
13
|
+
@controller = controller
|
14
|
+
assign_instance_variables
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def assign_instance_variables
|
20
|
+
controller.view_assigns.each do |name, value|
|
21
|
+
instance_variable_set("@#{name}", value)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActionView
|
5
|
+
module Helpers
|
6
|
+
module UrlHelper
|
7
|
+
def link_to(title, url, options = {})
|
8
|
+
%Q{<a href="#{url}"#{ "class=\"#{options[:class]}\"" if options[:class] }>#{title}</a>}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|