including Enumerable in PStore

R

Reginald Tan

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
 
C

Christopher Dicely

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:


=C2=A0h =3D {0 =3D> "sword", 5 =3D> "hammer", 3 =3D> "arrow"}
=C2=A0puts h.map{ |x| x[0]}.max =C2=A0 # 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

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.
=C2=A0class PStore
=C2=A0 =C2=A0include Enumerable
=C2=A0 =C2=A0def each &block

=C2=A0 =C2=A0end
=C2=A0end

=C2=A0p =3D PStore.new("temp.store")
=C2=A0p.transaction do
=C2=A0 =C2=A0p[0] =3D "sword"
=C2=A0 =C2=A0p[1] =3D "hammer"
=C2=A0 =C2=A0p[2] =3D "arrow"
=C2=A0end

=C2=A0p.transaction do
=C2=A0 =C2=A0puts p.map{|x| x[0]}.max # wont work yet
=C2=A0end
 
R

Reginald Tan

Christopher Dicely wrote in post #995252:
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()?
 
R

Robert Klemme

Christopher Dicely wrote in post #995252:

Regarding your original code

h =3D {0 =3D> "sword", 5 =3D> "hammer", 3 =3D> "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
=3D> 5
irb(main):003:0> h.max_by {|k,v| k}.first
=3D> 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 =3D false)
@ps =3D ps
@read_only =3D 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

--=20
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/
 
C

Christopher Dicely

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 Klemme 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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,968
Messages
2,570,150
Members
46,697
Latest member
AugustNabo

Latest Threads

Top