P
Paul van Delst
(Warning: this post is waaaaay longer than I intended)
Hello,
Apologies if the subject line is a bit cryptic, but it's not easy to describe in a single
line. Anyway...
A while back an astute fellow on comp.lang.ruby (thanks RobertK!) provided me with some
advice on how to lay out ruby classes and code that ended up being so useful that I've
applied the same organisational template for several projects. I may have butchered the
advice, but basically I have the following sort of setup (apologies for the lengthy ASCII
art too),
---project1 (module Proj1)
| |--lib
| | |---project1.rb (class Proj1::Runner)
| | |---config
| | | |
| | | `--config.rb (class Proj1::Config)
| | |---base
| | | |
| | | `--base.rb (class Proj1::Base)
| | |---runner1 (module Proj1::Runner1)
| | | |
| | | |--base.rb (class Proj1::Runner1::Base < Proj1::Base)
| | | |--runner1.rb (class Proj1::Runner1::Runner < Proj1::Runner1::Base)
| | | |--class1.rb (class Proj1::Runner1::Class1 < Proj1::Runner1::Base)
| | | |--class2.rb (class Proj1::Runner1::Class2 < Proj1::Runner1::Base)
| | | ...etc
| | `---runner2 (== module Runner2)
| | |
| | |--base.rb (class Proj1::Runner2::Base < Proj::Base)
| | |--runner2.rb (class Proj1::Runner2::Runner < Proj1::Runner2::Base)
| | |--class1.rb (class Proj1::Runner2::Class1 < Proj1::Runner2::Base)
| | |--class2.rb (class Proj1::Runner2::Class2 < Proj1::Runner2::Base)
| | ...etc
| |--test
| | |
| ...etc
|
|-project2 (module Proj2)
| |--lib
| | |---project2.rb (class Proj2::Runner)
| | |---config
| | | |
| | | `--config.rb (class Proj2::Config)
| | |---base
| | | |
| | | `--base.rb (class Proj2::Base)
| | |---runner1 (module Proj2::Runner1)
| | | |
| | | |--base.rb (class Proj2::Runner1::Base < Proj2::Base)
| | | |--runner1.rb (class Proj2::Runner1::Runner < Proj2::Runner1::Base)
| | | |--class1.rb (class Proj2::Runner1::Class1 < Proj2::Runner1::Base)
| | | |--class2.rb (class Proj2::Runner1::Class2 < Proj2::Runner1::Base)
| | | ...etc
| | `---runner2 (== module Runner2)
| | |
| | |--base.rb (class Proj2::Runner2::Base < Proj2::Base)
| | |--runner2.rb (class Proj2::Runner2::Runner < Proj2::Runner2::Base)
| | |--class1.rb (class Proj2::Runner2::Class1 < Proj2::Runner2::Base)
| | |--class2.rb (class Proj2::Runner2::Class2 < Proj2::Runner2::Base)
| | ...etc
| |--test
| | |
...etc
etc.
For any project, call it module ProjectX:
- the lib/projectX.rb file (class Project2::Runner) is the interface to a
user. It's the "main" runner class if you like.
- the ProjectX::Config class holds, surprise, all the configuration info
(usually read in from a text file)
- the ProjectX::Base clase holds all the constants and methods that are
shared throughout any particular ProjectX module
- the ProjectX::RunnerY::Base class holds all the constants and methods
that are shared throughout any particular ProjectX::RunnerY module
- the ProjectX::RunnerY::Runner class is the controller that iterates over
all the current module classes (the Class1, Class2,...ClassZ etc) and
invokes their methods
- the ProjectX::RunnerY::ClassZ class actually does something. )
I like the above setup because it scales quite well. For some projects each "runnerY"
contains another layer. It's made it really easy to isolate functionality and test it.
Anyway, the problem I'm having now is I decided to consolidate all my code on a single
machine and tack on the various locations of everything onto the RUBYLIB envar, i.e.
export RUBYLIB=$HOME/ruby/project1/lib:$HOME/ruby/project2/lib: etc...
So now when I do something like the following in project2/lib/runner1/base.rb,
require 'base/base'
module Proj2
module Runner1
class Base < Proj2::Base
# This is how the config gets passed along in
# Runner1 module and holds shared Runner1 code
end
end
end
and in project2/lib/runner1/runner.rb,
require 'runner1/base'
module Proj2
module Runner1
class Runner < Proj2::Runner1::Base
RUNNERS=[Class1,Class2,....,ClassZ]
def run
RUNNERS.each do |run_class|
r = run_class.new
r.config = self.config
r.run
end
end
end
end
end
and invoke the main script, ruby searches the RUBYLIB paths and finds the "base/base.rb"
for project*1* first (since it's listed first) and loads that file instead of the
"base/base.rb" for project*2*. Thus, I get errors like:
project2/lib/runner1/base.rb:4: uninitialized constant Project2::Base (NameError)
If I swap the listing in my RUBYLIB envar so that the project2 directory is listed first,
everything is honky dory and runs fine.
Phewph! If you've made it this far, the beer is on me if we ever meet.
My questions are:
1) can I keep my current directory structure (which, believe it or not, seems quite
logical to me) and avoid these file loading order problems?
2) IF not, how to fix this? I like the idea of generic file names and classes (config,
base) across projects so that those that follow can grok multiple projects after studying
the docs for just one.
Thanks for any insight.
cheers,
paulv
Hello,
Apologies if the subject line is a bit cryptic, but it's not easy to describe in a single
line. Anyway...
A while back an astute fellow on comp.lang.ruby (thanks RobertK!) provided me with some
advice on how to lay out ruby classes and code that ended up being so useful that I've
applied the same organisational template for several projects. I may have butchered the
advice, but basically I have the following sort of setup (apologies for the lengthy ASCII
art too),
---project1 (module Proj1)
| |--lib
| | |---project1.rb (class Proj1::Runner)
| | |---config
| | | |
| | | `--config.rb (class Proj1::Config)
| | |---base
| | | |
| | | `--base.rb (class Proj1::Base)
| | |---runner1 (module Proj1::Runner1)
| | | |
| | | |--base.rb (class Proj1::Runner1::Base < Proj1::Base)
| | | |--runner1.rb (class Proj1::Runner1::Runner < Proj1::Runner1::Base)
| | | |--class1.rb (class Proj1::Runner1::Class1 < Proj1::Runner1::Base)
| | | |--class2.rb (class Proj1::Runner1::Class2 < Proj1::Runner1::Base)
| | | ...etc
| | `---runner2 (== module Runner2)
| | |
| | |--base.rb (class Proj1::Runner2::Base < Proj::Base)
| | |--runner2.rb (class Proj1::Runner2::Runner < Proj1::Runner2::Base)
| | |--class1.rb (class Proj1::Runner2::Class1 < Proj1::Runner2::Base)
| | |--class2.rb (class Proj1::Runner2::Class2 < Proj1::Runner2::Base)
| | ...etc
| |--test
| | |
| ...etc
|
|-project2 (module Proj2)
| |--lib
| | |---project2.rb (class Proj2::Runner)
| | |---config
| | | |
| | | `--config.rb (class Proj2::Config)
| | |---base
| | | |
| | | `--base.rb (class Proj2::Base)
| | |---runner1 (module Proj2::Runner1)
| | | |
| | | |--base.rb (class Proj2::Runner1::Base < Proj2::Base)
| | | |--runner1.rb (class Proj2::Runner1::Runner < Proj2::Runner1::Base)
| | | |--class1.rb (class Proj2::Runner1::Class1 < Proj2::Runner1::Base)
| | | |--class2.rb (class Proj2::Runner1::Class2 < Proj2::Runner1::Base)
| | | ...etc
| | `---runner2 (== module Runner2)
| | |
| | |--base.rb (class Proj2::Runner2::Base < Proj2::Base)
| | |--runner2.rb (class Proj2::Runner2::Runner < Proj2::Runner2::Base)
| | |--class1.rb (class Proj2::Runner2::Class1 < Proj2::Runner2::Base)
| | |--class2.rb (class Proj2::Runner2::Class2 < Proj2::Runner2::Base)
| | ...etc
| |--test
| | |
...etc
etc.
For any project, call it module ProjectX:
- the lib/projectX.rb file (class Project2::Runner) is the interface to a
user. It's the "main" runner class if you like.
- the ProjectX::Config class holds, surprise, all the configuration info
(usually read in from a text file)
- the ProjectX::Base clase holds all the constants and methods that are
shared throughout any particular ProjectX module
- the ProjectX::RunnerY::Base class holds all the constants and methods
that are shared throughout any particular ProjectX::RunnerY module
- the ProjectX::RunnerY::Runner class is the controller that iterates over
all the current module classes (the Class1, Class2,...ClassZ etc) and
invokes their methods
- the ProjectX::RunnerY::ClassZ class actually does something. )
I like the above setup because it scales quite well. For some projects each "runnerY"
contains another layer. It's made it really easy to isolate functionality and test it.
Anyway, the problem I'm having now is I decided to consolidate all my code on a single
machine and tack on the various locations of everything onto the RUBYLIB envar, i.e.
export RUBYLIB=$HOME/ruby/project1/lib:$HOME/ruby/project2/lib: etc...
So now when I do something like the following in project2/lib/runner1/base.rb,
require 'base/base'
module Proj2
module Runner1
class Base < Proj2::Base
# This is how the config gets passed along in
# Runner1 module and holds shared Runner1 code
end
end
end
and in project2/lib/runner1/runner.rb,
require 'runner1/base'
module Proj2
module Runner1
class Runner < Proj2::Runner1::Base
RUNNERS=[Class1,Class2,....,ClassZ]
def run
RUNNERS.each do |run_class|
r = run_class.new
r.config = self.config
r.run
end
end
end
end
end
and invoke the main script, ruby searches the RUBYLIB paths and finds the "base/base.rb"
for project*1* first (since it's listed first) and loads that file instead of the
"base/base.rb" for project*2*. Thus, I get errors like:
project2/lib/runner1/base.rb:4: uninitialized constant Project2::Base (NameError)
If I swap the listing in my RUBYLIB envar so that the project2 directory is listed first,
everything is honky dory and runs fine.
Phewph! If you've made it this far, the beer is on me if we ever meet.
My questions are:
1) can I keep my current directory structure (which, believe it or not, seems quite
logical to me) and avoid these file loading order problems?
2) IF not, how to fix this? I like the idea of generic file names and classes (config,
base) across projects so that those that follow can grok multiple projects after studying
the docs for just one.
Thanks for any insight.
cheers,
paulv