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
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../connection_adapters/sqlite_adapter"
|
4
|
+
require_relative "./persistence"
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
class Base
|
8
|
+
include ActiveRecord::Persistence
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
@attributes = attributes
|
12
|
+
@new_record = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args)
|
16
|
+
if self.class.connection.columns(self.class.table_name).include?(name)
|
17
|
+
@attributes[name]
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.find(id)
|
24
|
+
find_by_sql("SELECT * FROM #{table_name} WHERE id = #{id.to_i} LIMIT 1").first
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.all
|
28
|
+
ActiveRecord::Relation.new(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.where(*args)
|
32
|
+
all.where(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.find_by_sql(sql)
|
36
|
+
connection.execute(sql).map do |attributes|
|
37
|
+
object = new(attributes)
|
38
|
+
object.instance_variable_set("@new_record", false)
|
39
|
+
|
40
|
+
object
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.table_name
|
45
|
+
name.downcase.pluralize
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.connection
|
49
|
+
@@connection ||= SqliteAdapter.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Migration
|
5
|
+
def up
|
6
|
+
raise NoMethodError, "Subclass must implement method."
|
7
|
+
end
|
8
|
+
|
9
|
+
def down
|
10
|
+
raise NoMethodError, "Subclass must implement method."
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_column(table_name, column_name, column_type, options = {})
|
14
|
+
sql = ["ALTER TABLE #{table_name} ADD COLUMN #{column_name}"]
|
15
|
+
|
16
|
+
sql << column_type.to_s.upcase
|
17
|
+
sql << "NOT NULL" if options[:null] == false
|
18
|
+
sql << "DEFAULT #{options[:default]}" if options[:default]
|
19
|
+
sql << "UNIQUE" if options[:unique] == true
|
20
|
+
|
21
|
+
execute(sql.join(" "))
|
22
|
+
end
|
23
|
+
|
24
|
+
def remove_column(table_name, column_name)
|
25
|
+
execute("ALTER TABLE #{table_name} DROP COLUMN #{column_name};")
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_table(table_name, options = {})
|
29
|
+
sql = "CREATE TABLE IF NOT EXISTS #{table_name}"
|
30
|
+
column_options = []
|
31
|
+
|
32
|
+
if options[:id] || !options.key?(:id)
|
33
|
+
column_options << {
|
34
|
+
name: :id,
|
35
|
+
type: :integer,
|
36
|
+
primary_key: true,
|
37
|
+
null: false
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
column_options += options.fetch(:columns, [])
|
42
|
+
|
43
|
+
columns_sql = column_options.map do |column|
|
44
|
+
next(column) if column.is_a?(String)
|
45
|
+
|
46
|
+
column_sql = []
|
47
|
+
|
48
|
+
column_sql << column[:name]
|
49
|
+
column_sql << column[:type]&.to_s&.upcase
|
50
|
+
column_sql << "PRIMARY KEY" if column[:primary_key] == true
|
51
|
+
column_sql << "NOT NULL" if column[:null] == false
|
52
|
+
column_sql << "DEFAULT #{column[:default]}" if column[:default]
|
53
|
+
column_sql << "UNIQUE" if column[:unique] == true
|
54
|
+
|
55
|
+
column_sql.compact.join(" ")
|
56
|
+
end
|
57
|
+
|
58
|
+
sql += " (#{columns_sql.join(", ")})"
|
59
|
+
sql += ";"
|
60
|
+
|
61
|
+
execute(sql)
|
62
|
+
end
|
63
|
+
|
64
|
+
def drop_table(table_name)
|
65
|
+
execute("DROP TABLE IF EXISTS #{table_name};")
|
66
|
+
end
|
67
|
+
|
68
|
+
def rename_table(old_table_name, new_table_name)
|
69
|
+
execute("ALTER TABLE #{old_table_name} RENAME TO #{new_table_name};")
|
70
|
+
end
|
71
|
+
|
72
|
+
def execute(*args)
|
73
|
+
ActiveRecord::Base.connection.execute(*args)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Migrator
|
5
|
+
ALLOWED_DIRECTIONS = %w[up down].freeze
|
6
|
+
|
7
|
+
def initialize(direction:)
|
8
|
+
unless ALLOWED_DIRECTIONS.include?(direction.to_s)
|
9
|
+
raise ArgumentError, "direction must be one of: #{ALLOWED_DIRECTIONS.inspect}"
|
10
|
+
end
|
11
|
+
|
12
|
+
@direction = direction.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
process_migrations!
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def process_migrations!
|
22
|
+
files_to_process = up_migration? ? pending_migration_files : processed_migration_files.last(1)
|
23
|
+
|
24
|
+
files_to_process.each do |filepath|
|
25
|
+
require filepath
|
26
|
+
|
27
|
+
filename = filepath.split("/").last.sub(".rb", "")
|
28
|
+
migration_version = filename.split("_").first
|
29
|
+
migration_class_name = filename.split("_", 2).last.camelize
|
30
|
+
migration_class = Object.const_get(migration_class_name)
|
31
|
+
|
32
|
+
migration_class.new.public_send(@direction)
|
33
|
+
up_migration? ? add_to_schema_migrations(migration_version) :
|
34
|
+
remove_from_schema_migrations(migration_version)
|
35
|
+
|
36
|
+
migration_verb = up_migration? ? "processed" : "rolled back"
|
37
|
+
puts "Migration #{migration_verb} (#{filename})."
|
38
|
+
end
|
39
|
+
|
40
|
+
dump_schema! if files_to_process.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def up_migration?
|
44
|
+
@direction == "up"
|
45
|
+
end
|
46
|
+
|
47
|
+
def pending_migration_files
|
48
|
+
@pending_migration_files ||= migration_files - processed_migration_files
|
49
|
+
end
|
50
|
+
|
51
|
+
def processed_migration_files
|
52
|
+
@processed_migration_files ||= migration_files.select do |filepath|
|
53
|
+
file_version = filepath.split("/").last.split("_").first
|
54
|
+
processed_versions.include?(file_version)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def migration_files
|
59
|
+
@migration_files ||= Dir.glob(Jun.root.join("db/migrate/*.rb")).sort
|
60
|
+
end
|
61
|
+
|
62
|
+
def processed_versions
|
63
|
+
ActiveRecord::Base.connection.execute("SELECT * FROM schema_migrations;").map do |attributes|
|
64
|
+
attributes[:version]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_to_schema_migrations(version)
|
69
|
+
ActiveRecord::Base.connection.execute("INSERT INTO schema_migrations (version) VALUES (#{version});")
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_from_schema_migrations(version)
|
73
|
+
ActiveRecord::Base.connection.execute("DELETE FROM schema_migrations WHERE version = #{version};")
|
74
|
+
end
|
75
|
+
|
76
|
+
def dump_schema!
|
77
|
+
Jun::CLI.process_command(["db:schema:dump"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module Persistence
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def create(attributes = {})
|
11
|
+
object = new(attributes)
|
12
|
+
object.save
|
13
|
+
|
14
|
+
object
|
15
|
+
end
|
16
|
+
|
17
|
+
def primary_key=(value)
|
18
|
+
@primary_key = value.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
def primary_key
|
22
|
+
defined?(@primary_key) ? @primary_key : :id
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save
|
27
|
+
if new_record?
|
28
|
+
result = self.class.connection.execute(
|
29
|
+
<<~SQL
|
30
|
+
INSERT INTO #{self.class.table_name}
|
31
|
+
(#{@attributes.keys.join(",")})
|
32
|
+
VALUES (#{@attributes.values.map { |v| "'#{v}'" }.join(",")})
|
33
|
+
RETURNING *;
|
34
|
+
SQL
|
35
|
+
)
|
36
|
+
|
37
|
+
@attributes[self.class.primary_key] = result.first[self.class.primary_key]
|
38
|
+
@new_record = false
|
39
|
+
else
|
40
|
+
self.class.connection.execute(
|
41
|
+
<<~SQL
|
42
|
+
UPDATE #{self.class.table_name}
|
43
|
+
SET #{@attributes.map { |k, v| "#{k} = #{v.nil? ? 'NULL' : "'#{v}'"}" }.join(",")}
|
44
|
+
WHERE id = #{id};
|
45
|
+
SQL
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def new_record?
|
53
|
+
@new_record
|
54
|
+
end
|
55
|
+
|
56
|
+
def persisted?
|
57
|
+
!new_record?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
class Relation
|
5
|
+
def initialize(klass)
|
6
|
+
@klass = klass
|
7
|
+
@where_clauses = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def where!(condition)
|
11
|
+
clause = condition.is_a?(String) ? condition : condition.map { |k, v| "#{k} = #{v}" }.join(" AND ")
|
12
|
+
@where_clauses << clause
|
13
|
+
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def where(condition)
|
18
|
+
clone.where!(condition)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_sql
|
22
|
+
sql = "SELECT * FROM #{@klass.table_name}"
|
23
|
+
sql += " WHERE #{@where_clauses.join(" AND ")}" if @where_clauses.any?
|
24
|
+
|
25
|
+
sql
|
26
|
+
end
|
27
|
+
|
28
|
+
def records
|
29
|
+
@records ||= @klass.find_by_sql(to_sql)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias to_a records
|
33
|
+
|
34
|
+
def first
|
35
|
+
records.first
|
36
|
+
end
|
37
|
+
|
38
|
+
def each(&block)
|
39
|
+
records.each(&block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def second
|
5
|
+
self[1]
|
6
|
+
end
|
7
|
+
|
8
|
+
def third
|
9
|
+
self[2]
|
10
|
+
end
|
11
|
+
|
12
|
+
def fourth
|
13
|
+
self[3]
|
14
|
+
end
|
15
|
+
|
16
|
+
def fifth
|
17
|
+
self[4]
|
18
|
+
end
|
19
|
+
|
20
|
+
def third_to_last
|
21
|
+
self[-3]
|
22
|
+
end
|
23
|
+
|
24
|
+
def second_to_last
|
25
|
+
self[-2]
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
def stringify_keys
|
5
|
+
transform_keys(&:to_s)
|
6
|
+
end
|
7
|
+
|
8
|
+
def stringify_keys!
|
9
|
+
transform_keys!(&:to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def symbolize_keys
|
13
|
+
transform_keys { |key| key.to_sym rescue key }
|
14
|
+
end
|
15
|
+
|
16
|
+
def symbolize_keys!
|
17
|
+
transform_keys! { |key| key.to_sym rescue key }
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class String
|
4
|
+
def at(position)
|
5
|
+
self[position]
|
6
|
+
end
|
7
|
+
|
8
|
+
def from(position)
|
9
|
+
self[position, length]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to(position)
|
13
|
+
position += size if position.negative?
|
14
|
+
position = -1 if position.negative?
|
15
|
+
|
16
|
+
self[0, position + 1]
|
17
|
+
end
|
18
|
+
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,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,45 @@
|
|
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
|
+
|
18
|
+
def initialize!
|
19
|
+
return false if initialized? || Jun.application.nil?
|
20
|
+
|
21
|
+
# Add app/* directories to autoload paths.
|
22
|
+
Jun::ActiveSupport::Dependencies.autoload_paths += Jun.root.join("app").children
|
23
|
+
|
24
|
+
# Set up routes and make its helpers available to controllers & views.
|
25
|
+
require Jun.root.join("config/routes.rb")
|
26
|
+
url_helpers = Jun.application.routes.url_helpers
|
27
|
+
Jun::ActionController::Base.include(url_helpers)
|
28
|
+
Jun::ActionView::Base.include(url_helpers)
|
29
|
+
|
30
|
+
# Include all helpers in app/helpers directory.
|
31
|
+
Dir.glob(Jun.root.join("app/helpers/**/*.rb")).each do |filepath|
|
32
|
+
helper_class_name = File.basename(filepath, ".rb").camelize
|
33
|
+
helper_class = Object.const_get(helper_class_name)
|
34
|
+
|
35
|
+
Jun::ActionView::Base.include(helper_class)
|
36
|
+
end
|
37
|
+
|
38
|
+
@initialized = true
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialized?
|
42
|
+
!!@initialized
|
43
|
+
end
|
44
|
+
end
|
45
|
+
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,35 @@
|
|
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
|
+
db_filepath = Jun.root.join("db/app.db")
|
10
|
+
|
11
|
+
if File.exist?(db_filepath)
|
12
|
+
puts "Database already exists."
|
13
|
+
else
|
14
|
+
File.open(db_filepath, "w") {}
|
15
|
+
create_schema_migrations_table
|
16
|
+
puts "Created database in #{db_filepath}."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_schema_migrations_table
|
23
|
+
ActiveRecord::Base.connection.execute(
|
24
|
+
<<~SQL
|
25
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
26
|
+
version text NOT NULL PRIMARY KEY
|
27
|
+
);"
|
28
|
+
SQL
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
+
db_filepath = Jun.root.join("db/app.db")
|
10
|
+
|
11
|
+
File.delete(db_filepath) if File.exist?(db_filepath)
|
12
|
+
puts "Dropped database."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|