diff --git a/CHANGELOG.md b/CHANGELOG.md index ec5904c7..559d36c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,6 +136,12 @@ https://github.com/capistrano/capistrano/compare/v3.2.1...v3.3.3 This allows roles to specify properties common to all servers and then for individual servers to modify them, keeping things DRY + +* Enhancements (@Kriechi) + * Added validate method to DSL to allow validation of certain values + - validate values before assignment inside of `set(:key, value)` + - should raise a `Capistrano::ValidationError` if invalid + * Added default validation for Capistrano-specific variables Breaking Changes: * By using Ruby's noecho method introduced in Ruby version 1.9.3, we dropped support for Ruby versions prior to 1.9.3. See [issue #878](https://github.com/capistrano/capistrano/issues/878) and [PR #1112](https://github.com/capistrano/capistrano/pull/1112) for more information. (@kaikuchn) diff --git a/README.md b/README.md index 9b388ee8..832a66dc 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,24 @@ $ bundle exec cap production deploy --prereqs $ bundle exec cap production deploy --trace ``` +## Validation of variables + +To validate a variable, each time before it is set, define a validation: + +```ruby +validate :some_key do |key, value| + if value.length < 5 + raise Capistrano::ValidationError, "Length of #{key} is too short!" + end +end +``` + +Multiple validation can be assigned to a single key. Validations will be executed +in the order of registration. + +Validations can be used to ensure certain properties of user-supplied values, +e.g. from `ask` or `ENV`. `` + ## Testing Capistrano has two test suites: an RSpec suite and a Cucumber suite. The diff --git a/lib/capistrano/configuration.rb b/lib/capistrano/configuration.rb index c56bf567..cbff7815 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -4,6 +4,8 @@ require_relative 'configuration/server' require_relative 'configuration/servers' module Capistrano + class ValidationError < Exception; end + class Configuration def initialize(config = nil) @@ -24,11 +26,15 @@ module Capistrano end def set(key, value) + invoke_validations key, value config[key] = value end def set_if_empty(key, value) - config[key] = value unless config.has_key? key + unless config.has_key? key + invoke_validations key, value + config[key] = value + end end def delete(key) @@ -43,6 +49,12 @@ module Capistrano return value end + def validate(key, &validator) + vs = (validators[key] || []) + vs << validator + validators[key] = vs + end + def keys config.keys end @@ -129,6 +141,10 @@ module Capistrano @config ||= Hash.new end + def validators + @validators ||= Hash.new + end + def fetch_for(key, default, &block) if block_given? config.fetch(key, &block) @@ -140,5 +156,13 @@ module Capistrano def callable_without_parameters?(x) x.respond_to?(:call) && ( !x.respond_to?(:arity) || x.arity == 0) end + + def invoke_validations(key, value) + return unless validators.has_key? key + + validators[key].each do |validator| + validator.call(key, value) + end + end end end diff --git a/lib/capistrano/defaults.rb b/lib/capistrano/defaults.rb index 92dff7cb..5eb82d83 100644 --- a/lib/capistrano/defaults.rb +++ b/lib/capistrano/defaults.rb @@ -1,3 +1,12 @@ +validate :application do |key, value| + changed_value = value.gsub(/[^[[:alnum:]]]/, '_') + if value != changed_value + warn "Invalid value for :application detected! Try using this: " + warn " set :application, '#{changed_value}'" + raise Capistrano::ValidationError + end +end + set_if_empty :scm, :git set_if_empty :branch, :master set_if_empty :deploy_to, -> { "/var/www/#{fetch(:application)}" } diff --git a/spec/lib/capistrano/configuration_spec.rb b/spec/lib/capistrano/configuration_spec.rb index ea2fa52e..880dba03 100644 --- a/spec/lib/capistrano/configuration_spec.rb +++ b/spec/lib/capistrano/configuration_spec.rb @@ -130,6 +130,22 @@ module Capistrano expect { subject }.to raise_error end end + + context 'validations' do + before do + config.validate :key do |_, value| + raise Capistrano::ValidationError unless value.length > 3 + end + end + + it 'validates without error' do + expect{ config.set(:key, 'longer_value') }.not_to raise_error + end + + it 'raises an exception' do + expect{ config.set(:key, 'sho') }.to raise_error(Capistrano::ValidationError) + end + end end describe 'keys' do