Something like import/package in java?

Hi,

I keep feeling frustrated with my ruby project.

My problem is one of organization.

My code looks a bit like this:

lib/
main.rb
comman ds/
logio.rb
create.rb
chat.rb
server/
io/
protocol/
utils/

And I keep writing code like

require ‘commands/logio’
require ‘commands/create’
require ‘commands/chat’

etc.

What is annoying is the disconnect from what actually happens.

With “require ‘commands/logio’” I am actually importing the two
classes LogIn and LogOut as well as ensuring a bunch of other files
are included.
“require ‘commands/create’” is importing classes CreateNew and
ReturnDead and so on.

To some extent I can fix this by enforcing a strict 1 file - 1 class
policy, but the problem does not quite go away.

In java it’s a bit simpler although far from perfect: “import
commands.*” would import all the classes in one sweep, and of course
the strict 1 file - 1 class policy is maintained in java for public
classes.

What I’d love to have is something like this instead of require:

<logio.rb>
package commands

class LogIn

end

class LogOut

end

end

<main.rb>
import commands.LogIn

where import goes into the commands dir and just loads that single
class.

Is there something like this available? Any ideas?

/C

Christoffer Lernö wrote:

main.rb

LogIn and LogOut as well as ensuring a bunch of other files are included.

/C

Not really. If you’re worried about namespace pollution, you can put
everything into a module. Besides including the module and “importing”
everything within it, I don’t think there’s a way to selectively include
classes or methods from the module.

What exactly are your concerns? Neatness aside.

On 30 Jun 2008, at 19:19, Michael M. wrote:

end
selectively include classes or methods from the module.

What exactly are your concerns? Neatness aside.

Readability, clear division of responsibilities, ease of development.

  • Even with unit test suites I don’t feel secure that the test isn’t
    conditional on files included by other tests.
  • If I want to include all classes from a dir, I have to manually scan
    through the dir.
  • Clear cut organization of files.
  • Crystal clear dependencies between files.
  • Lots of other small worries I can’t quite put my finger on.

There’s some breaking point around 20+ files for me (excluding unit
tests), when it becomes really important to have a well-defined
organization of my classes, and here just heaping classes into
different files doesn’t quite cut it for me.

/C

“- Clear cut organization of files.”

I am not entirely sure where you are heading here. For my latest project
I am using a yaml file which requires about 50 different .rb files,
while this is probably not the best layout, it is easy to add or remove
files to that in one place, and quite clear for me.

Christoffer Lernö wrote:

There’s some breaking point around 20+ files for me (excluding unit
tests), when it becomes really important to have a well-defined
organization of my classes, and here just heaping classes into different
files doesn’t quite cut it for me.

Think of require in terms of “load this file” rather than “import
everything defined in this file”. All require does is an exactly-once
execution of the file it finds to load, which will in most cases add
methods, modules, or classes to the global namespace.

To namespace your methods modules and classes, you want to embed them
within other modules. The typical convention is that your x/y/z dir
hierarchy will be mirrored in the z.rb file with a nested module X;
module Y; class Z structure.

Coming from Java, the first thing to remember is that Ruby does not
impose (or gift, depending on your perspective) a mandatory file path ==
package structure, so you’re free (or required) to do namespacing
however you see fit. If you want a 50-deep dir hierarchy of .rb files to
all dump stuff into the global namespace, you can do so. But you would
typically apply your own namespacing for exactly the reasons you
describe above.

The closest rough equivalent to import would be include, which lets you
pull in a whole module (as a namespace) into a given module or class
(thereby making the former module’s namespaced constants available
without full qualification).

  • Charlie

Christoffer Lernö wrote:

For me it is not so much a namespace-issue. It is rather dependency and
include issue. The most frequent use of java imports for me is actually
as an indicator of dependencies as I only in very special circumstances
would use the fully qualified name of a class.

I think what you’re looking for is a static dependency tracking
mechanism in Ruby, a dynamic language. There is not really any simple
way to get such dependency tracking, and certainly nothing built into
Ruby itself. And I don’t think this is particularly unusual among
dynamic languages.

  • Charlie

On 1 Jul 2008, at 15:02, Charles Oliver N. wrote:

tests), when it becomes really important to have a well-defined
path == package structure, so you’re free (or required) to do
namespacing however you see fit. If you want a 50-deep dir hierarchy
of .rb files to all dump stuff into the global namespace, you can do
so. But you would typically apply your own namespacing for exactly
the reasons you describe above.

For me it is not so much a namespace-issue. It is rather dependency
and include issue. The most frequent use of java imports for me is
actually as an indicator of dependencies as I only in very special
circumstances would use the fully qualified name of a class.

This means that I can read from the imports the actual dependencies on
external classes. This helps a lot during refactoring and testing.

Perhaps you feel I don’t understand your point, and that may be so. I
don’t quite see how using namespaces would solve these issues.

I suppose part of my include worries could be solved with something
like this:

8<----------------

def package path
relative_path, file_name = File.split($PROGRAM_NAME)
top_path = File.expand_path(relative_path)
package_path = path.split(/./)
current_path = top_path
built_path = “”
package_path.reverse.each do |path_part|
current_path, dir = File.split(current_path)
built_path += “#{File::SEPARATOR}#{dir}”
raise “Package not in correct directory structure, expected
#{package_path.join(File::SEPARATOR)} was #{built_path}” if path_part !
= dir
end
$package_top_dir = current_path
end

def import path
$package_top_dir ||= current_path
import_path_parts = path.split(/./)
file = import_path_parts.pop
import_dir = Dir.new($package_top_dir + File::SEPARATOR +
(file.empty? ? “” : import_path_parts.join(File::SEPARATOR)))
if (file == “*”)
import_dir.entries.each do |file|
require(import_dir.path + File::SEPARATOR + file) if file =~ /
.rb$/
end
else
require import_dir.path + File::SEPARATOR + file + “.rb”
end
end

8<--------------

If you have this code then the line

package “some_dir.next_dir”
import “some_other_dir.foo”

in file lib/some_dir/next_dir/bar.rb

will require the file lib/some_other_dir/foo.rb regardless from what
directory you run the file (excluding playing around with … and .)

(It would be neater to be able to automatically add the contents of
bar.rb to be part of a module named SomeDirNext::NextDir, by default)

/C

On 2 Jul 2008, at 20:56, Charles Oliver N. wrote:

unusual among dynamic languages.
Static dependency tracking? Yes perhaps sort of that. It doesn’t have
to be perfect, just work in most cases. But I am also a simpler way to
include a file when organizing files in a file hierarchy (stuff like
require ‘…/somepath/somefile’ is rather unclear and will fail if the
file is run from the wrong directory).

/C