How to make interface explicit with Hash stores?

I have a class that uses hash as its internal store. What’s a good way
to communicate the interface of my methods. It seems like the user would
only know how to use it if there’s a comment or a test case. This is the
interface that I want them to use:

book = Book.new
book.add_content(1 => some_text)
book.add_content(2 => some_text)

This is the class that I have:

class Book
def initialize()
@hash = {}
end

def add_contents(hash)
  @hash.merge! hash
end

def contents
  @hash
end

end

On Tue, Nov 1, 2011 at 3:14 AM, Reginald T. [email protected]
wrote:

I have a class that uses hash as its internal store. What’s a good way
to communicate the interface of my methods.

Do you need to leak this implementation detail? You could make
add_content a two argument method and changing it like this:

def add_content(k, v)
@hash.merge(Hash[k,v]) # or @hash.merge({k => v})
end


Michael K.

http://citizen428.net | http://twitter.com/citizen428

Michael K. wrote in post #1029550:

On Tue, Nov 1, 2011 at 3:14 AM, Reginald T. [email protected]
wrote:

I have a class that uses hash as its internal store. What’s a good way
to communicate the interface of my methods.

Do you need to leak this implementation detail? You could make
add_content a two argument method and changing it like this:

def add_content(k, v)
@hash.merge(Hash[k,v]) # or @hash.merge({k => v})
end


Michael K.

http://citizen428.net | http://twitter.com/citizen428

That’s a good point about hiding implementation. requiring 2 arguments
looks like a good way to go about what i’m trying to do. Thanks Michael!

On Tue, Nov 1, 2011 at 10:13, Michael K. [email protected] wrote:

def add_content(k, v)
@hash.merge(Hash[k,v]) # or @hash.merge({k => v})
end

I think perhaps you want #merge! not #merge.

However, given that this can only take two arguments, the only reason I
can
think of for doing it this way is so that you can chain multiple
add_content calls: book.add_content(a, b).add_content(c, d). Otherwise,
why
not just define

def []=(k, v)
@hash[k] = v
end

and then you can have book[1] = 2? Maybe it doesn’t make sense to have a
Book look like a Hash, though, in which case you can define some other
foo=
method, if that’s meaningful. Method name aside, the @hash.merge!
approach
would be good if you can have more than one addition in one step:

def add(h = {})
@hash.merge!(h)
end

Which also avoids having to do add_content(a, b).add_content(c, d). #add
seems like it’s a bit too generic, but perhaps in the actual code it’ll
have a more meaningful name.

On Tue, Nov 1, 2011 at 11:13 AM, Michael K. [email protected]
wrote:

On Tue, Nov 1, 2011 at 3:14 AM, Reginald T. [email protected] wrote:

I have a class that uses hash as its internal store. What’s a good way
to communicate the interface of my methods.

Document them.

Do you need to leak this implementation detail? You could make
add_content a two argument method and changing it like this:

def add_content(k, v)
@hash.merge(Hash[k,v]) # or @hash.merge({k => v})
end

I don’t see how changing the argument count of #add_content has
anything to do with “leaking implementation detail”. It’s perfectly
OK to use #add_content with a single Hash argument - regardless of
internal representation. I think you are mixing two unrelated things
here.

The only place where leaking actually comes into play is return
values: both methods leak the internal Hash. But that can be easily
avoided by returning self and using #dup.

Whether it is wise to use a single Hash argument when the intention is
actually to just add a pair (of what btw?) is another question. So,
to remedy this one could do

class Book
def initialize
@structure = {}
end

Add another chapter with title.

def add_content(chapter, title)
raise “Wrong arguments” unless Integer === chapter && String ===
title
@structure[chapter] = title.dup.freeze
self
end

Get the (ordered) list of chapter numbers and titles.

def contents
@structure.dup
# or: @structure.sort_by {|chapter, title| chapter}
end
end

Kind regards

robert