belt 0.2.11 → 0.2.13
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/CHANGELOG.md +42 -1
- data/lib/belt/cli/app_detection.rb +19 -5
- data/lib/belt/cli/contracts_command.rb +142 -0
- data/lib/belt/cli/destroy_command.rb +74 -0
- data/lib/belt/cli/frontend_command.rb +23 -0
- data/lib/belt/cli/generate_command.rb +11 -9
- data/lib/belt/cli/logs_command.rb +634 -0
- data/lib/belt/cli/new_command.rb +2 -2
- data/lib/belt/cli/routes_command/schema_loader.rb +17 -7
- data/lib/belt/cli/routes_command.rb +7 -3
- data/lib/belt/cli/setup_command.rb +1 -1
- data/lib/belt/cli/views_command.rb +96 -11
- data/lib/belt/cli.rb +6 -0
- data/lib/belt/root.rb +12 -3
- data/lib/belt/route_dsl.rb +9 -6
- data/lib/belt/version.rb +1 -1
- data/lib/belt.rb +1 -0
- data/lib/templates/frontend_infra/frontend.tf.erb +2 -1
- data/lib/templates/generate/controller.rb.erb +13 -18
- data/lib/templates/generate/model.rb.erb +1 -14
- data/lib/templates/module/frontend.tf.erb +2 -1
- data/lib/templates/module/main.tf.erb +1 -1
- data/lib/templates/new_app/AGENTS.md.erb +4 -4
- data/lib/templates/new_app/Gemfile.erb +0 -2
- data/lib/templates/new_app/README.md.erb +2 -2
- data/lib/templates/new_app/gitignore.erb +3 -0
- data/lib/templates/new_app/lambda/api.rb.erb +0 -6
- data/lib/templates/new_app/lambda/config/environment.rb.erb +0 -6
- data/lib/templates/new_app/lambda/models/application_record.rb.erb +0 -2
- data/lib/templates/plugin/AGENTS.md.erb +1 -1
- data/lib/templates/plugin/README.md.erb +1 -1
- metadata +11 -3
- /data/lib/templates/new_app/config/{schema.tf.rb.erb → contracts.rb.erb} +0 -0
- /data/lib/templates/new_app/config/{routes.tf.rb.erb → routes.rb.erb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43bc6e6cb774005faa3bab6b5133403255636327e3dd017ad4acad8fd9a2c884
|
|
4
|
+
data.tar.gz: fe11f89e1efa821750dea2953c70bbc21fe24bea394d418c2b870a2804adf331
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5c4900624a6435b178455b5aa7e6f34c08d7c2e75e2d1ec3c1b69e717a5964a390ba59a4ff8ccad1be797d29273bddc4354c235f83070d00bb7517d60bcb38e
|
|
7
|
+
data.tar.gz: a47bc3063743a278e4241faf65c510ede23ec922c167469f2654627f3079bb558893194f717979002354e07153e41cee257785e8b0f7dea187fc391fb351e5f3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.2.12
|
|
4
|
+
|
|
5
|
+
### Rename: `routes.tf.rb` → `routes.rb`, `schema.tf.rb` → `contracts.rb`
|
|
6
|
+
|
|
7
|
+
The `.tf.rb` extension was a vestige of when these files produced HCL output — that hasn't been the case for a while. New apps now get clean, Rails-familiar names:
|
|
8
|
+
|
|
9
|
+
- `config/routes.rb` — API route definitions
|
|
10
|
+
- `config/contracts.rb` — API request/response contracts
|
|
11
|
+
|
|
12
|
+
**Backward compatible:** All detection helpers (`Belt.root`, `Belt.routes_file`, `Belt.contracts_file`, `find_routes_file_path`, `find_contracts_file_path`) check new names first, then fall back to `*.tf.rb` and `infrastructure/` paths. Existing apps continue working without changes.
|
|
13
|
+
|
|
14
|
+
### New command: `belt contracts`
|
|
15
|
+
|
|
16
|
+
Dedicated CLI command for inspecting API contracts, separate from `belt routes`:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
belt contracts # Human-readable table of request/response models
|
|
20
|
+
belt contracts -f json # JSON output (for tooling/CI)
|
|
21
|
+
belt contracts -g post # Filter by pattern
|
|
22
|
+
belt contracts --file path.rb # Explicit file override
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Previously, contracts were only accessible as a side-effect of `belt routes -f json --schema <file>`. Now they have their own first-class command. `belt routes -f json` continues to include models for backward compatibility.
|
|
26
|
+
|
|
27
|
+
### Other changes
|
|
28
|
+
|
|
29
|
+
- `Belt.schema_file` is now an alias for `Belt.contracts_file`
|
|
30
|
+
- `find_schema_file_path` is now an alias for `find_contracts_file_path`
|
|
31
|
+
- Module template updated: `source` points to `config/routes.rb`
|
|
32
|
+
- All scaffold/generator help text and templates reference new filenames
|
|
33
|
+
|
|
34
|
+
## 0.2.13
|
|
35
|
+
|
|
36
|
+
### Template cleanup
|
|
37
|
+
|
|
38
|
+
- **Model template**: single-line `attr_accessor` instead of one per attribute; removed redundant `to_h` (ActiveItem::Base already provides it)
|
|
39
|
+
- **Controller template**: uses implicit response pattern (instance variable assigns) instead of explicit `success_response` calls; `response_status :created` for create actions, `head :no_content` for destroy
|
|
40
|
+
- **Gemfile template**: removed `activeitem` and `lambda_loadout` (already belt gem dependencies)
|
|
41
|
+
- **gitignore template**: excludes `lambda/lib/routes/` (generated artifact from `belt routes`)
|
|
42
|
+
- **Removed `require 'activeitem'`** from api.rb, environment.rb, and application_record.rb templates (belt requires it transitively)
|
|
43
|
+
- **Removed `ActiveItem.configure` boilerplate** from api.rb and environment.rb templates (activeitem 0.0.13+ defaults `table_prefix` and `environment` from ENV vars)
|
|
44
|
+
- Bumped activeitem dependency to `>= 0.0.13`
|
|
4
45
|
|
|
5
46
|
## 0.2.11
|
|
6
47
|
|
|
@@ -40,17 +40,31 @@ module Belt
|
|
|
40
40
|
name.to_s.downcase.tr('_', '-')
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
# Finds routes
|
|
43
|
+
# Finds routes file checking config/routes.rb first, then legacy paths.
|
|
44
44
|
def find_routes_file_path
|
|
45
|
-
candidates = [
|
|
45
|
+
candidates = [
|
|
46
|
+
'config/routes.rb',
|
|
47
|
+
'config/routes.tf.rb',
|
|
48
|
+
'infrastructure/routes.tf.rb'
|
|
49
|
+
]
|
|
46
50
|
candidates.find { |f| File.exist?(f) }
|
|
47
51
|
end
|
|
48
52
|
|
|
49
|
-
# Finds
|
|
50
|
-
def
|
|
51
|
-
candidates = [
|
|
53
|
+
# Finds contracts file checking config/contracts.rb first, then legacy paths.
|
|
54
|
+
def find_contracts_file_path
|
|
55
|
+
candidates = [
|
|
56
|
+
'config/contracts.rb',
|
|
57
|
+
'config/contracts.tf.rb',
|
|
58
|
+
'config/schema.tf.rb',
|
|
59
|
+
'infrastructure/schema.tf.rb'
|
|
60
|
+
]
|
|
52
61
|
candidates.find { |f| File.exist?(f) }
|
|
53
62
|
end
|
|
63
|
+
|
|
64
|
+
# Legacy alias for backward compatibility
|
|
65
|
+
def find_schema_file_path
|
|
66
|
+
find_contracts_file_path
|
|
67
|
+
end
|
|
54
68
|
end
|
|
55
69
|
end
|
|
56
70
|
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'optparse'
|
|
5
|
+
require_relative '../route_dsl'
|
|
6
|
+
require_relative 'routes_command/schema_loader'
|
|
7
|
+
|
|
8
|
+
module Belt
|
|
9
|
+
module CLI
|
|
10
|
+
class ContractsCommand
|
|
11
|
+
include RoutesCommand::SchemaLoader
|
|
12
|
+
|
|
13
|
+
def self.run(args)
|
|
14
|
+
new(args).run
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(args)
|
|
18
|
+
@options = {}
|
|
19
|
+
parse_options(args)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def run
|
|
23
|
+
contracts_file = find_contracts_file
|
|
24
|
+
unless contracts_file
|
|
25
|
+
abort 'Error: No contracts file found. ' \
|
|
26
|
+
'Expected config/contracts.rb (or config/contracts.tf.rb, config/schema.tf.rb)'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
models = load_contracts(contracts_file)
|
|
30
|
+
|
|
31
|
+
if @options[:format] == 'json'
|
|
32
|
+
output_json(models)
|
|
33
|
+
else
|
|
34
|
+
output_concise(models)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def parse_options(args)
|
|
41
|
+
OptionParser.new do |opts|
|
|
42
|
+
opts.banner = 'Usage: belt contracts [options]'
|
|
43
|
+
|
|
44
|
+
opts.on('-f', '--format FORMAT', 'Output format: concise (default), json') do |format|
|
|
45
|
+
@options[:format] = format
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
opts.on('-g', '--grep PATTERN', 'Filter contracts matching pattern') do |pattern|
|
|
49
|
+
@options[:grep] = pattern
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts.on('--file FILE', 'Path to contracts file (overrides auto-detection)') do |file|
|
|
53
|
+
@options[:contracts_file] = file
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts.on('-h', '--help', 'Show this help') do
|
|
57
|
+
puts opts
|
|
58
|
+
exit
|
|
59
|
+
end
|
|
60
|
+
end.parse!(args)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def find_contracts_file
|
|
64
|
+
if @options[:contracts_file]
|
|
65
|
+
file = @options[:contracts_file]
|
|
66
|
+
return file if File.exist?(file)
|
|
67
|
+
|
|
68
|
+
abort "Error: Specified contracts file not found: #{file}"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
candidates = [
|
|
72
|
+
'config/contracts.rb',
|
|
73
|
+
'config/contracts.tf.rb',
|
|
74
|
+
'config/schema.tf.rb',
|
|
75
|
+
'infrastructure/schema.tf.rb'
|
|
76
|
+
]
|
|
77
|
+
candidates.find { |f| File.exist?(f) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def load_contracts(file)
|
|
81
|
+
Belt.instance_variable_set(:@application, nil)
|
|
82
|
+
begin
|
|
83
|
+
eval(File.read(file), binding, file) # rubocop:disable Security/Eval
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
abort "Error: Failed to load contracts file #{file}: #{e.message}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
schema = Belt.application.schema.to_h
|
|
89
|
+
models = build_models_from_schema(schema)
|
|
90
|
+
models = apply_grep(models) if @options[:grep]
|
|
91
|
+
models
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def apply_grep(models)
|
|
95
|
+
pattern = Regexp.new(@options[:grep], Regexp::IGNORECASE)
|
|
96
|
+
models.select do |m|
|
|
97
|
+
m[:name].match?(pattern) ||
|
|
98
|
+
m[:kind].match?(pattern) ||
|
|
99
|
+
m[:description].match?(pattern)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def output_json(models)
|
|
104
|
+
puts JSON.pretty_generate(models: models)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def output_concise(models)
|
|
108
|
+
return puts('No contracts defined.') if models.empty?
|
|
109
|
+
|
|
110
|
+
requests = models.select { |m| m[:kind] == 'request' }
|
|
111
|
+
responses = models.select { |m| m[:kind] == 'response' }
|
|
112
|
+
|
|
113
|
+
if requests.any?
|
|
114
|
+
puts 'REQUEST MODELS'
|
|
115
|
+
puts '-' * 60
|
|
116
|
+
requests.each { |m| print_model(m) }
|
|
117
|
+
puts
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if responses.any?
|
|
121
|
+
puts 'RESPONSE MODELS'
|
|
122
|
+
puts '-' * 60
|
|
123
|
+
responses.each { |m| print_model(m) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
puts "\n#{models.length} contract(s) total (#{requests.length} request, #{responses.length} response)"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def print_model(model)
|
|
130
|
+
required = model[:required] || []
|
|
131
|
+
props = (model[:properties] || {}).map do |name, meta|
|
|
132
|
+
type = meta['type'] || meta[:type] || 'string'
|
|
133
|
+
req_marker = required.include?(name.to_s) ? ' *' : ''
|
|
134
|
+
"#{name}:#{type}#{req_marker}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
puts " #{model[:name]} (#{model[:kind]})"
|
|
138
|
+
puts " #{props.join(', ')}" if props.any?
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -258,6 +258,9 @@ module Belt
|
|
|
258
258
|
end
|
|
259
259
|
end
|
|
260
260
|
|
|
261
|
+
# Empty S3 buckets before destroy — terraform can't delete non-empty buckets
|
|
262
|
+
empty_s3_buckets_in_state
|
|
263
|
+
|
|
261
264
|
# Run destroy with auto-approve (user already confirmed)
|
|
262
265
|
unless system('terraform', 'destroy', '-auto-approve')
|
|
263
266
|
puts "\n✗ terraform destroy failed."
|
|
@@ -269,6 +272,77 @@ module Belt
|
|
|
269
272
|
puts ' ✓ Infrastructure destroyed.'
|
|
270
273
|
end
|
|
271
274
|
|
|
275
|
+
def empty_s3_buckets_in_state
|
|
276
|
+
output = `terraform state list 2>/dev/null`
|
|
277
|
+
return unless Process.last_status.success?
|
|
278
|
+
|
|
279
|
+
bucket_resources = output.lines.map(&:strip).grep(/\Aaws_s3_bucket\./)
|
|
280
|
+
return if bucket_resources.empty?
|
|
281
|
+
|
|
282
|
+
bucket_resources.each do |resource|
|
|
283
|
+
bucket_name = resolve_bucket_name(resource)
|
|
284
|
+
next unless bucket_name
|
|
285
|
+
|
|
286
|
+
empty_s3_bucket(bucket_name)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def resolve_bucket_name(resource)
|
|
291
|
+
output = `terraform state show '#{resource}' 2>/dev/null`
|
|
292
|
+
return nil unless Process.last_status.success?
|
|
293
|
+
|
|
294
|
+
match = output.match(/^\s*bucket\s*=\s*"([^"]+)"/)
|
|
295
|
+
match&.[](1)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def empty_s3_bucket(bucket_name)
|
|
299
|
+
# Check if bucket exists
|
|
300
|
+
`aws s3api head-bucket --bucket '#{bucket_name}' 2>&1`
|
|
301
|
+
return unless Process.last_status.success?
|
|
302
|
+
|
|
303
|
+
puts " Emptying S3 bucket: #{bucket_name}"
|
|
304
|
+
|
|
305
|
+
# Delete all object versions (handles versioned buckets)
|
|
306
|
+
`aws s3 rm 's3://#{bucket_name}' --recursive 2>/dev/null`
|
|
307
|
+
|
|
308
|
+
# Also delete versioned objects and delete markers
|
|
309
|
+
delete_all_versions(bucket_name)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def delete_all_versions(bucket_name)
|
|
313
|
+
loop do
|
|
314
|
+
output = `aws s3api list-object-versions --bucket '#{bucket_name}' --max-items 1000 2>/dev/null`
|
|
315
|
+
break unless Process.last_status.success?
|
|
316
|
+
|
|
317
|
+
begin
|
|
318
|
+
data = JSON.parse(output)
|
|
319
|
+
rescue JSON::ParserError
|
|
320
|
+
break
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
versions = (data['Versions'] || []) + (data['DeleteMarkers'] || [])
|
|
324
|
+
break if versions.empty?
|
|
325
|
+
|
|
326
|
+
# Build delete objects payload
|
|
327
|
+
objects = versions.map do |v|
|
|
328
|
+
{ 'Key' => v['Key'], 'VersionId' => v['VersionId'] }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
delete_payload = JSON.generate({ 'Objects' => objects, 'Quiet' => true })
|
|
332
|
+
|
|
333
|
+
# Use a temp file for the payload since it can be large
|
|
334
|
+
require 'tempfile'
|
|
335
|
+
Tempfile.create(['delete-objects', '.json']) do |f|
|
|
336
|
+
f.write(delete_payload)
|
|
337
|
+
f.flush
|
|
338
|
+
`aws s3api delete-objects --bucket '#{bucket_name}' --delete 'file://#{f.path}' 2>/dev/null`
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# If there's no next token, we're done
|
|
342
|
+
break unless data['NextToken'] || data['IsTruncated']
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
272
346
|
def destroy_frontend
|
|
273
347
|
dir = 'frontend'
|
|
274
348
|
if Dir.exist?(dir)
|
|
@@ -59,6 +59,7 @@ module Belt
|
|
|
59
59
|
|
|
60
60
|
install_dependencies(dest_dir)
|
|
61
61
|
setup_frontend_infra_for_existing_environments
|
|
62
|
+
generate_views_for_existing_resources
|
|
62
63
|
|
|
63
64
|
return if @quiet || !@announce
|
|
64
65
|
|
|
@@ -104,6 +105,28 @@ module Belt
|
|
|
104
105
|
puts " create #{frontend_tf}" unless @quiet
|
|
105
106
|
end
|
|
106
107
|
|
|
108
|
+
def generate_views_for_existing_resources
|
|
109
|
+
routes_file = find_routes_file_path
|
|
110
|
+
return unless routes_file && File.exist?(routes_file)
|
|
111
|
+
|
|
112
|
+
resources = extract_resources_from_routes(routes_file)
|
|
113
|
+
return if resources.empty?
|
|
114
|
+
|
|
115
|
+
puts "\n Detected existing resources: #{resources.join(', ')}" unless @quiet
|
|
116
|
+
puts ' Generating views...' unless @quiet
|
|
117
|
+
|
|
118
|
+
require_relative 'views_command'
|
|
119
|
+
resources.each do |resource_name|
|
|
120
|
+
fields = ViewsCommand.read_schema_fields(resource_name)
|
|
121
|
+
ViewsCommand.new(resource_name, fields, force: true).generate
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def extract_resources_from_routes(routes_file)
|
|
126
|
+
content = File.read(routes_file)
|
|
127
|
+
content.scan(/resources\s+:(\w+)/).flatten.uniq
|
|
128
|
+
end
|
|
129
|
+
|
|
107
130
|
def copy_template(src_dir, dest_dir)
|
|
108
131
|
Dir.glob("#{src_dir}/**/*", File::FNM_DOTMATCH).each do |src|
|
|
109
132
|
next if File.directory?(src)
|
|
@@ -38,8 +38,8 @@ module Belt
|
|
|
38
38
|
Creates:
|
|
39
39
|
lambda/models/<name>.rb Model with validations and fields
|
|
40
40
|
lambda/controllers/<app>/<names>_controller.rb RESTful controller (index, show, create, update, destroy)
|
|
41
|
-
config/routes.
|
|
42
|
-
config/
|
|
41
|
+
config/routes.rb Route entry added
|
|
42
|
+
config/contracts.rb API response contract added
|
|
43
43
|
lambda/lib/routes/<app>_routes.rb Route manifest updated
|
|
44
44
|
infrastructure/modules/app/dynamodb.tf DynamoDB table generated
|
|
45
45
|
frontend/src/pages/<names>/ React pages (if frontend exists)
|
|
@@ -60,7 +60,7 @@ module Belt
|
|
|
60
60
|
notes: <<~NOTES
|
|
61
61
|
Creates:
|
|
62
62
|
lambda/models/<name>.rb Model class inheriting from ApplicationRecord
|
|
63
|
-
config/
|
|
63
|
+
config/contracts.rb API response contract added
|
|
64
64
|
infrastructure/modules/app/dynamodb.tf DynamoDB table generated
|
|
65
65
|
NOTES
|
|
66
66
|
},
|
|
@@ -333,8 +333,8 @@ module Belt
|
|
|
333
333
|
puts "\nFiles created/updated:"
|
|
334
334
|
puts " lambda/models/#{@singular_name}.rb"
|
|
335
335
|
puts " lambda/controllers/#{@app_name}/#{@resource_name}_controller.rb"
|
|
336
|
-
puts " #{find_routes_file_path || 'config/routes.
|
|
337
|
-
puts " #{
|
|
336
|
+
puts " #{find_routes_file_path || 'config/routes.rb'} (updated)"
|
|
337
|
+
puts " #{find_contracts_file_path || 'config/contracts.rb'} (updated)"
|
|
338
338
|
puts " lambda/lib/routes/#{@app_name}_routes.rb (updated)"
|
|
339
339
|
puts " frontend/src/pages/#{@resource_name}/ (views)" if Dir.exist?('frontend/src')
|
|
340
340
|
end
|
|
@@ -478,14 +478,16 @@ module Belt
|
|
|
478
478
|
puts " update #{schema_file}"
|
|
479
479
|
end
|
|
480
480
|
|
|
481
|
-
# Map generator field types to
|
|
481
|
+
# Map generator field types to schema DSL types.
|
|
482
|
+
# Preserves :text, :date, :datetime so views can render appropriate form inputs.
|
|
482
483
|
def schema_type_for(field_type)
|
|
483
484
|
case field_type.to_s
|
|
484
|
-
when 'text' then '
|
|
485
|
+
when 'text' then 'text'
|
|
485
486
|
when 'integer' then 'integer'
|
|
486
487
|
when 'float' then 'number'
|
|
487
488
|
when 'boolean' then 'boolean'
|
|
488
|
-
when 'date'
|
|
489
|
+
when 'date' then 'date'
|
|
490
|
+
when 'datetime' then 'datetime'
|
|
489
491
|
else 'string'
|
|
490
492
|
end
|
|
491
493
|
end
|
|
@@ -505,7 +507,7 @@ module Belt
|
|
|
505
507
|
return unless Dir.exist?('frontend/src')
|
|
506
508
|
return if @skip_views
|
|
507
509
|
|
|
508
|
-
Belt::CLI::ViewsCommand.new(@name, @fields).generate
|
|
510
|
+
Belt::CLI::ViewsCommand.new(@name, @fields, force: @force).generate
|
|
509
511
|
end
|
|
510
512
|
end
|
|
511
513
|
end
|