File-based Code Encapsulation

T

Trans

Hi--

I'm having a little debate with myself. On my current project I have a
bunch of little reusable task scripts that a command line tool runs.
The scripts are written as the top-level (although I actually simulate
the top-level when running them). So for example a script would just be
something like:

# example.rb

def example
puts "This is an example!"
end

Then on the command line I would do:

% mytool example
This is an example!

That's all well and good, but many of the scripts have generally useful
routines and I would like them to be accessible by other programs too,
not just my command line tool. So I thoght maybe it would be better if
a module were required to wrap the defs.

# another.rb

module MyToolAnother

def another
puts "This is another!"
end

end

That works, of course, but it adds an additonal layer of essentially
redundant code, which IMHO is ugly.

Then I got to thinking. Why don't we write resuable lib in this fashion
anyway and just create our own containers on the fly when loading them?

MyToolExample = load_as_module "example.rb"

What intersting about that is then we could determine in what capacity
it is to be used. For example:

# adds module_function
MyToolExample = load_as_function_module "example.rb"

# adds self extend
MyToolExample = load_as_self_extended_module "example.rb"

Or even

MyToolExample = load_as_class "example.rb"

We could even have include and extend take a lib path.

include "example.rb"

Of course this effectively puts encapsulation, at least at the top
level, on a per-file basis. But in many respects that seems kind of
nice. It increases flexability and reduces configuration complexity.

So what do your think? Is this technique worth promoting? Or am I being
silly and should just wrap all my scripts in modules?

T.
 
A

ara.t.howard

Hi--

I'm having a little debate with myself. On my current project I have a
bunch of little reusable task scripts that a command line tool runs.
The scripts are written as the top-level (although I actually simulate
the top-level when running them). So for example a script would just be
something like:

# example.rb

def example
puts "This is an example!"
end

Then on the command line I would do:

% mytool example
This is an example!

That's all well and good, but many of the scripts have generally useful
routines and I would like them to be accessible by other programs too,
not just my command line tool. So I thoght maybe it would be better if
a module were required to wrap the defs.

# another.rb

module MyToolAnother

def another
puts "This is another!"
end

end

That works, of course, but it adds an additonal layer of essentially
redundant code, which IMHO is ugly.

Then I got to thinking. Why don't we write resuable lib in this fashion
anyway and just create our own containers on the fly when loading them?

MyToolExample = load_as_module "example.rb"

What intersting about that is then we could determine in what capacity
it is to be used. For example:

# adds module_function
MyToolExample = load_as_function_module "example.rb"

# adds self extend
MyToolExample = load_as_self_extended_module "example.rb"

Or even

MyToolExample = load_as_class "example.rb"

We could even have include and extend take a lib path.

include "example.rb"

Of course this effectively puts encapsulation, at least at the top
level, on a per-file basis. But in many respects that seems kind of
nice. It increases flexability and reduces configuration complexity.

So what do your think? Is this technique worth promoting? Or am I being
silly and should just wrap all my scripts in modules?

T.

wrap up the functionality into a lib, require that from your scripts, and make
accessing instance methods from the command-line easy. i've used this pattern
many times. here is an example from our NRT (near-real-time) system which
allows me to call any module function from the command line:

mussel: ~ > cat nrtlib.rb
module NRT
def self.foobar(*a) p a end

# many, many, many lines of code
end


now i have a single command line program which loads this lib then takes the
first command line argument as a method to send to the module. remaining
command-line parms are parsed in a sensible way allowing ints, strings,
floats, bools, lists, hashes, and combinations of those. whatever value is
returned by the function is dumped on stdout. examples.


mussel:~ > nrt foobar
--- []

mussel:~ > nrt foobar 42
---
- 42

mussel:~ > nrt foobar 1,2,3
---
- - 1
- 2
- 3

mussel:~ > nrt foobar 1,2,3 k:v
---
- - 1
- 2
- 3
- k: v

mussel:~ > nrt foobar 1,2,3 k:v a:b
---
- - 1
- 2
- 3
- k: v
a: b

mussel:~ > nrt foobar 42.0 string
---
- 42.0
- string

mussel:~ > nrt foobar 42.0 string key:val
---
- 42.0
- string
- key: val


now, yaml input is also allows by giving '--' as a commnad line arg

mussel:~ > nrt foobar 42.0 string key:val | nrt foobar --
---
- 42.0
- string
- key: val

and the arg '-' indicates a simple unix style list, one element per line, on
stdin:

mussel:~ > printf "a \n b \n c \n" | nrt foobar -
---
- a
- b
- c


the result is that any library/module routine is instantly availible from the
command line - makes testing a breeze!

obviously not all routines are easy to use this way but, by sticking to simple
interfaces/function call signatures many of them can be.

it's a useful pattern.

food for thought.


-a
 
S

spooq

now i have a single command line program which loads this lib then takes the
first command line argument as a method to send to the module. remaining
command-line parms are parsed in a sensible way allowing ints, strings,
floats, bools, lists, hashes, and combinations of those. whatever value is
returned by the function is dumped on stdout.

Would it be possible for you to post this code?
 
A

ara.t.howard

Would it be possible for you to post this code?

sure. i've been trying to organize it into a library, but it's hard to
abstract. i'll see if i can gin up something later today.

regards.

-a
 
T

Trans

gabriele renzi wrote me with an interesting observation:
If you just add

and a directory is a module, too so that
require 'foo'
loads module Foo
and
require 'foo/bar'
requires module Foo::Bar

and you have the python's import system, which is a very good thing imo
and could reduce ruby's stdlib size of a 1/2% :)

then i found this post from Guido van Rossum in 2003

http://mail.python.org/pipermail/python-dev/2003-December/041065.html

(did it ever happen?)

this really gets me wondering, how do Ruby's and Python's loading
mechanisms match-up? which do you think is better?

T.
 
T

Trans

wrap up the functionality into a lib, require that from your scripts, and make
accessing instance methods from the command-line easy.

in this particluar case it's not suitable to separate the functionality
into separate lib b/c it's important that these scripts be
self-contained.
i've used this pattern
many times. here is an example from our NRT (near-real-time) system which
allows me to call any module function from the command line:

[snip examples]

the result is that any library/module routine is instantly availible from the
command line - makes testing a breeze!

obviously not all routines are easy to use this way but, by sticking to simple
interfaces/function call signatures many of them can be.

it's a useful pattern.

food for thought.

very cool. yea, i wondn't mind adding a tool like that to my current
project. i wonder how extensive a tool like that could be. would it be
possible to run arbitrary methods --even if they were in classes or
non-function modules?

T.
 
M

Michael P. Soulier

then i found this post from Guido van Rossum in 2003

http://mail.python.org/pipermail/python-dev/2003-December/041065.html

(did it ever happen?)
Yes.

this really gets me wondering, how do Ruby's and Python's loading
mechanisms match-up? which do you think is better?

I prefer Python's, honestly. I also like that in Perl, the convention
at least is that what you use is what the namespace is. So, if I use

use File::Copy;

Then the functions are in File::Copy::* by convention.

Mind you, you'd typically use it like

use File::Copy qw(move copy);

And just call move() or copy() directly.

In Ruby I guess that would be

use File::Copy
include File::Copy

to achieve the same?

Coming from Perl and Python, I did find Ruby's system confusing. The
poor documentation makes it even more so.

Mike
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top