Skip to content

Commit

Permalink
Added Preferences::AccessorHelpers::defaults_kvc_accessor and refacto…
Browse files Browse the repository at this point in the history
…red a bit.
  • Loading branch information
alloy committed Nov 9, 2008
1 parent 1e67ef9 commit 4733f6c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 28 deletions.
43 changes: 39 additions & 4 deletions lib/abstract_preferences.rb
Expand Up @@ -43,7 +43,7 @@ class << self
def inherited(klass)
super
method = klass.name.split('::').last.scan(/[A-Z][a-z]*/).map { |x| x.downcase }.join('_')
Preferences.class_eval "def #{method}; #{klass.name}.instance end"
Preferences.class_eval "def #{method}; #{klass.name}.instance end", __FILE__, __LINE__
end

# The key in the preferences that represents the section class.
Expand Down Expand Up @@ -196,15 +196,50 @@ def inspect
end
end

module StringArrayWrapperHelper
def string_array_kvc_wrapper_accessor(name, path_to_eval_to_object)
# Extend your class with this module to get access to a few KVC accessor helper methods.
module AccessorHelpers
# Defines a kvc_accessor which reads and writes
# to the specified preferences path (<tt>path_to_eval_to_object</tt>).
#
# This is useful for binding, for instance, UI elements
# to an array in the NSUserDefaults which is normally immutable.
#
# class PreferencesController < OSX::NSWindowController
# defaults_kvc_accessor :an_array_of_dictionaries, 'preferences.keyword.url_mappings'
# end
#
# Binding a NSArrayController to File's Owner with key path: <tt>an_array_of_dictionaries</tt>,
# will perform the following read/write actions on the NSUserDefaults:
#
# preferences_controller.valueForKey('an_array_of_dictionaries') # => [{'key' => 'value 1'}, {'key' => 'value 2'}]
# preferences_controller.setValueForKey([{'key' => 'value 1'}], 'an_array_of_dictionaries')
# preferences_controller.valueForKey('an_array_of_dictionaries') # => [{'key' => 'value 1'}]
def defaults_kvc_accessor(name, path_to_eval_to_object)
kvc_accessor(name)

class_eval %{
def #{name}
@#{name} ||= #{path_to_eval_to_object}_wrapped
@#{name} ||= #{path_to_eval_to_object}
end
def #{name}=(new_defaults)
#{path_to_eval_to_object} = @#{name} = new_defaults
end
}, __FILE__, __LINE__
end

# Defines read and write KVC accessors like defaults_kvc_accessor does,
# but is used specifically for defaults defined with Namespace#string_array_defaults_accessor.
#
# class PreferencesController < OSX::NSWindowController
# defualts_string_array_kvc_accessor :an_array_of_strings, 'preferences.keyword.highlight_words'
# end
#
# See Namespace#string_array_defaults_accessor for more info.
def defualts_string_array_kvc_accessor(name, path_to_eval_to_object)
defaults_kvc_accessor(name, "#{path_to_eval_to_object}_wrapped")

class_eval %{
def #{name}=(new_wrappers)
if new_wrappers.length < #{name}.length
Preferences::StringArrayWrapper.destroy(#{name}.first.class, new_wrappers)
Expand Down
74 changes: 50 additions & 24 deletions test/abstract_preferences_test.rb
Expand Up @@ -4,7 +4,8 @@
class Preferences
class TestDefaults < Namespace
defaults_accessor :an_option, true
string_array_defaults_accessor :an_array, %w{ foo bar baz }, 'TestDefaultsStringWrapper'
string_array_defaults_accessor :a_string_array, %w{ foo bar baz }, 'TestDefaultsStringWrapper'
defaults_accessor :an_array, %w{ foo bar baz }
end

register_default_values!
Expand Down Expand Up @@ -58,8 +59,8 @@ class TestDefaults < Namespace
end

it "should return an array of wrapped strings for a string_array_defaults_accessor" do
assert @prefs.an_array_wrapped.all? { |x| x.is_a? TestDefaultsStringWrapper }
@prefs.an_array_wrapped.map { |x| x.valueForKey('string') }.should == %w{ foo bar baz }
assert @prefs.a_string_array_wrapped.all? { |x| x.is_a? TestDefaultsStringWrapper }
@prefs.a_string_array_wrapped.map { |x| x.valueForKey('string') }.should == %w{ foo bar baz }
end

it "should return the key path for the defaults_accessor" do
Expand Down Expand Up @@ -87,70 +88,95 @@ class TestDefaults < Namespace
end

after do
@prefs.an_array = %w{ foo bar baz }
@prefs.a_string_array = %w{ foo bar baz }
end

it "should be a subclass of Preferences::StringArrayWrapper" do
TestDefaultsStringWrapper.superclass.should.be Preferences::StringArrayWrapper
end

it "should know it's key path" do
TestDefaultsStringWrapper.key_path.should == 'Preferences.TestDefaults.an_array'
TestDefaultsStringWrapper.key_path.should == 'Preferences.TestDefaults.a_string_array'
end

it "should update the string it wraps in the array at the configured key path" do
@prefs.an_array_wrapped.first.string = 'new_foo'
@prefs.an_array.should == %w{ new_foo bar baz }
@prefs.a_string_array_wrapped.first.string = 'new_foo'
@prefs.a_string_array.should == %w{ new_foo bar baz }

@prefs.an_array_wrapped.last.string = 'new_baz'
@prefs.an_array.should == %w{ new_foo bar new_baz }
@prefs.a_string_array_wrapped.last.string = 'new_baz'
@prefs.a_string_array.should == %w{ new_foo bar new_baz }
end

it "should add the string it wraps to the array at the configured key path if initialized without index, this happens when a NSArrayController initializes an instance" do
wrapper = TestDefaultsStringWrapper.alloc.init
wrapper.string = 'without index'
@prefs.an_array.last.should == 'without index'
@prefs.a_string_array.last.should == 'without index'
wrapper.index.should == 3
end

it "should remove the strings the wrappers wrap from the array at the configured key path and reset the indices of the wrappers" do
wrapped = @prefs.an_array_wrapped
wrapped = @prefs.a_string_array_wrapped
new_wrapped = [wrapped[1]]
Preferences::StringArrayWrapper.destroy(TestDefaultsStringWrapper, new_wrapped)
@prefs.an_array.should == %w{ bar }
@prefs.a_string_array.should == %w{ bar }
new_wrapped.first.index.should == 0
end
end

class ClassThatExtendsWithStringArrayWrapperHelper < OSX::NSObject
extend Preferences::StringArrayWrapperHelper
class ClassThatExtendsWithAccessorHelpers < OSX::NSObject
extend Preferences::AccessorHelpers

string_array_kvc_wrapper_accessor :a_kvc_array, 'Preferences::TestDefaults.instance.an_array'
defualts_string_array_kvc_accessor :a_kvc_string_array, 'Preferences::TestDefaults.instance.a_string_array'
end

describe "A class that extends with Preferences::StringArrayWrapperHelper" do
describe "A class that extends with Preferences::AccessorHelpers and uses ::defualts_string_array_kvc_accessor" do
before do
@instance = ClassThatExtendsWithStringArrayWrapperHelper.alloc.init
@instance = ClassThatExtendsWithAccessorHelpers.alloc.init
end

after do
Preferences::TestDefaults.instance.an_array = %w{ foo bar baz }
Preferences::TestDefaults.instance.a_string_array = %w{ foo bar baz }
end

it "should define a kvc_accessor" do
@instance.valueForKey('a_kvc_array').map { |x| x.string }.should == %w{ foo bar baz }
it "should define a defaults kvc reader accessor" do
@instance.valueForKey('a_kvc_string_array').map { |x| x.string }.should == %w{ foo bar baz }
end

it "should remove wrappers from the preferences which are removed from the array given to the kvc setter" do
Preferences::TestDefaults.instance.an_array = %w{ foo bar baz bla boo }
Preferences::TestDefaults.instance.a_string_array = %w{ foo bar baz bla boo }

2.times do
wrappers = Preferences::TestDefaults.instance.an_array_wrapped
wrappers = Preferences::TestDefaults.instance.a_string_array_wrapped
wrappers.delete_at(1)
@instance.a_kvc_array = wrappers
@instance.a_kvc_string_array = wrappers
end

@instance.a_kvc_array.map { |x| x.string }.should == %w{ foo bla boo }
@instance.a_kvc_string_array.map { |x| x.string }.should == %w{ foo bla boo }
end
end

class ClassThatExtendsWithAccessorHelpers < OSX::NSObject
extend Preferences::AccessorHelpers

defaults_kvc_accessor :a_kvc_array, 'preferences.test_defaults.an_array'
end

describe "A class that extends with Preferences::AccessorHelpers and uses defaults_kvc_accessor" do
before do
@instance = ClassThatExtendsWithAccessorHelpers.alloc.init
end

after do
Preferences::TestDefaults.instance.an_array = %w{ foo bar baz }
end

it "should define a defaults kvc reader accessor" do
@instance.valueForKey('a_kvc_array').should == %w{ foo bar baz }
end

it "should define a defaults kvc writer accessor" do
@instance.setValue_forKey(['bar'], 'a_kvc_array')
@instance.a_kvc_array.should == ['bar']
end
end

Expand Down

0 comments on commit 4733f6c

Please sign in to comment.