Written June 28, 2007. Tagged Ruby, OS X, TextMate.
In TextMate commands, you sometimes need to to persist information. Perhaps you want to remember what box the user checked, or their last input in some dialog.
When I needed persistence in my Greasemonkey bundle a while back, I wrapped the functionality in a Preferences
class to keep things tidy. When I needed it again today, I tidied up that class further and moved it to a YAML storage which made for less and nicer-looking code.
You can use it like so:
# require or paste the Preferences class here
# Store some values
prefs = Preferences[:foo_bundle]
prefs[:name] = "John Doe"
prefs[:age] = 42
prefs.merge!(:likes_pizza => true, :likes_broccoli => false)
# A reboot later…
prefs = Preferences[:foo_bundle]
puts "Your name is #{prefs[:name]} and you are #{prefs[:age]} years old."
puts "I know all this:"
p prefs.to_hash
# Forget age
prefs.delete(:age)
# Forget everything
prefs.clear!
String and symbol keys are interchangeable, so you could use Preferences["foo_bundle"]["name"]
if you prefer, or even mix and match.
Download with unit tests and all, or see below for the meaty bits:
# By Henrik Nyh <https://henrik.nyh.se> 2007-06-27
# Free to modify and redistribute with credit.
class Preferences
%w{yaml fileutils}.each { |lib| require lib }
# Each preference id should be a singleton
@@instances = {}
class << self; private :new; end
def self.[](id)
raise(ArgumentError, "Preference id must match /\A[a-zA-Z0-9_]+\Z/.") unless id.to_s =~ /\A\w+\Z/
@@instances[id.to_sym] ||= new(id)
end
def initialize(id)
@id = id.to_s
end
# Singleton code ends
def [](key)
value = to_hash[key.to_sym]
end
def []=(key, value)
merge!({key => value})
value
end
def merge!(new_hash)
symbolized_keys = new_hash.inject({}) { |h, (k, v)| h[k.to_sym] = v; h }
@hash = to_hash.merge(symbolized_keys)
persist!
end
def delete(key)
key = key.to_sym
value = self[key]
@hash.delete(key)
persist!
value
end
def clear!
File.delete(path) if File.exist?(path)
flush_cache!
end
def flush_cache!
@hash = nil
end
def to_hash
@hash ||= YAML.load_file(path) rescue {}
end
private
def path
"#{ENV['HOME']}/Library/Preferences/com.macromates.textmate.#{@id}.yaml"
end
def persist!
File.open(path, "w") { |out| YAML.dump(@hash, out) }
@hash
end
end