diff --git a/lib/puppet/string/action.rb b/lib/puppet/string/action.rb index ee3b2991b..0f5032ffb 100644 --- a/lib/puppet/string/action.rb +++ b/lib/puppet/string/action.rb @@ -1,121 +1,121 @@ # -*- coding: utf-8 -*- require 'puppet/string' require 'puppet/string/option' class Puppet::String::Action attr_reader :name def to_s "#{@string}##{@name}" end def initialize(string, name, attrs = {}) 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 # Initially, this was defined to allow the @action.invoke pattern, which is # a very natural way to invoke behaviour given our introspection # capabilities. Heck, our initial plan was to have the string delegate to # the action object for invocation and all. # # It turns out that we have a binding problem to solve: @string was bound to # the parent class, not the subclass instance, and we don't pass the # appropriate context or change the binding enough to make this work. # # We could hack around it, by either mandating that you pass the context in # to invoke, or try to get the binding right, but that has probably got # subtleties that we don't instantly think of – especially around threads. # # So, we are pulling this method for now, and will return it to life when we # have the time to resolve the problem. For now, you should replace... # # @action = @string.get_action(name) # @action.invoke(arg1, arg2, arg3) # # ...with... # # @action = @string.get_action(name) # @string.send(@action.name, arg1, arg2, arg3) # # I understand that is somewhat cumbersome, but it functions as desired. # --daniel 2011-03-31 # # PS: This code is left present, but commented, to support this chunk of # documentation, for the benefit of the reader. # # def invoke(*args, &block) # @string.send(name, *args, &block) # end - def invoke=(block) + def when_invoked=(block) # We need to build an instance method as a wrapper, using normal code, to # be able to expose argument defaulting between the caller and definer in # the Ruby API. An extra method is, sadly, required for Ruby 1.8 to work. # # In future this also gives us a place to hook in additional behaviour # such as calling out to the action instance to validate and coerce # parameters, which avoids any exciting context switching and all. # # Hopefully we can improve this when we finally shuffle off the last of # Ruby 1.8 support, but that looks to be a few "enterprise" release eras # away, so we are pretty stuck with this for now. # # Patches to make this work more nicely with Ruby 1.9 using runtime # version checking and all are welcome, but they can't actually help if # the results are not totally hidden away in here. # # Incidentally, we though about vendoring evil-ruby and actually adjusting # the internal C structure implementation details under the hood to make # this stuff work, because it would have been cleaner. Which gives you an # idea how motivated we were to make this cleaner. Sorry. --daniel 2011-03-31 internal_name = "#{@name} implementation, required on Ruby 1.8".to_sym file = __FILE__ + "+eval" line = __LINE__ + 1 wrapper = "def #{@name}(*args, &block) args << {} unless args.last.is_a? Hash args << block if block_given? self.__send__(#{internal_name.inspect}, *args) end" if @string.is_a?(Class) @string.class_eval do eval wrapper, nil, file, line end @string.define_method(internal_name, &block) else @string.instance_eval do eval wrapper, nil, file, line end @string.meta_def(internal_name, &block) end end def add_option(option) option.aliases.each do |name| if conflict = get_option(name) then raise ArgumentError, "Option #{option} conflicts with existing option #{conflict}" elsif conflict = @string.get_option(name) then raise ArgumentError, "Option #{option} conflicts with existing option #{conflict} on #{@string}" end end option.aliases.each do |name| @options[name] = option end 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 e76044470..e7c03273b 100644 --- a/lib/puppet/string/action_builder.rb +++ b/lib/puppet/string/action_builder.rb @@ -1,30 +1,31 @@ require 'puppet/string' require 'puppet/string/action' class Puppet::String::ActionBuilder attr_reader :action def self.build(string, name, &block) raise "Action #{name.inspect} must specify a block" unless block new(string, name, &block).action end + private def initialize(string, name, &block) @string = string @action = Puppet::String::Action.new(string, name) instance_eval(&block) end # Ideally the method we're defining here would be added to the action, and a # method on the string would defer to it, but we can't get scope correct, # so we stick with this. --daniel 2011-03-24 - def invoke(&block) - raise "Invoke called on an ActionBuilder with no corresponding Action" unless @action - @action.invoke = block + def when_invoked(&block) + raise "when_invoked on an ActionBuilder with no corresponding Action" unless @action + @action.when_invoked = block end def option(*declaration, &block) option = Puppet::String::OptionBuilder.build(@action, *declaration, &block) @action.add_option(option) end end diff --git a/lib/puppet/string/action_manager.rb b/lib/puppet/string/action_manager.rb index 7d22a0c52..9f0aa7582 100644 --- a/lib/puppet/string/action_manager.rb +++ b/lib/puppet/string/action_manager.rb @@ -1,49 +1,49 @@ require 'puppet/string/action_builder' module Puppet::String::ActionManager # Declare that this app can take a specific action, and provide # the code to do so. def action(name, &block) @actions ||= {} raise "Action #{name} already defined for #{self}" if action?(name) action = Puppet::String::ActionBuilder.build(self, name, &block) @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 ||= {} raise "Action #{name} already defined for #{self}" if action?(name) - @actions[name] = Puppet::String::Action.new(self, name, :invoke => block) + @actions[name] = Puppet::String::Action.new(self, name, :when_invoked => block) end def actions @actions ||= {} result = @actions.keys if self.is_a?(Class) and superclass.respond_to?(:actions) result += superclass.actions elsif self.class.respond_to?(:actions) result += self.class.actions end result.sort end def get_action(name) @actions ||= {} result = @actions[name.to_sym] if result.nil? if self.is_a?(Class) and superclass.respond_to?(:get_action) result = superclass.get_action(name) elsif self.class.respond_to?(:get_action) result = self.class.get_action(name) end end return result end def action?(name) actions.include?(name.to_sym) end end diff --git a/lib/puppet/string/catalog.rb b/lib/puppet/string/catalog.rb index c6de47708..441c7ee7d 100644 --- a/lib/puppet/string/catalog.rb +++ b/lib/puppet/string/catalog.rb @@ -1,40 +1,40 @@ require 'puppet/string/indirector' Puppet::String::Indirector.define(:catalog, '0.0.1') do action(:apply) do - invoke do |catalog, options| + when_invoked do |catalog, options| report = Puppet::Transaction::Report.new("apply") report.configuration_version = catalog.version Puppet::Util::Log.newdestination(report) begin benchmark(:notice, "Finished catalog run") do catalog.apply(:report => report) end rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Failed to apply catalog: #{detail}" end report.finalize_report report end end action(:download) do - invoke do |certname, facts, options| + when_invoked do |certname, facts, options| Puppet::Resource::Catalog.terminus_class = :rest facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))} catalog = nil retrieval_duration = thinmark do catalog = Puppet::String[:catalog, '0.0.1'].find(certname, facts_to_upload) end catalog = catalog.to_ral catalog.finalize catalog.retrieval_duration = retrieval_duration catalog.write_class_file catalog end end end diff --git a/lib/puppet/string/catalog/select.rb b/lib/puppet/string/catalog/select.rb index a8f4480cd..11670e2e7 100644 --- a/lib/puppet/string/catalog/select.rb +++ b/lib/puppet/string/catalog/select.rb @@ -1,10 +1,10 @@ # Select and show a list of resources of a given type. Puppet::String.define(:catalog, '0.0.1') do action :select do - invoke do |host, type, options| + when_invoked do |host, type, options| catalog = Puppet::Resource::Catalog.indirection.find(host) catalog.resources.reject { |res| res.type != type }.each { |res| puts res } end end end diff --git a/lib/puppet/string/certificate.rb b/lib/puppet/string/certificate.rb index 53f731e81..7b72b112c 100644 --- a/lib/puppet/string/certificate.rb +++ b/lib/puppet/string/certificate.rb @@ -1,28 +1,28 @@ require 'puppet/string/indirector' require 'puppet/ssl/host' Puppet::String::Indirector.define(:certificate, '0.0.1') do action :generate do - invoke do |name, options| + when_invoked do |name, options| host = Puppet::SSL::Host.new(name) host.generate_certificate_request host.certificate_request.class.indirection.save(host.certificate_request) end end action :list do - invoke do |options| + when_invoked do |options| Puppet::SSL::Host.indirection.search("*", { :for => :certificate_request, }).map { |h| h.inspect } end end action :sign do - invoke do |name, options| + when_invoked do |name, options| Puppet::SSL::Host.indirection.save(Puppet::SSL::Host.new(name)) end end end diff --git a/lib/puppet/string/config.rb b/lib/puppet/string/config.rb index 49a1688fc..8a9417148 100644 --- a/lib/puppet/string/config.rb +++ b/lib/puppet/string/config.rb @@ -1,12 +1,12 @@ require 'puppet/string' Puppet::String.define(:config, '0.0.1') do action(:print) do - invoke do |*args| + when_invoked do |*args| options = args.pop Puppet.settings[:configprint] = args.join(",") Puppet.settings.print_config_options nil end end end diff --git a/lib/puppet/string/configurer.rb b/lib/puppet/string/configurer.rb index 2520d4188..257f97e90 100644 --- a/lib/puppet/string/configurer.rb +++ b/lib/puppet/string/configurer.rb @@ -1,12 +1,12 @@ require 'puppet/string' Puppet::String.define(:configurer, '0.0.1') do action(:synchronize) do - invoke do |certname, options| + when_invoked do |certname, options| facts = Puppet::String[:facts, '0.0.1'].find(certname) catalog = Puppet::String[:catalog, '0.0.1'].download(certname, facts) report = Puppet::String[:catalog, '0.0.1'].apply(catalog) report end end end diff --git a/lib/puppet/string/facts.rb b/lib/puppet/string/facts.rb index 31298813b..6bd9904b0 100644 --- a/lib/puppet/string/facts.rb +++ b/lib/puppet/string/facts.rb @@ -1,18 +1,18 @@ require 'puppet/string/indirector' require 'puppet/node/facts' Puppet::String::Indirector.define(:facts, '0.0.1') do set_default_format :yaml # Upload our facts to the server action(:upload) do - invoke do |options| + when_invoked do |options| Puppet::Node::Facts.indirection.terminus_class = :facter facts = Puppet::Node::Facts.indirection.find(Puppet[:certname]) Puppet::Node::Facts.indirection.terminus_class = :rest Puppet::Node::Facts.indirection.save(facts) Puppet.notice "Uploaded facts for '#{Puppet[:certname]}'" nil end end end diff --git a/lib/puppet/string/indirector.rb b/lib/puppet/string/indirector.rb index bb081533f..0c7d043bb 100644 --- a/lib/puppet/string/indirector.rb +++ b/lib/puppet/string/indirector.rb @@ -1,94 +1,94 @@ require 'puppet' require 'puppet/string' class Puppet::String::Indirector < Puppet::String option "--terminus TERMINUS" do desc "REVISIT: You can select a terminus, which has some bigger effect that we should describe in this file somehow." end def self.indirections Puppet::Indirector::Indirection.instances.collect { |t| t.to_s }.sort end def self.terminus_classes(indirection) Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort end def call_indirection_method(method, *args) options = args.pop options.has_key?(:terminus) and set_terminus(options[:terminus]) begin result = indirection.__send__(method, *args) rescue => detail puts detail.backtrace if Puppet[:trace] raise "Could not call '#{method}' on '#{indirection_name}': #{detail}" end indirection.reset_terminus_class return result end action :destroy do - invoke { |*args| call_indirection_method(:destroy, *args) } + when_invoked { |*args| call_indirection_method(:destroy, *args) } end action :find do - invoke { |*args| call_indirection_method(:find, *args) } + when_invoked { |*args| call_indirection_method(:find, *args) } end action :save do - invoke { |*args| call_indirection_method(:save, *args) } + when_invoked { |*args| call_indirection_method(:save, *args) } end action :search do - invoke { |*args| call_indirection_method(:search, *args) } + when_invoked { |*args| call_indirection_method(:search, *args) } end # Print the configuration for the current terminus class action :info do - invoke do |*args| + when_invoked do |*args| options = args.pop options.has_key?(:terminus) and set_terminus(options[:terminus]) if t = indirection.terminus_class puts "Run mode '#{Puppet.run_mode.name}': #{t}" else $stderr.puts "No default terminus class for run mode '#{Puppet.run_mode.name}'" end indirection.reset_terminus_class end end attr_accessor :from def indirection_name @indirection_name || name.to_sym end # Here's your opportunity to override the indirection name. By default # it will be the same name as the string. def set_indirection_name(name) @indirection_name = name end # Return an indirection associated with an string, if one exists # One usually does. def indirection unless @indirection @indirection = Puppet::Indirector::Indirection.instance(indirection_name) @indirection or raise "Could not find terminus for #{indirection_name}" end @indirection end def set_terminus(from) begin indirection.terminus_class = from rescue => detail raise "Could not set '#{indirection.name}' terminus to '#{from}' (#{detail}); valid terminus types are #{self.class.terminus_classes(indirection.name).join(", ") }" end end end diff --git a/lib/puppet/string/report.rb b/lib/puppet/string/report.rb index 5b617e49e..da3ca8504 100644 --- a/lib/puppet/string/report.rb +++ b/lib/puppet/string/report.rb @@ -1,15 +1,15 @@ require 'puppet/string/indirector' Puppet::String::Indirector.define(:report, '0.0.1') do action(:submit) do - invoke do |report, options| + when_invoked do |report, options| begin Puppet::Transaction::Report.terminus_class = :rest report.save rescue => detail puts detail.backtrace if Puppet[:trace] Puppet.err "Could not send report: #{detail}" end end end end diff --git a/spec/unit/application/string_base_spec.rb b/spec/unit/application/string_base_spec.rb index 71e67283d..753d911d1 100755 --- a/spec/unit/application/string_base_spec.rb +++ b/spec/unit/application/string_base_spec.rb @@ -1,150 +1,150 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') require 'puppet/application/string_base' require 'tmpdir' class Puppet::Application::StringBase::Basetest < Puppet::Application::StringBase end describe Puppet::Application::StringBase do before :all do @dir = Dir.mktmpdir $LOAD_PATH.push(@dir) FileUtils.mkdir_p(File.join @dir, 'puppet', 'string') File.open(File.join(@dir, 'puppet', 'string', 'basetest.rb'), 'w') do |f| f.puts "Puppet::String.define(:basetest, '0.0.1')" end Puppet::String.define(:basetest, '0.0.1') do option("--[no-]boolean") option("--mandatory MANDATORY") option("--optional [OPTIONAL]") action :foo do option("--action") - invoke { |*args| args.length } + when_invoked { |*args| args.length } end end end after :all do FileUtils.remove_entry_secure @dir $LOAD_PATH.pop end let :app do app = Puppet::Application::StringBase::Basetest.new app.stubs(:exit) app.stubs(:puts) app.command_line.stubs(:subcommand_name).returns 'subcommand' Puppet::Util::Log.stubs(:newdestination) app end describe "#preinit" do before :each do app.command_line.stubs(:args).returns %w{} end describe "parsing the command line" do context "with just an action" do before :all do app.command_line.stubs(:args).returns %w{foo} app.preinit end it "should set the string based on the type" do app.string.name.should == :basetest end it "should set the format based on the string default" do app.format.should == :pson end it "should find the action" do app.action.should be app.action.name.should == :foo end end it "should fail if no action is given" do expect { app.preinit }. should raise_error ArgumentError, /No action given/ end it "should report a sensible error when options with = fail" do app.command_line.stubs(:args).returns %w{--action=bar foo} expect { app.preinit }. should raise_error ArgumentError, /Unknown option "--action"/ end it "should fail if an action option is before the action" do app.command_line.stubs(:args).returns %w{--action foo} expect { app.preinit }. should raise_error ArgumentError, /Unknown option "--action"/ end it "should fail if an unknown option is before the action" do app.command_line.stubs(:args).returns %w{--bar foo} expect { app.preinit }. should raise_error ArgumentError, /Unknown option "--bar"/ end it "should not fail if an unknown option is after the action" do app.command_line.stubs(:args).returns %w{foo --bar} app.preinit app.action.name.should == :foo app.string.should_not be_option :bar app.action.should_not be_option :bar end it "should accept --bar as an argument to a mandatory option after action" do app.command_line.stubs(:args).returns %w{foo --mandatory --bar} app.preinit and app.parse_options app.action.name.should == :foo app.options.should == { :mandatory => "--bar" } end it "should accept --bar as an argument to a mandatory option before action" do app.command_line.stubs(:args).returns %w{--mandatory --bar foo} app.preinit and app.parse_options app.action.name.should == :foo app.options.should == { :mandatory => "--bar" } end it "should not skip when --foo=bar is given" do app.command_line.stubs(:args).returns %w{--mandatory=bar --bar foo} expect { app.preinit }. should raise_error ArgumentError, /Unknown option "--bar"/ end end end describe "#setup" do it "should remove the action name from the arguments" do app.command_line.stubs(:args).returns %w{--mandatory --bar foo} app.preinit and app.parse_options and app.setup app.arguments.should == [{ :mandatory => "--bar" }] end end describe "#main" do before do app.string = Puppet::String[:basetest, '0.0.1'] app.action = app.string.get_action(:foo) app.format = :pson app.arguments = ["myname", "myarg"] end it "should send the specified verb and name to the string" do app.string.expects(:foo).with(*app.arguments) app.main end it "should use its render method to render any result" do app.expects(:render).with(app.arguments.length + 1) app.main end end end diff --git a/spec/unit/string/action_builder_spec.rb b/spec/unit/string/action_builder_spec.rb index fde010d51..5f6f1c08f 100755 --- a/spec/unit/string/action_builder_spec.rb +++ b/spec/unit/string/action_builder_spec.rb @@ -1,59 +1,59 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') require 'puppet/string/action_builder' describe Puppet::String::ActionBuilder do describe "::build" do it "should build an action" do action = Puppet::String::ActionBuilder.build(nil, :foo) do end action.should be_a(Puppet::String::Action) action.name.should == :foo end it "should define a method on the string which invokes the action" do string = Puppet::String.new(:action_builder_test_string, '0.0.1') action = Puppet::String::ActionBuilder.build(string, :foo) do - invoke do + when_invoked do "invoked the method" end end string.foo.should == "invoked the method" end it "should require a block" do lambda { Puppet::String::ActionBuilder.build(nil, :foo) }. should raise_error("Action :foo must specify a block") end describe "when handling options" do let :string do Puppet::String.new(:option_handling, '0.0.1') end it "should have a #option DSL function" do method = nil Puppet::String::ActionBuilder.build(string, :foo) do method = self.method(:option) end method.should be end it "should define an option without a block" do action = Puppet::String::ActionBuilder.build(string, :foo) do option "--bar" end action.should be_option :bar end it "should accept an empty block" do action = Puppet::String::ActionBuilder.build(string, :foo) do option "--bar" do # This space left deliberately blank. end end action.should be_option :bar end end end end diff --git a/spec/unit/string/action_manager_spec.rb b/spec/unit/string/action_manager_spec.rb index 5ca55b387..b8baf80a5 100755 --- a/spec/unit/string/action_manager_spec.rb +++ b/spec/unit/string/action_manager_spec.rb @@ -1,233 +1,233 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') # This is entirely an internal class for String, so we have to load it instead of our class. require 'puppet/string' class ActionManagerTester include Puppet::String::ActionManager end describe Puppet::String::ActionManager do subject { ActionManagerTester.new } describe "when included in a class" do it "should be able to define an action" do subject.action(:foo) do - invoke { "something "} + when_invoked { "something "} end end it "should be able to define a 'script' style action" do subject.script :bar do "a bar is where beer is found" end end it "should be able to list defined actions" do subject.action(:foo) do - invoke { "something" } + when_invoked { "something" } end subject.action(:bar) do - invoke { "something" } + when_invoked { "something" } end subject.actions.should =~ [:foo, :bar] end it "should list 'script' actions" do subject.script :foo do "foo" end subject.actions.should =~ [:foo] end it "should list both script and normal actions" do subject.action :foo do - invoke do "foo" end + when_invoked do "foo" end end subject.script :bar do "a bar is where beer is found" end subject.actions.should =~ [:foo, :bar] end it "should be able to indicate when an action is defined" do subject.action(:foo) do - invoke { "something" } + when_invoked { "something" } end subject.should be_action(:foo) end it "should indicate an action is defined for script actions" do subject.script :foo do "foo" end subject.should be_action :foo end it "should correctly treat action names specified as strings" do subject.action(:foo) do - invoke { "something" } + when_invoked { "something" } end subject.should be_action("foo") end end describe "when used to extend a class" do subject { Class.new.extend(Puppet::String::ActionManager) } it "should be able to define an action" do subject.action(:foo) do - invoke { "something "} + when_invoked { "something "} end end it "should be able to list defined actions" do subject.action(:foo) do - invoke { "something" } + when_invoked { "something" } end subject.action(:bar) do - invoke { "something" } + when_invoked { "something" } end subject.actions.should include(:bar) subject.actions.should include(:foo) end it "should be able to indicate when an action is defined" do subject.action(:foo) { "something" } subject.should be_action(:foo) end end describe "when used both at the class and instance level" do before do @klass = Class.new do include Puppet::String::ActionManager extend Puppet::String::ActionManager end @instance = @klass.new end it "should be able to define an action at the class level" do @klass.action(:foo) do - invoke { "something "} + when_invoked { "something "} end end it "should create an instance method when an action is defined at the class level" do @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.foo.should == "something" end it "should be able to define an action at the instance level" do @instance.action(:foo) do - invoke { "something "} + when_invoked { "something "} end end it "should create an instance method when an action is defined at the instance level" do @instance.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.foo.should == "something" end it "should be able to list actions defined at the class level" do @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @klass.action(:bar) do - invoke { "something" } + when_invoked { "something" } end @klass.actions.should include(:bar) @klass.actions.should include(:foo) end it "should be able to list actions defined at the instance level" do @instance.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.action(:bar) do - invoke { "something" } + when_invoked { "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to list actions defined at both instance and class level" do @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.action(:bar) do - invoke { "something" } + when_invoked { "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to indicate when an action is defined at the class level" do @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.should be_action(:foo) end it "should be able to indicate when an action is defined at the instance level" do @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.should be_action(:foo) end it "should list actions defined in superclasses" do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:parent) do - invoke { "a" } + when_invoked { "a" } end @subclass.action(:sub) do - invoke { "a" } + when_invoked { "a" } end @instance.action(:instance) do - invoke { "a" } + when_invoked { "a" } end @instance.should be_action(:parent) @instance.should be_action(:sub) @instance.should be_action(:instance) end it "should create an instance method when an action is defined in a superclass" do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:foo) do - invoke { "something" } + when_invoked { "something" } end @instance.foo.should == "something" end end describe "#get_action" do let :parent_class do parent_class = Class.new(Puppet::String) parent_class.action(:foo) {} parent_class end it "should check that we can find inherited actions when we are a class" do Class.new(parent_class).get_action(:foo).name.should == :foo end it "should check that we can find inherited actions when we are an instance" do instance = parent_class.new(:foo, '0.0.0') instance.get_action(:foo).name.should == :foo end end end diff --git a/spec/unit/string/action_spec.rb b/spec/unit/string/action_spec.rb index 1d25ff156..b6fe87a63 100755 --- a/spec/unit/string/action_spec.rb +++ b/spec/unit/string/action_spec.rb @@ -1,173 +1,173 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb') require 'puppet/string/action' describe Puppet::String::Action do describe "when validating the action name" do [nil, '', 'foo bar', '-foobar'].each do |input| it "should treat #{input.inspect} as an invalid name" do expect { Puppet::String::Action.new(nil, input) }. should raise_error(/is an invalid action name/) end end end describe "when invoking" do it "should be able to call other actions on the same object" do string = Puppet::String.new(:my_string, '0.0.1') do action(:foo) do - invoke { 25 } + when_invoked { 25 } end action(:bar) do - invoke { "the value of foo is '#{foo}'" } + when_invoked { "the value of foo is '#{foo}'" } end end string.foo.should == 25 string.bar.should == "the value of foo is '25'" end # bar is a class action calling a class action # quux is a class action calling an instance action # baz is an instance action calling a class action # qux is an instance action calling an instance action it "should be able to call other actions on the same object when defined on a class" do class Puppet::String::MyStringBaseClass < Puppet::String action(:foo) do - invoke { 25 } + when_invoked { 25 } end action(:bar) do - invoke { "the value of foo is '#{foo}'" } + when_invoked { "the value of foo is '#{foo}'" } end action(:quux) do - invoke { "qux told me #{qux}" } + when_invoked { "qux told me #{qux}" } end end string = Puppet::String::MyStringBaseClass.new(:my_inherited_string, '0.0.1') do action(:baz) do - invoke { "the value of foo in baz is '#{foo}'" } + when_invoked { "the value of foo in baz is '#{foo}'" } end action(:qux) do - invoke { baz } + when_invoked { baz } end end string.foo.should == 25 string.bar.should == "the value of foo is '25'" string.quux.should == "qux told me the value of foo in baz is '25'" string.baz.should == "the value of foo in baz is '25'" string.qux.should == "the value of foo in baz is '25'" end context "when calling the Ruby API" do let :string do Puppet::String.new(:ruby_api, '1.0.0') do action :bar do - invoke do |options| + when_invoked do |options| options end end end end it "should work when no options are supplied" do options = string.bar options.should == {} end it "should work when options are supplied" do options = string.bar :bar => "beer" options.should == { :bar => "beer" } end end end describe "with action-level options" do it "should support options with an empty block" do string = Puppet::String.new(:action_level_options, '0.0.1') do action :foo do option "--bar" do # this line left deliberately blank end end end string.should_not be_option :bar string.get_action(:foo).should be_option :bar end it "should return only action level options when there are no string options" do string = Puppet::String.new(:action_level_options, '0.0.1') do action :foo do option "--bar" end end string.get_action(:foo).options.should =~ [:bar] end describe "with both string and action options" do let :string do Puppet::String.new(:action_level_options, '0.0.1') do action :foo do option "--bar" end action :baz do option "--bim" end option "--quux" end end it "should return combined string and action options" do string.get_action(:foo).options.should =~ [:bar, :quux] end it "should fetch options that the string inherited" do parent = Class.new(Puppet::String) parent.option "--foo" child = parent.new(:inherited_options, '0.0.1') do option "--bar" action :action do option "--baz" end end action = child.get_action(:action) action.should be [:baz, :bar, :foo].each do |name| action.get_option(name).should be_an_instance_of Puppet::String::Option end end it "should get an action option when asked" do string.get_action(:foo).get_option(:bar). should be_an_instance_of Puppet::String::Option end it "should get a string option when asked" do string.get_action(:foo).get_option(:quux). should be_an_instance_of Puppet::String::Option end it "should return options only for this action" do string.get_action(:baz).options.should =~ [:bim, :quux] end end it_should_behave_like "things that declare options" do def add_options_to(&block) string = Puppet::String.new(:with_options, '0.0.1') do action(:foo, &block) end string.get_action(:foo) end end it "should fail when a string option duplicates an action option" do expect { Puppet::String.new(:action_level_options, '0.0.1') do option "--foo" action :bar do option "--foo" end end }.should raise_error ArgumentError, /Option foo conflicts with existing option foo/i end end end diff --git a/spec/unit/string_spec.rb b/spec/unit/string_spec.rb index ddf855475..358668f9b 100755 --- a/spec/unit/string_spec.rb +++ b/spec/unit/string_spec.rb @@ -1,145 +1,145 @@ #!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb') describe Puppet::String do before :all do @strings = Puppet::String::StringCollection.instance_variable_get("@strings").dup end before :each do Puppet::String::StringCollection.instance_variable_get("@strings").clear end after :all do Puppet::String::StringCollection.instance_variable_set("@strings", @strings) end describe "#define" do it "should register the string" do string = Puppet::String.define(:string_test_register, '0.0.1') string.should == Puppet::String[:string_test_register, '0.0.1'] end it "should load actions" do Puppet::String.any_instance.expects(:load_actions) Puppet::String.define(:string_test_load_actions, '0.0.1') end it "should require a version number" do proc { Puppet::String.define(:no_version) }.should raise_error(ArgumentError) end end describe "#initialize" do it "should require a version number" do proc { Puppet::String.new(:no_version) }.should raise_error(ArgumentError) end it "should require a valid version number" do proc { Puppet::String.new(:bad_version, 'Rasins') }.should raise_error(ArgumentError) end it "should instance-eval any provided block" do face = Puppet::String.new(:string_test_block, '0.0.1') do action(:something) do - invoke { "foo" } + when_invoked { "foo" } end end face.something.should == "foo" end end it "should have a name" do Puppet::String.new(:me, '0.0.1').name.should == :me end it "should stringify with its own name" do Puppet::String.new(:me, '0.0.1').to_s.should =~ /\bme\b/ end it "should allow overriding of the default format" do face = Puppet::String.new(:me, '0.0.1') face.set_default_format :foo face.default_format.should == :foo end it "should default to :pson for its format" do Puppet::String.new(:me, '0.0.1').default_format.should == :pson end # Why? it "should create a class-level autoloader" do Puppet::String.autoloader.should be_instance_of(Puppet::Util::Autoload) end it "should try to require strings that are not known" do Puppet::String::StringCollection.expects(:require).with "puppet/string/foo" Puppet::String::StringCollection.expects(:require).with "foo@0.0.1/puppet/string/foo" Puppet::String[:foo, '0.0.1'] end it "should be able to load all actions in all search paths" it_should_behave_like "things that declare options" do def add_options_to(&block) Puppet::String.new(:with_options, '0.0.1', &block) end end describe "with string-level options" do it "should not return any action-level options" do string = Puppet::String.new(:with_options, '0.0.1') do option "--foo" option "--bar" action :baz do option "--quux" end end string.options.should =~ [:foo, :bar] end it "should fail when a string option duplicates an action option" do expect { Puppet::String.new(:action_level_options, '0.0.1') do action :bar do option "--foo" end option "--foo" end }.should raise_error ArgumentError, /Option foo conflicts with existing option foo on/i end it "should work when two actions have the same option" do string = Puppet::String.new(:with_options, '0.0.1') do action :foo do option "--quux" end action :bar do option "--quux" end end string.get_action(:foo).options.should =~ [:quux] string.get_action(:bar).options.should =~ [:quux] end end describe "with inherited options" do let :string do parent = Class.new(Puppet::String) parent.option("--inherited") string = parent.new(:example, '0.2.1') string.option("--local") string end describe "#options" do it "should list inherited options" do string.options.should =~ [:inherited, :local] end end describe "#get_option" do it "should return an inherited option object" do string.get_option(:inherited).should be_an_instance_of Puppet::String::Option end end end end