belt 0.6.3 → 0.6.5
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 +36 -1
- data/README.md +11 -0
- data/lib/belt/cli/command_registry.rb +93 -0
- data/lib/belt/cli/deploy_command.rb +15 -1
- data/lib/belt/cli/routes_command.rb +1 -0
- data/lib/belt/cli.rb +45 -4
- data/lib/belt/docs/plugins.md +34 -0
- data/lib/belt/route_dsl.rb +29 -2
- data/lib/belt/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39a9c396d05b2acfc5c69adc875f0050beb9395a2dc9f31dd4ac2d2221e011db
|
|
4
|
+
data.tar.gz: ddf3e2d95ea98eafc808f0d2e144196257f4880fb1b8d65ae063dd27becb9213
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f3463e2d5e75fc653437accecb76a3a66683cd28bf7781a9aadbef167b2cf3f18ad4d5e32fb3cb5f57c6e406929253f6f762ebecec5a4d20b2723c9a8118efd8
|
|
7
|
+
data.tar.gz: f1ccdeca8439a8063940c981f9c05a24abb319bcb0aa76c7212e0883fc6a9c4db7ee10f0e2da1f93105d1bca68844bd108ca7e820b8a462c900d309d35231905
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 0.6.5
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Per-route throttling in `routes.rb`.** Any route or resource accepts
|
|
8
|
+
`throttle: { rate: 2, burst: 5 }` (requests/second + burst). `belt routes -f json` emits it
|
|
9
|
+
as `throttle` on the route and conveyor-belt 0.0.20+ applies it as API Gateway stage method
|
|
10
|
+
settings, winning over the Lambda's `throttle:` in `config/lambda/<fn>.yml`. Malformed values
|
|
11
|
+
raise at load time. (Fizzy #1756)
|
|
12
|
+
|
|
13
|
+
## 0.6.4
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Plugins can register top-level commands.** A gem that ships
|
|
18
|
+
`lib/belt/commands/<name>_command.rb` defining `Belt::Commands::<Name>Command.run(args)`
|
|
19
|
+
gets `belt <name> ...` (underscores in the file name become hyphens), listed under
|
|
20
|
+
"Plugin Commands" in `belt --help`. Discovery mirrors `GeneratorRegistry`
|
|
21
|
+
(`Belt::CLI::CommandRegistry`). Optional `.description` for help text and `.standalone?`
|
|
22
|
+
to run outside a Belt project. Built-in commands and terraform actions win on a name
|
|
23
|
+
clash. `belt help` / `belt --help` / `belt -h` now print usage and exit 0.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **The data-migrations step of `belt deploy` no longer fails with "You have already
|
|
28
|
+
activated json X, but your Gemfile requires json Y".** The child `belt db:migrate` now
|
|
29
|
+
starts with the app's bundle loaded (`BUNDLE_GEMFILE` + `-rbundler/setup`), so the belt
|
|
30
|
+
CLI's own `require 'json'` can't activate a newer json than the app's lockfile pins.
|
|
31
|
+
- **A fresh environment's Lambdas missing their `config/lambda/*.yml` `env_vars`** (nested
|
|
32
|
+
envs especially, since they are always created fresh) is fixed in conveyor-belt 0.0.19:
|
|
33
|
+
the provider's create path read only the HCL `lambda_config` map and ignored
|
|
34
|
+
`lambda_config_dir`, so the YAML env vars, `iam_policy_arns` and triggers landed only on
|
|
35
|
+
a second deploy. Existing apps on `version = "~> 0.0.16"` pick it up with
|
|
36
|
+
`terraform init -upgrade` in the env's infrastructure directory.
|
|
37
|
+
|
|
38
|
+
## 0.6.3
|
|
4
39
|
|
|
5
40
|
### Added
|
|
6
41
|
|
data/README.md
CHANGED
|
@@ -209,6 +209,15 @@ end
|
|
|
209
209
|
|
|
210
210
|
**Key point:** `namespace` and `scope` are purely organizational — they affect URL paths and controller module resolution but never change which Lambda handles the request. Use `function` when you need routes to go to a different Lambda.
|
|
211
211
|
|
|
212
|
+
**Per-route throttling:** any route or resource accepts `throttle: { rate:, burst: }` (steady-state requests per second plus the burst the token bucket absorbs). `belt routes -f json` emits it and the conveyor-belt provider (0.0.20+) applies it as API Gateway stage method settings. A route's `throttle` wins over the serving Lambda's `throttle:` in `config/lambda/<fn>.yml`, which in turn wins over the provider's stage-level `throttle` block.
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
gateway :api, auth: :none do
|
|
216
|
+
post '/connect/chat', to: 'chat#create', throttle: { rate: 2, burst: 5 }
|
|
217
|
+
resources :leads, only: [:create], throttle: { rate: 1, burst: 3 }
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
212
221
|
### Nested Resource DSL
|
|
213
222
|
|
|
214
223
|
Inside a `resources` block, you can use Rails-like `member`, `collection`, `scope`, and singular `resource` for DRYer route definitions:
|
|
@@ -830,6 +839,8 @@ No central registry file, no initializer hook. Belt discovers generators by scan
|
|
|
830
839
|
|
|
831
840
|
That is the whole contract. After `bundle install`, `belt generate <name>` and `belt destroy <name>` just work.
|
|
832
841
|
|
|
842
|
+
Plugins can also add a top-level command: a file at `lib/belt/commands/<name>_command.rb` defining `Belt::Commands::<Name>Command.run(args)` becomes `belt <name> ...` and shows up under "Plugin Commands" in `belt --help`. Optional `.description` and `.standalone?` (run outside a project). Built-in commands win on a name clash. See `Belt::CLI::CommandRegistry` and `belt explain plugins`.
|
|
843
|
+
|
|
833
844
|
See `Belt::CLI::GeneratorRegistry` and the CHANGELOG entry for **0.1.13 Generator Extension API**.
|
|
834
845
|
|
|
835
846
|
### Plugin layout (canonical)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Belt
|
|
4
|
+
module CLI
|
|
5
|
+
# Discovers top-level CLI commands provided by external gems.
|
|
6
|
+
#
|
|
7
|
+
# Gems register commands by placing a file at:
|
|
8
|
+
# lib/belt/commands/<name>_command.rb
|
|
9
|
+
#
|
|
10
|
+
# Each command class must:
|
|
11
|
+
# - Be named <Name>Command (e.g., ConnectCommand)
|
|
12
|
+
# - Live in the Belt::Commands module
|
|
13
|
+
# - Implement .run(args)
|
|
14
|
+
# - Implement .description for help text (optional)
|
|
15
|
+
# - Implement .standalone? returning true to run outside a Belt project (optional)
|
|
16
|
+
#
|
|
17
|
+
# Example gem structure:
|
|
18
|
+
# belt-connect/
|
|
19
|
+
# lib/
|
|
20
|
+
# belt/
|
|
21
|
+
# commands/
|
|
22
|
+
# connect_command.rb
|
|
23
|
+
#
|
|
24
|
+
# The command is then available as:
|
|
25
|
+
# belt connect ...
|
|
26
|
+
#
|
|
27
|
+
# Underscores in the file name become hyphens in the command name
|
|
28
|
+
# (lib/belt/commands/feature_flags_command.rb → `belt feature-flags`).
|
|
29
|
+
# Built-in commands win on a name clash; see Belt::CLI.plugin_command.
|
|
30
|
+
module CommandRegistry
|
|
31
|
+
COMMAND_PATH = 'lib/belt/commands'
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Returns a hash of { "name" => command_class } for all discovered commands.
|
|
35
|
+
def discovered_commands
|
|
36
|
+
@discovered_commands ||= discover_all
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns just the names of discovered commands.
|
|
40
|
+
def command_names
|
|
41
|
+
discovered_commands.keys
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Returns the command class for a given name, or nil if not found.
|
|
45
|
+
def find(name)
|
|
46
|
+
discovered_commands[name]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Reset cache (useful in tests)
|
|
50
|
+
def reset!
|
|
51
|
+
@discovered_commands = nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def discover_all
|
|
57
|
+
commands = {}
|
|
58
|
+
|
|
59
|
+
Gem.loaded_specs.each_value do |spec|
|
|
60
|
+
commands_dir = File.join(spec.gem_dir, COMMAND_PATH)
|
|
61
|
+
next unless File.directory?(commands_dir)
|
|
62
|
+
|
|
63
|
+
Dir.glob(File.join(commands_dir, '*_command.rb')).each do |file|
|
|
64
|
+
basename = File.basename(file, '.rb').sub(/_command\z/, '')
|
|
65
|
+
klass = load_command_class(file, basename)
|
|
66
|
+
commands[basename.tr('_', '-')] = klass if klass
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
commands
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def load_command_class(file, basename)
|
|
74
|
+
require file
|
|
75
|
+
|
|
76
|
+
class_name = "#{classify(basename)}Command"
|
|
77
|
+
Belt::Commands.const_get(class_name, false)
|
|
78
|
+
rescue LoadError, NameError => e
|
|
79
|
+
warn "[Belt] Warning: Failed to load command '#{basename}' from #{file}: #{e.message}"
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def classify(name)
|
|
84
|
+
name.split('_').map(&:capitalize).join
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Namespace for top-level command classes provided by external gems
|
|
91
|
+
module Commands
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -923,13 +923,27 @@ module Belt
|
|
|
923
923
|
return true if @skip_migrations || !DbMigrateCommand.migrations?(@project_root)
|
|
924
924
|
|
|
925
925
|
puts "\n━━━ data migrations ━━━"
|
|
926
|
-
return true if system(Gem.ruby, BELT_EXE, 'db:migrate', @env, '--yes',
|
|
926
|
+
return true if system(migration_child_env, Gem.ruby, BELT_EXE, 'db:migrate', @env, '--yes',
|
|
927
|
+
chdir: @project_root)
|
|
927
928
|
|
|
928
929
|
puts "\n ⚠ Data migrations failed for #{@env}. The deploy itself succeeded."
|
|
929
930
|
puts " Fix the migration and run `belt db:migrate #{@env}` to retry."
|
|
930
931
|
false
|
|
931
932
|
end
|
|
932
933
|
|
|
934
|
+
# Load the app's bundle before the belt CLI does. Otherwise the CLI's own
|
|
935
|
+
# `require 'json'` activates the newest installed json, and db:migrate's
|
|
936
|
+
# `bundler/setup` then fails with "You have already activated json X,
|
|
937
|
+
# but your Gemfile requires json Y" (Fizzy #1734).
|
|
938
|
+
def migration_child_env
|
|
939
|
+
gemfile = File.join(@project_root, 'Gemfile')
|
|
940
|
+
return {} unless File.exist?(gemfile)
|
|
941
|
+
|
|
942
|
+
rubyopt = ENV.fetch('RUBYOPT', '')
|
|
943
|
+
rubyopt = "-rbundler/setup #{rubyopt}".strip unless rubyopt.include?('bundler/setup')
|
|
944
|
+
{ 'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => rubyopt }
|
|
945
|
+
end
|
|
946
|
+
|
|
933
947
|
def deploy_frontend_if_exists
|
|
934
948
|
require_relative 'frontend_registry'
|
|
935
949
|
require_relative 'frontend_deploy_command'
|
data/lib/belt/cli.rb
CHANGED
|
@@ -31,6 +31,7 @@ require_relative 'cli/logs_command'
|
|
|
31
31
|
require_relative 'cli/doctor_command'
|
|
32
32
|
require_relative 'cli/plugin_command'
|
|
33
33
|
require_relative 'cli/explain_command'
|
|
34
|
+
require_relative 'cli/command_registry'
|
|
34
35
|
|
|
35
36
|
module Belt
|
|
36
37
|
module CLI
|
|
@@ -56,7 +57,8 @@ module Belt
|
|
|
56
57
|
'dns' => Belt::CLI::DnsCommand,
|
|
57
58
|
'frontend' => Belt::CLI::FrontendEnvCommand,
|
|
58
59
|
%w[server s] => Belt::CLI::ServerCommand,
|
|
59
|
-
%w[version --version -v] => ->(_args) { puts "Belt #{Belt::VERSION}" }
|
|
60
|
+
%w[version --version -v] => ->(_args) { puts "Belt #{Belt::VERSION}" },
|
|
61
|
+
%w[help --help -h] => ->(_args) { puts usage }
|
|
60
62
|
}.freeze
|
|
61
63
|
|
|
62
64
|
COMMANDS = COMMANDS_DEFINITION.each_with_object({}) do |(keys, handler), hash|
|
|
@@ -66,7 +68,7 @@ module Belt
|
|
|
66
68
|
TERRAFORM_ACTIONS = Belt::CLI::TerraformCommand::ACTIONS
|
|
67
69
|
|
|
68
70
|
# Commands that can run without being inside a Belt project
|
|
69
|
-
STANDALONE_COMMANDS = %w[new version --version -v doctor explain].freeze
|
|
71
|
+
STANDALONE_COMMANDS = %w[new version --version -v help --help -h doctor explain].freeze
|
|
70
72
|
|
|
71
73
|
def self.start(args)
|
|
72
74
|
command = args.shift
|
|
@@ -76,8 +78,10 @@ module Belt
|
|
|
76
78
|
exit 1
|
|
77
79
|
end
|
|
78
80
|
|
|
81
|
+
plugin = plugin_command(command)
|
|
82
|
+
|
|
79
83
|
# For project-level commands, find and chdir to the project root
|
|
80
|
-
ensure_project_root!(command) unless
|
|
84
|
+
ensure_project_root!(command) unless standalone?(command, plugin)
|
|
81
85
|
|
|
82
86
|
# `belt destroy` is ambiguous: could be terraform destroy or belt destroy <generator>.
|
|
83
87
|
return if command == 'destroy' && route_destroy_command(args)
|
|
@@ -85,7 +89,7 @@ module Belt
|
|
|
85
89
|
# Terraform shorthand: belt init wups, belt plan wups, belt apply wups, belt destroy wups
|
|
86
90
|
return Belt::CLI::TerraformCommand.run(command, args) if TERRAFORM_ACTIONS.include?(command)
|
|
87
91
|
|
|
88
|
-
handler = COMMANDS[command]
|
|
92
|
+
handler = COMMANDS[command] || plugin
|
|
89
93
|
|
|
90
94
|
# If no built-in command matched, try running it as a rake task
|
|
91
95
|
if handler.nil?
|
|
@@ -102,7 +106,43 @@ module Belt
|
|
|
102
106
|
end
|
|
103
107
|
end
|
|
104
108
|
|
|
109
|
+
# Returns the gem-provided command class for `name`, or nil. Built-in
|
|
110
|
+
# commands and terraform actions win on a name clash.
|
|
111
|
+
def self.plugin_command(name)
|
|
112
|
+
return nil if name.nil? || builtin_command?(name)
|
|
113
|
+
|
|
114
|
+
CommandRegistry.find(name)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.builtin_command?(name)
|
|
118
|
+
COMMANDS.key?(name) || TERRAFORM_ACTIONS.include?(name)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.standalone?(command, plugin)
|
|
122
|
+
return STANDALONE_COMMANDS.include?(command) unless plugin
|
|
123
|
+
|
|
124
|
+
plugin.respond_to?(:standalone?) && plugin.standalone?
|
|
125
|
+
end
|
|
126
|
+
|
|
105
127
|
def self.usage
|
|
128
|
+
base_usage.sub("\nRake Tasks:\n", "#{plugin_usage}\nRake Tasks:\n")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# "Plugin Commands:" help section for gem-provided commands ("" when none).
|
|
132
|
+
def self.plugin_usage
|
|
133
|
+
names = CommandRegistry.command_names.reject { |name| builtin_command?(name) }.sort
|
|
134
|
+
return '' if names.empty?
|
|
135
|
+
|
|
136
|
+
lines = names.map do |name|
|
|
137
|
+
klass = CommandRegistry.find(name)
|
|
138
|
+
description = klass.respond_to?(:description) ? klass.description.to_s : ''
|
|
139
|
+
" #{name.ljust(42)} #{description}".rstrip
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
"\nPlugin Commands:\n#{lines.join("\n")}\n"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.base_usage
|
|
106
146
|
<<~USAGE
|
|
107
147
|
Usage: belt <command> [options]
|
|
108
148
|
|
|
@@ -151,6 +191,7 @@ module Belt
|
|
|
151
191
|
destroy [environment] <env> terraform destroy for environment
|
|
152
192
|
output [environment] <env> terraform output for environment
|
|
153
193
|
--version Show Belt version
|
|
194
|
+
help, --help, -h Show this help
|
|
154
195
|
|
|
155
196
|
Rake Tasks:
|
|
156
197
|
Any rake task from your Gemfile dependencies can be run directly:
|
data/lib/belt/docs/plugins.md
CHANGED
|
@@ -45,6 +45,38 @@ Belt discovers plugins automatically when:
|
|
|
45
45
|
|
|
46
46
|
Optional: `.destroy(args)` and `.description` for destroy path and help text.
|
|
47
47
|
|
|
48
|
+
## Top-level Commands
|
|
49
|
+
|
|
50
|
+
A plugin can also add its own `belt <name> ...` command, the way `belt-connect`
|
|
51
|
+
adds `belt connect`:
|
|
52
|
+
|
|
53
|
+
1. Gem has a file at `lib/belt/commands/<name>_command.rb`
|
|
54
|
+
2. Class is `Belt::Commands::<Name>Command`
|
|
55
|
+
3. Class implements `.run(args)` (required); `args` is everything after `<name>`
|
|
56
|
+
|
|
57
|
+
Optional: `.description` (shown under "Plugin Commands" in `belt --help`) and
|
|
58
|
+
`.standalone?` returning `true` to run outside a Belt project. Otherwise the
|
|
59
|
+
command runs from the project root, like built-ins.
|
|
60
|
+
|
|
61
|
+
Underscores in the file name become hyphens (`feature_flags_command.rb` →
|
|
62
|
+
`belt feature-flags`). Built-in commands and terraform actions win on a name
|
|
63
|
+
clash, so a plugin can't shadow `belt deploy`.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# lib/belt/commands/connect_command.rb
|
|
67
|
+
module Belt
|
|
68
|
+
module Commands
|
|
69
|
+
class ConnectCommand
|
|
70
|
+
def self.description = 'Connect widgets to your app'
|
|
71
|
+
|
|
72
|
+
def self.run(args)
|
|
73
|
+
# belt connect widget orders --env dev → args == ['widget', 'orders', '--env', 'dev']
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
48
80
|
## Generator Checklist
|
|
49
81
|
|
|
50
82
|
A good plugin generator typically installs:
|
|
@@ -71,6 +103,8 @@ belt-messaging/
|
|
|
71
103
|
│ │ ├── version.rb
|
|
72
104
|
│ │ ├── controllers/ # Default controllers
|
|
73
105
|
│ │ └── templates/ # ERB templates for generator
|
|
106
|
+
│ ├── commands/
|
|
107
|
+
│ │ └── messaging_command.rb # ← optional `belt messaging`
|
|
74
108
|
│ └── generators/
|
|
75
109
|
│ └── messaging_generator.rb # ← auto-discovered
|
|
76
110
|
└── spec/
|
data/lib/belt/route_dsl.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Belt
|
|
|
9
9
|
|
|
10
10
|
class Route
|
|
11
11
|
attr_reader :method, :path, :auth, :lambda, :cors, :tables, :route_type,
|
|
12
|
-
:controller, :action, :request_model, :response_model, :response_context
|
|
12
|
+
:controller, :action, :request_model, :response_model, :response_context, :throttle
|
|
13
13
|
|
|
14
14
|
def initialize(method, path, options = {})
|
|
15
15
|
@method = method.to_s.upcase
|
|
@@ -17,6 +17,7 @@ module Belt
|
|
|
17
17
|
@auth = options[:auth]
|
|
18
18
|
@lambda = options[:lambda]
|
|
19
19
|
@cors = options.fetch(:cors, true)
|
|
20
|
+
@throttle = self.class.normalize_throttle(options[:throttle])
|
|
20
21
|
@tables = options[:tables] || []
|
|
21
22
|
@route_type = options[:route_type] || :action
|
|
22
23
|
@controller = options[:controller]
|
|
@@ -26,6 +27,31 @@ module Belt
|
|
|
26
27
|
@response_context = options[:response_context]&.to_s
|
|
27
28
|
end
|
|
28
29
|
|
|
30
|
+
# Per-route API Gateway limit: `throttle: { rate: 2, burst: 5 }` (requests per
|
|
31
|
+
# second, token bucket). Emitted by `belt routes -f json` and applied by the
|
|
32
|
+
# conveyor-belt provider (0.0.20+) as stage method settings; it wins over the
|
|
33
|
+
# Lambda's `throttle:` in config/lambda/<fn>.yml. Returns nil when unset.
|
|
34
|
+
def self.normalize_throttle(raw)
|
|
35
|
+
return nil if raw.nil?
|
|
36
|
+
|
|
37
|
+
unless raw.is_a?(Hash)
|
|
38
|
+
raise ArgumentError, "throttle must be a Hash like { rate: 2, burst: 5 } (got #{raw.inspect})"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
rate = raw[:rate] || raw['rate']
|
|
42
|
+
burst = raw[:burst] || raw['burst']
|
|
43
|
+
raise ArgumentError, 'throttle needs rate (requests/second) and burst' if rate.nil? || burst.nil?
|
|
44
|
+
|
|
45
|
+
unless rate.is_a?(Numeric) && rate.positive?
|
|
46
|
+
raise ArgumentError, "throttle rate must be a number > 0 (got #{rate.inspect})"
|
|
47
|
+
end
|
|
48
|
+
unless burst.is_a?(Integer) && burst.positive?
|
|
49
|
+
raise ArgumentError, "throttle burst must be an integer > 0 (got #{burst.inspect})"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
{ rate: rate, burst: burst }
|
|
53
|
+
end
|
|
54
|
+
|
|
29
55
|
def resource?
|
|
30
56
|
@route_type == :resource || @route_type == :resources
|
|
31
57
|
end
|
|
@@ -494,7 +520,8 @@ module Belt
|
|
|
494
520
|
action: action,
|
|
495
521
|
request_model: options[:request_model],
|
|
496
522
|
response_model: options[:response_model],
|
|
497
|
-
response_context: options[:response_context]
|
|
523
|
+
response_context: options[:response_context],
|
|
524
|
+
throttle: options[:throttle]
|
|
498
525
|
}
|
|
499
526
|
|
|
500
527
|
@routes << Belt::Route.new(method, path, route_options)
|
data/lib/belt/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: belt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
@@ -118,6 +118,7 @@ files:
|
|
|
118
118
|
- lib/belt/cli/backup_runner.rb
|
|
119
119
|
- lib/belt/cli/bucket_security.rb
|
|
120
120
|
- lib/belt/cli/cognito_sharer.rb
|
|
121
|
+
- lib/belt/cli/command_registry.rb
|
|
121
122
|
- lib/belt/cli/console_command.rb
|
|
122
123
|
- lib/belt/cli/contracts_command.rb
|
|
123
124
|
- lib/belt/cli/db_copy_command.rb
|