class TrieStoreTest < Test::Unit::TestCase
def setup
@t = TrieStore.new
end
def test_should_insert
@t.insert('hello', 1)
end
def test_should_search
@t.insert('hello', :value1)
assert_equal [ :value1 ], @t.search('hello')
end
def test_should_search_with_prefix
@t.insert('hello', :value1)
assert_equal [ :value1 ], @t.search('he')
end
def test_should_not_search_missing_values
@t.insert('hello', :value)
assert !@t.search('goodbye')
end
def test_should_return_unique_matches
@t.insert('hello', :value1)
@t.insert('help', :value2)
assert_equal [ :value1 ], @t.search('hello')
end
def test_should_return_common_matches
@t.insert('hello', :value1)
@t.insert('help', :value2)
assert_equal [ :value1, :value2 ], @t.search('hel')
end
end
class TrieStore
def initialize
@children, @data = {}, []
end
def insert(term, value)
return if term.empty?
head = term[0, 1]
rest = term[1..-1]
@data |= [value]
@children[head] ||= TrieStore.new
@children[head].insert(rest, value)
end
def search(term)
case term.size
when 0 then []
when 1 then @data
else
head = term[0, 1]
rest = term[1..-1]
if child = @children[head]
child.search(rest)
else
[]
end
end
end
end
Props to Jeremy Voorhis for this cool and simple Hash maneuver.
class Hash
def rewrite mapping
inject({}) do |rewritten_hash, (original_key, value)|
rewritten_hash[mapping.fetch(original_key, original_key)] = value
rewritten_hash
end
end
end
Example usage:
h = { :human => 'squishy', :robot => 'tinny' }
h.rewrite(:human => :mammal)
# => { :mammal => 'squishy', :robot => 'tinny' }
Interestingly, this is the first time I’ve seen argument decomposition used in a truly clear and helpful way.
In a Rails Migration, specifying a column as binary generates a blob column. Blob columns, unfortunately, are allocated differently than any other MySQL column type; specifically, they aren’t supported in memory engine-backed tables.
Varbinary columns, on the other hand, are supported. In order to provide varbinary columns in a Migration, I wrote up this simple core extension.
# Specify :varbinary => true in column creation
# No support yet for altering tables to varbinary
class ActiveRecord::ConnectionAdapters::MysqlAdapter < AbstractAdapter
def type_to_sql_with_varbinary(type, limit = nil, precision = nil, scale = nil)
return type_to_sql_without_varbinary(type, limit, precision, scale) unless :varbinary == type.to_sym
"varbinary(#{limit})"
end
alias_method_chain :type_to_sql, :varbinary
end
class ActiveRecord::ConnectionAdapters::TableDefinition
def column_with_varbinary name, type, options = {}
return column_without_varbinary name, "varbinary", options if options.delete(:varbinary)
column_without_varbinary name, type, options
end
alias_method_chain :column, :varbinary
end
def foo
return 1
ensure
return 2
end
foo
=> 2
When trying to set up indices on a set of mysql tables, a number of annoying errors can occur that necessitate re-running a migration.
This simple hack lets you specify a :force => true on add_index or remove_index calls.
# Allows you to specify indices to add in a migration that will only be created if they do not
# already exist, or to remove indices only if they already exist
module ActiveRecord
module ConnectionAdapters
class MysqlAdapter
def add_index_with_unless_exists table_name, column_name, options = {}
if options.kind_of?(Hash) && options.delete(:force)
begin
add_index_without_unless_exists table_name, column_name, options
rescue
if /^Mysql::Error: Duplicate key name /.match($!.message)
puts "Failed to create index #{table_name} #{column_name} #{options.inspect}"
else
raise $!
end
end
else
add_index_without_unless_exists table_name, column_name, options
end
end
alias_method_chain :add_index, :unless_exists
def remove_index_with_unless_doesnt_exist table_name, options = {}
if options.kind_of?(Hash) && options.delete(:force)
begin
remove_index_without_unless_doesnt_exist table_name, options
rescue
if /^Mysql::Error: Can't DROP/.match($!.message)
puts "Failed to drop index #{table_name} #{options.inspect}"
else
raise $!
end
end
else
remove_index_without_unless_doesnt_exist table_name, options
end
end
alias_method_chain :remove_index, :unless_doesnt_exist
end
end
end