Skip to content

Commit 0dc1374

Browse files
authored
Generate db files during hanami new (#180)
1 parent 2ffa120 commit 0dc1374

File tree

13 files changed

+792
-15
lines changed

13 files changed

+792
-15
lines changed

lib/hanami/cli/commands/app.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ def self.extended(base)
4343
register "generate", aliases: ["g"] do |prefix|
4444
prefix.register "action", Generate::Action
4545
prefix.register "component", Generate::Component
46-
prefix.register "slice", Generate::Slice
4746
prefix.register "operation", Generate::Operation
4847
prefix.register "part", Generate::Part
48+
prefix.register "slice", Generate::Slice
4949
prefix.register "view", Generate::View
5050
end
5151
end

lib/hanami/cli/commands/gem/new.rb

+75-11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,27 @@ class New < Command
2525
SKIP_ASSETS_DEFAULT = false
2626
private_constant :SKIP_ASSETS_DEFAULT
2727

28+
# @since 2.2.0
29+
# @api private
30+
SKIP_DB_DEFAULT = false
31+
private_constant :SKIP_DB_DEFAULT
32+
33+
# @since 2.2.0
34+
# @api private
35+
DATABASE_SQLITE = "sqlite"
36+
37+
# @since 2.2.0
38+
# @api private
39+
DATABASE_POSTGRES = "postgres"
40+
41+
# @since 2.2.0
42+
# @api private
43+
DATABASE_MYSQL = "mysql"
44+
45+
# @since 2.2.0
46+
# @api private
47+
SUPPORTED_DATABASES = [DATABASE_SQLITE, DATABASE_POSTGRES, DATABASE_MYSQL].freeze
48+
2849
desc "Generate a new Hanami app"
2950

3051
# @since 2.0.0
@@ -47,14 +68,28 @@ class New < Command
4768
# @api private
4869
option :skip_assets, type: :boolean, required: false,
4970
default: SKIP_ASSETS_DEFAULT,
50-
desc: "Skip assets"
71+
desc: "Skip including hanami-assets"
72+
73+
# @since 2.2.0
74+
# @api private
75+
option :skip_db, type: :boolean, required: false,
76+
default: SKIP_DB_DEFAULT,
77+
desc: "Skip including hanami-db"
78+
79+
# @since 2.2.0
80+
# @api private
81+
option :database, type: :string, required: false,
82+
default: DATABASE_SQLITE,
83+
desc: "Database adapter (supported: sqlite, mysql, postgres)"
5184

5285
# rubocop:disable Layout/LineLength
5386
example [
54-
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
55-
"bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
56-
"bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
57-
"bookshelf --skip-assets # Generate a new Hanami app without assets"
87+
"bookshelf # Generate a new Hanami app in `bookshelf/' directory, using `Bookshelf' namespace",
88+
"bookshelf --head # Generate a new Hanami app, using Hanami HEAD version from GitHub `main' branches",
89+
"bookshelf --skip-install # Generate a new Hanami app, but it skips Hanami installation",
90+
"bookshelf --skip-assets # Generate a new Hanami app without hanmai-assets",
91+
"bookshelf --skip-db # Generate a new Hanami app without hanami-db",
92+
"bookshelf --database={sqlite|postgres|mysql} # Generate a new Hanami app with a specified database (default: sqlite)",
5893
]
5994
# rubocop:enable Layout/LineLength
6095

@@ -75,20 +110,36 @@ def initialize(
75110
@system_call = system_call
76111
end
77112

78-
# rubocop:enable Metrics/ParameterLists
79-
80-
# rubocop:disable Metrics/AbcSize
113+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
81114

82115
# @since 2.0.0
83116
# @api private
84-
def call(app:, head: HEAD_DEFAULT, skip_install: SKIP_INSTALL_DEFAULT, skip_assets: SKIP_ASSETS_DEFAULT, **)
117+
def call(
118+
app:,
119+
head: HEAD_DEFAULT,
120+
skip_install: SKIP_INSTALL_DEFAULT,
121+
skip_assets: SKIP_ASSETS_DEFAULT,
122+
skip_db: SKIP_DB_DEFAULT,
123+
database: nil
124+
)
125+
# rubocop:enable Metrics/ParameterLists
85126
app = inflector.underscore(app)
86127

87128
raise PathAlreadyExistsError.new(app) if fs.exist?(app)
129+
raise ConflictingOptionsError.new("--skip-db", "--database") if skip_db && database
130+
131+
normalized_database ||= normalize_database(database)
88132

89133
fs.mkdir(app)
90134
fs.chdir(app) do
91-
context = Generators::Context.new(inflector, app, head: head, skip_assets: skip_assets)
135+
context = Generators::Context.new(
136+
inflector,
137+
app,
138+
head: head,
139+
skip_assets: skip_assets,
140+
skip_db: skip_db,
141+
database: normalized_database
142+
)
92143
generator.call(app, context: context) do
93144
if skip_install
94145
out.puts "Skipping installation, please enter `#{app}' directory and run `bundle exec hanami install'"
@@ -112,14 +163,27 @@ def call(app:, head: HEAD_DEFAULT, skip_install: SKIP_INSTALL_DEFAULT, skip_asse
112163
end
113164
end
114165
end
115-
# rubocop:enable Metrics/AbcSize
166+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
116167

117168
private
118169

119170
attr_reader :bundler
120171
attr_reader :generator
121172
attr_reader :system_call
122173

174+
def normalize_database(database)
175+
case database
176+
when nil, "sqlite", "sqlite3"
177+
DATABASE_SQLITE
178+
when "mysql", "mysql2"
179+
DATABASE_MYSQL
180+
when "postgres", "postgresql", "pg"
181+
DATABASE_POSTGRES
182+
else
183+
raise DatabaseNotSupportedError.new(database, SUPPORTED_DATABASES)
184+
end
185+
end
186+
123187
def run_install_command!(head:)
124188
head_flag = head ? " --head" : ""
125189
bundler.exec("hanami install#{head_flag}").tap do |result|

lib/hanami/cli/errors.rb

+16
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,21 @@ def initialize(scheme)
9191
super("`#{scheme}' is not a supported db scheme")
9292
end
9393
end
94+
95+
# @since 2.2.0
96+
# @api public
97+
class DatabaseNotSupportedError < Error
98+
def initialize(invalid_database, supported_databases)
99+
super("`#{invalid_database}' is not a supported database. Supported databases are: #{supported_databases.join(', ')}")
100+
end
101+
end
102+
103+
# @since 2.2.0
104+
# @api public
105+
class ConflictingOptionsError < Error
106+
def initialize(option1, option2)
107+
super("`#{option1}' and `#{option2}' cannot be used together")
108+
end
109+
end
94110
end
95111
end

lib/hanami/cli/generators/context.rb

+42
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,44 @@ def generate_assets?
8080
!options.fetch(:skip_assets, false)
8181
end
8282

83+
# @since 2.2.0
84+
# @api private
85+
def generate_db?
86+
!options.fetch(:skip_db, false)
87+
end
88+
89+
# @since 2.2.0
90+
# @api private
91+
def generate_sqlite?
92+
database_option == Commands::Gem::New::DATABASE_SQLITE
93+
end
94+
95+
# @since 2.2.0
96+
# @api private
97+
def generate_postgres?
98+
database_option == Commands::Gem::New::DATABASE_POSTGRES
99+
end
100+
101+
# @since 2.2.0
102+
# @api private
103+
def generate_mysql?
104+
database_option == Commands::Gem::New::DATABASE_MYSQL
105+
end
106+
107+
# @since 2.2.0
108+
# @api private
109+
def database_url
110+
if generate_sqlite?
111+
"sqlite://db/#{app}.sqlite"
112+
elsif generate_postgres?
113+
"postgres://localhost/#{app}"
114+
elsif generate_mysql?
115+
"mysql://localhost/#{app}"
116+
else
117+
raise "Unknown database option: #{database_option}"
118+
end
119+
end
120+
83121
# @since 2.1.0
84122
# @api private
85123
def bundled_views?
@@ -108,6 +146,10 @@ def ruby_omit_hash_values?
108146

109147
private
110148

149+
def database_option
150+
options.fetch(:database, Commands::Gem::New::DATABASE_SQLITE)
151+
end
152+
111153
# @since 2.0.0
112154
# @api private
113155
attr_reader :inflector

lib/hanami/cli/generators/gem/app.rb

+9
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ def generate_app(app, context) # rubocop:disable Metrics/AbcSize
6868
fs.write("app/assets/images/favicon.ico", file("favicon.ico"))
6969
end
7070

71+
if context.generate_db?
72+
fs.write("app/db/repo.rb", t("repo.erb", context))
73+
fs.write("app/db/relation.rb", t("relation.erb", context))
74+
fs.write("app/db/struct.rb", t("struct.erb", context))
75+
fs.touch("app/structs/.keep")
76+
fs.touch("app/repos/.keep")
77+
fs.touch("config/db/migrate/.keep")
78+
end
79+
7180
fs.write("app/operation.rb", t("operation.erb", context))
7281

7382
fs.write("public/404.html", file("404.html"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# This is checked into source control, so put sensitive values into `.env.local`
2+
<%- if generate_db? -%>
3+
DATABASE_URL=<%= database_url %>
4+
<%- end -%>

lib/hanami/cli/generators/gem/app/gemfile.erb

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ source "https://rubygems.org"
77
<%= hanami_gem("assets") %>
88
<%- end -%>
99
<%= hanami_gem("controller") %>
10+
<%- if generate_db? -%>
11+
<%= hanami_gem("db") %>
12+
<%- end -%>
1013
<%= hanami_gem("router") %>
1114
<%= hanami_gem("validations") %>
1215
<%= hanami_gem("view") %>
@@ -15,6 +18,13 @@ gem "dry-types", "~> 1.0", ">= 1.6.1"
1518
gem "dry-operation", github: "dry-rb/dry-operation"
1619
gem "puma"
1720
gem "rake"
21+
<%- if generate_sqlite? -%>
22+
gem "sqlite3"
23+
<%- elsif generate_postgres? -%>
24+
gem "pg"
25+
<%- elsif generate_mysql? -%>
26+
gem "mysql2"
27+
<%- end -%>
1828

1929
group :development do
2030
<%= hanami_gem("webconsole") %>

lib/hanami/cli/generators/gem/app/gitignore.erb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.env
1+
.env*.local
22
log/*
33
<%- if generate_assets? -%>
44
public/

lib/hanami/cli/generators/gem/app/operation.erb

+4
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,9 @@ require "dry/operation"
55

66
module <%= camelized_app_name %>
77
class Operation < Dry::Operation
8+
<%- if generate_db? -%>
9+
# Provide `transaction do ... end` method for database transactions
10+
include Dry::Operation::Extensions::ROM
11+
<%- end -%>
812
end
913
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
require "hanami/db/relation"
4+
5+
module <%= camelized_app_name %>
6+
module DB
7+
class Relation < Hanami::DB::Relation
8+
end
9+
end
10+
end
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
require "hanami/db/repo"
4+
5+
module <%= camelized_app_name %>
6+
module DB
7+
class Repo < Hanami::DB::Repo
8+
end
9+
end
10+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
require "hanami/db/struct"
4+
5+
module <%= camelized_app_name %>
6+
module DB
7+
class Struct < Hanami::DB::Struct
8+
end
9+
end
10+
end

0 commit comments

Comments
 (0)