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:

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

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.

···

--
Michael Morin
Guide to Ruby

Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company

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

···

On 30 Jun 2008, at 19:19, Michael Morin wrote:

Christoffer Lernö wrote:

What I'd love to have is something like this instead of require:
<logio.rb>
package commands
class LogIn
...
end
class LogOut
...
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

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.

"- 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.

···

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

Christoffer Lernö wrote:

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.

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

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 1 Jul 2008, at 15:02, Charles Oliver Nutter wrote:

Christoffer Lernö wrote:

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.

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.

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

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

···

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

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.