require 'fileutils'
class MemoStack
def MemoStack.memo_stack(kind, opts={})
case kind
when :local then LocalMemoStack.new(opts)
else
raise "Unknown kind -- #{kind}"
end
end
def create_memo
Memo.new(self)
end
end
class MemoStack::Memo
attr_reader :stack, :memo_id
def initialize(stack, memo_id=nil)
@stack = stack
@memo_id = memo_id
end
def update!(params={})
@stack.save(self, params)
end
def data
@stack.data_of(self)
end
def kind
@stack.kind_of(self).to_sym
end
end
class LocalMemoStack < MemoStack
attr_reader :basepath
def initialize(opts={})
if opts.has_key? :path
then path = opts[:path]
else raise ArgumentError, "required path"
end
if File.exist?(path)
if FileTest.directory?(path)
then @basepath = path
else raise "the specified path is not unsupported MemoStack type"
end
else
FileUtils.mkdir_p(path)
@basepath = path
end
end
def last_created
path = all_path.sort_by {|i| File.ctime(i) }.last
memo_id = File.basename(path).split('.').first
Memo.new(self, memo_id)
end
def save(memo, params)
data = params[:data] ; raise "requrie data" if data.nil?
kind = params[:kind] ; raise "requrie kind" if kind.nil?
mid = memo.memo_id || __timestamp__
path = File.join(@basepath, "#{mid}.#{kind}")
File.open(path, "w") { |io| io.write(data) }
if memo.memo_id.nil? then
memo.instance_eval { @memo_id = mid }
end
end
def data_of memo
path = __path_to_memo__(memo.memo_id)
File.read(path)
end
def kind_of memo
path = __path_to_memo__(memo.memo_id)
path.split('.').last
end
private
def all_path
Dir[File.join(@basepath, "*")].find_all {|i| FileTest.file?(i) }
end
def __path_to_memo__(memo_id)
candidates = Dir[File.join(@basepath, "#{memo_id}.*")]
candidates.sort_by{|i| File.stat(i).mtime }.last
end
def __timestamp__
ts = Time.now.to_f
while __path_to_memo__(ts)
ts = Time.now.to_f
end
ts
end
end
def MemoStack(kind, opts={})
MemoStack.memo_stack(kind, opts)
end
if __FILE__ == $0 then
require 'test/unit'
class TestMemoStack < Test::Unit::TestCase
def setup
@stack_path = "/tmp/hogehoge.memostack"
@stack = MemoStack(:local, :path=>"/tmp/hogehoge.memostack")
end
def teardown
remove_local_stack(@stack_path)
end
def test_create_local_memo_stack
stack_path = "/tmp/#{Time.now.to_f.to_s}.memostack"
assert_equal( false,
File.exist?(stack_path),
'メモスタックは存在しない')
assert_kind_of( MemoStack,
MemoStack(:local, :path=>stack_path),
'新規メモスタックを生成')
assert_equal( true,
File.exist?(stack_path),
'メモスタックが新しく作られた')
ensure
remove_local_stack(stack_path)
end
def test_create_local_memo_stack
stack_path = "/tmp/#{Time.now.to_f.to_s}.memostack"
MemoStack(:local, :path=>stack_path)
assert_equal( true,
File.exist?(stack_path),
'メモスタックが存在')
assert_kind_of( MemoStack,
MemoStack(:local, :path=>stack_path),
'既存のメモスタック')
ensure
remove_local_stack(stack_path)
end
def test_create_memo
assert_nothing_raised { @stack.create_memo }
end
def test_memo_update!
memo = @stack.create_memo
data = "hello, world"
kind = :txt
assert_nothing_raised {
memo.update!(:data=>data, :kind=>kind)
}
assert_equal(data, memo.data)
assert_equal(kind, memo.kind)
end
def test_last_created
memo = @stack.create_memo
data = "hello, world"
kind = :txt
memo.update!(:data=>data, :kind=>kind)
assert_nothing_raised { memo = @stack.last_created }
assert_equal(data, memo.data)
assert_equal(kind, memo.kind)
end
private
def remove_local_stack(path)
FileUtils.rm_rf(path)
end
end
end