jun 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) 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 +4 -9
  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 +20 -0
  11. data/lib/jun/action_controller/rendering.rb +88 -0
  12. data/lib/jun/action_dispatch/routing/mapper.rb +29 -0
  13. data/lib/jun/action_dispatch/routing/route_set.rb +65 -0
  14. data/lib/jun/action_view/base.rb +26 -0
  15. data/lib/jun/action_view/helpers/url_helper.rb +13 -0
  16. data/lib/jun/action_view/helpers.rb +11 -0
  17. data/lib/jun/active_record.rb +41 -0
  18. data/lib/jun/active_support/dependencies.rb +31 -0
  19. data/lib/jun/active_support/inflector.rb +119 -0
  20. data/lib/jun/application.rb +18 -0
  21. data/lib/jun/cli/commands/base.rb +17 -0
  22. data/lib/jun/cli/commands/db/create.rb +15 -0
  23. data/lib/jun/cli/commands/db/drop.rb +15 -0
  24. data/lib/jun/cli/commands/db/migrate.rb +15 -0
  25. data/lib/jun/cli/commands/new.rb +57 -0
  26. data/lib/jun/cli/commands/server.rb +17 -0
  27. data/lib/jun/cli/commands/version.rb +13 -0
  28. data/lib/jun/cli/generators/new/Gemfile.erb +9 -0
  29. data/lib/jun/cli/generators/new/README.md.erb +3 -0
  30. data/lib/jun/cli/generators/new/app/controllers/application_controller.rb.erb +4 -0
  31. data/lib/jun/cli/generators/new/app/helpers/application_helper.rb.erb +4 -0
  32. data/lib/jun/cli/generators/new/app/views/layouts/application.html.erb.erb +11 -0
  33. data/lib/jun/cli/generators/new/config/application.rb.erb +18 -0
  34. data/lib/jun/cli/generators/new/config/routes.rb.erb +4 -0
  35. data/lib/jun/cli/generators/new/config.ru.erb +7 -0
  36. data/lib/jun/cli/generators/new/db/app.db.erb +0 -0
  37. data/lib/jun/cli.rb +28 -0
  38. data/lib/jun/connection_adapters/sqlite_adapter.rb +19 -0
  39. data/lib/jun/version.rb +3 -1
  40. data/lib/jun.rb +39 -2
  41. metadata +107 -24
  42. data/.gitignore +0 -10
  43. data/.rspec +0 -2
  44. 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: a23824148b7546d35ec51bc05200095f47bb5ffcb3604597a2c1606b893ae1c5
4
+ data.tar.gz: e6e03e5e69d3cebd025a9194de2488539a096c9864fbb53b720f43c5f7d5f68b
5
5
  SHA512:
6
- metadata.gz: c282aabb2f3057321548c32de8058e65b13d433f885e71fc5a3ca405351cb609a8a98bc288162f0c8a67591ac2106d63c55d121cd6fdf6c3bc376e0d3f2001c5
7
- data.tar.gz: 8fdd848aad1a8cdafd410e0090b4b85a6a09f7b63e0dee7fbd072ed08890e0b2ceebd989a1aca5665458cda3e8a7ac0c2292c78cd0ee6f7ff236888f9ef604ed
6
+ metadata.gz: 0a50ed3137ed9926647d32e534c908a1f6bca9e235a6de53f6969932be97dca05bbb44dec9bffd4d3de3b1b5d96cdcc227b2c0728a176a3433dd98cb85e0b403
7
+ data.tar.gz: 78931eee706054b9b6f85585b3dd0af3055a9ff6641439802aea254c1f3c0c23292802e4c40147f17bea8e72b6d6e1ce5fe3b85455260ef559ca9dedeadcc40e
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,6 +1,6 @@
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
+ 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
- TODO: Write usage instructions here
23
+ _TBD_
24
24
 
25
25
  ## Development
26
26
 
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.
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 "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."
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"
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,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
@@ -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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ class Create < Base
8
+ def process(*args)
9
+ puts "Creating database..."
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ class Drop < Base
8
+ def process(*args)
9
+ puts "Dropping database..."
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ module DB
7
+ class Migrate < Base
8
+ def process(*args)
9
+ puts "Running migrations..."
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jun
4
+ module CLI
5
+ module Commands
6
+ class Version < Base
7
+ def process(*args)
8
+ puts "Jun #{Jun::VERSION}"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "jun", path: "/Users/zoran/dev/jun"
4
+ gem "puma"
5
+
6
+ group :development do
7
+ gem "pry"
8
+ gem "rerun"
9
+ end
@@ -0,0 +1,3 @@
1
+ # New Jun App
2
+
3
+ Your newly created Jun app.
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationController < Jun::ActionController::Base
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApplicationHelper
4
+ end
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Jun App</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ </head>
7
+
8
+ <body>
9
+ <%%= yield %>
10
+ </body>
11
+ </html>
@@ -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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ Jun.application.routes.draw do
4
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require_relative "config/application"
5
+ require_relative "config/routes"
6
+
7
+ run Jun.application
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jun
2
- VERSION = "0.0.1"
4
+ VERSION = "0.1.0"
3
5
  end
data/lib/jun.rb CHANGED
@@ -1,5 +1,42 @@
1
- require "jun/version"
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.1
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: 2016-04-07 00:00:00.000000000 Z
11
+ date: 2022-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
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: '1.11'
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: '1.11'
68
+ version: '13.0'
27
69
  - !ruby/object:Gem::Dependency
28
- name: rake
70
+ name: minitest
29
71
  requirement: !ruby/object:Gem::Requirement
30
72
  requirements:
31
73
  - - "~>"
32
74
  - !ruby/object:Gem::Version
33
- version: '10.0'
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: '10.0'
82
+ version: '5.0'
41
83
  - !ruby/object:Gem::Dependency
42
- name: rspec
84
+ name: rubocop
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
87
  - - "~>"
46
88
  - !ruby/object:Gem::Version
47
- version: '3.0'
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: '3.0'
55
- description: A simple web framework inspired by Rails, built with the hopes of learning
56
- more about Rails internals in the process.
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
- - zoran1991@gmail.com
59
- executables: []
113
+ - zspesic@gmail.com
114
+ executables:
115
+ - jun
60
116
  extensions: []
61
117
  extra_rdoc_files: []
62
118
  files:
63
- - ".gitignore"
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: '0'
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
- rubyforge_project:
95
- rubygems_version: 2.5.1
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
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
- .DS_Store
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
data/.travis.yml DELETED
@@ -1,4 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3.0
4
- before_install: gem install bundler -v 1.11.2