diff options
-rw-r--r-- | lib/facter.rb | 85 | ||||
-rw-r--r-- | lib/facter/collection.rb | 72 | ||||
-rwxr-xr-x | spec/integration/facter.rb | 23 | ||||
-rwxr-xr-x | spec/unit/collection.rb | 131 | ||||
-rwxr-xr-x | spec/unit/facter.rb | 63 |
5 files changed, 282 insertions, 92 deletions
diff --git a/lib/facter.rb b/lib/facter.rb index ebc4299..36a68b8 100644 --- a/lib/facter.rb +++ b/lib/facter.rb @@ -19,6 +19,7 @@ module Facter require 'facter/fact' + require 'facter/collection' include Comparable include Enumerable @@ -38,20 +39,19 @@ module Facter # - @@facts = Hash.new { |hash, key| - key = key.to_s.downcase.intern - if hash.include?(key) - hash[key] - else - nil - end - } GREEN = "[0;32m" RESET = "[0m" @@debug = 0 # module methods + def self.collection + unless defined?(@collection) and @collection + @collection = Facter::Collection.new + end + @collection + end + # Return the version of the library. def self.version return FACTERVERSION @@ -70,38 +70,25 @@ module Facter # Return a fact object by name. If you use this, you still have to call # 'value' on it to retrieve the actual value. def self.[](name) - @@facts[name] + collection.fact(name) end - # Add a resolution mechanism for a named fact. This does not distinguish - # between adding a new fact and adding a new way to resolve a fact. - def self.add(name, options = {}, &block) - unless fact = @@facts[name] - fact = Facter::Fact.new(name, options) - @@facts[name] = fact - end - - unless block - return fact + class << self + [:add, :fact, :flush, :list, :to_hash, :value].each do |method| + define_method(method) do |*args| + collection.send(method, *args) + end end + end - fact.add(&block) - return fact + # Add a resolution mechanism for a named fact. This does not distinguish + # between adding a new fact and adding a new way to resolve a fact. + def self.add(name, options = {}, &block) + collection.add(name, options, &block) end class << self - include Enumerable - # Iterate across all of the facts. - def each - @@facts.each { |name,fact| - value = fact.value - if ! value.nil? - yield name.to_s, fact.value - end - } - end - # Allow users to call fact names directly on the Facter class, # either retrieving the value or comparing it to an existing value. def method_missing(name, *args) @@ -111,7 +98,7 @@ module Facter name = name.to_s.sub(/\?$/,'') end - if fact = @@facts[name] + if fact = @collection.fact(name) if question value = fact.value.downcase args.each do |arg| @@ -164,39 +151,9 @@ module Facter end end - # Flush all cached values. - def self.flush - @@facts.each { |name, fact| fact.flush } - end - - # Return a list of all of the facts. - def self.list - return @@facts.keys - end - # Remove them all. def self.reset - @@facts.clear - end - - # Return a hash of all of our facts. - def self.to_hash - @@facts.inject({}) do |h, ary| - value = ary[1].value - if ! value.nil? - # For backwards compatibility, convert the fact name to a string. - h[ary[0].to_s] = value - end - h - end - end - - def self.value(name) - if fact = @@facts[name] - fact.value - else - nil - end + @collection = nil end # Load all of the default facts diff --git a/lib/facter/collection.rb b/lib/facter/collection.rb new file mode 100644 index 0000000..1113375 --- /dev/null +++ b/lib/facter/collection.rb @@ -0,0 +1,72 @@ +require 'facter' +require 'facter/resolution' + +# Manage which facts exist and how we access them. Largely just a wrapper +# around a hash of facts. +class Facter::Collection + # Return a fact object by name. If you use this, you still have to call + # 'value' on it to retrieve the actual value. + def [](name) + value(name) + end + + # Add a resolution mechanism for a named fact. This does not distinguish + # between adding a new fact and adding a new way to resolve a fact. + def add(name, options = {}, &block) + name = canonize(name) + + unless fact = @facts[name] + fact = Facter::Fact.new(name, options) + @facts[name] = fact + end + + fact.add(&block) if block + + return fact + end + + # Return a fact by name. + def fact(name) + @facts[canonize(name)] + end + + # Flush all cached values. + def flush + @facts.each { |name, fact| fact.flush } + end + + def initialize + @facts = Hash.new + end + + # Return a list of all of the facts. + def list + return @facts.keys + end + + # Return a hash of all of our facts. + def to_hash + @facts.inject({}) do |h, ary| + value = ary[1].value + if ! value.nil? + # For backwards compatibility, convert the fact name to a string. + h[ary[0].to_s] = value + end + h + end + end + + def value(name) + if fact = @facts[canonize(name)] + fact.value + end + end + + private + + # Provide a consistent means of getting the exact same fact name + # every time. + def canonize(name) + name.to_s.downcase.to_sym + end +end diff --git a/spec/integration/facter.rb b/spec/integration/facter.rb new file mode 100755 index 0000000..b503c9d --- /dev/null +++ b/spec/integration/facter.rb @@ -0,0 +1,23 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +describe Facter do + before do + Facter.reset + Facter.loadfacts + end + + it "should create a new collection if one does not exist" do + Facter.reset + Facter::Collection.expects(:new).returns "coll" + Facter.collection.should == "coll" + Facter.reset + end + + it "should remove the collection when reset" do + old = Facter.collection + Facter.reset + Facter.collection.should_not equal(old) + end +end diff --git a/spec/unit/collection.rb b/spec/unit/collection.rb new file mode 100755 index 0000000..1602e55 --- /dev/null +++ b/spec/unit/collection.rb @@ -0,0 +1,131 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'facter/collection' + +describe Facter::Collection do + it "should have a method for adding facts" do + Facter::Collection.new.should respond_to(:add) + end + + describe "when adding facts" do + before do + @coll = Facter::Collection.new + end + + it "should create a new fact if no fact with the same name already exists" do + fact = mock 'fact' + Facter::Fact.expects(:new).with { |name, *args| name == :myname }.returns fact + + @coll.add(:myname) + end + + describe "and a block is provided" do + it "should use the block to add a resolution to the fact" do + fact = mock 'fact' + Facter::Fact.expects(:new).returns fact + + fact.expects(:add) + + @coll.add(:myname) {} + end + end + end + + it "should have a method for retrieving facts by name" do + Facter::Collection.new.should respond_to(:fact) + end + + describe "when retrieving facts" do + before do + @coll = Facter::Collection.new + + @fact = @coll.add("YayNess") + end + + it "should return the fact instance specified by the name" do + @coll.fact("YayNess").should equal(@fact) + end + + it "should be case-insensitive" do + @coll.fact("yayness").should equal(@fact) + end + + it "should treat strings and symbols equivalently" do + @coll.fact(:yayness).should equal(@fact) + end + end + + it "should have a method for returning a fact's value" do + Facter::Collection.new.should respond_to(:value) + end + + describe "when returning a fact's value" do + before do + @coll = Facter::Collection.new + @fact = @coll.add("YayNess") + + @fact.stubs(:value).returns "result" + end + + it "should return the result of calling :value on the fact" do + @fact.expects(:value).returns "result" + + @coll.value("YayNess").should == "result" + end + + it "should be case-insensitive" do + @coll.value("yayness").should_not be_nil + end + + it "should treat strings and symbols equivalently" do + @coll.value(:yayness).should_not be_nil + end + end + + it "should return the fact's value when the array index method is used" do + @coll = Facter::Collection.new + @coll.expects(:value).with("myfact").returns "foo" + @coll["myfact"].should == "foo" + end + + it "should have a method for flushing all facts" do + @coll = Facter::Collection.new + @fact = @coll.add("YayNess") + + @fact.expects(:flush) + + @coll.flush + end + + it "should have a method that returns all fact names" do + @coll = Facter::Collection.new + @coll.add(:one) + @coll.add(:two) + + @coll.list.sort.should == [:one, :two].sort + end + + it "should have a method for returning a hash of fact values" do + Facter::Collection.new.should respond_to(:to_hash) + end + + describe "when returning a hash of values" do + before do + @coll = Facter::Collection.new + @fact = @coll.add(:one) + @fact.stubs(:value).returns "me" + end + + it "should return a hash of fact names and values with the fact names as strings" do + @coll.to_hash.should == {"one" => "me"} + end + + it "should not include facts that did not return a value" do + f = @coll.add(:two) + f.stubs(:value).returns nil + @coll.to_hash.should_not be_include(:two) + end + end +end diff --git a/spec/unit/facter.rb b/spec/unit/facter.rb index e794281..dd1a24b 100755 --- a/spec/unit/facter.rb +++ b/spec/unit/facter.rb @@ -3,30 +3,42 @@ require File.dirname(__FILE__) + '/../spec_helper' describe Facter do - def tearhook(&block) - @tearhooks << block + + it "should have a version" do + Facter.version.should =~ /^[0-9]+(\.[0-9]+)*$/ end - def setup - Facter.loadfacts + it "should have a method for returning its collection" do + Facter.should respond_to(:collection) + end - @tmpfiles = [] - @tearhooks = [] + it "should cache the collection" do + Facter.collection.should equal(Facter.collection) end - def teardown - # clear out the list of facts, so we start fresh for every test - Facter.clear + it "should delegate the :flush method to the collection" do + Facter.collection.expects(:flush) + Facter.flush + end - @tmpfiles.each do |file| - if FileTest.exists?(file) - system("rm -rf %s" % file) - end - end + it "should delegate the :fact method to the collection" do + Facter.collection.expects(:fact) + Facter.fact end - - it "should have a version" do - Facter.version.should =~ /^[0-9]+(\.[0-9]+)*$/ + + it "should delegate the :list method to the collection" do + Facter.collection.expects(:list) + Facter.list + end + + it "should delegate the :to_hash method to the collection" do + Facter.collection.expects(:to_hash) + Facter.to_hash + end + + it "should delegate the :value method to the collection" do + Facter.collection.expects(:value) + Facter.value end describe "when provided code as a string" do @@ -59,6 +71,11 @@ describe Facter do end end + # #33 Make sure we only get one mac address + it "should only return one mac address" do + Facter.value(:macaddress).should_not be_include(" ") + end + def test_onetrueconfine assert_nothing_raised { Facter.add("required") { @@ -623,11 +640,6 @@ some random stuff end end - # #33 Make sure we only get one mac address - it "should only return one mac address" do - Facter.value(:macaddress).should_not be_include(" ") - end - def test_flush val = "yay" Facter.add(:testing) do @@ -643,11 +655,6 @@ some random stuff assert_nothing_raised("Could not clear facter cache") do Facter.flush end - assert_equal(val, Facter.value(:testing), - "did not clear cache") - - + assert_equal(val, Facter.value(:testing), "did not clear cache") end end - -# $Id$ |