Browse files

Add more implementation / tests for Options class

  • Loading branch information...
1 parent c0f9f84 commit 6f1255ae63709b32cd776cb6d1a9250601d716cd @lsegal committed Feb 19, 2012
Showing with 141 additions and 22 deletions.
  1. +73 −16 lib/yard/options.rb
  2. +68 −6 spec/options_spec.rb
View
89 lib/yard/options.rb
@@ -24,7 +24,7 @@ module YARD
# end
# @example Initializing default option values
# class TemplateOptions < YARD::Options
- # def initialize
+ # def reset_defaults
# super
# self.format = :html
# self.template = :default
@@ -67,10 +67,6 @@ def self.default_attr(key, default)
attr_accessor(key)
end
- def initialize
- set_defaults
- end
-
# Delegates calls with Hash syntax to actual method with key name
#
# @example Calling on an option key with Hash syntax
@@ -95,7 +91,7 @@ def []=(key, value) send("#{key}=", value) end
def update(opts)
opts = opts.to_hash if Options === opts
opts.each do |key, value|
- self[key] = value if respond_to?("#{key}=")
+ self[key] = value
end
self
end
@@ -106,7 +102,7 @@ def update(opts)
# @param [Options, Hash] opts
# @return [Options] the newly created options object
def merge(opts)
- self.class.new.update(opts)
+ dup.update(opts)
end
# @return [Hash] Converts options object to an options hash. All keys
@@ -115,23 +111,84 @@ def to_hash
opts = {}
instance_variables.each do |ivar|
name = ivar.to_s.sub(/^@/, '')
- opts[name.to_sym] = send(name) if respond_to?(name)
+ opts[name.to_sym] = send(name)
end
opts
end
- unless defined? tap() # only for 1.8.6
- def tap(&block) yield(self); self end
+ # Yields over every option key and value
+ # @yield [key, value] every option key and value
+ # @yieldparam [Symbol] key the option key
+ # @yieldparam [Object] value the option value
+ # @return [void]
+ def each(&block)
+ instance_variables.each do |ivar|
+ name = ivar.to_s.sub(/^@/, '')
+ yield(name.to_sym, send(name))
+ end
+ end
+
+ # Inspects the object
+ def inspect
+ "<#{self.class}: #{to_hash.inspect}>"
end
- private
+ # @return [Boolean] whether another Options object equals the
+ # keys and values of this options object
+ def ==(other)
+ case other
+ when Options; to_hash == other.to_hash
+ when Hash; to_hash == other
+ else false
+ end
+ end
- def set_defaults
- defaults = self.class.instance_variable_get("@defaults")
- return unless defaults
- defaults.each do |key, value|
- self[key] = Proc === value ? value.call : value
+ # Handles setting and accessing of unregistered keys similar
+ # to an OpenStruct object.
+ def method_missing(meth, *args, &block)
+ if meth =~ /^(.+)=$/
+ log.debug "Attempting to set unregistered key #{$1} on #{self.class}"
+ instance_variable_set("@#{$1}", args.first)
+ elsif args.size == 0
+ log.debug "Attempting to access unregistered key #{meth} on #{self.class}"
+ instance_variable_get("@#{meth}")
+ else
+ super
end
end
+
+ # Resets all values to their defaults.
+ #
+ # @abstract Subclasses should override this method to perform custom
+ # value initialization if not using {default_attr}. Be sure to call
+ # +super+ so that default initialization can take place.
+ def reset_defaults
+ names_set = {}
+ self.class.ancestors.each do |klass| # look at all ancestors
+ defaults = klass.instance_variable_get("@defaults")
+ return unless defaults
+ defaults.each do |key, value|
+ next if names_set[key]
+ names_set[key] = true
+ self[key] = Proc === value ? value.call : value
+ end
+ end
+ end
+
+ # Deletes an option value for +key+
+ #
+ # @param [Symbol, String] key the key to delete a value for
+ # @return [Object] the value that was deleted
+ def delete(key)
+ val = self[key]
+ if instance_variable_defined?("@#{key}")
+ remove_instance_variable("@#{key}")
+ end
+ val
+ end
+
+ unless defined? tap() # only for 1.8.6
+ def tap(&block) yield(self); self end
+ end
end
end
View
74 spec/options_spec.rb
@@ -11,14 +11,45 @@ def initialize; self.foo = "abc" end
class DefaultOptions1 < YARD::Options
default_attr :foo, 'HELLO'
end
- DefaultOptions1.new.foo.should == 'HELLO'
+ o = DefaultOptions1.new
+ o.reset_defaults
+ o.foo.should == 'HELLO'
end
it "should call lambda if value is a Proc" do
class DefaultOptions2 < YARD::Options
default_attr :foo, lambda { 100 }
end
- DefaultOptions2.new.foo.should == 100
+ o = DefaultOptions2.new
+ o.reset_defaults
+ o.foo.should == 100
+ end
+ end
+
+ describe '#reset_defaults' do
+ it "should not define defaults until reset is called" do
+ class ResetDefaultOptions1 < YARD::Options
+ default_attr :foo, 'FOO'
+ end
+ ResetDefaultOptions1.new.foo.should be_nil
+ o = ResetDefaultOptions1.new
+ o.reset_defaults
+ o.foo.should == 'FOO'
+ end
+ end
+
+ describe '#delete' do
+ it "should delete an option" do
+ o = FooOptions.new
+ o.delete(:foo)
+ o.to_hash.should == {}
+ end
+
+ it "should not error if an option is deleted that does not exist" do
+ o = FooOptions.new
+ o.delete(:foo)
+ o.delete(:foo)
+ o.to_hash.should == {}
end
end
@@ -34,17 +65,42 @@ class DefaultOptions2 < YARD::Options
o[:foo] = "xyz"
o[:foo].should == "xyz"
end
+
+ it "should allow setting of unregistered keys" do
+ o = FooOptions.new
+ o[:bar] = "foo"
+ o[:bar].should == "foo"
+ end
+ end
+
+ describe '#method_missing' do
+ it "should allow setting of unregistered keys" do
+ o = FooOptions.new
+ o.bar = 'foo'
+ o.bar.should == 'foo'
+ end
+
+ it "should allow getting values of unregistered keys (return nil)" do
+ FooOptions.new.bar.should be_nil
+ end
+
+ it "should print debugging messages about unregistered keys" do
+ log.should_receive(:debug).with("Attempting to access unregistered key bar on FooOptions")
+ FooOptions.new.bar
+ log.should_receive(:debug).with("Attempting to set unregistered key bar on FooOptions")
+ FooOptions.new.bar = 1
+ end
end
describe '#update' do
it "should allow updating of options" do
FooOptions.new.update(:foo => "xyz").foo.should == "xyz"
end
- it "should ignore keys with no setter" do
+ it "should not ignore keys with no setter (OpenStruct behaviour)" do
o = FooOptions.new
o.update(:bar => "xyz")
- o.to_hash.should == {:foo => "abc"}
+ o.to_hash.should == {:foo => "abc", :bar => "xyz"}
end
end
@@ -54,6 +110,12 @@ class DefaultOptions2 < YARD::Options
o.merge(:foo => "xyz").object_id.should_not == o.object_id
o.merge(:foo => "xyz").to_hash.should == {:foo => "xyz"}
end
+
+ it "should add in values from original object" do
+ o = FooOptions.new
+ o.update(:bar => "foo")
+ o.merge(:baz => 1).to_hash.should == {:foo => "abc", :bar => "foo", :baz => 1}
+ end
end
describe '#to_hash' do
@@ -82,10 +144,10 @@ def foo; "HELLO#{@foo}" end
it "should ignore ivars with no accessor" do
class ToHashOptions3 < YARD::Options
attr_accessor :foo
- def initialize; @foo = 1; @bar = "IGNORE" end
+ def initialize; @foo = 1; @bar = "NOIGNORE" end
end
o = ToHashOptions3.new
- o.to_hash.should == {:foo => 1}
+ o.to_hash.should == {:foo => 1, :bar => "NOIGNORE"}
end
end

0 comments on commit 6f1255a

Please sign in to comment.