hanami-cli 1.0.0.alpha1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +42 -0
  3. data/.gitignore +4 -2
  4. data/.rspec +1 -0
  5. data/.rubocop.yml +24 -0
  6. data/CHANGELOG.md +14 -1
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +8 -2
  9. data/LICENSE.txt +21 -0
  10. data/README.md +12 -605
  11. data/Rakefile +5 -10
  12. data/exe/hanami +10 -0
  13. data/hanami-cli.gemspec +21 -17
  14. data/lib/hanami/cli.rb +9 -122
  15. data/lib/hanami/cli/bundler.rb +73 -0
  16. data/lib/hanami/cli/command.rb +14 -386
  17. data/lib/hanami/cli/command_line.rb +17 -0
  18. data/lib/hanami/cli/commands.rb +26 -0
  19. data/lib/hanami/cli/commands/application.rb +63 -0
  20. data/lib/hanami/cli/commands/db/utils/database.rb +122 -0
  21. data/lib/hanami/cli/commands/db/utils/database_config.rb +48 -0
  22. data/lib/hanami/cli/commands/db/utils/mysql.rb +27 -0
  23. data/lib/hanami/cli/commands/db/utils/postgres.rb +49 -0
  24. data/lib/hanami/cli/commands/db/utils/sqlite.rb +37 -0
  25. data/lib/hanami/cli/commands/gem.rb +21 -0
  26. data/lib/hanami/cli/commands/gem/new.rb +77 -0
  27. data/lib/hanami/cli/commands/gem/version.rb +18 -0
  28. data/lib/hanami/cli/commands/monolith.rb +55 -0
  29. data/lib/hanami/cli/commands/monolith/console.rb +50 -0
  30. data/lib/hanami/cli/commands/monolith/db/create.rb +25 -0
  31. data/lib/hanami/cli/commands/monolith/db/create_migration.rb +29 -0
  32. data/lib/hanami/cli/commands/monolith/db/drop.rb +25 -0
  33. data/lib/hanami/cli/commands/monolith/db/migrate.rb +40 -0
  34. data/lib/hanami/cli/commands/monolith/db/reset.rb +26 -0
  35. data/lib/hanami/cli/commands/monolith/db/rollback.rb +55 -0
  36. data/lib/hanami/cli/commands/monolith/db/sample_data.rb +40 -0
  37. data/lib/hanami/cli/commands/monolith/db/seed.rb +40 -0
  38. data/lib/hanami/cli/commands/monolith/db/setup.rb +24 -0
  39. data/lib/hanami/cli/commands/monolith/db/structure/dump.rb +25 -0
  40. data/lib/hanami/cli/commands/monolith/db/version.rb +26 -0
  41. data/lib/hanami/cli/commands/monolith/generate.rb +14 -0
  42. data/lib/hanami/cli/commands/monolith/generate/action.rb +62 -0
  43. data/lib/hanami/cli/commands/monolith/generate/slice.rb +62 -0
  44. data/lib/hanami/cli/commands/monolith/install.rb +16 -0
  45. data/lib/hanami/cli/commands/monolith/version.rb +18 -0
  46. data/lib/hanami/cli/error.rb +8 -0
  47. data/lib/hanami/cli/generators/context.rb +38 -0
  48. data/lib/hanami/cli/generators/gem/application.rb +21 -0
  49. data/lib/hanami/cli/generators/gem/application/monolith.rb +83 -0
  50. data/lib/hanami/cli/generators/gem/application/monolith/action.erb +21 -0
  51. data/lib/hanami/cli/generators/gem/application/monolith/application.erb +8 -0
  52. data/lib/hanami/cli/generators/gem/application/monolith/config_ru.erb +5 -0
  53. data/lib/hanami/cli/generators/gem/application/monolith/entities.erb +9 -0
  54. data/lib/hanami/cli/generators/gem/application/monolith/env.erb +0 -0
  55. data/lib/hanami/cli/generators/gem/application/monolith/functions.erb +13 -0
  56. data/lib/hanami/cli/generators/gem/application/monolith/gemfile.erb +19 -0
  57. data/lib/hanami/cli/generators/gem/application/monolith/keep.erb +0 -0
  58. data/lib/hanami/cli/generators/gem/application/monolith/operation.erb +18 -0
  59. data/lib/hanami/cli/generators/gem/application/monolith/rakefile.erb +3 -0
  60. data/lib/hanami/cli/generators/gem/application/monolith/readme.erb +1 -0
  61. data/lib/hanami/cli/generators/gem/application/monolith/repository.erb +13 -0
  62. data/lib/hanami/cli/generators/gem/application/monolith/routes.erb +4 -0
  63. data/lib/hanami/cli/generators/gem/application/monolith/settings.erb +6 -0
  64. data/lib/hanami/cli/generators/gem/application/monolith/types.erb +10 -0
  65. data/lib/hanami/cli/generators/gem/application/monolith/validation_contract.erb +14 -0
  66. data/lib/hanami/cli/generators/gem/application/monolith/view_context.erb +15 -0
  67. data/lib/hanami/cli/generators/monolith/action.rb +123 -0
  68. data/lib/hanami/cli/generators/monolith/action/action.erb +13 -0
  69. data/lib/hanami/cli/generators/monolith/action/template.erb +0 -0
  70. data/lib/hanami/cli/generators/monolith/action/template.html.erb +2 -0
  71. data/lib/hanami/cli/generators/monolith/action/view.erb +13 -0
  72. data/lib/hanami/cli/generators/monolith/action_context.rb +76 -0
  73. data/lib/hanami/cli/generators/monolith/slice.rb +56 -0
  74. data/lib/hanami/cli/generators/monolith/slice/action.erb +9 -0
  75. data/lib/hanami/cli/generators/monolith/slice/entities.erb +9 -0
  76. data/lib/hanami/cli/generators/monolith/slice/keep.erb +0 -0
  77. data/lib/hanami/cli/generators/monolith/slice/repository.erb +10 -0
  78. data/lib/hanami/cli/generators/monolith/slice/routes.erb +2 -0
  79. data/lib/hanami/cli/generators/monolith/slice/view.erb +9 -0
  80. data/lib/hanami/cli/generators/monolith/slice_context.rb +33 -0
  81. data/lib/hanami/cli/repl/core.rb +55 -0
  82. data/lib/hanami/cli/repl/irb.rb +41 -0
  83. data/lib/hanami/cli/repl/pry.rb +29 -0
  84. data/lib/hanami/cli/system_call.rb +51 -0
  85. data/lib/hanami/cli/url.rb +34 -0
  86. data/lib/hanami/cli/version.rb +2 -3
  87. data/lib/hanami/console/context.rb +39 -0
  88. data/lib/hanami/console/plugins/slice_readers.rb +42 -0
  89. data/lib/hanami/rake_tasks.rb +52 -0
  90. metadata +134 -43
  91. data/.circleci/config.yml +0 -63
  92. data/.travis.yml +0 -22
  93. data/lib/hanami/cli/banner.rb +0 -129
  94. data/lib/hanami/cli/command_registry.rb +0 -215
  95. data/lib/hanami/cli/errors.rb +0 -46
  96. data/lib/hanami/cli/option.rb +0 -134
  97. data/lib/hanami/cli/parser.rb +0 -144
  98. data/lib/hanami/cli/program_name.rb +0 -21
  99. data/lib/hanami/cli/registry.rb +0 -330
  100. data/lib/hanami/cli/usage.rb +0 -91
  101. data/script/ci +0 -61
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./bundler"
4
+
5
+ module Hanami
6
+ module CLI
7
+ class CommandLine
8
+ def initialize(bundler: CLI::Bundler.new)
9
+ @bundler = bundler
10
+ end
11
+
12
+ def call(command)
13
+ @bundler.exec(command)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ # TODO: move elsewhere
5
+ def self.architecture
6
+ return :monolith if File.exist?("config/application.rb")
7
+ end
8
+
9
+ module CLI
10
+ module Commands
11
+ end
12
+
13
+ def self.register_commands!(architecture = Hanami.architecture)
14
+ commands = case architecture
15
+ when :monolith
16
+ require_relative "commands/monolith"
17
+ Commands::Monolith
18
+ else
19
+ require_relative "commands/gem"
20
+ Commands::Gem
21
+ end
22
+
23
+ extend(commands)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/files"
4
+
5
+ require_relative "db/utils/database"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Commands
10
+ class Application < Hanami::CLI::Command
11
+ module Environment
12
+ def call(**opts)
13
+ env = opts[:env]
14
+ ENV["HANAMI_ENV"] = env.to_s
15
+ super(**opts)
16
+ end
17
+ end
18
+
19
+ def self.inherited(klass)
20
+ super
21
+ klass.option(:env, required: false, default: "development", desc: "Application's environment")
22
+ klass.prepend(Environment)
23
+ end
24
+
25
+ def application
26
+ @application ||=
27
+ begin
28
+ require "hanami/init"
29
+ Hanami.application
30
+ end
31
+ end
32
+
33
+ def run_command(klass, *args)
34
+ klass.new(
35
+ out: out,
36
+ inflector: application.inflector,
37
+ fs: Dry::Files
38
+ ).call(*args)
39
+ end
40
+
41
+ def measure(desc)
42
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
43
+ result = yield
44
+ stop = Process.clock_gettime(Process::CLOCK_MONOTONIC)
45
+
46
+ if result
47
+ out.puts "=> #{desc} in #{(stop - start).round(4)}s"
48
+ else
49
+ out.puts "!!! => #{desc.inspect} FAILED"
50
+ end
51
+ end
52
+
53
+ def database
54
+ @database ||= Commands::DB::Utils::Database[application]
55
+ end
56
+
57
+ def database_config
58
+ database.config
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "database_config"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module DB
9
+ module Utils
10
+ class Database
11
+ attr_reader :application, :config
12
+
13
+ SCHEME_MAP = {
14
+ "sqlite" => -> {
15
+ require_relative("sqlite")
16
+ Sqlite
17
+ },
18
+ "postgres" => -> {
19
+ require_relative("postgres")
20
+ Postgres
21
+ },
22
+ "mysql" => -> {
23
+ require_relative("mysql")
24
+ Mysql
25
+ }
26
+ }.freeze
27
+
28
+ def self.[](application)
29
+ config = DatabaseConfig.new(application.settings.database_url)
30
+
31
+ resolver = SCHEME_MAP.fetch(config.db_type) do
32
+ raise "#{config.db_type} is not a supported db scheme"
33
+ end
34
+
35
+ klass = resolver.()
36
+
37
+ klass.new(application: application, config: config)
38
+ end
39
+
40
+ def initialize(application:, config:)
41
+ @application = application
42
+ @config = config
43
+ end
44
+
45
+ def create_command
46
+ raise NotImplementedError
47
+ end
48
+
49
+ def drop_command
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def dump_command
54
+ raise NotImplementedError
55
+ end
56
+
57
+ def load_command
58
+ raise NotImplementedError
59
+ end
60
+
61
+ def root_path
62
+ application.root
63
+ end
64
+
65
+ def rom_config
66
+ @rom_config ||=
67
+ begin
68
+ application.init_bootable(:persistence)
69
+ application.container["persistence.config"]
70
+ end
71
+ end
72
+
73
+ def name
74
+ config.db_name
75
+ end
76
+
77
+ def applied_migrations
78
+ sequel_migrator.applied_migrations
79
+ end
80
+
81
+ def gateway
82
+ rom_config.gateways[:default]
83
+ end
84
+
85
+ def connection
86
+ gateway.connection
87
+ end
88
+
89
+ def run_migrations(**options)
90
+ require "rom/sql"
91
+ ROM::SQL.with_gateway(gateway) do
92
+ migrator.run(options)
93
+ end
94
+ end
95
+
96
+ def migrator
97
+ @migrator ||=
98
+ begin
99
+ require "rom/sql"
100
+ ROM::SQL::Migration::Migrator.new(connection, path: File.join(root_path, "db/migrate"))
101
+ end
102
+ end
103
+
104
+ def applied_migrations
105
+ sequel_migrator.applied_migrations
106
+ end
107
+
108
+ private
109
+
110
+ def sequel_migrator
111
+ Sequel::TimestampMigrator.new(migrator.connection, migrations_path, {})
112
+ end
113
+
114
+ def migrations_path
115
+ File.join(root_path, "db/migrate")
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module DB
9
+ module Utils
10
+ class DatabaseConfig
11
+ attr_reader :uri
12
+
13
+ def initialize(database_url)
14
+ @uri = URI(database_url)
15
+ end
16
+
17
+ def hostname
18
+ uri.hostname
19
+ end
20
+ alias_method :host, :hostname
21
+
22
+ def user
23
+ uri.user
24
+ end
25
+ alias_method :username, :user
26
+
27
+ def password
28
+ uri.password
29
+ end
30
+ alias_method :pass, :password
31
+
32
+ def port
33
+ uri.port
34
+ end
35
+
36
+ def db_name
37
+ @db_name ||= uri.path.gsub(/\A\//, "")
38
+ end
39
+
40
+ def db_type
41
+ uri.scheme
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "database"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module DB
9
+ module Utils
10
+ class Mysql < Database
11
+ def create_command
12
+ raise "Not Implemented Yet"
13
+ end
14
+
15
+ def dump_command
16
+ raise "Not Implemented Yet"
17
+ end
18
+
19
+ def load_command
20
+ raise "Not Implemented Yet"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require_relative "database"
5
+
6
+ module Hanami
7
+ module CLI
8
+ module Commands
9
+ module DB
10
+ module Utils
11
+ class Postgres < Database
12
+ def create_command
13
+ system(cli_env_vars, "createdb #{escaped_name}")
14
+ end
15
+
16
+ def drop_command
17
+ system(cli_env_vars, "dropdb #{escaped_name}")
18
+ end
19
+
20
+ def dump_command
21
+ system(cli_env_vars, "pg_dump --schema-only --no-owner #{escaped_name} > #{dump_file}")
22
+ end
23
+
24
+ def load_command
25
+ raise "Not Implemented Yet"
26
+ end
27
+
28
+ def escaped_name
29
+ Shellwords.escape(name)
30
+ end
31
+
32
+ def cli_env_vars
33
+ @cli_env_vars ||= {}.tap do |vars|
34
+ vars["PGHOST"] = config.host.to_s
35
+ vars["PGPORT"] = config.port.to_s if config.port
36
+ vars["PGUSER"] = config.user.to_s if config.user
37
+ vars["PGPASSWORD"] = config.pass.to_s if config.pass
38
+ end
39
+ end
40
+
41
+ def dump_file
42
+ "#{root_path}/db/structure.sql"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "database"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module DB
9
+ module Utils
10
+ class Sqlite < Database
11
+ def create_command
12
+ rom_config
13
+ true
14
+ end
15
+
16
+ def drop_command
17
+ file_path.unlink
18
+ true
19
+ end
20
+
21
+ def dump_command
22
+ raise "Not Implemented Yet"
23
+ end
24
+
25
+ def load_command
26
+ raise "Not Implemented Yet"
27
+ end
28
+
29
+ def file_path
30
+ @file_path ||= Pathname("#{root_path}#{config.uri.path}").realpath
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Commands
6
+ module Gem
7
+ require_relative "gem/version"
8
+ # FIXME: temporary disabled for Hanami v2.0.0.alpha2
9
+ # require_relative "gem/new"
10
+
11
+ def self.extended(base)
12
+ base.module_eval do
13
+ register "version", Version, aliases: ["v", "-v", "--version"]
14
+ # FIXME: temporary disabled for Hanami v2.0.0.alpha2
15
+ # register "new", New
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/command"
4
+ require "hanami/cli/bundler"
5
+ require "hanami/cli/command_line"
6
+ require "hanami/cli/generators/gem/application"
7
+ require "dry/files"
8
+
9
+ module Hanami
10
+ module CLI
11
+ module Commands
12
+ module Gem
13
+ class New < Command
14
+ ARCHITECTURES = %w[monolith micro].freeze
15
+ private_constant :ARCHITECTURES
16
+
17
+ DEFAULT_ARCHITECTURE = ARCHITECTURES.first
18
+ private_constant :DEFAULT_ARCHITECTURE
19
+
20
+ DEFAULT_SLICE_NAME = "main"
21
+ private_constant :DEFAULT_SLICE_NAME
22
+
23
+ DEFAULT_SLICE_URL_PREFIX = "/"
24
+ private_constant :DEFAULT_SLICE_URL_PREFIX
25
+
26
+ argument :app, required: true, desc: "The application name"
27
+
28
+ option :architecture, alias: "arch", default: DEFAULT_ARCHITECTURE,
29
+ values: ARCHITECTURES, desc: "The architecture"
30
+
31
+ option :slice, default: DEFAULT_SLICE_NAME, desc: %(The initial slice name, only for "monolith" architecture)
32
+ option :slice_url_prefix, default: DEFAULT_SLICE_URL_PREFIX,
33
+ desc: %(The initial slice URL prefix, only for "monolith" architecture)
34
+
35
+ def initialize(fs: Dry::Files.new, bundler: CLI::Bundler.new(fs: fs),
36
+ command_line: CLI::CommandLine.new(bundler: bundler), **other)
37
+ @bundler = bundler
38
+ @command_line = command_line
39
+ super(fs: fs, **other)
40
+ end
41
+
42
+ def call(app:, architecture: DEFAULT_ARCHITECTURE, slice: DEFAULT_SLICE_NAME,
43
+ slice_url_prefix: DEFAULT_SLICE_URL_PREFIX, **)
44
+ app = inflector.underscore(app)
45
+
46
+ fs.mkdir(app)
47
+ fs.chdir(app) do
48
+ generator(architecture).call(app, slice, slice_url_prefix) do
49
+ bundler.install!
50
+ run_install_commmand!
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ attr_reader :bundler
58
+ attr_reader :command_line
59
+
60
+ def generator(architecture)
61
+ unless ARCHITECTURES.include?(architecture)
62
+ raise ArgumentError.new("unknown architecture `#{architecture}'")
63
+ end
64
+
65
+ Generators::Gem::Application[architecture, fs, inflector, command_line]
66
+ end
67
+
68
+ def run_install_commmand!
69
+ command_line.call("hanami install").tap do |result|
70
+ raise "hanami install failed\n\n\n#{result.err.inspect}" unless result.successful?
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end