hanami-cli 2.1.1 → 2.2.0.beta2
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +29 -13
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -1
- data/README.md +13 -7
- data/docker-compose.yml +14 -0
- data/hanami-cli.gemspec +2 -2
- data/lib/hanami/cli/command.rb +2 -2
- data/lib/hanami/cli/commands/app/command.rb +2 -16
- data/lib/hanami/cli/commands/app/db/command.rb +148 -0
- data/lib/hanami/cli/commands/app/db/create.rb +21 -11
- data/lib/hanami/cli/commands/app/db/drop.rb +21 -10
- data/lib/hanami/cli/commands/app/db/migrate.rb +50 -12
- data/lib/hanami/cli/commands/app/db/prepare.rb +68 -0
- data/lib/hanami/cli/commands/app/db/seed.rb +22 -21
- data/lib/hanami/cli/commands/app/db/structure/dump.rb +32 -7
- data/lib/hanami/cli/commands/app/db/structure/load.rb +54 -0
- data/lib/hanami/cli/commands/app/db/utils/database.rb +100 -75
- data/lib/hanami/cli/commands/app/db/utils/mysql.rb +59 -6
- data/lib/hanami/cli/commands/app/db/utils/postgres.rb +42 -21
- data/lib/hanami/cli/commands/app/db/utils/sqlite.rb +58 -10
- data/lib/hanami/cli/commands/app/db/version.rb +14 -8
- data/lib/hanami/cli/commands/app/generate/action.rb +4 -3
- data/lib/hanami/cli/commands/app/generate/command.rb +60 -0
- data/lib/hanami/cli/commands/app/generate/component.rb +49 -0
- data/lib/hanami/cli/commands/app/generate/migration.rb +47 -0
- data/lib/hanami/cli/commands/app/generate/operation.rb +26 -0
- data/lib/hanami/cli/commands/app/generate/part.rb +1 -1
- data/lib/hanami/cli/commands/app/generate/relation.rb +29 -0
- data/lib/hanami/cli/commands/app/generate/repo.rb +42 -0
- data/lib/hanami/cli/commands/app/generate/slice.rb +20 -3
- data/lib/hanami/cli/commands/app/generate/struct.rb +27 -0
- data/lib/hanami/cli/commands/app/install.rb +1 -1
- data/lib/hanami/cli/commands/app/middleware.rb +1 -1
- data/lib/hanami/cli/commands/app/server.rb +2 -2
- data/lib/hanami/cli/commands/app.rb +21 -2
- data/lib/hanami/cli/commands/gem/new.rb +78 -14
- data/lib/hanami/cli/errors.rb +28 -0
- data/lib/hanami/cli/files.rb +22 -0
- data/lib/hanami/cli/generators/app/action_context.rb +5 -13
- data/lib/hanami/cli/generators/app/component/component.erb +8 -0
- data/lib/hanami/cli/generators/app/component/slice_component.erb +8 -0
- data/lib/hanami/cli/generators/app/component.rb +61 -0
- data/lib/hanami/cli/generators/app/component_context.rb +82 -0
- data/lib/hanami/cli/generators/app/migration.rb +66 -0
- data/lib/hanami/cli/generators/app/operation.rb +49 -0
- data/lib/hanami/cli/generators/app/part_context.rb +5 -21
- data/lib/hanami/cli/generators/app/relation.rb +45 -0
- data/lib/hanami/cli/generators/app/repo.rb +41 -0
- data/lib/hanami/cli/generators/app/ruby_file_writer.rb +151 -0
- data/lib/hanami/cli/generators/app/slice/{entities.erb → operation.erb} +1 -3
- data/lib/hanami/cli/generators/app/slice/relation.erb +8 -0
- data/lib/hanami/cli/generators/app/slice/{slice.erb → repo.erb} +3 -1
- data/lib/hanami/cli/generators/app/slice/struct.erb +8 -0
- data/lib/hanami/cli/generators/app/slice.rb +14 -6
- data/lib/hanami/cli/generators/app/slice_context.rb +9 -2
- data/lib/hanami/cli/generators/app/struct.rb +40 -0
- data/lib/hanami/cli/generators/app/view_context.rb +4 -16
- data/lib/hanami/cli/generators/constants.rb +39 -0
- data/lib/hanami/cli/generators/context.rb +48 -0
- data/lib/hanami/cli/generators/gem/app/action.erb +3 -0
- data/lib/hanami/cli/generators/gem/app/env.erb +4 -0
- data/lib/hanami/cli/generators/gem/app/gemfile.erb +11 -0
- data/lib/hanami/cli/generators/gem/app/gitignore.erb +4 -1
- data/lib/hanami/cli/generators/gem/app/operation.erb +13 -0
- data/lib/hanami/cli/generators/gem/app/relation.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/repo.erb +10 -0
- data/lib/hanami/cli/generators/gem/app/struct.erb +10 -0
- data/lib/hanami/cli/generators/gem/app.rb +19 -0
- data/lib/hanami/cli/ruby_file_generator.rb +123 -0
- data/lib/hanami/cli/version.rb +1 -1
- metadata +39 -17
- data/lib/hanami/cli/commands/app/db/create_migration.rb +0 -32
- data/lib/hanami/cli/commands/app/db/reset.rb +0 -28
- data/lib/hanami/cli/commands/app/db/rollback.rb +0 -81
- data/lib/hanami/cli/commands/app/db/sample_data.rb +0 -42
- data/lib/hanami/cli/commands/app/db/setup.rb +0 -26
- data/lib/hanami/cli/commands/app/db/utils/database_config.rb +0 -60
- data/lib/hanami/cli/generators/app/slice/repository.erb +0 -10
@@ -19,18 +19,20 @@ module Hanami
|
|
19
19
|
|
20
20
|
# @since 2.0.0
|
21
21
|
# @api private
|
22
|
-
def call(app, slice, url, context:
|
22
|
+
def call(app, slice, url, context: nil, **opts)
|
23
|
+
context ||= SliceContext.new(inflector, app, slice, url, **opts)
|
24
|
+
|
23
25
|
fs.inject_line_at_class_bottom(
|
24
26
|
fs.join("config", "routes.rb"), "class Routes", t("routes.erb", context).chomp
|
25
27
|
)
|
26
28
|
|
27
29
|
fs.mkdir(directory = "slices/#{slice}")
|
28
30
|
|
29
|
-
# fs.write("#{directory}/config/slice.rb", t("slice.erb", context))
|
30
31
|
fs.write(fs.join(directory, "action.rb"), t("action.erb", context))
|
31
32
|
fs.write(fs.join(directory, "view.rb"), t("view.erb", context))
|
32
33
|
fs.write(fs.join(directory, "views", "helpers.rb"), t("helpers.erb", context))
|
33
34
|
fs.write(fs.join(directory, "templates", "layouts", "app.html.erb"), t("app_layout.erb", context))
|
35
|
+
fs.write(fs.join(directory, "operation.rb"), t("operation.erb", context))
|
34
36
|
|
35
37
|
if context.bundled_assets?
|
36
38
|
fs.write(fs.join(directory, "assets", "js", "app.js"), t("app_js.erb", context))
|
@@ -38,15 +40,21 @@ module Hanami
|
|
38
40
|
fs.write(fs.join(directory, "assets", "images", "favicon.ico"), file("favicon.ico"))
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
+
if context.generate_db?
|
44
|
+
fs.write(fs.join(directory, "db", "relation.rb"), t("relation.erb", context))
|
45
|
+
fs.write(fs.join(directory, "relations", ".keep"), t("keep.erb", context))
|
46
|
+
|
47
|
+
fs.write(fs.join(directory, "db", "repo.rb"), t("repo.erb", context))
|
48
|
+
fs.write(fs.join(directory, "repos", ".keep"), t("keep.erb", context))
|
49
|
+
|
50
|
+
fs.write(fs.join(directory, "db", "struct.rb"), t("struct.erb", context))
|
51
|
+
fs.write(fs.join(directory, "structs", ".keep"), t("keep.erb", context))
|
52
|
+
end
|
43
53
|
|
44
54
|
fs.write(fs.join(directory, "actions/.keep"), t("keep.erb", context))
|
45
55
|
fs.write(fs.join(directory, "views/.keep"), t("keep.erb", context))
|
46
56
|
fs.write(fs.join(directory, "templates/.keep"), t("keep.erb", context))
|
47
57
|
fs.write(fs.join(directory, "templates/layouts/.keep"), t("keep.erb", context))
|
48
|
-
# fs.write(fs.join(directory, entities/.keep"), t("keep.erb", context))
|
49
|
-
# fs.write(fs.join(directory, repositories/.keep"), t("keep.erb", context))
|
50
58
|
end
|
51
59
|
|
52
60
|
private
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "../context"
|
4
|
+
require_relative "../constants"
|
4
5
|
|
5
6
|
module Hanami
|
6
7
|
module CLI
|
@@ -11,10 +12,10 @@ module Hanami
|
|
11
12
|
class SliceContext < Generators::Context
|
12
13
|
# @since 2.0.0
|
13
14
|
# @api private
|
14
|
-
def initialize(inflector, app, slice, url)
|
15
|
+
def initialize(inflector, app, slice, url, **options)
|
15
16
|
@slice = slice
|
16
17
|
@url = url
|
17
|
-
super(inflector, app)
|
18
|
+
super(inflector, app, **options)
|
18
19
|
end
|
19
20
|
|
20
21
|
# @since 2.0.0
|
@@ -47,6 +48,12 @@ module Hanami
|
|
47
48
|
%(<%= javascript_tag "app" %>)
|
48
49
|
end
|
49
50
|
|
51
|
+
# @since 2.2.0
|
52
|
+
# @api private
|
53
|
+
def generate_db?
|
54
|
+
!options.fetch(:skip_db, false)
|
55
|
+
end
|
56
|
+
|
50
57
|
private
|
51
58
|
|
52
59
|
attr_reader :slice
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
module Generators
|
6
|
+
module App
|
7
|
+
# @since 2.2.0
|
8
|
+
# @api private
|
9
|
+
class Struct
|
10
|
+
# @since 2.2.0
|
11
|
+
# @api private
|
12
|
+
def initialize(fs:, inflector:, out: $stdout)
|
13
|
+
@fs = fs
|
14
|
+
@inflector = inflector
|
15
|
+
@out = out
|
16
|
+
end
|
17
|
+
|
18
|
+
# @since 2.2.0
|
19
|
+
# @api private
|
20
|
+
def call(key:, namespace:, base_path:)
|
21
|
+
RubyFileWriter.new(
|
22
|
+
fs: fs,
|
23
|
+
inflector: inflector,
|
24
|
+
).call(
|
25
|
+
key: key,
|
26
|
+
namespace: namespace,
|
27
|
+
base_path: base_path,
|
28
|
+
extra_namespace: "Structs",
|
29
|
+
relative_parent_class: "DB::Struct",
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :fs, :inflector, :out
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "slice_context"
|
4
4
|
require "dry/files/path"
|
5
|
+
require_relative "../constants"
|
5
6
|
|
6
7
|
module Hanami
|
7
8
|
module CLI
|
@@ -12,19 +13,6 @@ module Hanami
|
|
12
13
|
# @since 2.1.0
|
13
14
|
# @api private
|
14
15
|
class ViewContext < SliceContext
|
15
|
-
# TODO: move these constants somewhere that will let us reuse them
|
16
|
-
KEY_SEPARATOR = "."
|
17
|
-
private_constant :KEY_SEPARATOR
|
18
|
-
|
19
|
-
NAMESPACE_SEPARATOR = "::"
|
20
|
-
private_constant :NAMESPACE_SEPARATOR
|
21
|
-
|
22
|
-
INDENTATION = " "
|
23
|
-
private_constant :INDENTATION
|
24
|
-
|
25
|
-
OFFSET = INDENTATION * 2
|
26
|
-
private_constant :OFFSET
|
27
|
-
|
28
16
|
# @since 2.1.0
|
29
17
|
# @api private
|
30
18
|
attr_reader :key
|
@@ -76,7 +64,7 @@ module Hanami
|
|
76
64
|
# @api private
|
77
65
|
def module_namespace_declaration
|
78
66
|
namespaces.each_with_index.map { |token, i|
|
79
|
-
"#{
|
67
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}module #{inflector.camelize(token)}"
|
80
68
|
}.join($/)
|
81
69
|
end
|
82
70
|
|
@@ -84,14 +72,14 @@ module Hanami
|
|
84
72
|
# @api private
|
85
73
|
def module_namespace_end
|
86
74
|
namespaces.each_with_index.map { |_, i|
|
87
|
-
"#{
|
75
|
+
"#{NESTED_OFFSET}#{INDENTATION * i}end"
|
88
76
|
}.reverse.join($/)
|
89
77
|
end
|
90
78
|
|
91
79
|
# @since 2.1.0
|
92
80
|
# @api private
|
93
81
|
def module_namespace_offset
|
94
|
-
"#{
|
82
|
+
"#{NESTED_OFFSET}#{INDENTATION * namespaces.count}"
|
95
83
|
end
|
96
84
|
end
|
97
85
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Hanami
|
4
|
+
module CLI
|
5
|
+
# @since 2.2.0
|
6
|
+
# @api private
|
7
|
+
module Generators
|
8
|
+
# @since 2.2.0
|
9
|
+
# @api private
|
10
|
+
INDENTATION = " "
|
11
|
+
private_constant :INDENTATION
|
12
|
+
|
13
|
+
# @since 2.2.0
|
14
|
+
# @api private
|
15
|
+
OFFSET = INDENTATION
|
16
|
+
private_constant :OFFSET
|
17
|
+
|
18
|
+
# @since 2.2.0
|
19
|
+
# @api private
|
20
|
+
NESTED_OFFSET = INDENTATION * 2
|
21
|
+
private_constant :OFFSET
|
22
|
+
|
23
|
+
# @since 2.2.0
|
24
|
+
# @api private
|
25
|
+
KEY_SEPARATOR = %r{[.\/]}
|
26
|
+
private_constant :KEY_SEPARATOR
|
27
|
+
|
28
|
+
# @since 2.2.0
|
29
|
+
# @api private
|
30
|
+
MATCHER_PATTERN = /::|\./
|
31
|
+
private_constant :MATCHER_PATTERN
|
32
|
+
|
33
|
+
# @since 2.2.0
|
34
|
+
# @api private
|
35
|
+
NAMESPACE_SEPARATOR = "::"
|
36
|
+
private_constant :NAMESPACE_SEPARATOR
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -80,6 +80,44 @@ module Hanami
|
|
80
80
|
!options.fetch(:skip_assets, false)
|
81
81
|
end
|
82
82
|
|
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
|
+
generate_db? && database_option == Commands::Gem::New::DATABASE_SQLITE
|
93
|
+
end
|
94
|
+
|
95
|
+
# @since 2.2.0
|
96
|
+
# @api private
|
97
|
+
def generate_postgres?
|
98
|
+
generate_db? && database_option == Commands::Gem::New::DATABASE_POSTGRES
|
99
|
+
end
|
100
|
+
|
101
|
+
# @since 2.2.0
|
102
|
+
# @api private
|
103
|
+
def generate_mysql?
|
104
|
+
generate_db? && 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
|
+
"mysql2://localhost/#{app}"
|
116
|
+
else
|
117
|
+
raise "Unknown database option: #{database_option}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
83
121
|
# @since 2.1.0
|
84
122
|
# @api private
|
85
123
|
def bundled_views?
|
@@ -92,6 +130,12 @@ module Hanami
|
|
92
130
|
Hanami.bundled?("hanami-assets")
|
93
131
|
end
|
94
132
|
|
133
|
+
# @since 2.2.0
|
134
|
+
# @api private
|
135
|
+
def bundled_dry_monads?
|
136
|
+
Hanami.bundled?("dry-monads")
|
137
|
+
end
|
138
|
+
|
95
139
|
# @since 2.1.0
|
96
140
|
# @api private
|
97
141
|
#
|
@@ -102,6 +146,10 @@ module Hanami
|
|
102
146
|
|
103
147
|
private
|
104
148
|
|
149
|
+
def database_option
|
150
|
+
options.fetch(:database, Commands::Gem::New::DATABASE_SQLITE)
|
151
|
+
end
|
152
|
+
|
105
153
|
# @since 2.0.0
|
106
154
|
# @api private
|
107
155
|
attr_reader :inflector
|
@@ -2,8 +2,11 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require "hanami/action"
|
5
|
+
require "dry/monads"
|
5
6
|
|
6
7
|
module <%= camelized_app_name %>
|
7
8
|
class Action < Hanami::Action
|
9
|
+
# Provide `Success` and `Failure` for pattern matching on operation results
|
10
|
+
include Dry::Monads[:result]
|
8
11
|
end
|
9
12
|
end
|
@@ -7,13 +7,24 @@ source "https://rubygems.org"
|
|
7
7
|
<%= hanami_gem("assets") %>
|
8
8
|
<%- end -%>
|
9
9
|
<%= hanami_gem("controller") %>
|
10
|
+
<%- if generate_db? -%>
|
11
|
+
<%= hanami_gem("db") %>
|
12
|
+
<%- end -%>
|
10
13
|
<%= hanami_gem("router") %>
|
11
14
|
<%= hanami_gem("validations") %>
|
12
15
|
<%= hanami_gem("view") %>
|
13
16
|
|
14
17
|
gem "dry-types", "~> 1.0", ">= 1.6.1"
|
18
|
+
gem "dry-operation", github: "dry-rb/dry-operation"
|
15
19
|
gem "puma"
|
16
20
|
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 -%>
|
17
28
|
|
18
29
|
group :development do
|
19
30
|
<%= hanami_gem("webconsole") %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# auto_register: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "dry/operation"
|
5
|
+
|
6
|
+
module <%= camelized_app_name %>
|
7
|
+
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 -%>
|
12
|
+
end
|
13
|
+
end
|
@@ -68,6 +68,25 @@ module Hanami
|
|
68
68
|
fs.write("app/assets/images/favicon.ico", file("favicon.ico"))
|
69
69
|
end
|
70
70
|
|
71
|
+
if context.generate_db?
|
72
|
+
fs.write("app/db/relation.rb", t("relation.erb", context))
|
73
|
+
fs.write("app/relations/.keep", t("keep.erb", context))
|
74
|
+
|
75
|
+
fs.write("app/db/repo.rb", t("repo.erb", context))
|
76
|
+
fs.write("app/repos/.keep", t("keep.erb", context))
|
77
|
+
|
78
|
+
fs.write("app/db/struct.rb", t("struct.erb", context))
|
79
|
+
fs.write("app/structs/.keep", t("keep.erb", context))
|
80
|
+
|
81
|
+
fs.write("config/db/migrate/.keep" , t("keep.erb", context))
|
82
|
+
|
83
|
+
if context.generate_sqlite?
|
84
|
+
fs.write("db/.keep" , t("keep.erb", context))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
fs.write("app/operation.rb", t("operation.erb", context))
|
89
|
+
|
71
90
|
fs.write("public/404.html", file("404.html"))
|
72
91
|
fs.write("public/500.html", file("500.html"))
|
73
92
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ripper"
|
4
|
+
|
5
|
+
module Hanami
|
6
|
+
module CLI
|
7
|
+
class RubyFileGenerator
|
8
|
+
# @api private
|
9
|
+
# @since 2.2.0
|
10
|
+
class GeneratedUnparseableCodeError < Error
|
11
|
+
def initialize(source_code)
|
12
|
+
super(
|
13
|
+
<<~ERROR_MESSAGE
|
14
|
+
Sorry, the code we generated is not valid Ruby.
|
15
|
+
|
16
|
+
Here's what we got:
|
17
|
+
|
18
|
+
#{source_code}
|
19
|
+
|
20
|
+
Please fix the errors and try again.
|
21
|
+
ERROR_MESSAGE
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
INDENT = " "
|
27
|
+
|
28
|
+
def self.class(class_name, **args)
|
29
|
+
new(class_name: class_name, **args).to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.module(*names, **args)
|
33
|
+
module_names = if names.first.is_a?(Array)
|
34
|
+
names.first
|
35
|
+
else
|
36
|
+
names
|
37
|
+
end
|
38
|
+
|
39
|
+
new(modules: module_names, class_name: nil, parent_class: nil, **args).to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(
|
43
|
+
class_name: nil,
|
44
|
+
parent_class: nil,
|
45
|
+
modules: [],
|
46
|
+
header: [],
|
47
|
+
body: []
|
48
|
+
)
|
49
|
+
@class_name = class_name
|
50
|
+
@parent_class = parent_class
|
51
|
+
@modules = modules
|
52
|
+
@header = header.any? ? (header + [""]) : []
|
53
|
+
@body = body
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
definition = lines(modules).map { |line| "#{line}\n" }.join
|
58
|
+
source_code = [header, definition].flatten.join("\n")
|
59
|
+
ensure_parseable!(source_code)
|
60
|
+
source_code
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_reader(
|
66
|
+
:class_name,
|
67
|
+
:parent_class,
|
68
|
+
:modules,
|
69
|
+
:header,
|
70
|
+
:body
|
71
|
+
)
|
72
|
+
|
73
|
+
def lines(remaining_modules)
|
74
|
+
this_module, *rest_modules = remaining_modules
|
75
|
+
if this_module
|
76
|
+
with_module_lines(this_module, lines(rest_modules))
|
77
|
+
elsif class_name
|
78
|
+
class_lines
|
79
|
+
else
|
80
|
+
body
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def with_module_lines(module_name, contents_lines)
|
85
|
+
[
|
86
|
+
"module #{module_name}",
|
87
|
+
*contents_lines.map { |line| indent(line) },
|
88
|
+
"end"
|
89
|
+
]
|
90
|
+
end
|
91
|
+
|
92
|
+
def class_lines
|
93
|
+
[
|
94
|
+
class_definition,
|
95
|
+
*body.map { |line| indent(line) },
|
96
|
+
"end"
|
97
|
+
].compact
|
98
|
+
end
|
99
|
+
|
100
|
+
def class_definition
|
101
|
+
if parent_class
|
102
|
+
"class #{class_name} < #{parent_class}"
|
103
|
+
else
|
104
|
+
"class #{class_name}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def indent(line)
|
109
|
+
if line.strip.empty?
|
110
|
+
""
|
111
|
+
else
|
112
|
+
INDENT + line
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def ensure_parseable!(source_code)
|
117
|
+
unless Ripper.sexp(source_code)
|
118
|
+
raise GeneratedUnparseableCodeError.new(source_code)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|