Best practice? libs and modules

Hello,

another 'best practice' question. Example: I have a library which I
think should go into

lib/foo/bar/baz.rb

Should I use 'module ... ' wrappers to reflect the directory structure?
I.e.

module Foo
  module Bar
     class Baz
     end
  end
end

I'd like to be a good ruby-citizen. My personal preference though would
be something different:

module Baz
  class SomethingElse
  end
end

so the scope would be

s=Baz::SomethingElse.new

Patrick

···

--
Posted via http://www.ruby-forum.com/.

Patrick Gundlach wrote:

Hello,

another 'best practice' question. Example: I have a library which I
think should go into

lib/foo/bar/baz.rb

Should I use 'module ... ' wrappers to reflect the directory structure?
I.e.

module Foo
  module Bar
     class Baz
     end
  end
end

I'd like to be a good ruby-citizen. My personal preference though would
be something different:

module Baz
  class SomethingElse
  end
end

so the scope would be

s=Baz::SomethingElse.new

The latter is fine. So long as your top-level namespace is
unique (i.e. the name of your project, hopefully), the
underlying structure is inconsequential.

···

Patrick

--
Posted via http://www.ruby-forum.com/\.

Hello,

another 'best practice' question. Example: I have a library which I
think should go into

lib/foo/bar/baz.rb

[snip]

I'd like to be a good ruby-citizen. My personal preference though would
be something different:

module Baz
class SomethingElse
end
end

so the scope would be

s=Baz::SomethingElse.new

How you scope your class has a subjective element. Only you can decide what the best organization of your class hierarchy is, for your uses.

However, in the general case, yes, definitely put your class under at least one level of scoping, such as you illustrate above, especially if you plan to release your library for others to use. Namespace collisions are no fun.

Just by way of example, I generally organize classes under a module namespace that reflects the name of the project. Then, under that, I will organize similar classes under further names.

So:

Iowa::KeyValueCoding
Iowa::Caches::LRUCache
Iowa::Pools::DBConnectionPool

etc...

I have even done this with certain Ruby class extensions.

So, for example, I have added some things to String and Hash, but don't want to either force my users to use my extensions, or interfere with my users ability to use their own extensions. So, any place where I am going to need one of those extensions, I work with Iowa::String or Iowa::Hash instead of String and Hash. It's a little more typing for me, but better for my users.

Kirk Haines

···

On Sat, 2 Sep 2006, Patrick Gundlach wrote:

If you're going this way,

class Foo::Bar::Baz

leaves out lots of indentation.

···

On Sep 1, 2006, at 8:13 AM, Patrick Gundlach wrote:

Hello,

another 'best practice' question. Example: I have a library which I
think should go into

lib/foo/bar/baz.rb

Should I use 'module ... ' wrappers to reflect the directory structure?
I.e.

module Foo
  module Bar
     class Baz
     end
  end
end

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com

Hello Eero and Kirk,

thank you for your valuable comments. My "namespace" is very unlikely to
collide with anything else, and yes, I have started a little rubyforge
project, so the library will be released into public.

I appreciate your replies,

Patrick

···

--
Posted via http://www.ruby-forum.com/.

khaines@enigo.com wrote:

So, for example, I have added some things to String and Hash, but don't
want to either force my users to use my extensions, or interfere with my
users ability to use their own extensions. So, any place where I am going
to need one of those extensions, I work with Iowa::String or Iowa::Hash
instead of String and Hash. It's a little more typing for me, but better
for my users.

There's a downside to this though. To take advantage of those
extensions your uses have to use these special subclasses too. As long
as your not changing the behavior of a built-in you're generally okay.

T.

That's really cool!

However, it only really works if you don't reference things in the Foo
or Bar modules. E.g.:

class Foo::Bar::Baz
end

class Foo::Bar::Qux
  def fetch_me_a_baz
    Foo::Bar::Baz.new() # <- need to use explicit namespace here
  end
end

Versus the nested case:

module Foo
  module Bar
    class Baz
    end

    class Qux
      def fetch_me_a_baz
        Baz.new() # <- this line is simpler
      end
    end
  end
end

Well, that was a bit wordier than I expected it to be. I guess it's a
tradeoff that you can make depending on how many times you need to use
Baz from inside Qux. Nice!

-RYaN

···

On 9/1/06, Eric Hodel <drbrain@segment7.net> wrote:

If you're going this way,

class Foo::Bar::Baz

leaves out lots of indentation.

For structuring my projects, I tend to keep nested modules and classes
as internal objects. That is to say, I have ProjectName::Base but then
any other dependent classes I'll stick in
ProjectName::Support::Connection or what have you. Of course, if
someone is equally likely to use Base as well as Connection, then I'll
stick them both just in ProjectName.

Of course, this really is just preference. Either way would be fine.
(And besides, I'm (very) far from being an expert, so take what I say
with a grain of salt.)

M.T.

No, it really doesn't matter if I am changing the behavior of a built in or not (which I'm not).

If I just opened String and put my extensions into there, I'd run the substantial risk that someone might use a core extension from Rails' ActiveSupport or Facets that conflicts. And even if there are no conflicts today, tomorrow there might be.

Or if I just used LRUCache instead of Iowa::Caches::LRUCache, and someone decided to use the Facets LRUCache in an app, it would cause bad things to happen (I like the Facets LRUCache's simplicity; I hate that it requires extending anything that is to be stored in it, BTW).

So yes, if one of my users wants to take advantage of something I have in one of the extensions, they have to type a few more characters to create the object. I am quite willing to sacrafice a few characters for the relative security against namespace collisions, though. The cost is very small, and the benefit is large.

Kirk Haines

···

On Sat, 2 Sep 2006, Trans wrote:

So, for example, I have added some things to String and Hash, but don't
want to either force my users to use my extensions, or interfere with my
users ability to use their own extensions. So, any place where I am going
to need one of those extensions, I work with Iowa::String or Iowa::Hash
instead of String and Hash. It's a little more typing for me, but better
for my users.

There's a downside to this though. To take advantage of those
extensions your uses have to use these special subclasses too. As long
as your not changing the behavior of a built-in you're generally okay.

khaines@enigo.com wrote:

>> So, for example, I have added some things to String and Hash, but don't
>> want to either force my users to use my extensions, or interfere with my
>> users ability to use their own extensions. So, any place where I am going
>> to need one of those extensions, I work with Iowa::String or Iowa::Hash
>> instead of String and Hash. It's a little more typing for me, but better
>> for my users.
>
> There's a downside to this though. To take advantage of those
> extensions your uses have to use these special subclasses too. As long
> as your not changing the behavior of a built-in you're generally okay.

No, it really doesn't matter if I am changing the behavior of a built in
or not (which I'm not).

If I just opened String and put my extensions into there, I'd run the
substantial risk that someone might use a core extension from
Rails' ActiveSupport or Facets that conflicts. And even if there are no
conflicts today, tomorrow there might be.

Or if I just used LRUCache instead of Iowa::Caches::LRUCache, and someone
decided to use the Facets LRUCache in an app, it would cause bad things to
happen (I like the Facets LRUCache's simplicity; I hate that it requires
extending anything that is to be stored in it, BTW).

Hmm... If you have a fix for that I'd love to incorporate it.

So yes, if one of my users wants to take advantage of something I have
in one of the extensions, they have to type a few more characters to
create the object. I am quite willing to sacrafice a few characters for
the relative security against namespace collisions, though. The cost is
very small, and the benefit is large.

But then how will I use Kirk's String extensions and Tom's string
extensions?

It's a tade off and what we really need is proper namespaces to fix the
problem.

T.

···

On Sat, 2 Sep 2006, Trans wrote:

Or if I just used LRUCache instead of Iowa::Caches::LRUCache, and someone
decided to use the Facets LRUCache in an app, it would cause bad things to
happen (I like the Facets LRUCache's simplicity; I hate that it requires
extending anything that is to be stored in it, BTW).

Hmm... If you have a fix for that I'd love to incorporate it.

Well, let me tell you what I did.

The mixin is used to turn the desired class into something that can be a linked list node. It's a neat idea, but I don't like having to pollute my namespace, and it breaks down messily in some instances (try using the cache to cache fixnums...).

So, I took a couple ideas from the implementation and built a doubly linked list implementation that doesn't need the mixin and also provides fast random access by using a hash in conjunction with the list. So data access, modification, inserts, and deletes are all pretty fast, and no penalty is paid for having large numbers of items in the cache.

I then used this as the basis for an LRU cache implementation. Mine will also expire elements based on age. So, for example, a cache with a max ttl of 1800 seconds will expire elements that haven't been accessed in more than half an hour, even if the cache isn't full. I pay a small cost for this capability, though, in terms of speed, so it may not be something you would want in your implementation.

The cache should be threadsafe, and I also have a capability in mine for finalizers -- blocks of code that are invoked immediately before something is expired from the cache -- which also does not come for free.

I can happily make a version of the cache which doesn't include the TTL stuff or the finalizers, though, if you want it. Note that the mixin approach of your current LRU cache definitely makes for the fastest cache, though, so I guess it depends on what one is selecting for. I'm using a modified version of your LRUCache (I added finalizer support) in a place where I need a basic LRU cache, can live with the mixin, and just want it to be as fast as possible.

You might also be able to use the raw linked list?

So yes, if one of my users wants to take advantage of something I have
in one of the extensions, they have to type a few more characters to
create the object. I am quite willing to sacrafice a few characters for
the relative security against namespace collisions, though. The cost is
very small, and the benefit is large.

But then how will I use Kirk's String extensions and Tom's string
extensions?

I suppose I could make Iowa::String delegate any method it doesn't know down to the original string it was initialized with. That requires that it keep a reference to that original string, though, which is also a tradeoff.

Kirk Haines

···

On Sun, 3 Sep 2006, Trans wrote:

khaines@enigo.com wrote:

Well, let me tell you what I did.

The mixin is used to turn the desired class into something that can be a
linked list node. It's a neat idea, but I don't like having to pollute my
namespace, and it breaks down messily in some instances (try using the
cache to cache fixnums...).

So, I took a couple ideas from the implementation and built a doubly
linked list implementation that doesn't need the mixin and also provides
fast random access by using a hash in conjunction with the list. So data
access, modification, inserts, and deletes are all pretty fast, and no
penalty is paid for having large numbers of items in the cache.

I then used this as the basis for an LRU cache implementation. Mine will
also expire elements based on age. So, for example, a cache with a max
ttl of 1800 seconds will expire elements that haven't been accessed in
more than half an hour, even if the cache isn't full. I pay a small cost
for this capability, though, in terms of speed, so it may not be something
you would want in your implementation.

The cache should be threadsafe, and I also have a capability in mine for
finalizers -- blocks of code that are invoked immediately before something
is expired from the cache -- which also does not come for free.

I can happily make a version of the cache which doesn't include the TTL
stuff or the finalizers, though, if you want it. Note that the mixin
approach of your current LRU cache definitely makes for the fastest cache,
though, so I guess it depends on what one is selecting for. I'm using a
modified version of your LRUCache (I added finalizer support) in a place
where I need a basic LRU cache, can live with the mixin, and just want it
to be as fast as possible.

Well, I think those are useful features. It would be nice if it could
be implemented in such a way that the TTL and finializers were options
--but zero overhead when not used. But even if that's not reasonable,
then a second type of LRUCache per your implementation is still welcome
in Facets.

You might also be able to use the raw linked list?

Yes!

T.