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.
- 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
|