Including Enumerable in PStore

Hi guys. I want my PStore to act more like a Hash. Basically I want to
add “map” functionality to it. For a regular hash, you can do the
following:

h = {0 => “sword”, 5 => “hammer”, 3 => “arrow”}
puts h.map{ |x| x[0]}.max # gives the highest key which is 5

I want pstore to be able to do same thing. I’ve included Enumerable but
I do have to implement the each method which I dont know how to go
about. Any suggestions? Thanks

class PStore
include Enumerable
def each &block

end

end

p = PStore.new(“temp.store”)
p.transaction do
p[0] = “sword”
p[1] = “hammer”
p[2] = “arrow”
end

p.transaction do
puts p.map{|x| x[0]}.max # wont work yet
end

On Tue, Apr 26, 2011 at 6:34 PM, Reginald T. [email protected]
wrote:

I want pstore to be able to do same thing. I’ve included Enumerable but
I do have to implement the each method which I dont know how to go
about. Any suggestions? Thanks

It looks like, during a transaction, the content of a PStore is
represent by a Hash in the instance variable @table, so you if you
want the PStore to act like a Hash, something like this should work:

class PStore
def each &blk
in_transaction
@table.each &blk
end
end

(If you call this without a block, it looks like you get an enumerator
over the state of the store as of the transaction it was captured in,
which can be used outside of that transaction, and that could be
useful for some things.)

Obviously, this is exploiting details of the implementation rather
than the public interface, so it’s at more risk of getting broken in a
new version of the library than would be an approach that relies only
on the public interface.

Christopher D. wrote in post #995252:

On Tue, Apr 26, 2011 at 6:34 PM, Reginald T. [email protected]
wrote:

I want pstore to be able to do same thing. I’ve included Enumerable but
I do have to implement the each method which I dont know how to go
about. Any suggestions? Thanks

It looks like, during a transaction, the content of a PStore is
represent by a Hash in the instance variable @table, so you if you
want the PStore to act like a Hash, something like this should work:

class PStore
def each &blk
in_transaction
@table.each &blk
end
end

(If you call this without a block, it looks like you get an enumerator
over the state of the store as of the transaction it was captured in,
which can be used outside of that transaction, and that could be
useful for some things.)

Obviously, this is exploiting details of the implementation rather
than the public interface, so it’s at more risk of getting broken in a
new version of the library than would be an approach that relies only
on the public interface.

Thanks. I didnt realized before that opening up a class would give you
access to instance variables as well. I tried to access the table
variable before using self.table but there was no getter method defined
for it so I had a bit of trouble.

Good catch on - being able to get enum and use it outside transaction.

Btw, I find having to do everything in transaction quite annoying and
tedious. Do you see any better way of implementing it instead of always
passing a block to transaction()?

On Tue, Apr 26, 2011 at 11:08 PM, Reginald T. [email protected]
wrote:

Btw, I find you see any better way of implementing it instead of always
passing a block to transaction()?

Well, I have to second what Robert K. said and suggest that if you
don’t want the transactional behavior, you may be better off using a
different mechanism. On the other hand, if its just that a lot of your
use is one-operation transactions and you just get tired of typing
store.transaction { store[:foo] } to read from the store, then you
just need to DRY up your code by creating methods that let you get the
same effect without the typing.

To avoid monkeypatching, create a subclass of PStore:

class EasyStore < PStore
def transaction_optional(meth, *args)
transaction { send(meth, *args) }
rescue PStore::Error
send(meth, *args)
end

alias []= put

[:fetch, :delete, :roots, :put].each do |meth|
define_method((meth.to_s+“!”).to_sym) do |*args|
transaction_optional(meth, *args)
end
end
end

On Wed, Apr 27, 2011 at 8:08 AM, Reginald T. [email protected]
wrote:

Christopher D. wrote in post #995252:

On Tue, Apr 26, 2011 at 6:34 PM, Reginald T. [email protected]
wrote:

I want pstore to be able to do same thing. I’ve included Enumerable but
I do have to implement the each method which I dont know how to go
about. Any suggestions? Thanks

Regarding your original code

h = {0 => “sword”, 5 => “hammer”, 3 => “arrow”}
puts h.map{ |x| x[0]}.max # gives the highest key which is 5

With a Hash I’d rather

irb(main):002:0> h.keys.max
=> 5
irb(main):003:0> h.max_by {|k,v| k}.first
=> 5

Thanks. I didnt realized before that opening up a class would give you
access to instance variables as well. I tried to access the table
variable before using self.table but there was no getter method defined
for it so I had a bit of trouble.

Good catch on - being able to get enum and use it outside transaction.

Btw, I find having to do everything in transaction quite annoying and
tedious. Do you see any better way of implementing it instead of always
passing a block to transaction()?

If you do not want to use transactions you can as well just write a
Hash with Marshal and not use PStore at all. Transaction safety and
persistent consistency is the main feature to use PStore.

A proper implementation of Hash like access that would not depend on
the internals could roughly work like this:

untested!

class PStoreHash
attr_accessor :read_only

def initialize(ps, read_only = false)
@ps = ps
@read_only = read_only
end

def method_missing(*a,&b)
@ps.transaction read_only do |ps|
PSWrap.new(ps).send(*a,&b)
end
end

class PSWrap < SimpleDelegator
include Enumerable

def keys; roots; end
def values; roots.map {|r| self[r]}; end

def each
  if block_given?
    roots.each {|r| yield r, self[r]}
    self
  else
    to_a
  end
end

end
end

Kind regards

robert