Something like import/package in java?

C

Christoffer Lernö

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
 
M

Michael Morin

Christoffer said:
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
http://ruby.about.com/
Become an About.com Guide: beaguide.about.com
About.com is part of the New York Times Company
 
C

Christoffer Lernö

Not really. If you're worried about namespace pollution, you can =20
put everything into a module. Besides including the module and =20
"importing" everything within it, I don't think there's a way to =20
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 =20
conditional on files included by other tests.
- If I want to include all classes from a dir, I have to manually scan =20=

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 =20
tests), when it becomes really important to have a well-defined =20
organization of my classes, and here just heaping classes into =20
different files doesn't quite cut it for me.


/C=
 
M

Marc Heiler

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

Charles Oliver Nutter

Christoffer said:
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
 
C

Christoffer Lernö

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

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

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

This means that I can read from the imports the actual dependencies on =20=

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 =20=

don't quite see how using namespaces would solve these issues.


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

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

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

=3D dir
end
$package_top_dir =3D current_path
end

def import path
$package_top_dir ||=3D current_path
import_path_parts =3D path.split(/\./)
file =3D import_path_parts.pop
import_dir =3D Dir.new($package_top_dir + File::SEPARATOR + =20
(file.empty? ? "" : import_path_parts.join(File::SEPARATOR)))
if (file =3D=3D "*")
import_dir.entries.each do |file|
require(import_dir.path + File::SEPARATOR + file) if file =3D~ /=20=

\.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 =20
directory you run the file (excluding playing around with .. and .)

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



/C=
 
C

Charles Oliver Nutter

Christoffer said:
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
 
C

Christoffer Lernö

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

Static dependency tracking? Yes perhaps sort of that. It doesn't have =20=

to be perfect, just work in most cases. But I am also a simpler way to =20=

include a file when organizing files in a file hierarchy (stuff like =20
require '../somepath/somefile' is rather unclear and will fail if the =20=

file is run from the wrong directory).

/C=
 

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

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top