jun 0.0.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +13 -0
  3. data/Gemfile +3 -1
  4. data/LICENSE.txt +1 -1
  5. data/README.md +18 -20
  6. data/Rakefile +13 -3
  7. data/bin/console +3 -9
  8. data/exe/jun +7 -0
  9. data/jun.gemspec +28 -18
  10. data/lib/jun/action_controller/base.rb +16 -0
  11. data/lib/jun/action_controller/callbacks.rb +46 -0
  12. data/lib/jun/action_controller/metal.rb +22 -0
  13. data/lib/jun/action_controller/redirecting.rb +17 -0
  14. data/lib/jun/action_controller/rendering.rb +84 -0
  15. data/lib/jun/action_dispatch/routing/mapper.rb +54 -0
  16. data/lib/jun/action_dispatch/routing/route_set.rb +112 -0
  17. data/lib/jun/action_dispatch/routing/welcome.html.erb +41 -0
  18. data/lib/jun/action_view/base.rb +26 -0
  19. data/lib/jun/action_view/helpers/url_helper.rb +13 -0
  20. data/lib/jun/action_view/helpers.rb +11 -0
  21. data/lib/jun/active_record/base.rb +52 -0
  22. data/lib/jun/active_record/migration.rb +76 -0
  23. data/lib/jun/active_record/migrator.rb +80 -0
  24. data/lib/jun/active_record/persistence.rb +60 -0
  25. data/lib/jun/active_record/relation.rb +42 -0
  26. data/lib/jun/active_support/core_ext/array/access.rb +27 -0
  27. data/lib/jun/active_support/core_ext/array/conversion.rb +10 -0
  28. data/lib/jun/active_support/core_ext/hash/transformation.rb +19 -0
  29. data/lib/jun/active_support/core_ext/string/access.rb +18 -0
  30. data/lib/jun/active_support/core_ext/string/inflector.rb +119 -0
  31. data/lib/jun/active_support/core_ext.rb +5 -0
  32. data/lib/jun/active_support/dependencies.rb +31 -0
  33. data/lib/jun/application.rb +45 -0
  34. data/lib/jun/cli/commands/base.rb +17 -0
  35. data/lib/jun/cli/commands/db/create.rb +35 -0
  36. data/lib/jun/cli/commands/db/drop.rb +18 -0
  37. data/lib/jun/cli/commands/db/migrate.rb +15 -0
  38. data/lib/jun/cli/commands/db/rollback.rb +15 -0
  39. data/lib/jun/cli/commands/db/schema/dump.rb +24 -0
  40. data/lib/jun/cli/commands/db/schema/load.rb +43 -0
  41. data/lib/jun/cli/commands/db/seed.rb +19 -0
  42. data/lib/jun/cli/commands/generate/migration.rb +27 -0
  43. data/lib/jun/cli/commands/new.rb +62 -0
  44. data/lib/jun/cli/commands/server.rb +17 -0
  45. data/lib/jun/cli/commands/version.rb +13 -0
  46. data/lib/jun/cli/generator_templates/migration.rb.erb +11 -0
  47. data/lib/jun/cli/generator_templates/new_app/Gemfile.erb +9 -0
  48. data/lib/jun/cli/generator_templates/new_app/README.md.erb +3 -0
  49. data/lib/jun/cli/generator_templates/new_app/app/controllers/application_controller.rb.erb +4 -0
  50. data/lib/jun/cli/generator_templates/new_app/app/helpers/application_helper.rb.erb +4 -0
  51. data/lib/jun/cli/generator_templates/new_app/app/models/application_record.rb.erb +4 -0
  52. data/lib/jun/cli/generator_templates/new_app/app/views/layouts/application.html.erb.erb +11 -0
  53. data/lib/jun/cli/generator_templates/new_app/bin/console.erb +8 -0
  54. data/lib/jun/cli/generator_templates/new_app/config/application.rb.erb +12 -0
  55. data/lib/jun/cli/generator_templates/new_app/config/environment.rb.erb +7 -0
  56. data/lib/jun/cli/generator_templates/new_app/config/routes.rb.erb +4 -0
  57. data/lib/jun/cli/generator_templates/new_app/config.ru.erb +6 -0
  58. data/lib/jun/cli/generator_templates/new_app/db/seeds.rb.erb +9 -0
  59. data/lib/jun/cli.rb +33 -0
  60. data/lib/jun/connection_adapters/sqlite_adapter.rb +19 -0
  61. data/lib/jun/version.rb +3 -1
  62. data/lib/jun.rb +50 -2
  63. metadata +129 -24
  64. data/.gitignore +0 -10
  65. data/.rspec +0 -2
  66. data/.travis.yml +0 -4
@@ -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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Array
4
+ def to_sentence(delimiter: ", ", last_delimiter: "and")
5
+ return "" if none?
6
+ return self.first if one?
7
+
8
+ "#{self[0...-1].join(delimiter)} #{last_delimiter} #{self[-1]}"
9
+ end
10
+ 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.expand_path("core_ext/**/*.rb", __dir__)].sort.each do |filepath|
4
+ require_relative filepath
5
+ 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
@@ -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
+ ActiveRecord::Migrator.new(direction: :up).call
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end