hanami 1.0.0 → 1.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/FEATURES.md +12 -0
  4. data/README.md +3 -0
  5. data/bin/hanami +3 -3
  6. data/hanami.gemspec +9 -9
  7. data/lib/hanami.rb +27 -0
  8. data/lib/hanami/action/routing_helpers.rb +1 -1
  9. data/lib/hanami/application_configuration.rb +0 -8
  10. data/lib/hanami/application_name.rb +1 -3
  11. data/lib/hanami/application_namespace.rb +1 -1
  12. data/lib/hanami/cli/commands.rb +101 -0
  13. data/lib/hanami/cli/commands/assets.rb +16 -0
  14. data/lib/hanami/cli/commands/assets/precompile.rb +42 -0
  15. data/lib/hanami/cli/commands/command.rb +180 -0
  16. data/lib/hanami/cli/commands/console.rb +95 -0
  17. data/lib/hanami/cli/commands/db.rb +30 -0
  18. data/lib/hanami/cli/commands/db/apply.rb +32 -0
  19. data/lib/hanami/cli/commands/db/console.rb +44 -0
  20. data/lib/hanami/cli/commands/db/create.rb +32 -0
  21. data/lib/hanami/cli/commands/db/drop.rb +32 -0
  22. data/lib/hanami/cli/commands/db/migrate.rb +39 -0
  23. data/lib/hanami/cli/commands/db/prepare.rb +32 -0
  24. data/lib/hanami/cli/commands/db/version.rb +32 -0
  25. data/lib/hanami/cli/commands/destroy.rb +26 -0
  26. data/lib/hanami/cli/commands/destroy/action.rb +131 -0
  27. data/lib/hanami/cli/commands/destroy/app.rb +159 -0
  28. data/lib/hanami/cli/commands/destroy/mailer.rb +74 -0
  29. data/lib/hanami/cli/commands/destroy/migration.rb +51 -0
  30. data/lib/hanami/cli/commands/destroy/model.rb +84 -0
  31. data/lib/hanami/cli/commands/generate.rb +28 -0
  32. data/lib/hanami/cli/commands/generate/action.rb +224 -0
  33. data/lib/hanami/cli/commands/generate/action/action.erb +8 -0
  34. data/lib/hanami/cli/commands/generate/action/action_spec.minitest.erb +11 -0
  35. data/lib/hanami/{generators/action/action_spec.rspec.tt → cli/commands/generate/action/action_spec.rspec.erb} +1 -3
  36. data/lib/hanami/cli/commands/generate/action/action_without_view.erb +9 -0
  37. data/lib/hanami/cli/commands/generate/action/view.erb +5 -0
  38. data/lib/hanami/cli/commands/generate/action/view_spec.minitest.erb +15 -0
  39. data/lib/hanami/{generators/action/view_spec.rspec.tt → cli/commands/generate/action/view_spec.rspec.erb} +2 -4
  40. data/lib/hanami/cli/commands/generate/app.rb +231 -0
  41. data/lib/hanami/{generators/app/application.rb.tt → cli/commands/generate/app/application.erb} +6 -6
  42. data/lib/hanami/{generators → cli/commands/generate}/app/favicon.ico +0 -0
  43. data/lib/hanami/{generators/action/template.tt → cli/commands/generate/app/gitkeep.erb} +0 -0
  44. data/lib/hanami/cli/commands/generate/app/layout.erb +7 -0
  45. data/lib/hanami/cli/commands/generate/app/layout_spec.minitest.erb +11 -0
  46. data/lib/hanami/cli/commands/generate/app/layout_spec.rspec.erb +11 -0
  47. data/lib/hanami/{generators/app/config/routes.rb.tt → cli/commands/generate/app/routes.erb} +0 -0
  48. data/lib/hanami/{generators/app/templates/application.html.erb.tt → cli/commands/generate/app/template.erb.erb} +1 -1
  49. data/lib/hanami/{generators/app/templates/application.html.haml.tt → cli/commands/generate/app/template.haml.erb} +1 -1
  50. data/lib/hanami/{generators/app/templates/application.html.slim.tt → cli/commands/generate/app/template.slim.erb} +1 -1
  51. data/lib/hanami/cli/commands/generate/mailer.rb +94 -0
  52. data/lib/hanami/cli/commands/generate/mailer/mailer.erb +7 -0
  53. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.minitest.erb +7 -0
  54. data/lib/hanami/cli/commands/generate/mailer/mailer_spec.rspec.erb +5 -0
  55. data/lib/hanami/cli/commands/generate/migration.rb +41 -0
  56. data/lib/hanami/{generators/migration/migration.rb.tt → cli/commands/generate/migration/migration.erb} +0 -0
  57. data/lib/hanami/cli/commands/generate/model.rb +98 -0
  58. data/lib/hanami/cli/commands/generate/model/entity.erb +2 -0
  59. data/lib/hanami/cli/commands/generate/model/entity_spec.minitest.erb +5 -0
  60. data/lib/hanami/cli/commands/generate/model/entity_spec.rspec.erb +3 -0
  61. data/lib/hanami/{generators/model/migration.rb.tt → cli/commands/generate/model/migration.erb} +1 -1
  62. data/lib/hanami/cli/commands/generate/model/repository.erb +2 -0
  63. data/lib/hanami/cli/commands/generate/model/repository_spec.minitest.erb +5 -0
  64. data/lib/hanami/cli/commands/generate/model/repository_spec.rspec.erb +3 -0
  65. data/lib/hanami/cli/commands/generate/secret.rb +48 -0
  66. data/lib/hanami/cli/commands/new.rb +575 -0
  67. data/lib/hanami/{generators/application/container/.env.development.tt → cli/commands/new/.env.development.erb} +1 -1
  68. data/lib/hanami/{generators/application/container/.env.test.tt → cli/commands/new/.env.test.erb} +1 -1
  69. data/lib/hanami/{generators/app/config/initializers/.gitkeep → cli/commands/new/.gitkeep.erb} +0 -0
  70. data/lib/hanami/cli/commands/new/Gemfile.erb +57 -0
  71. data/lib/hanami/cli/commands/new/README.md.erb +35 -0
  72. data/lib/hanami/{generators/application/container/config.ru.tt → cli/commands/new/config.ru.erb} +0 -0
  73. data/lib/hanami/{generators/application/container/config/boot.rb.tt → cli/commands/new/config/boot.erb} +0 -0
  74. data/lib/hanami/{generators/application/container/config/environment.rb.tt → cli/commands/new/config/environment.erb} +8 -8
  75. data/lib/hanami/{generators/application/container/gitignore.tt → cli/commands/new/gitignore.erb} +0 -0
  76. data/lib/hanami/{generators/application/container/gitignore_with_sqlite.tt → cli/commands/new/gitignore_with_sqlite.erb} +0 -0
  77. data/lib/hanami/cli/commands/new/hanamirc.erb +3 -0
  78. data/lib/hanami/cli/commands/new/lib/project.erb +2 -0
  79. data/lib/hanami/{generators/application/container/Rakefile.minitest.tt → cli/commands/new/minitest/Rakefile.erb} +0 -0
  80. data/lib/hanami/{generators/application/container/features_helper.rb.minitest.tt → cli/commands/new/minitest/features_helper.erb} +0 -0
  81. data/lib/hanami/{generators/application/container/spec_helper.rb.minitest.tt → cli/commands/new/minitest/spec_helper.erb} +0 -0
  82. data/lib/hanami/{generators/application/container/Rakefile.rspec.tt → cli/commands/new/rspec/Rakefile.erb} +0 -0
  83. data/lib/hanami/{generators/application/container/capybara.rb.rspec.tt → cli/commands/new/rspec/capybara.erb} +0 -0
  84. data/lib/hanami/{generators/application/container/features_helper.rb.rspec.tt → cli/commands/new/rspec/features_helper.erb} +0 -0
  85. data/lib/hanami/{generators/application/container/rspec.rspec.tt → cli/commands/new/rspec/rspec.erb} +0 -0
  86. data/lib/hanami/{generators/application/container/spec_helper.rb.rspec.tt → cli/commands/new/rspec/spec_helper.erb} +0 -0
  87. data/lib/hanami/{generators/application/container/.gitignore → cli/commands/new/schema.sql.erb} +0 -0
  88. data/lib/hanami/cli/commands/project.rb +421 -0
  89. data/lib/hanami/cli/commands/routes.rb +21 -0
  90. data/lib/hanami/cli/commands/server.rb +39 -0
  91. data/lib/hanami/cli/commands/templates.rb +31 -0
  92. data/lib/hanami/cli/commands/version.rb +19 -0
  93. data/lib/hanami/common_logger.rb +14 -4
  94. data/lib/hanami/components.rb +39 -1
  95. data/lib/hanami/components/components.rb +36 -1
  96. data/lib/hanami/config/sessions.rb +1 -1
  97. data/lib/hanami/environment.rb +20 -50
  98. data/lib/hanami/hanamirc.rb +7 -30
  99. data/lib/hanami/middleware.rb +0 -26
  100. data/lib/hanami/static.rb +1 -1
  101. data/lib/hanami/templates/welcome.html.erb +1 -1
  102. data/lib/hanami/version.rb +1 -1
  103. data/lib/hanami/welcome.rb +1 -11
  104. metadata +125 -135
  105. data/lib/hanami/cli.rb +0 -154
  106. data/lib/hanami/cli_base.rb +0 -49
  107. data/lib/hanami/cli_sub_commands/assets.rb +0 -24
  108. data/lib/hanami/cli_sub_commands/db.rb +0 -138
  109. data/lib/hanami/cli_sub_commands/destroy.rb +0 -115
  110. data/lib/hanami/cli_sub_commands/generate.rb +0 -150
  111. data/lib/hanami/commands/apps.rb +0 -4
  112. data/lib/hanami/commands/assets/precompile.rb +0 -26
  113. data/lib/hanami/commands/command.rb +0 -77
  114. data/lib/hanami/commands/console.rb +0 -107
  115. data/lib/hanami/commands/db/apply.rb +0 -19
  116. data/lib/hanami/commands/db/console.rb +0 -41
  117. data/lib/hanami/commands/db/create.rb +0 -19
  118. data/lib/hanami/commands/db/drop.rb +0 -19
  119. data/lib/hanami/commands/db/migrate.rb +0 -30
  120. data/lib/hanami/commands/db/prepare.rb +0 -19
  121. data/lib/hanami/commands/db/version.rb +0 -19
  122. data/lib/hanami/commands/generate/abstract.rb +0 -76
  123. data/lib/hanami/commands/generate/action.rb +0 -322
  124. data/lib/hanami/commands/generate/app.rb +0 -154
  125. data/lib/hanami/commands/generate/mailer.rb +0 -131
  126. data/lib/hanami/commands/generate/migration.rb +0 -75
  127. data/lib/hanami/commands/generate/model.rb +0 -132
  128. data/lib/hanami/commands/generate/secret_token.rb +0 -37
  129. data/lib/hanami/commands/new/abstract.rb +0 -178
  130. data/lib/hanami/commands/new/app.rb +0 -129
  131. data/lib/hanami/commands/new/container.rb +0 -119
  132. data/lib/hanami/commands/routes.rb +0 -26
  133. data/lib/hanami/commands/server.rb +0 -32
  134. data/lib/hanami/generators/action/action.rb.tt +0 -8
  135. data/lib/hanami/generators/action/action_spec.minitest.tt +0 -12
  136. data/lib/hanami/generators/action/action_without_view.rb.tt +0 -9
  137. data/lib/hanami/generators/action/view.rb.tt +0 -5
  138. data/lib/hanami/generators/action/view_spec.minitest.tt +0 -16
  139. data/lib/hanami/generators/app/.gitkeep.tt +0 -1
  140. data/lib/hanami/generators/app/views/application_layout.rb.tt +0 -7
  141. data/lib/hanami/generators/application/container/.gitkeep +0 -1
  142. data/lib/hanami/generators/application/container/Gemfile.tt +0 -56
  143. data/lib/hanami/generators/application/container/config/initializers/.gitkeep +0 -0
  144. data/lib/hanami/generators/application/container/db/.gitkeep +0 -1
  145. data/lib/hanami/generators/application/container/hanamirc.tt +0 -4
  146. data/lib/hanami/generators/application/container/lib/chirp/entities/.gitkeep +0 -1
  147. data/lib/hanami/generators/application/container/lib/chirp/mailers/.gitkeep +0 -0
  148. data/lib/hanami/generators/application/container/lib/chirp/mailers/templates/.gitkeep +0 -0
  149. data/lib/hanami/generators/application/container/lib/chirp/repositories/.gitkeep +0 -1
  150. data/lib/hanami/generators/application/container/lib/project.rb.tt +0 -2
  151. data/lib/hanami/generators/application/container/schema.sql.tt +0 -0
  152. data/lib/hanami/generators/database_config.rb +0 -114
  153. data/lib/hanami/generators/generatable.rb +0 -69
  154. data/lib/hanami/generators/generator.rb +0 -52
  155. data/lib/hanami/generators/mailer/mailer.rb.tt +0 -7
  156. data/lib/hanami/generators/mailer/mailer_spec.rb.minitest.tt +0 -7
  157. data/lib/hanami/generators/mailer/mailer_spec.rb.rspec.tt +0 -5
  158. data/lib/hanami/generators/mailer/template.html.tt +0 -0
  159. data/lib/hanami/generators/mailer/template.txt.tt +0 -0
  160. data/lib/hanami/generators/model/entity.rb.tt +0 -2
  161. data/lib/hanami/generators/model/entity_spec.minitest.tt +0 -5
  162. data/lib/hanami/generators/model/entity_spec.rspec.tt +0 -3
  163. data/lib/hanami/generators/model/repository.rb.tt +0 -2
  164. data/lib/hanami/generators/model/repository_spec.minitest.tt +0 -5
  165. data/lib/hanami/generators/model/repository_spec.rspec.tt +0 -3
  166. data/lib/hanami/generators/template_engine.rb +0 -44
  167. data/lib/hanami/generators/test_framework.rb +0 -55
@@ -0,0 +1,7 @@
1
+ class Mailers::<%= mailer.classify %>
2
+ include Hanami::Mailer
3
+
4
+ from <%= from %>
5
+ to <%= to %>
6
+ subject <%= subject %>
7
+ end
@@ -0,0 +1,7 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Mailers::<%= mailer.classify %> do
4
+ it 'delivers email' do
5
+ mail = Mailers::<%= mailer.classify %>.deliver
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe Mailers::<%= mailer.classify %> do
2
+ it 'delivers email' do
3
+ mail = Mailers::<%= mailer.classify %>.deliver
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ module Hanami
2
+ class CLI
3
+ module Commands
4
+ module Generate
5
+ # @since 1.1.0
6
+ # @api private
7
+ class Migration < Command
8
+ requires "environment"
9
+ desc "Generate a migration"
10
+
11
+ argument :migration, required: true, desc: "The migration name (eg. `create_users`)"
12
+
13
+ example [
14
+ "create_users # Generate `db/migrations/#{Project.migration_timestamp}_create_users.rb`"
15
+ ]
16
+
17
+ # @since 1.1.0
18
+ # @api private
19
+ def call(migration:, **options)
20
+ migration = Utils::String.underscore(migration)
21
+ context = Context.new(migration: migration, options: options)
22
+
23
+ generate_migration(context)
24
+ end
25
+
26
+ private
27
+
28
+ # @since 1.1.0
29
+ # @api private
30
+ def generate_migration(context)
31
+ source = templates.find("migration.erb")
32
+ destination = project.migration(context)
33
+
34
+ generate_file(source, destination, context)
35
+ say(:create, destination)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,98 @@
1
+ module Hanami
2
+ class CLI
3
+ module Commands
4
+ module Generate
5
+ # @since 1.1.0
6
+ # @api private
7
+ class Model < Command
8
+ requires "environment"
9
+
10
+ desc "Generate a model"
11
+
12
+ argument :model, required: true, desc: "Model name (eg. `user`)"
13
+ option :skip_migration, type: :boolean, default: false, desc: "Skip migration"
14
+
15
+ example [
16
+ "user # Generate `User` entity, `UserRepository` repository, and the migration",
17
+ "user --skip-migration # Generate `User` entity and `UserRepository` repository"
18
+ ]
19
+
20
+ # @since 1.1.0
21
+ # @api private
22
+ def call(model:, **options)
23
+ model = Utils::String.underscore(model)
24
+ relation = Utils::String.pluralize(model)
25
+ migration = "create_#{relation}"
26
+ context = Context.new(model: model, relation: relation, migration: migration, test: options.fetch(:test), options: options)
27
+
28
+ generate_entity(context)
29
+ generate_repository(context)
30
+ generate_migration(context)
31
+ generate_entity_spec(context)
32
+ generate_repository_spec(context)
33
+ end
34
+
35
+ private
36
+
37
+ # @since 1.1.0
38
+ # @api private
39
+ def generate_entity(context)
40
+ source = templates.find("entity.erb")
41
+ destination = project.entity(context)
42
+
43
+ generate_file(source, destination, context)
44
+ say(:create, destination)
45
+ end
46
+
47
+ # @since 1.1.0
48
+ # @api private
49
+ def generate_repository(context)
50
+ source = templates.find("repository.erb")
51
+ destination = project.repository(context)
52
+
53
+ generate_file(source, destination, context)
54
+ say(:create, destination)
55
+ end
56
+
57
+ # @since 1.1.0
58
+ # @api private
59
+ def generate_migration(context)
60
+ return if skip_migration?(context)
61
+
62
+ source = templates.find("migration.erb")
63
+ destination = project.migration(context)
64
+
65
+ generate_file(source, destination, context)
66
+ say(:create, destination)
67
+ end
68
+
69
+ # @since 1.1.0
70
+ # @api private
71
+ def generate_entity_spec(context)
72
+ source = templates.find("entity_spec.#{context.test}.erb")
73
+ destination = project.entity_spec(context)
74
+
75
+ generate_file(source, destination, context)
76
+ say(:create, destination)
77
+ end
78
+
79
+ # @since 1.1.0
80
+ # @api private
81
+ def generate_repository_spec(context)
82
+ source = templates.find("repository_spec.#{context.test}.erb")
83
+ destination = project.repository_spec(context)
84
+
85
+ generate_file(source, destination, context)
86
+ say(:create, destination)
87
+ end
88
+
89
+ # @since 1.1.0
90
+ # @api private
91
+ def skip_migration?(context)
92
+ context.options.fetch(:skip_migration, false)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,2 @@
1
+ class <%= model.classify %> < Hanami::Entity
2
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe <%= model.classify %> do
4
+ # place your tests here
5
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.describe <%= model.classify %> do
2
+ # place your tests here
3
+ end
@@ -1,6 +1,6 @@
1
1
  Hanami::Model.migration do
2
2
  change do
3
- create_table :<%= config[:table_name] %> do
3
+ create_table :<%= relation %> do
4
4
  primary_key :id
5
5
 
6
6
  column :created_at, DateTime, null: false
@@ -0,0 +1,2 @@
1
+ class <%= model.classify %>Repository < Hanami::Repository
2
+ end
@@ -0,0 +1,5 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe <%= model.classify %>Repository do
4
+ # place your tests here
5
+ end
@@ -0,0 +1,3 @@
1
+ RSpec.describe <%= model.classify %>Repository do
2
+ # place your tests here
3
+ end
@@ -0,0 +1,48 @@
1
+ require "hanami/utils/blank"
2
+ require "securerandom"
3
+
4
+ module Hanami
5
+ class CLI
6
+ module Commands
7
+ module Generate
8
+ # @since 1.1.0
9
+ # @api private
10
+ class Secret < Command
11
+ requires "environment"
12
+
13
+ desc "Generate session secret"
14
+
15
+ argument :app, desc: "The application name (eg. `web`)"
16
+
17
+ example [
18
+ " # Prints secret (eg. `#{Project.app_sessions_secret}`)",
19
+ "web # Prints session secret (eg. `WEB_SESSIONS_SECRET=#{Project.app_sessions_secret}`)"
20
+ ]
21
+
22
+ # @since 1.1.0
23
+ # @api private
24
+ def call(app: nil, **options)
25
+ context = Context.new(app: app, options: options)
26
+
27
+ generate_secret(context)
28
+ end
29
+
30
+ private
31
+
32
+ # @since 1.1.0
33
+ # @api private
34
+ def generate_secret(context)
35
+ secret = project.app_sessions_secret
36
+
37
+ if Hanami::Utils::Blank.blank?(context.app)
38
+ puts secret
39
+ else
40
+ puts "Set the following environment variable to provide the secret token:"
41
+ puts %(#{context.app.upcase}_SESSIONS_SECRET="#{secret}")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,575 @@
1
+ require "hanami/cli/commands/generate/app"
2
+
3
+ module Hanami
4
+ # Hanami CLI
5
+ #
6
+ # @since 1.1.0
7
+ class CLI
8
+ module Commands
9
+ # @since 1.1.0
10
+ # @api private
11
+ class New < Command # rubocop:disable Metrics/ClassLength
12
+ # @since 1.1.0
13
+ # @api private
14
+ class DatabaseConfig
15
+ # @since 1.1.0
16
+ # @api private
17
+ SUPPORTED_ENGINES = {
18
+ 'mysql' => { type: :sql, mri: 'mysql2', jruby: 'jdbc-mysql' },
19
+ 'mysql2' => { type: :sql, mri: 'mysql2', jruby: 'jdbc-mysql' },
20
+ 'postgresql' => { type: :sql, mri: 'pg', jruby: 'jdbc-postgres' },
21
+ 'postgres' => { type: :sql, mri: 'pg', jruby: 'jdbc-postgres' },
22
+ 'sqlite' => { type: :sql, mri: 'sqlite3', jruby: 'jdbc-sqlite3' },
23
+ 'sqlite3' => { type: :sql, mri: 'sqlite3', jruby: 'jdbc-sqlite3' }
24
+ }.freeze
25
+
26
+ # @since 1.1.0
27
+ # @api private
28
+ DEFAULT_ENGINE = 'sqlite'.freeze
29
+
30
+ # @since 1.1.0
31
+ # @api private
32
+ attr_reader :engine
33
+
34
+ # @since 1.1.0
35
+ # @api private
36
+ attr_reader :name
37
+
38
+ # @since 1.1.0
39
+ # @api private
40
+ def initialize(engine, name)
41
+ @engine = engine
42
+ @name = name
43
+
44
+ unless SUPPORTED_ENGINES.key?(engine.to_s) # rubocop:disable Style/GuardClause
45
+ warn %(`#{engine}' is not a valid database engine)
46
+ exit(1)
47
+ end
48
+ end
49
+
50
+ # @since 1.1.0
51
+ # @api private
52
+ def to_hash
53
+ {
54
+ gem: gem,
55
+ uri: uri,
56
+ type: type
57
+ }
58
+ end
59
+
60
+ # @since 1.1.0
61
+ # @api private
62
+ def type
63
+ SUPPORTED_ENGINES[engine][:type]
64
+ end
65
+
66
+ # @since 1.1.0
67
+ # @api private
68
+ def sql?
69
+ type == :sql
70
+ end
71
+
72
+ # @since 1.1.0
73
+ # @api private
74
+ def sqlite?
75
+ %w[sqlite sqlite3].include?(engine)
76
+ end
77
+
78
+ private
79
+
80
+ # @since 1.1.0
81
+ # @api private
82
+ def platform
83
+ Hanami::Utils.jruby? ? :jruby : :mri
84
+ end
85
+
86
+ # @since 1.1.0
87
+ # @api private
88
+ def platform_prefix
89
+ 'jdbc:'.freeze if Hanami::Utils.jruby?
90
+ end
91
+
92
+ # @since 1.1.0
93
+ # @api private
94
+ def uri
95
+ {
96
+ development: environment_uri(:development),
97
+ test: environment_uri(:test)
98
+ }
99
+ end
100
+
101
+ # @since 1.1.0
102
+ # @api private
103
+ def gem
104
+ SUPPORTED_ENGINES[engine][platform]
105
+ end
106
+
107
+ # @since 1.1.0
108
+ # @api private
109
+ def base_uri # rubocop:disable Metrics/MethodLength
110
+ case engine
111
+ when 'mysql', 'mysql2'
112
+ if Hanami::Utils.jruby?
113
+ "mysql://localhost/#{name}"
114
+ else
115
+ "mysql2://localhost/#{name}"
116
+ end
117
+ when 'postgresql', 'postgres'
118
+ "postgresql://localhost/#{name}"
119
+ when 'sqlite', 'sqlite3'
120
+ "sqlite://db/#{Shellwords.escape(name)}"
121
+ end
122
+ end
123
+
124
+ # @since 1.1.0
125
+ # @api private
126
+ def environment_uri(environment)
127
+ case engine
128
+ when 'sqlite', 'sqlite3'
129
+ "#{platform_prefix}#{base_uri}_#{environment}.sqlite"
130
+ else
131
+ "#{platform_prefix if sql?}#{base_uri}_#{environment}"
132
+ end
133
+ end
134
+ end
135
+
136
+ # @since 1.1.0
137
+ # @api private
138
+ class TestFramework
139
+ # @since 1.1.0
140
+ # @api private
141
+ RSPEC = 'rspec'.freeze
142
+
143
+ # @since 1.1.0
144
+ # @api private
145
+ MINITEST = 'minitest'.freeze
146
+
147
+ # @since 1.1.0
148
+ # @api private
149
+ VALID_FRAMEWORKS = [MINITEST, RSPEC].freeze
150
+
151
+ # @since 1.1.0
152
+ # @api private
153
+ attr_reader :framework
154
+
155
+ # @since 1.1.0
156
+ # @api private
157
+ def initialize(hanamirc, framework)
158
+ @framework = (framework || hanamirc.options.fetch(:test))
159
+ assert_framework!
160
+ end
161
+
162
+ # @since 1.1.0
163
+ # @api private
164
+ def rspec?
165
+ framework == RSPEC
166
+ end
167
+
168
+ # @since 1.1.0
169
+ # @api private
170
+ def minitest?
171
+ framework == MINITEST
172
+ end
173
+
174
+ private
175
+
176
+ # @since 1.1.0
177
+ # @api private
178
+ def assert_framework!
179
+ unless supported_framework? # rubocop:disable Style/GuardClause
180
+ warn "`#{framework}' is not a valid test framework. Please use one of: #{valid_test_frameworks.join(', ')}"
181
+ exit(1)
182
+ end
183
+ end
184
+
185
+ # @since 1.1.0
186
+ # @api private
187
+ def valid_test_frameworks
188
+ VALID_FRAMEWORKS.map { |name| "`#{name}'" }
189
+ end
190
+
191
+ # @since 1.1.0
192
+ # @api private
193
+ def supported_framework?
194
+ VALID_FRAMEWORKS.include?(framework)
195
+ end
196
+ end
197
+
198
+ # @since 1.1.0
199
+ # @api private
200
+ class TemplateEngine
201
+ # @since 1.1.0
202
+ # @api private
203
+ class UnsupportedTemplateEngine < ::StandardError
204
+ end
205
+
206
+ # @since 1.1.0
207
+ # @api private
208
+ SUPPORTED_ENGINES = %w[erb haml slim].freeze
209
+
210
+ # @since 1.1.0
211
+ # @api private
212
+ DEFAULT_ENGINE = 'erb'.freeze
213
+
214
+ # @since 1.1.0
215
+ # @api private
216
+ attr_reader :name
217
+
218
+ # @since 1.1.0
219
+ # @api private
220
+ def initialize(hanamirc, engine)
221
+ @name = (engine || hanamirc.options.fetch(:template))
222
+ assert_engine!
223
+ end
224
+
225
+ private
226
+
227
+ # @since 1.1.0
228
+ # @api private
229
+ def assert_engine!
230
+ unless supported_engine? # rubocop:disable Style/GuardClause
231
+ warn "`#{name}' is not a valid template engine. Please use one of: #{valid_template_engines.join(', ')}"
232
+ exit(1)
233
+ end
234
+ end
235
+
236
+ # @since 1.1.0
237
+ # @api private
238
+ def valid_template_engines
239
+ SUPPORTED_ENGINES.map { |name| "`#{name}'" }
240
+ end
241
+
242
+ # @since 1.1.0
243
+ # @api private
244
+ def supported_engine?
245
+ SUPPORTED_ENGINES.include?(@name.to_s)
246
+ end
247
+ end
248
+
249
+ # @since 1.1.0
250
+ # @api private
251
+ DEFAULT_APPLICATION_NAME = 'web'.freeze
252
+
253
+ # @since 1.1.0
254
+ # @api private
255
+ DEFAULT_APPLICATION_BASE_URL = '/'.freeze
256
+
257
+ # @since 1.1.0
258
+ # @api private
259
+ attr_reader :target_path
260
+
261
+ desc "Generate a new Hanami project"
262
+ argument :project, required: true, desc: "The project name"
263
+
264
+ option :database, desc: "Database (#{DatabaseConfig::SUPPORTED_ENGINES.keys.join('/')})", default: DatabaseConfig::DEFAULT_ENGINE, aliases: ["-d"]
265
+ option :application_name, desc: "App name", default: DEFAULT_APPLICATION_NAME
266
+ option :application_base_url, desc: "App base URL", default: DEFAULT_APPLICATION_BASE_URL
267
+ option :template, desc: "Template engine (#{TemplateEngine::SUPPORTED_ENGINES.join('/')})", default: TemplateEngine::DEFAULT_ENGINE
268
+ option :test, desc: "Project testing framework (#{TestFramework::VALID_FRAMEWORKS.join('/')})", default: Hanami::Hanamirc::DEFAULT_TEST_SUITE
269
+ option :hanami_head, desc: "Use Hanami HEAD (true/false)", type: :boolean, default: false
270
+
271
+ example [
272
+ "bookshelf # Basic usage",
273
+ "bookshelf --test=rspec # Setup RSpec testing framework",
274
+ "bookshelf --database=postgres # Setup Postgres database",
275
+ "bookshelf --template=slim # Setup Slim template engine",
276
+ "bookshelf --hanami-head # Use Hanami HEAD"
277
+ ]
278
+
279
+ # @since 1.1.0
280
+ # @api private
281
+ #
282
+ # rubocop:disable Metrics/AbcSize
283
+ # rubocop:disable Metrics/MethodLength
284
+ def call(project:, **options)
285
+ project = Utils::String.underscore(project)
286
+ database_config = DatabaseConfig.new(options[:database], project)
287
+ test_framework = TestFramework.new(hanamirc, options[:test])
288
+ template_engine = TemplateEngine.new(hanamirc, options[:template])
289
+ options[:project] = project
290
+
291
+ context = Context.new(
292
+ project: project,
293
+ database: database_config.type,
294
+ database_config_hash: database_config.to_hash,
295
+ database_config: database_config,
296
+ test_framework: test_framework,
297
+ template_engine: template_engine,
298
+ test: options.fetch(:test),
299
+ application_name: options.fetch(:application_name),
300
+ application_base_url: options.fetch(:application_base_url),
301
+ hanami_head: options.fetch(:hanami_head),
302
+ hanami_model_version: '1.1.0.beta1',
303
+ code_reloading: code_reloading?,
304
+ hanami_version: hanami_version,
305
+ project_module: Utils::String.classify(project),
306
+ options: options
307
+ )
308
+
309
+ assert_project_name!(context)
310
+
311
+ directory = project_directory(project)
312
+ files.mkdir(directory)
313
+
314
+ Dir.chdir(directory) do
315
+ generate_application_templates(context)
316
+ generate_empty_directories(context)
317
+ generate_test_templates(context)
318
+ generate_sql_templates(context)
319
+ generate_git_templates(context)
320
+
321
+ init_git
322
+
323
+ generate_app(context)
324
+ end
325
+ end
326
+ # rubocop:enable Metrics/MethodLength
327
+ # rubocop:enable Metrics/AbcSize
328
+
329
+ private
330
+
331
+ # @since 1.1.0
332
+ # @api private
333
+ def assert_project_name!(context)
334
+ if context.project.include?(File::SEPARATOR) # rubocop:disable Style/GuardClause
335
+ raise ArgumentError.new("PROJECT must not contain #{File::SEPARATOR}.")
336
+ end
337
+ end
338
+
339
+ # @since 1.1.0
340
+ # @api private
341
+ #
342
+ # rubocop:disable Metrics/AbcSize
343
+ # rubocop:disable Metrics/MethodLength
344
+ def generate_application_templates(context)
345
+ source = templates.find("hanamirc.erb")
346
+ destination = project.hanamirc(context)
347
+ generate_file(source, destination, context)
348
+
349
+ source = templates.find(".env.development.erb")
350
+ destination = project.env(context, "development")
351
+ generate_file(source, destination, context)
352
+
353
+ source = templates.find(".env.test.erb")
354
+ destination = project.env(context, "test")
355
+ generate_file(source, destination, context)
356
+
357
+ source = templates.find("README.md.erb")
358
+ destination = project.readme(context)
359
+ generate_file(source, destination, context)
360
+
361
+ source = templates.find("Gemfile.erb")
362
+ destination = project.gemfile(context)
363
+ generate_file(source, destination, context)
364
+
365
+ source = templates.find("config.ru.erb")
366
+ destination = project.config_ru(context)
367
+ generate_file(source, destination, context)
368
+
369
+ source = templates.find("config", "boot.erb")
370
+ destination = project.boot(context)
371
+ generate_file(source, destination, context)
372
+
373
+ source = templates.find("config", "environment.erb")
374
+ destination = project.environment(context)
375
+ generate_file(source, destination, context)
376
+
377
+ source = templates.find("lib", "project.erb")
378
+ destination = project.project(context)
379
+ generate_file(source, destination, context)
380
+ end
381
+ # rubocop:enable Metrics/MethodLength
382
+ # rubocop:enable Metrics/AbcSize
383
+
384
+ # rubocop:disable Metrics/AbcSize
385
+ # rubocop:disable Metrics/MethodLength
386
+ def generate_empty_directories(context)
387
+ source = templates.find(".gitkeep.erb")
388
+
389
+ destination = project.keep(project.public_directory(context))
390
+ generate_file(source, destination, context)
391
+
392
+ destination = project.keep(project.initializers(context))
393
+ generate_file(source, destination, context)
394
+
395
+ destination = project.keep(project.entities(context))
396
+ generate_file(source, destination, context)
397
+
398
+ destination = project.keep(project.repositories(context))
399
+ generate_file(source, destination, context)
400
+
401
+ destination = project.keep(project.mailers(context))
402
+ generate_file(source, destination, context)
403
+
404
+ destination = project.keep(project.mailers_templates(context))
405
+ generate_file(source, destination, context)
406
+
407
+ destination = project.keep(project.entities_spec(context))
408
+ generate_file(source, destination, context)
409
+
410
+ destination = project.keep(project.repositories_spec(context))
411
+ generate_file(source, destination, context)
412
+
413
+ destination = project.keep(project.mailers_spec(context))
414
+ generate_file(source, destination, context)
415
+
416
+ destination = project.keep(project.support_spec(context))
417
+ generate_file(source, destination, context)
418
+
419
+ if context.database_config.sql? # rubocop:disable Style/ConditionalAssignment
420
+ destination = project.keep(project.migrations(context))
421
+ else
422
+ destination = project.keep(project.db(context))
423
+ end
424
+
425
+ generate_file(source, destination, context)
426
+ end
427
+ # rubocop:enable Metrics/MethodLength
428
+ # rubocop:enable Metrics/AbcSize
429
+
430
+ # rubocop:disable Metrics/AbcSize
431
+ # rubocop:disable Metrics/MethodLength
432
+ # rubocop:disable Style/IdenticalConditionalBranches
433
+ def generate_test_templates(context)
434
+ if context.test_framework.rspec?
435
+ source = templates.find("rspec", "Rakefile.erb")
436
+ destination = project.rakefile(context)
437
+ generate_file(source, destination, context)
438
+
439
+ source = templates.find("rspec", "rspec.erb")
440
+ destination = project.dotrspec(context)
441
+ generate_file(source, destination, context)
442
+
443
+ source = templates.find("rspec", "spec_helper.erb")
444
+ destination = project.spec_helper(context)
445
+ generate_file(source, destination, context)
446
+
447
+ source = templates.find("rspec", "features_helper.erb")
448
+ destination = project.features_helper(context)
449
+ generate_file(source, destination, context)
450
+
451
+ source = templates.find("rspec", "capybara.erb")
452
+ destination = project.capybara(context)
453
+ generate_file(source, destination, context)
454
+ else # minitest (default)
455
+ source = templates.find("minitest", "Rakefile.erb")
456
+ destination = project.rakefile(context)
457
+ generate_file(source, destination, context)
458
+
459
+ source = templates.find("minitest", "spec_helper.erb")
460
+ destination = project.spec_helper(context)
461
+ generate_file(source, destination, context)
462
+
463
+ source = templates.find("minitest", "features_helper.erb")
464
+ destination = project.features_helper(context)
465
+ generate_file(source, destination, context)
466
+ end
467
+ end
468
+ # rubocop:enable Style/IdenticalConditionalBranches
469
+ # rubocop:enable Metrics/MethodLength
470
+ # rubocop:enable Metrics/AbcSize
471
+
472
+ # @since 1.1.0
473
+ # @api private
474
+ def generate_sql_templates(context)
475
+ return unless context.database_config.sql?
476
+
477
+ source = templates.find("schema.sql.erb")
478
+ destination = project.db_schema(context)
479
+ generate_file(source, destination, context)
480
+ end
481
+
482
+ # @since 1.1.0
483
+ # @api private
484
+ def generate_git_templates(context)
485
+ return if git_dir_present?
486
+
487
+ source = context.database_config.sqlite? ? 'gitignore_with_sqlite.erb' : 'gitignore.erb'
488
+ source = templates.find(source)
489
+ destination = project.gitignore(context)
490
+
491
+ generate_file(source, destination, context)
492
+ end
493
+
494
+ # @since 1.1.0
495
+ # @api private
496
+ def target_path
497
+ Pathname.pwd
498
+ end
499
+
500
+ # @since 1.1.0
501
+ # @api private
502
+ def generate_app(context)
503
+ Hanami::CLI::Commands::New::App.new(command_name: "generate app", out: @out, files: @files).call(app: context.application_name, application_base_url: context.application_base_url, **context.options)
504
+ end
505
+
506
+ # @since 1.1.0
507
+ # @api private
508
+ def init_git
509
+ return if git_dir_present?
510
+
511
+ say(:run, "git init . from \".\"")
512
+ system("git init #{Shellwords.escape(target)}", out: File::NULL)
513
+ end
514
+
515
+ # @since 1.1.0
516
+ # @api private
517
+ def git_dir_present?
518
+ files.directory?('.git')
519
+ end
520
+
521
+ # @since 1.1.0
522
+ # @api private
523
+ def target
524
+ Pathname.new('.')
525
+ end
526
+
527
+ # @since 1.1.0
528
+ # @api private
529
+ def hanamirc
530
+ @hanamirc ||= Hanamirc.new(Pathname.new('.'))
531
+ end
532
+
533
+ # @since 1.1.0
534
+ # @api private
535
+ def project_directory(project)
536
+ @name == '.' ? '.' : project
537
+ end
538
+
539
+ # @since 1.1.0
540
+ # @api private
541
+ def code_reloading?
542
+ !Hanami::Utils.jruby?
543
+ end
544
+
545
+ # @since 1.1.0
546
+ # @api private
547
+ def hanami_version
548
+ Hanami::Version.gem_requirement
549
+ end
550
+
551
+ # @since 1.1.0
552
+ # @api private
553
+ def generate_file(source, destination, context)
554
+ super
555
+ say(:create, destination)
556
+ end
557
+
558
+ # @since 1.1.0
559
+ # @api private
560
+ class App < Commands::Generate::App
561
+ requirements.clear
562
+
563
+ # @since 1.1.0
564
+ # @api private
565
+ def initialize(*)
566
+ super
567
+ @templates = Templates.new(self.class.superclass)
568
+ end
569
+ end
570
+ end
571
+ end
572
+
573
+ register "new", Commands::New
574
+ end
575
+ end