summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/facter.rb85
-rw-r--r--lib/facter/collection.rb72
-rwxr-xr-xspec/integration/facter.rb23
-rwxr-xr-xspec/unit/collection.rb131
-rwxr-xr-xspec/unit/facter.rb63
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 = ""
RESET = ""
@@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$