jun 0.0.1 → 0.3.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.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +13 -0
  3. data/Gemfile +3 -1
  4. data/LICENSE.txt +1 -1
  5. data/README.md +18 -20
  6. data/Rakefile +13 -3
  7. data/bin/console +3 -9
  8. data/exe/jun +7 -0
  9. data/jun.gemspec +28 -18
  10. data/lib/jun/action_controller/base.rb +16 -0
  11. data/lib/jun/action_controller/callbacks.rb +46 -0
  12. data/lib/jun/action_controller/metal.rb +22 -0
  13. data/lib/jun/action_controller/redirecting.rb +17 -0
  14. data/lib/jun/action_controller/rendering.rb +84 -0
  15. data/lib/jun/action_dispatch/routing/mapper.rb +54 -0
  16. data/lib/jun/action_dispatch/routing/route_set.rb +112 -0
  17. data/lib/jun/action_dispatch/routing/welcome.html.erb +41 -0
  18. data/lib/jun/action_view/base.rb +26 -0
  19. data/lib/jun/action_view/helpers/url_helper.rb +13 -0
  20. data/lib/jun/action_view/helpers.rb +11 -0
  21. data/lib/jun/active_record/base.rb +52 -0
  22. data/lib/jun/active_record/migration.rb +76 -0
  23. data/lib/jun/active_record/migrator.rb +80 -0
  24. data/lib/jun/active_record/persistence.rb +60 -0
  25. data/lib/jun/active_record/relation.rb +42 -0
  26. data/lib/jun/active_support/core_ext/array/access.rb +27 -0
  27. data/lib/jun/active_support/core_ext/array/conversion.rb +10 -0
  28. data/lib/jun/active_support/core_ext/hash/transformation.rb +19 -0
  29. data/lib/jun/active_support/core_ext/string/access.rb +18 -0
  30. data/lib/jun/active_support/core_ext/string/inflector.rb +119 -0
  31. data/lib/jun/active_support/core_ext.rb +5 -0
  32. data/lib/jun/active_support/dependencies.rb +31 -0
  33. data/lib/jun/application.rb +45 -0
  34. data/lib/jun/cli/commands/base.rb +17 -0
  35. data/lib/jun/cli/commands/db/create.rb +35 -0
  36. data/lib/jun/cli/commands/db/drop.rb +18 -0
  37. data/lib/jun/cli/commands/db/migrate.rb +15 -0
  38. data/lib/jun/cli/commands/db/rollback.rb +15 -0
  39. data/lib/jun/cli/commands/db/schema/dump.rb +24 -0
  40. data/lib/jun/cli/commands/db/schema/load.rb +43 -0
  41. data/lib/jun/cli/commands/db/seed.rb +19 -0
  42. data/lib/jun/cli/commands/generate/migration.rb +27 -0
  43. data/lib/jun/cli/commands/new.rb +62 -0
  44. data/lib/jun/cli/commands/server.rb +17 -0
  45. data/lib/jun/cli/commands/version.rb +13 -0
  46. data/lib/jun/cli/generator_templates/migration.rb.erb +11 -0
  47. data/lib/jun/cli/generator_templates/new_app/Gemfile.erb +9 -0
  48. data/lib/jun/cli/generator_templates/new_app/README.md.erb +3 -0
  49. data/lib/jun/cli/generator_templates/new_app/app/controllers/application_controller.rb.erb +4 -0
  50. data/lib/jun/cli/generator_templates/new_app/app/helpers/application_helper.rb.erb +4 -0
  51. data/lib/jun/cli/generator_templates/new_app/app/models/application_record.rb.erb +4 -0
  52. data/lib/jun/cli/generator_templates/new_app/app/views/layouts/application.html.erb.erb +11 -0
  53. data/lib/jun/cli/generator_templates/new_app/bin/console.erb +8 -0
  54. data/lib/jun/cli/generator_templates/new_app/config/application.rb.erb +12 -0
  55. data/lib/jun/cli/generator_templates/new_app/config/environment.rb.erb +7 -0
  56. data/lib/jun/cli/generator_templates/new_app/config/routes.rb.erb +4 -0
  57. data/lib/jun/cli/generator_templates/new_app/config.ru.erb +6 -0
  58. data/lib/jun/cli/generator_templates/new_app/db/seeds.rb.erb +9 -0
  59. data/lib/jun/cli.rb +33 -0
  60. data/lib/jun/connection_adapters/sqlite_adapter.rb +19 -0
  61. data/lib/jun/version.rb +3 -1
  62. data/lib/jun.rb +50 -2
  63. metadata +129 -24
  64. data/.gitignore +0 -10
  65. data/.rspec +0 -2
  66. data/.travis.yml +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a0443a05754d92c8facf0a4497603884ed58833f
4
- data.tar.gz: ea40d29cd50879cb73bbdd3130ec84684980d097
2
+ SHA256:
3
+ metadata.gz: 70a404db6997b3aa8f1a682cfaac1d401e5a78aa2c6344645aed5819f9a7c617
4
+ data.tar.gz: 6ef4729b48e1f780a2004515c00a2ee6efbea77cade3522d942d05f602b509b8
5
5
  SHA512:
6
- metadata.gz: c282aabb2f3057321548c32de8058e65b13d433f885e71fc5a3ca405351cb609a8a98bc288162f0c8a67591ac2106d63c55d121cd6fdf6c3bc376e0d3f2001c5
7
- data.tar.gz: 8fdd848aad1a8cdafd410e0090b4b85a6a09f7b63e0dee7fbd072ed08890e0b2ceebd989a1aca5665458cda3e8a7ac0c2292c78cd0ee6f7ff236888f9ef604ed
6
+ metadata.gz: fa512bde19df4fb617e5d49f6336433cff2ed31ba3dcfba942ed4473b8136a943ff92afcf0fb0a9dcf3f9ebc795f796393e0340a23bbfec183f7987cab90b841
7
+ data.tar.gz: 5bf62a8d2e2cfaedfce5435b44bc54388f2792b48459b028436c4baf40c8971a9bb6a53c3e63eda0126087142da5742cee470cb69890c50c2d23af228f883cad
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  gemspec
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Zoran Pesic
3
+ Copyright (c) 2021 Zoran Pesic
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,37 +1,35 @@
1
1
  # Jun
2
2
 
3
- Jun is a simple web application framework inspired by Rails, built with the hopes of learning much more about Rails internals in the process.
3
+ [![Gem Version](https://badge.fury.io/rb/jun.svg)](https://badge.fury.io/rb/jun)
4
4
 
5
- ## Installation
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
- Add this line to your application's Gemfile:
7
+ ## Getting Started
8
8
 
9
- ```ruby
10
- gem 'jun'
11
- ```
9
+ Install the gem:
12
10
 
13
- And then execute:
11
+ ```
12
+ $ gem install jun
13
+ ```
14
14
 
15
- $ bundle
15
+ Then, create a new Jun application:
16
16
 
17
- Or install it yourself as:
17
+ ```
18
+ $ jun new my_app
19
+ ```
18
20
 
19
- $ gem install jun
21
+ Change directory into `my_app` and start up the server:
20
22
 
21
- ## Usage
23
+ ```
24
+ $ cd my_app
25
+ $ bin/jun server
26
+ ```
22
27
 
23
- TODO: Write usage instructions here
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 spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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 "rspec/core/rake_task"
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
- RSpec::Core::RakeTask.new(:spec)
14
+ RuboCop::RakeTask.new
5
15
 
6
- task :default => :spec
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
- # You can add fixtures and/or initialization code here to make experimenting
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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "jun"
5
+ require "jun/cli"
6
+
7
+ Jun::CLI.process_command(ARGV)
data/jun.gemspec CHANGED
@@ -1,25 +1,35 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
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 = "jun"
8
- spec.version = Jun::VERSION
9
- spec.authors = ["Zoran"]
10
- spec.email = ["zoran1991@gmail.com"]
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.summary = %q{A simple Ruby web framework.}
13
- spec.description = %q{A simple web framework inspired by Rails, built with the hopes of learning more about Rails internals in the process.}
14
- spec.homepage = "https://github.com/zokioki/jun"
15
- spec.license = "MIT"
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.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
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.add_development_dependency "bundler", "~> 1.11"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "rspec", "~> 3.0"
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
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "helpers/url_helper"
4
+
5
+ module Jun
6
+ module ActionView
7
+ module Helpers
8
+ include Jun::ActionView::Helpers::UrlHelper
9
+ end
10
+ end
11
+ end