hanami-cli 0.2.0 → 2.0.0.alpha2

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.
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 +25 -1
  6. data/CHANGELOG.md +39 -1
  7. data/CODE_OF_CONDUCT.md +84 -0
  8. data/Gemfile +13 -6
  9. data/LICENSE.txt +21 -0
  10. data/README.md +13 -488
  11. data/Rakefile +8 -15
  12. data/bin/console +1 -0
  13. data/exe/hanami +10 -0
  14. data/hanami-cli.gemspec +24 -18
  15. data/lib/hanami/cli.rb +10 -121
  16. data/lib/hanami/cli/bundler.rb +73 -0
  17. data/lib/hanami/cli/command.rb +16 -355
  18. data/lib/hanami/cli/command_line.rb +17 -0
  19. data/lib/hanami/cli/commands.rb +26 -0
  20. data/lib/hanami/cli/commands/application.rb +63 -0
  21. data/lib/hanami/cli/commands/db/utils/database.rb +122 -0
  22. data/lib/hanami/cli/commands/db/utils/database_config.rb +48 -0
  23. data/lib/hanami/cli/commands/db/utils/mysql.rb +27 -0
  24. data/lib/hanami/cli/commands/db/utils/postgres.rb +49 -0
  25. data/lib/hanami/cli/commands/db/utils/sqlite.rb +37 -0
  26. data/lib/hanami/cli/commands/gem.rb +21 -0
  27. data/lib/hanami/cli/commands/gem/new.rb +77 -0
  28. data/lib/hanami/cli/commands/gem/version.rb +18 -0
  29. data/lib/hanami/cli/commands/monolith.rb +55 -0
  30. data/lib/hanami/cli/commands/monolith/console.rb +50 -0
  31. data/lib/hanami/cli/commands/monolith/db/create.rb +25 -0
  32. data/lib/hanami/cli/commands/monolith/db/create_migration.rb +29 -0
  33. data/lib/hanami/cli/commands/monolith/db/drop.rb +25 -0
  34. data/lib/hanami/cli/commands/monolith/db/migrate.rb +40 -0
  35. data/lib/hanami/cli/commands/monolith/db/reset.rb +26 -0
  36. data/lib/hanami/cli/commands/monolith/db/rollback.rb +55 -0
  37. data/lib/hanami/cli/commands/monolith/db/sample_data.rb +40 -0
  38. data/lib/hanami/cli/commands/monolith/db/seed.rb +40 -0
  39. data/lib/hanami/cli/commands/monolith/db/setup.rb +24 -0
  40. data/lib/hanami/cli/commands/monolith/db/structure/dump.rb +25 -0
  41. data/lib/hanami/cli/commands/monolith/db/version.rb +26 -0
  42. data/lib/hanami/cli/commands/monolith/generate.rb +14 -0
  43. data/lib/hanami/cli/commands/monolith/generate/action.rb +62 -0
  44. data/lib/hanami/cli/commands/monolith/generate/slice.rb +62 -0
  45. data/lib/hanami/cli/commands/monolith/install.rb +16 -0
  46. data/lib/hanami/cli/commands/monolith/version.rb +18 -0
  47. data/lib/hanami/cli/error.rb +8 -0
  48. data/lib/hanami/cli/generators/context.rb +38 -0
  49. data/lib/hanami/cli/generators/gem/application.rb +21 -0
  50. data/lib/hanami/cli/generators/gem/application/monolith.rb +83 -0
  51. data/lib/hanami/cli/generators/gem/application/monolith/action.erb +21 -0
  52. data/lib/hanami/cli/generators/gem/application/monolith/application.erb +8 -0
  53. data/lib/hanami/cli/generators/gem/application/monolith/config_ru.erb +5 -0
  54. data/lib/hanami/cli/generators/gem/application/monolith/entities.erb +9 -0
  55. data/lib/hanami/cli/generators/gem/application/monolith/env.erb +0 -0
  56. data/lib/hanami/cli/generators/gem/application/monolith/functions.erb +13 -0
  57. data/lib/hanami/cli/generators/gem/application/monolith/gemfile.erb +19 -0
  58. data/lib/hanami/cli/generators/gem/application/monolith/keep.erb +0 -0
  59. data/lib/hanami/cli/generators/gem/application/monolith/operation.erb +18 -0
  60. data/lib/hanami/cli/generators/gem/application/monolith/rakefile.erb +3 -0
  61. data/lib/hanami/cli/generators/gem/application/monolith/readme.erb +1 -0
  62. data/lib/hanami/cli/generators/gem/application/monolith/repository.erb +13 -0
  63. data/lib/hanami/cli/generators/gem/application/monolith/routes.erb +4 -0
  64. data/lib/hanami/cli/generators/gem/application/monolith/settings.erb +6 -0
  65. data/lib/hanami/cli/generators/gem/application/monolith/types.erb +10 -0
  66. data/lib/hanami/cli/generators/gem/application/monolith/validation_contract.erb +14 -0
  67. data/lib/hanami/cli/generators/gem/application/monolith/view_context.erb +15 -0
  68. data/lib/hanami/cli/generators/monolith/action.rb +123 -0
  69. data/lib/hanami/cli/generators/monolith/action/action.erb +13 -0
  70. data/lib/hanami/cli/generators/monolith/action/template.erb +0 -0
  71. data/lib/hanami/cli/generators/monolith/action/template.html.erb +2 -0
  72. data/lib/hanami/cli/generators/monolith/action/view.erb +13 -0
  73. data/lib/hanami/cli/generators/monolith/action_context.rb +76 -0
  74. data/lib/hanami/cli/generators/monolith/slice.rb +56 -0
  75. data/lib/hanami/cli/generators/monolith/slice/action.erb +9 -0
  76. data/lib/hanami/cli/generators/monolith/slice/entities.erb +9 -0
  77. data/lib/hanami/cli/generators/monolith/slice/keep.erb +0 -0
  78. data/lib/hanami/cli/generators/monolith/slice/repository.erb +10 -0
  79. data/lib/hanami/cli/generators/monolith/slice/routes.erb +2 -0
  80. data/lib/hanami/cli/generators/monolith/slice/view.erb +9 -0
  81. data/lib/hanami/cli/generators/monolith/slice_context.rb +33 -0
  82. data/lib/hanami/cli/repl/core.rb +55 -0
  83. data/lib/hanami/cli/repl/irb.rb +41 -0
  84. data/lib/hanami/cli/repl/pry.rb +29 -0
  85. data/lib/hanami/cli/system_call.rb +51 -0
  86. data/lib/hanami/cli/url.rb +34 -0
  87. data/lib/hanami/cli/version.rb +4 -3
  88. data/lib/hanami/console/context.rb +39 -0
  89. data/lib/hanami/console/plugins/slice_readers.rb +42 -0
  90. data/lib/hanami/rake_tasks.rb +52 -0
  91. metadata +138 -41
  92. data/.travis.yml +0 -16
  93. data/lib/hanami/cli/banner.rb +0 -126
  94. data/lib/hanami/cli/command_registry.rb +0 -213
  95. data/lib/hanami/cli/errors.rb +0 -32
  96. data/lib/hanami/cli/option.rb +0 -125
  97. data/lib/hanami/cli/parser.rb +0 -122
  98. data/lib/hanami/cli/program_name.rb +0 -19
  99. data/lib/hanami/cli/registry.rb +0 -328
  100. data/lib/hanami/cli/usage.rb +0 -88
  101. data/script/ci +0 -51
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../application"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module Monolith
9
+ module DB
10
+ module Structure
11
+ class Dump < Application
12
+ desc "Dumps database structure to db/structure.sql file"
13
+
14
+ def call(*)
15
+ measure("#{database.name} structure dumped to db/structure.sql") do
16
+ database.dump_command
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../application"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module Monolith
9
+ module DB
10
+ class Version < Application
11
+ desc "Print schema version"
12
+
13
+ option :target, desc: "Target migration number", aliases: ["-t"]
14
+
15
+ def call(target: nil, **)
16
+ migration = database.applied_migrations.last
17
+ version = migration ? File.basename(migration, ".*") : "not available"
18
+
19
+ out.puts "=> current schema version is #{version}"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Commands
6
+ module Monolith
7
+ module Generate
8
+ require_relative "./generate/slice"
9
+ require_relative "./generate/action"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/command"
4
+ require "hanami/cli/generators/monolith/action"
5
+ require "dry/inflector"
6
+ require "dry/files"
7
+ require "shellwords"
8
+
9
+ module Hanami
10
+ module CLI
11
+ module Commands
12
+ module Monolith
13
+ module Generate
14
+ class Action < Command
15
+ # TODO: ideally the default format should lookup
16
+ # slice configuration (Action's `default_response_format`)
17
+ DEFAULT_FORMAT = "html"
18
+ private_constant :DEFAULT_FORMAT
19
+
20
+ DEFAULT_SKIP_VIEW = false
21
+ private_constant :DEFAULT_SKIP_VIEW
22
+
23
+ argument :slice, required: true, desc: "Slice name"
24
+ argument :name, required: true, desc: "Action name"
25
+ option :url, required: false, type: :string, desc: "Action URL"
26
+ option :http, required: false, type: :string, desc: "Action HTTP method"
27
+ option :format, required: false, type: :string, default: DEFAULT_FORMAT, desc: "Template format"
28
+ option :skip_view, required: false, type: :boolean, default: DEFAULT_SKIP_VIEW,
29
+ desc: "Skip view and template generation"
30
+
31
+ def initialize(fs: Dry::Files.new, inflector: Dry::Inflector.new,
32
+ generator: Generators::Monolith::Action.new(fs: fs, inflector: inflector), **)
33
+ @generator = generator
34
+ super(fs: fs)
35
+ end
36
+
37
+ # rubocop:disable Metrics/ParameterLists
38
+ def call(slice:, name:, url: nil, http: nil, format: DEFAULT_FORMAT, skip_view: DEFAULT_SKIP_VIEW, **)
39
+ slice = inflector.underscore(Shellwords.shellescape(slice))
40
+ name = inflector.underscore(Shellwords.shellescape(name))
41
+ *controller, action = name.split(ACTION_SEPARATOR)
42
+
43
+ if controller.empty?
44
+ raise ArgumentError.new("cannot parse controller and action name: `#{name}'\n\texample: users.show")
45
+ end
46
+
47
+ generator.call(slice, controller, action, url, http, format, skip_view)
48
+ end
49
+ # rubocop:enable Metrics/ParameterLists
50
+
51
+ private
52
+
53
+ ACTION_SEPARATOR = "."
54
+ private_constant :ACTION_SEPARATOR
55
+
56
+ attr_reader :generator
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/command"
4
+ require "hanami/cli/generators/monolith/slice"
5
+ require "dry/inflector"
6
+ require "dry/files"
7
+ require "shellwords"
8
+ require "hanami/cli/url"
9
+
10
+ module Hanami
11
+ module CLI
12
+ module Commands
13
+ module Monolith
14
+ module Generate
15
+ class Slice < Command
16
+ argument :name, required: true, desc: "The slice name"
17
+ option :url_prefix, required: false, type: :string, desc: "The slice URL prefix"
18
+
19
+ def initialize(fs: Dry::Files.new, inflector: Dry::Inflector.new,
20
+ generator: Generators::Monolith::Slice.new(fs: fs, inflector: inflector), **)
21
+ @generator = generator
22
+ super(fs: fs)
23
+ end
24
+
25
+ def call(name:, url_prefix: nil, **)
26
+ require "hanami/setup"
27
+
28
+ app = inflector.underscore(Hanami.application.namespace)
29
+ name = inflector.underscore(Shellwords.shellescape(name))
30
+ url_prefix = sanitize_url_prefix(name, url_prefix)
31
+
32
+ generator.call(app, name, url_prefix)
33
+ end
34
+
35
+ private
36
+
37
+ DEFAULT_URL_PREFIX = "/"
38
+ private_constant :DEFAULT_URL_PREFIX
39
+
40
+ attr_reader :generator
41
+
42
+ def sanitize_url_prefix(name, url_prefix)
43
+ result = url_prefix
44
+ result = inflector.underscore(Shellwords.shellescape(result)) unless result.nil?
45
+
46
+ result ||= DEFAULT_URL_PREFIX + name
47
+ CLI::URL.call(result)
48
+ rescue ArgumentError
49
+ raise ArgumentError.new("invalid URL prefix: `#{url_prefix}'")
50
+ end
51
+
52
+ def valid_url?(url)
53
+ return false if url.nil?
54
+
55
+ url.start_with?(DEFAULT_URL_PREFIX)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/command"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module Monolith
9
+ class Install < Command
10
+ def call(*)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/cli/command"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Commands
8
+ module Monolith
9
+ class Version < Command
10
+ def call(*)
11
+ require "hanami/version"
12
+ out.puts "v#{Hanami::VERSION}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ class Error < StandardError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/version"
4
+
5
+ module Hanami
6
+ module CLI
7
+ module Generators
8
+ class Context
9
+ def initialize(inflector, app)
10
+ @inflector = inflector
11
+ @app = app
12
+ end
13
+
14
+ def ctx
15
+ binding
16
+ end
17
+
18
+ def hanami_version
19
+ Hanami::Version.gem_requirement
20
+ end
21
+
22
+ def classified_app_name
23
+ inflector.classify(app)
24
+ end
25
+
26
+ def underscored_app_name
27
+ inflector.underscore(app)
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :inflector
33
+
34
+ attr_reader :app
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ module CLI
5
+ module Generators
6
+ module Gem
7
+ module Application
8
+ class << self
9
+ def call(architecture, fs, inflector, command_line)
10
+ require_relative "./application/#{architecture}"
11
+
12
+ generator_name = inflector.classify(architecture).to_sym
13
+ const_get(generator_name).new(fs: fs, inflector: inflector, command_line: command_line)
14
+ end
15
+ alias_method :[], :call
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "shellwords"
5
+ require "hanami/cli/generators/context"
6
+
7
+ module Hanami
8
+ module CLI
9
+ module Generators
10
+ module Gem
11
+ module Application
12
+ class Monolith
13
+ def initialize(fs:, inflector:, command_line:)
14
+ super()
15
+ @fs = fs
16
+ @inflector = inflector
17
+ @command_line = command_line
18
+ end
19
+
20
+ def call(app, slice, slice_url_prefix, context: Context.new(inflector, app), &blk)
21
+ generate_app(app, context)
22
+ blk.call
23
+ generate_slice!(slice, slice_url_prefix)
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :fs
29
+
30
+ attr_reader :inflector
31
+
32
+ attr_reader :command_line
33
+
34
+ def generate_app(app, context) # rubocop:disable Metrics/AbcSize
35
+ fs.write(".env", t("env.erb", context))
36
+
37
+ fs.write("README.md", t("readme.erb", context))
38
+ fs.write("Gemfile", t("gemfile.erb", context))
39
+ fs.write("Rakefile", t("rakefile.erb", context))
40
+ fs.write("config.ru", t("config_ru.erb", context))
41
+
42
+ fs.write("config/application.rb", t("application.erb", context))
43
+ fs.write("config/settings.rb", t("settings.erb", context))
44
+ fs.write("config/routes.rb", t("routes.erb", context))
45
+
46
+ fs.write("lib/tasks/.keep", t("keep.erb", context))
47
+
48
+ fs.write("lib/#{app}/entities/.keep", t("keep.erb", context))
49
+ fs.write("lib/#{app}/persistence/relations/.keep", t("keep.erb", context))
50
+ fs.write("lib/#{app}/validation/contract.rb", t("validation_contract.erb", context))
51
+ fs.write("lib/#{app}/view/context.rb", t("view_context.erb", context))
52
+ fs.write("lib/#{app}/action.rb", t("action.erb", context))
53
+ fs.write("lib/#{app}/entities.rb", t("entities.erb", context))
54
+ fs.write("lib/#{app}/functions.rb", t("functions.erb", context))
55
+ fs.write("lib/#{app}/operation.rb", t("operation.erb", context))
56
+ fs.write("lib/#{app}/repository.rb", t("repository.erb", context))
57
+ fs.write("lib/#{app}/types.rb", t("types.erb", context))
58
+ end
59
+
60
+ def generate_slice!(slice_name, slice_url_prefix)
61
+ slice_name = Shellwords.shellescape(slice_name)
62
+ slice_url_prefix = Shellwords.shellescape(slice_url_prefix)
63
+
64
+ command_line.call("hanami generate slice #{slice_name} --url-prefix=#{slice_url_prefix}").tap do |result|
65
+ raise "hanami generate slice #{slice_name} failed\n\n\n#{result.err.inspect}" unless result.successful?
66
+ end
67
+ end
68
+
69
+ def template(path, context)
70
+ require "erb"
71
+
72
+ ERB.new(
73
+ File.read(__dir__ + "/monolith/#{path}")
74
+ ).result(context.ctx)
75
+ end
76
+
77
+ alias_method :t, :template
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,21 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ require "json" # required for Hanami::Action::Flash to work
5
+ require "hanami/action"
6
+ require "hanami/action/cookies"
7
+ require "hanami/action/csrf_protection"
8
+ require "hanami/action/session"
9
+
10
+ module <%= classified_app_name %>
11
+ class Action < Hanami::Action
12
+ def self.inherited(klass)
13
+ super
14
+
15
+ # These will need to be sorted by the framework eventually
16
+ klass.include Hanami::Action::Cookies
17
+ klass.include Hanami::Action::Session
18
+ klass.include Hanami::Action::CSRFProtection
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami"
4
+
5
+ module <%= classified_app_name %>
6
+ class Application < Hanami::Application
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/boot"
4
+
5
+ run Hanami.app
@@ -0,0 +1,9 @@
1
+ # auto_register: false
2
+ # frozen_string_literal: true
3
+
4
+ module <%= classified_app_name %>
5
+ module Entities
6
+ end
7
+ end
8
+
9
+ Dir[File.join(__dir__, "entities", "*.rb")].each(&method(:require))