diff --git a/CHANGELOG.md b/CHANGELOG.md index 85f011d6..3e8df3c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ Reverse Chronological Order: * Refactored and simplified property filtering code (@townsen) * Minor changes + * Add role_properties() method (see capistrano.github.io PR for doc) (@townsen) * Add equality syntax ( eg. port: 1234) for property filtering (@townsen) * Add documentation regarding property filtering (@townsen) * Clarify wording and recommendation in stage template. (@Kriechi) diff --git a/lib/capistrano/configuration.rb b/lib/capistrano/configuration.rb index 18d548c6..92e1ec65 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -63,6 +63,10 @@ module Capistrano servers.roles_for(names) end + def role_properties_for(names, &block) + servers.role_properties_for(names, &block) + end + def primary(role) servers.fetch_primary(role) end diff --git a/lib/capistrano/configuration/filter.rb b/lib/capistrano/configuration/filter.rb index 7185ff64..2e22a193 100644 --- a/lib/capistrano/configuration/filter.rb +++ b/lib/capistrano/configuration/filter.rb @@ -5,7 +5,7 @@ module Capistrano class Filter def initialize type, values = nil raise "Invalid filter type #{type}" unless [:host,:role].include? type - av = Array(values) + av = Array(values).dup @mode = case when av.size == 0 then :none when av.include?(:all) then :all diff --git a/lib/capistrano/configuration/servers.rb b/lib/capistrano/configuration/servers.rb index 094ae885..9f2809a9 100644 --- a/lib/capistrano/configuration/servers.rb +++ b/lib/capistrano/configuration/servers.rb @@ -22,6 +22,23 @@ module Capistrano s.select { |server| server.select?(options) } end + def role_properties_for(rolenames) + roles = rolenames.to_set + rps = Set.new unless block_given? + roles_for(rolenames).each do |host| + host.roles.intersection(roles).each do |role| + [host.properties.fetch(role)].flatten(1).each do |props| + if block_given? + yield host, role, props + else + rps << (props || {}).merge( role: role, hostname: host.hostname ) + end + end + end + end + block_given? ? nil: rps + end + def fetch_primary(role) hosts = roles_for([role]) hosts.find(&:primary) || hosts.first diff --git a/lib/capistrano/dsl/env.rb b/lib/capistrano/dsl/env.rb index b1c7b8ff..c12a4083 100644 --- a/lib/capistrano/dsl/env.rb +++ b/lib/capistrano/dsl/env.rb @@ -47,6 +47,10 @@ module Capistrano env.roles_for(names.flatten) end + def role_properties(*names, &block) + env.role_properties_for(names, &block) + end + def release_roles(*names) if names.last.is_a? Hash names.last.merge!({ :exclude => :no_release }) diff --git a/spec/integration/dsl_spec.rb b/spec/integration/dsl_spec.rb index 72dfb9ff..e4014fd4 100644 --- a/spec/integration/dsl_spec.rb +++ b/spec/integration/dsl_spec.rb @@ -501,4 +501,43 @@ describe Capistrano::DSL do end + describe 'role_properties()' do + + before do + dsl.role :redis, %w[example1.com example2.com], redis: { port: 6379, type: :slave } + dsl.server 'example1.com', roles: %w{web}, active: true, web: { port: 80 } + dsl.server 'example2.com', roles: %w{web redis}, web: { port: 81 }, redis: { type: :master } + dsl.server 'example3.com', roles: %w{app}, primary: true + end + + it 'retrieves properties for a single role as a set' do + rps = dsl.role_properties(:app) + expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app}]) + end + + it 'retrieves properties for multiple roles as a set' do + rps = dsl.role_properties(:app, :web) + expect(rps).to eq(Set[{ hostname: 'example3.com', role: :app},{ hostname: 'example1.com', role: :web, port: 80},{ hostname: 'example2.com', role: :web, port: 81}]) + end + + it 'yields the properties for a single role' do + recipient = mock('recipient') + recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave}) + recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master}) + dsl.role_properties(:redis) do |host, role, props| + recipient.doit(host, role, props) + end + end + + it 'yields the properties for multiple roles' do + recipient = mock('recipient') + recipient.expects(:doit).with('example1.com', :redis, { port: 6379, type: :slave}) + recipient.expects(:doit).with('example2.com', :redis, { port: 6379, type: :master}) + recipient.expects(:doit).with('example3.com', :app, nil) + dsl.role_properties(:redis, :app) do |host, role, props| + recipient.doit(host, role, props) + end + end + end + end