jun 0.0.1 → 0.1.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 +4 -9
- 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 +20 -0
- data/lib/jun/action_controller/rendering.rb +88 -0
- data/lib/jun/action_dispatch/routing/mapper.rb +29 -0
- data/lib/jun/action_dispatch/routing/route_set.rb +65 -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.rb +41 -0
- data/lib/jun/active_support/dependencies.rb +31 -0
- data/lib/jun/active_support/inflector.rb +119 -0
- data/lib/jun/application.rb +18 -0
- data/lib/jun/cli/commands/base.rb +17 -0
- data/lib/jun/cli/commands/db/create.rb +15 -0
- data/lib/jun/cli/commands/db/drop.rb +15 -0
- data/lib/jun/cli/commands/db/migrate.rb +15 -0
- data/lib/jun/cli/commands/new.rb +57 -0
- data/lib/jun/cli/commands/server.rb +17 -0
- data/lib/jun/cli/commands/version.rb +13 -0
- data/lib/jun/cli/generators/new/Gemfile.erb +9 -0
- data/lib/jun/cli/generators/new/README.md.erb +3 -0
- data/lib/jun/cli/generators/new/app/controllers/application_controller.rb.erb +4 -0
- data/lib/jun/cli/generators/new/app/helpers/application_helper.rb.erb +4 -0
- data/lib/jun/cli/generators/new/app/views/layouts/application.html.erb.erb +11 -0
- data/lib/jun/cli/generators/new/config/application.rb.erb +18 -0
- data/lib/jun/cli/generators/new/config/routes.rb.erb +4 -0
- data/lib/jun/cli/generators/new/config.ru.erb +7 -0
- data/lib/jun/cli/generators/new/db/app.db.erb +0 -0
- data/lib/jun/cli.rb +28 -0
- data/lib/jun/connection_adapters/sqlite_adapter.rb +19 -0
- data/lib/jun/version.rb +3 -1
- data/lib/jun.rb +39 -2
- metadata +107 -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: a23824148b7546d35ec51bc05200095f47bb5ffcb3604597a2c1606b893ae1c5
|
4
|
+
data.tar.gz: e6e03e5e69d3cebd025a9194de2488539a096c9864fbb53b720f43c5f7d5f68b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a50ed3137ed9926647d32e534c908a1f6bca9e235a6de53f6969932be97dca05bbb44dec9bffd4d3de3b1b5d96cdcc227b2c0728a176a3433dd98cb85e0b403
|
7
|
+
data.tar.gz: 78931eee706054b9b6f85585b3dd0af3055a9ff6641439802aea254c1f3c0c23292802e4c40147f17bea8e72b6d6e1ce5fe3b85455260ef559ca9dedeadcc40e
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Jun
|
2
2
|
|
3
|
-
Jun is a simple web application framework
|
3
|
+
Jun is a simple, Rails-inspired web application framework. It is built with the goal of learning more about Rails internals. Not meant for production use.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -20,18 +20,13 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
_TBD_
|
24
24
|
|
25
25
|
## Development
|
26
26
|
|
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.
|
27
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
35
30
|
|
36
31
|
## License
|
37
32
|
|
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."
|
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"
|
28
|
+
spec.add_runtime_dependency "tilt"
|
29
|
+
spec.add_runtime_dependency "sqlite3"
|
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,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "rendering"
|
4
|
+
|
5
|
+
module Jun
|
6
|
+
module ActionController
|
7
|
+
class Base
|
8
|
+
include Jun::ActionController::Rendering
|
9
|
+
|
10
|
+
attr_accessor :request, :response
|
11
|
+
|
12
|
+
def handle_response(action)
|
13
|
+
public_send(action)
|
14
|
+
render(action) unless response_rendered?
|
15
|
+
|
16
|
+
response
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,88 @@
|
|
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 response_rendered?
|
75
|
+
!!@_response_rendered
|
76
|
+
end
|
77
|
+
|
78
|
+
def views_path
|
79
|
+
dirname = self.class.name.sub(/Controller\z/, "").underscore
|
80
|
+
Jun.root.join("app/views/#{dirname}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def layouts_path
|
84
|
+
Jun.root.join("app/views/layouts")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,29 @@
|
|
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
|
+
controller, action = to.split("#")
|
13
|
+
path = path.to_s.start_with?("/") ? path.to_s : "/#{path}"
|
14
|
+
|
15
|
+
@route_set.add_route("GET", path, controller, action, as)
|
16
|
+
end
|
17
|
+
|
18
|
+
def root(to:)
|
19
|
+
get "/", to: to, as: "root"
|
20
|
+
end
|
21
|
+
|
22
|
+
def resources(plural_name)
|
23
|
+
get "/#{plural_name}", to: "#{plural_name}#index", as: plural_name.to_s
|
24
|
+
get "/#{plural_name}/new", to: "#{plural_name}#new", as: "new_#{plural_name.to_s.singularize}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,65 @@
|
|
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
|
+
request = Rack::Request.new(env)
|
39
|
+
|
40
|
+
if route = find_route(request)
|
41
|
+
route.dispatch(request)
|
42
|
+
else
|
43
|
+
[404, { "Content-Type" => "text/plain" }, ["Not found"]]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_route(*args)
|
48
|
+
route = Route.new(*args)
|
49
|
+
@routes.push(route)
|
50
|
+
|
51
|
+
route
|
52
|
+
end
|
53
|
+
|
54
|
+
def find_route(request)
|
55
|
+
@routes.detect { |route| route.match?(request) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def draw(&block)
|
59
|
+
mapper = Jun::ActionDispatch::Routing::Mapper.new(self)
|
60
|
+
mapper.instance_eval(&block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -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,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "connection_adapters/sqlite_adapter"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
class Base
|
7
|
+
def initialize(attributes = {})
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(name, *args)
|
12
|
+
if self.class.connection.columns(self.class.table_name).include?(name)
|
13
|
+
@attributes[name]
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.find(id)
|
20
|
+
find_by_sql("SELECT * FROM #{table_name} WHERE id = #{id.to_i} LIMIT 1").first
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.all
|
24
|
+
find_by_sql("SELECT * FROM #{table_name}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.find_by_sql(sql)
|
28
|
+
connection.execute(sql).map do |attributes|
|
29
|
+
new(attributes)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.table_name
|
34
|
+
name.downcase.pluralize
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.connection
|
38
|
+
@@connection ||= SqliteAdapter.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module ActiveSupport
|
5
|
+
module Dependencies
|
6
|
+
extend self
|
7
|
+
|
8
|
+
attr_accessor :autoload_paths
|
9
|
+
self.autoload_paths = []
|
10
|
+
|
11
|
+
def find_file(filename)
|
12
|
+
autoload_paths.each do |path|
|
13
|
+
filepath = File.join(path, "#{filename}.rb")
|
14
|
+
return filepath if File.file?(filepath)
|
15
|
+
end
|
16
|
+
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Object
|
24
|
+
def self.const_missing(constant_name)
|
25
|
+
file = Jun::ActiveSupport::Dependencies.find_file(constant_name.to_s.underscore)
|
26
|
+
super if file.nil?
|
27
|
+
|
28
|
+
require file.sub(/\.rb\z/, "")
|
29
|
+
Object.const_get(constant_name)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Inflector
|
2
|
+
def self.included(base)
|
3
|
+
base.extend ClassMethods
|
4
|
+
end
|
5
|
+
|
6
|
+
PLURAL = {
|
7
|
+
'(quiz)$' => '\1zes',
|
8
|
+
'^(ox)$' => '\1en',
|
9
|
+
'([m|l])ouse$' => '\1ice',
|
10
|
+
'(matr|vert|ind)ix|ex$' => '\1ices',
|
11
|
+
'(x|ch|ss|sh)$' => '\1es',
|
12
|
+
'([^aeiouy]|qu)y$' => '\1ies',
|
13
|
+
'(hive)$' => '\1s',
|
14
|
+
'(?:([^f])fe|([lr])f)$' => '\1\2ves',
|
15
|
+
'(shea|lea|loa|thie)f$' => '\1ves',
|
16
|
+
'sis$' => 'ses',
|
17
|
+
'([ti])um$' => '\1a',
|
18
|
+
'(tomat|potat|ech|her|vet)o$' => '\1oes',
|
19
|
+
'(bu)s$' => '\1ses',
|
20
|
+
'(alias)$' => '\1es',
|
21
|
+
'(octop)us$' => '\1i',
|
22
|
+
'(ax|test)is$' => '\1es',
|
23
|
+
'(us)$' => '\1es',
|
24
|
+
'([^s]+)$' => '\1s'
|
25
|
+
}
|
26
|
+
|
27
|
+
SINGULAR = {
|
28
|
+
'(quiz)zes$' => '\1',
|
29
|
+
'(matr)ices$' => '\1ix',
|
30
|
+
'(vert|ind)ices$' => '\1ex',
|
31
|
+
'^(ox)en$' => '\1',
|
32
|
+
'(alias)es$' => '\1',
|
33
|
+
'(octop|vir)i$' => '\1us',
|
34
|
+
'(cris|ax|test)es$' => '\1is',
|
35
|
+
'(shoe)s$' => '\1',
|
36
|
+
'(o)es$' => '\1',
|
37
|
+
'(bus)es$' => '\1',
|
38
|
+
'([m|l])ice$' => '\1ouse',
|
39
|
+
'(x|ch|ss|sh)es$' => '\1',
|
40
|
+
'(m)ovies$' => '\1ovie',
|
41
|
+
'(s)eries$' => '\1eries',
|
42
|
+
'([^aeiouy]|qu)ies$' => '\1y',
|
43
|
+
'([lr])ves$' => '\1f',
|
44
|
+
'(tive)s$' => '\1',
|
45
|
+
'(hive)s$' => '\1',
|
46
|
+
'(li|wi|kni)ves$' => '\1fe',
|
47
|
+
'(shea|loa|lea|thie)ves$'=> '\1f',
|
48
|
+
'(^analy)ses$' => '\1sis',
|
49
|
+
'((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$' => '\1\2sis',
|
50
|
+
'([ti])a$' => '\1um',
|
51
|
+
'(n)ews$' => '\1ews',
|
52
|
+
'(h|bl)ouses$' => '\1ouse',
|
53
|
+
'(corpse)s$' => '\1',
|
54
|
+
'(us)es$' => '\1',
|
55
|
+
's$' => ''
|
56
|
+
}
|
57
|
+
|
58
|
+
UNCHANGEABLE = [
|
59
|
+
'sheep',
|
60
|
+
'fish',
|
61
|
+
'deer',
|
62
|
+
'moose',
|
63
|
+
'series',
|
64
|
+
'species',
|
65
|
+
'money',
|
66
|
+
'rice',
|
67
|
+
'information',
|
68
|
+
'equipment'
|
69
|
+
]
|
70
|
+
|
71
|
+
def pluralize
|
72
|
+
return self if UNCHANGEABLE.include? self
|
73
|
+
|
74
|
+
pattern, replacement = ""
|
75
|
+
PLURAL.each do |k, v|
|
76
|
+
if self.match(k)
|
77
|
+
pattern = Regexp.new(k)
|
78
|
+
replacement = v
|
79
|
+
break
|
80
|
+
end
|
81
|
+
end
|
82
|
+
self.sub(pattern, replacement)
|
83
|
+
end
|
84
|
+
|
85
|
+
def singularize
|
86
|
+
return self if UNCHANGEABLE.include? self
|
87
|
+
|
88
|
+
pattern, replacement = ""
|
89
|
+
SINGULAR.each do |k, v|
|
90
|
+
if self.match(k)
|
91
|
+
pattern = Regexp.new(k)
|
92
|
+
replacement = v
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
self.sub(pattern, replacement)
|
97
|
+
end
|
98
|
+
|
99
|
+
def underscore
|
100
|
+
gsub(/::/, '/').
|
101
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
102
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
103
|
+
tr("-", "_").
|
104
|
+
downcase
|
105
|
+
end
|
106
|
+
|
107
|
+
def camelize
|
108
|
+
sub(/^[a-z\d]*/) { |match| match.capitalize }.
|
109
|
+
gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.
|
110
|
+
gsub("/", "::")
|
111
|
+
end
|
112
|
+
|
113
|
+
module ClassMethods
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class String
|
118
|
+
include Inflector
|
119
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
class Application
|
5
|
+
def self.inherited(subclass)
|
6
|
+
super
|
7
|
+
Jun.app_class = subclass
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
routes.call(env)
|
12
|
+
end
|
13
|
+
|
14
|
+
def routes
|
15
|
+
@routes ||= Jun::ActionDispatch::Routing::RouteSet.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class Base
|
7
|
+
def self.command_name
|
8
|
+
name.sub("Jun::CLI::Commands::", "").underscore.gsub("/", ":")
|
9
|
+
end
|
10
|
+
|
11
|
+
def process(*args)
|
12
|
+
raise NoMethodError, "Subclass must implement method."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module Jun
|
7
|
+
module CLI
|
8
|
+
module Commands
|
9
|
+
class New < Base
|
10
|
+
def process(*args)
|
11
|
+
if Jun.root
|
12
|
+
puts "Already in a Jun app..."
|
13
|
+
else
|
14
|
+
generate_new_app(args.first)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_new_app(app_name)
|
21
|
+
puts "Generating new Jun app (#{app_name})..."
|
22
|
+
|
23
|
+
templates = [
|
24
|
+
"config.ru",
|
25
|
+
"Gemfile",
|
26
|
+
"README.md",
|
27
|
+
"app/controllers/application_controller.rb",
|
28
|
+
"app/helpers/application_helper.rb",
|
29
|
+
"app/views/layouts/application.html.erb",
|
30
|
+
"config/application.rb",
|
31
|
+
"config/routes.rb",
|
32
|
+
"db/app.db"
|
33
|
+
]
|
34
|
+
|
35
|
+
FileUtils.mkdir_p(app_name)
|
36
|
+
|
37
|
+
FileUtils.chdir(app_name) do
|
38
|
+
templates.each do |filepath|
|
39
|
+
template_filepath = File.expand_path("../generators/new/#{filepath}.erb", __dir__)
|
40
|
+
template = Tilt::ERBTemplate.new(template_filepath)
|
41
|
+
template_locals = { app_name: app_name }
|
42
|
+
file_body = template.render(nil, template_locals)
|
43
|
+
|
44
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
45
|
+
|
46
|
+
File.open(filepath, "w") { |f| f.write(file_body) }
|
47
|
+
puts "created #{filepath}"
|
48
|
+
end
|
49
|
+
|
50
|
+
puts "Installing dependencies..."
|
51
|
+
Bundler.with_original_env { system("bundle install") }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jun
|
4
|
+
module CLI
|
5
|
+
module Commands
|
6
|
+
class Server < Base
|
7
|
+
def process(*args)
|
8
|
+
if Jun.root
|
9
|
+
system("rerun --background -- rackup -p 3001")
|
10
|
+
else
|
11
|
+
abort("Command \"#{self.class.command_name}\" must be run inside of a Jun app.")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jun"
|
4
|
+
|
5
|
+
Jun::ActiveSupport::Dependencies.autoload_paths += Jun.root.join("app").children
|
6
|
+
|
7
|
+
# Include all helpers in app/helpers directory
|
8
|
+
Dir.glob(Jun.root.join("app/helpers/**/*.rb")).each do |filepath|
|
9
|
+
helper_class_name = File.basename(filepath, ".rb").camelize
|
10
|
+
helper_class = Object.const_get(helper_class_name)
|
11
|
+
|
12
|
+
Jun::ActionView::Base.include(helper_class)
|
13
|
+
end
|
14
|
+
|
15
|
+
module <%= app_name.camelize %>
|
16
|
+
class Application < Jun::Application
|
17
|
+
end
|
18
|
+
end
|
File without changes
|
data/lib/jun/cli.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Dir[File.expand_path("cli/commands/**/*.rb", __dir__)].each do |filepath|
|
4
|
+
require_relative filepath
|
5
|
+
end
|
6
|
+
|
7
|
+
module Jun
|
8
|
+
module CLI
|
9
|
+
class << self
|
10
|
+
COMMAND_KLASSES = [
|
11
|
+
Jun::CLI::Commands::New,
|
12
|
+
Jun::CLI::Commands::DB::Create,
|
13
|
+
Jun::CLI::Commands::DB::Migrate,
|
14
|
+
Jun::CLI::Commands::DB::Drop,
|
15
|
+
Jun::CLI::Commands::Server,
|
16
|
+
Jun::CLI::Commands::Version
|
17
|
+
].freeze
|
18
|
+
|
19
|
+
def process_command(argv)
|
20
|
+
command_name = argv.shift
|
21
|
+
command_klass = COMMAND_KLASSES.find { |klass| klass.command_name == command_name }
|
22
|
+
abort("Command \"#{command_name}\" not found.") if command_klass.nil?
|
23
|
+
|
24
|
+
command_klass.new.process(*argv)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sqlite3"
|
4
|
+
|
5
|
+
class SqliteAdapter
|
6
|
+
def initialize
|
7
|
+
@db = SQLite3::Database.new(Jun.root.join("db/app.db").to_s, results_as_hash: true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(sql)
|
11
|
+
@db.execute(sql).each do |row|
|
12
|
+
row.keys.each { |key| row[(key.to_sym rescue key) || key] = row.delete(key) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def columns(table_name)
|
17
|
+
@db.table_info(table_name).map { |info| info["name"].to_sym }
|
18
|
+
end
|
19
|
+
end
|
data/lib/jun/version.rb
CHANGED
data/lib/jun.rb
CHANGED
@@ -1,5 +1,42 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
require_relative "jun/version"
|
6
|
+
require_relative "jun/active_support/inflector"
|
7
|
+
require_relative "jun/active_support/dependencies"
|
8
|
+
require_relative "jun/action_dispatch/routing/route_set"
|
9
|
+
require_relative "jun/active_record"
|
10
|
+
require_relative "jun/action_controller/base"
|
11
|
+
require_relative "jun/application"
|
2
12
|
|
3
13
|
module Jun
|
4
|
-
|
14
|
+
class << self
|
15
|
+
attr_accessor :app_class
|
16
|
+
|
17
|
+
def application
|
18
|
+
@application ||= app_class&.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def root
|
22
|
+
project_root_path
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def project_root_path
|
28
|
+
current_dir = Dir.pwd
|
29
|
+
root_file = "config.ru"
|
30
|
+
|
31
|
+
while current_dir && File.directory?(current_dir) && !File.exist?("#{current_dir}/#{root_file}")
|
32
|
+
parent_dir = File.dirname(current_dir)
|
33
|
+
current_dir = parent_dir != current_dir && parent_dir
|
34
|
+
end
|
35
|
+
|
36
|
+
root_dir = current_dir if File.exist?("#{current_dir}/#{root_file}")
|
37
|
+
return if root_dir.nil?
|
38
|
+
|
39
|
+
Pathname.new(File.realpath(root_dir))
|
40
|
+
end
|
41
|
+
end
|
5
42
|
end
|
metadata
CHANGED
@@ -1,82 +1,166 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zoran
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
15
57
|
requirement: !ruby/object:Gem::Requirement
|
16
58
|
requirements:
|
17
59
|
- - "~>"
|
18
60
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
61
|
+
version: '13.0'
|
20
62
|
type: :development
|
21
63
|
prerelease: false
|
22
64
|
version_requirements: !ruby/object:Gem::Requirement
|
23
65
|
requirements:
|
24
66
|
- - "~>"
|
25
67
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
68
|
+
version: '13.0'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
70
|
+
name: minitest
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
30
72
|
requirements:
|
31
73
|
- - "~>"
|
32
74
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
75
|
+
version: '5.0'
|
34
76
|
type: :development
|
35
77
|
prerelease: false
|
36
78
|
version_requirements: !ruby/object:Gem::Requirement
|
37
79
|
requirements:
|
38
80
|
- - "~>"
|
39
81
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
82
|
+
version: '5.0'
|
41
83
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
84
|
+
name: rubocop
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
44
86
|
requirements:
|
45
87
|
- - "~>"
|
46
88
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
89
|
+
version: '1.21'
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - "~>"
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
|
56
|
-
|
96
|
+
version: '1.21'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.14'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.14'
|
111
|
+
description: A simple web framework inspired by Rails.
|
57
112
|
email:
|
58
|
-
-
|
59
|
-
executables:
|
113
|
+
- zspesic@gmail.com
|
114
|
+
executables:
|
115
|
+
- jun
|
60
116
|
extensions: []
|
61
117
|
extra_rdoc_files: []
|
62
118
|
files:
|
63
|
-
- ".
|
64
|
-
- ".rspec"
|
65
|
-
- ".travis.yml"
|
119
|
+
- ".rubocop.yml"
|
66
120
|
- Gemfile
|
67
121
|
- LICENSE.txt
|
68
122
|
- README.md
|
69
123
|
- Rakefile
|
70
124
|
- bin/console
|
71
125
|
- bin/setup
|
126
|
+
- exe/jun
|
72
127
|
- jun.gemspec
|
73
128
|
- lib/jun.rb
|
129
|
+
- lib/jun/action_controller/base.rb
|
130
|
+
- lib/jun/action_controller/rendering.rb
|
131
|
+
- lib/jun/action_dispatch/routing/mapper.rb
|
132
|
+
- lib/jun/action_dispatch/routing/route_set.rb
|
133
|
+
- lib/jun/action_view/base.rb
|
134
|
+
- lib/jun/action_view/helpers.rb
|
135
|
+
- lib/jun/action_view/helpers/url_helper.rb
|
136
|
+
- lib/jun/active_record.rb
|
137
|
+
- lib/jun/active_support/dependencies.rb
|
138
|
+
- lib/jun/active_support/inflector.rb
|
139
|
+
- lib/jun/application.rb
|
140
|
+
- lib/jun/cli.rb
|
141
|
+
- lib/jun/cli/commands/base.rb
|
142
|
+
- lib/jun/cli/commands/db/create.rb
|
143
|
+
- lib/jun/cli/commands/db/drop.rb
|
144
|
+
- lib/jun/cli/commands/db/migrate.rb
|
145
|
+
- lib/jun/cli/commands/new.rb
|
146
|
+
- lib/jun/cli/commands/server.rb
|
147
|
+
- lib/jun/cli/commands/version.rb
|
148
|
+
- lib/jun/cli/generators/new/Gemfile.erb
|
149
|
+
- lib/jun/cli/generators/new/README.md.erb
|
150
|
+
- lib/jun/cli/generators/new/app/controllers/application_controller.rb.erb
|
151
|
+
- lib/jun/cli/generators/new/app/helpers/application_helper.rb.erb
|
152
|
+
- lib/jun/cli/generators/new/app/views/layouts/application.html.erb.erb
|
153
|
+
- lib/jun/cli/generators/new/config.ru.erb
|
154
|
+
- lib/jun/cli/generators/new/config/application.rb.erb
|
155
|
+
- lib/jun/cli/generators/new/config/routes.rb.erb
|
156
|
+
- lib/jun/cli/generators/new/db/app.db.erb
|
157
|
+
- lib/jun/connection_adapters/sqlite_adapter.rb
|
74
158
|
- lib/jun/version.rb
|
75
159
|
homepage: https://github.com/zokioki/jun
|
76
160
|
licenses:
|
77
161
|
- MIT
|
78
162
|
metadata: {}
|
79
|
-
post_install_message:
|
163
|
+
post_install_message:
|
80
164
|
rdoc_options: []
|
81
165
|
require_paths:
|
82
166
|
- lib
|
@@ -84,16 +168,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
168
|
requirements:
|
85
169
|
- - ">="
|
86
170
|
- !ruby/object:Gem::Version
|
87
|
-
version:
|
171
|
+
version: 2.6.0
|
88
172
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
173
|
requirements:
|
90
174
|
- - ">="
|
91
175
|
- !ruby/object:Gem::Version
|
92
176
|
version: '0'
|
93
177
|
requirements: []
|
94
|
-
|
95
|
-
|
96
|
-
signing_key:
|
178
|
+
rubygems_version: 3.2.32
|
179
|
+
signing_key:
|
97
180
|
specification_version: 4
|
98
181
|
summary: A simple Ruby web framework.
|
99
182
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.travis.yml
DELETED