summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
authorDaniel Pittman <daniel@puppetlabs.com>2011-03-24 13:10:27 -0700
committerDaniel Pittman <daniel@puppetlabs.com>2011-04-04 10:19:33 -0700
commit5bba1a26140cd3326739b00c2d60dff9321d4044 (patch)
treee0fb09eca95550918dd504b6d7a9c6949b905406 /lib/puppet
parent1400fec62e4e692badc5b68eb7d6d947997d7204 (diff)
downloadpuppet-5bba1a26140cd3326739b00c2d60dff9321d4044.tar.gz
puppet-5bba1a26140cd3326739b00c2d60dff9321d4044.tar.xz
puppet-5bba1a26140cd3326739b00c2d60dff9321d4044.zip
(#6749) Implement support for options on strings and actions.
We want to support both strings and actions specifying options, to support generic wrappers that present strings to the user across multiple distinct front-ends. At the moment we focus on implementation of a generic CLI providing full control to all the strings, but we aim to support other programmatic interfaces including Ruby and RPC invocation as part of the overall change. We also detect, at the time they are declared, duplicate options. They are reported, like any duplicate, with an error thrown. Specifically: It is illegal to declare a duplicate option in the same scope, such as within the same string, or within the same action. This is unchanged. It is illegal to declare an option in an action that duplicates an option in the string, or vice-versa. This is reported when the duplicate is declared, so may report on either the string or action depending on sequence. It remains legal to duplicate the same option across multiple actions, with different meanings. There is no conflict, as the same option can't be passed to both simultaneously. Reviewed-By: Pieter van de Bruggen <pieter@puppetlabs.com>
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/string.rb8
-rw-r--r--lib/puppet/string/action.rb32
-rw-r--r--lib/puppet/string/action_builder.rb11
-rw-r--r--lib/puppet/string/action_manager.rb10
-rw-r--r--lib/puppet/string/option.rb24
-rw-r--r--lib/puppet/string/option_builder.rb25
-rw-r--r--lib/puppet/string/option_manager.rb46
7 files changed, 138 insertions, 18 deletions
diff --git a/lib/puppet/string.rb b/lib/puppet/string.rb
index 04db1f33b..517cf4506 100644
--- a/lib/puppet/string.rb
+++ b/lib/puppet/string.rb
@@ -2,12 +2,16 @@ require 'puppet'
require 'puppet/util/autoload'
class Puppet::String
- require 'puppet/string/action_manager'
require 'puppet/string/string_collection'
+ require 'puppet/string/action_manager'
include Puppet::String::ActionManager
extend Puppet::String::ActionManager
+ require 'puppet/string/option_manager'
+ include Puppet::String::OptionManager
+ extend Puppet::String::OptionManager
+
include Puppet::Util
class << self
@@ -58,7 +62,7 @@ class Puppet::String
def initialize(name, version, &block)
unless Puppet::String::StringCollection.validate_version(version)
- raise ArgumentError, "Cannot create string with invalid version number '#{version}'!"
+ raise ArgumentError, "Cannot create string #{name.inspect} with invalid version number '#{version}'!"
end
@name = Puppet::String::StringCollection.underscorize(name)
diff --git a/lib/puppet/string/action.rb b/lib/puppet/string/action.rb
index 5a7f3f203..4219aca0a 100644
--- a/lib/puppet/string/action.rb
+++ b/lib/puppet/string/action.rb
@@ -1,4 +1,5 @@
require 'puppet/string'
+require 'puppet/string/option'
class Puppet::String::Action
attr_reader :name
@@ -8,11 +9,10 @@ class Puppet::String::Action
end
def initialize(string, name, attrs = {})
- name = name.to_s
- raise "'#{name}' is an invalid action name" unless name =~ /^[a-z]\w*$/
-
- @string = string
- @name = name
+ raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/
+ @string = string
+ @name = name.to_sym
+ @options = {}
attrs.each do |k,v| send("#{k}=", v) end
end
@@ -27,4 +27,26 @@ class Puppet::String::Action
@string.meta_def(@name, &block)
end
end
+
+ def add_option(option)
+ if option? option.name then
+ raise ArgumentError, "#{option.name} duplicates an existing option on #{self}"
+ elsif @string.option? option.name then
+ raise ArgumentError, "#{option.name} duplicates an existing option on #{@string}"
+ end
+
+ @options[option.name] = option
+ end
+
+ def option?(name)
+ @options.include? name.to_sym
+ end
+
+ def options
+ (@options.keys + @string.options).sort
+ end
+
+ def get_option(name)
+ @options[name.to_sym] || @string.get_option(name)
+ end
end
diff --git a/lib/puppet/string/action_builder.rb b/lib/puppet/string/action_builder.rb
index b3db51104..fb2a749ae 100644
--- a/lib/puppet/string/action_builder.rb
+++ b/lib/puppet/string/action_builder.rb
@@ -5,10 +5,8 @@ class Puppet::String::ActionBuilder
attr_reader :action
def self.build(string, name, &block)
- name = name.to_s
- raise "Action '#{name}' must specify a block" unless block
- builder = new(string, name, &block)
- builder.action
+ raise "Action #{name.inspect} must specify a block" unless block
+ new(string, name, &block).action
end
def initialize(string, name, &block)
@@ -24,4 +22,9 @@ class Puppet::String::ActionBuilder
raise "Invoke called on an ActionBuilder with no corresponding Action" unless @action
@action.invoke = block
end
+
+ def option(name, attrs = {}, &block)
+ option = Puppet::String::OptionBuilder.build(@action, name, attrs, &block)
+ @action.add_option(option)
+ end
end
diff --git a/lib/puppet/string/action_manager.rb b/lib/puppet/string/action_manager.rb
index c29dbf454..c980142ce 100644
--- a/lib/puppet/string/action_manager.rb
+++ b/lib/puppet/string/action_manager.rb
@@ -5,20 +5,15 @@ module Puppet::String::ActionManager
# the code to do so.
def action(name, &block)
@actions ||= {}
- name = name.to_s.downcase.to_sym
-
raise "Action #{name} already defined for #{self}" if action?(name)
-
action = Puppet::String::ActionBuilder.build(self, name, &block)
-
- @actions[name] = action
+ @actions[action.name] = action
end
# This is the short-form of an action definition; it doesn't use the
# builder, just creates the action directly from the block.
def script(name, &block)
@actions ||= {}
- name = name.to_s.downcase.to_sym
raise "Action #{name} already defined for #{self}" if action?(name)
@actions[name] = Puppet::String::Action.new(self, name, :invoke => block)
end
@@ -36,7 +31,8 @@ module Puppet::String::ActionManager
end
def get_action(name)
- @actions[name].dup
+ @actions ||= {}
+ @actions[name.to_sym]
end
def action?(name)
diff --git a/lib/puppet/string/option.rb b/lib/puppet/string/option.rb
new file mode 100644
index 000000000..bdc3e07c5
--- /dev/null
+++ b/lib/puppet/string/option.rb
@@ -0,0 +1,24 @@
+class Puppet::String::Option
+ attr_reader :name, :string
+
+ def initialize(string, name, attrs = {})
+ raise "#{name.inspect} is an invalid option name" unless name.to_s =~ /^[a-z]\w*$/
+ @string = string
+ @name = name.to_sym
+ attrs.each do |k,v| send("#{k}=", v) end
+ end
+
+ def to_s
+ @name.to_s.tr('_', '-')
+ end
+
+ Types = [:boolean, :string]
+ def type
+ @type ||= :boolean
+ end
+ def type=(input)
+ value = begin input.to_sym rescue nil end
+ Types.include?(value) or raise ArgumentError, "#{input.inspect} is not a valid type"
+ @type = value
+ end
+end
diff --git a/lib/puppet/string/option_builder.rb b/lib/puppet/string/option_builder.rb
new file mode 100644
index 000000000..2087cbc99
--- /dev/null
+++ b/lib/puppet/string/option_builder.rb
@@ -0,0 +1,25 @@
+require 'puppet/string/option'
+
+class Puppet::String::OptionBuilder
+ attr_reader :option
+
+ def self.build(string, name, attrs = {}, &block)
+ new(string, name, attrs, &block).option
+ end
+
+ private
+ def initialize(string, name, attrs, &block)
+ @string = string
+ @option = Puppet::String::Option.new(string, name, attrs)
+ block and instance_eval(&block)
+ @option
+ end
+
+ # Metaprogram the simple DSL from the option class.
+ Puppet::String::Option.instance_methods.grep(/=$/).each do |setter|
+ next if setter =~ /^=/ # special case, darn it...
+
+ dsl = setter.sub(/=$/, '')
+ define_method(dsl) do |value| @option.send(setter, value) end
+ end
+end
diff --git a/lib/puppet/string/option_manager.rb b/lib/puppet/string/option_manager.rb
new file mode 100644
index 000000000..df3ae6b4b
--- /dev/null
+++ b/lib/puppet/string/option_manager.rb
@@ -0,0 +1,46 @@
+require 'puppet/string/option_builder'
+
+module Puppet::String::OptionManager
+ # Declare that this app can take a specific option, and provide
+ # the code to do so.
+ def option(name, attrs = {}, &block)
+ @options ||= {}
+ raise ArgumentError, "Option #{name} already defined for #{self}" if option?(name)
+ actions.each do |action|
+ if get_action(action).option?(name) then
+ raise ArgumentError, "Option #{name} already defined on action #{action} for #{self}"
+ end
+ end
+ option = Puppet::String::OptionBuilder.build(self, name, &block)
+ @options[option.name] = option
+ end
+
+ def options
+ @options ||= {}
+ result = @options.keys
+
+ if self.is_a?(Class) and superclass.respond_to?(:options)
+ result += superclass.options
+ elsif self.class.respond_to?(:options)
+ result += self.class.options
+ end
+ result.sort
+ end
+
+ def get_option(name)
+ @options ||= {}
+ result = @options[name.to_sym]
+ unless result then
+ if self.is_a?(Class) and superclass.respond_to?(:get_option)
+ result = superclass.get_option(name)
+ elsif self.class.respond_to?(:get_option)
+ result = self.class.get_option(name)
+ end
+ end
+ return result
+ end
+
+ def option?(name)
+ options.include? name.to_sym
+ end
+end