Newbee question. Runtime classloading

Hello, I’m Java developer and now explore Ruby for increasing
programming knowledge. I want to understand runtime class loading

I have an application:
/lib/parser_factory.rb
class ParserFactory
end

/lib/parser_format_x.rb
class ParserFormatA
end
/lib/parser_format_y.rb
class ParserFormatB
end
/lib/parser_format_z.rb
class ParserFormatC
end

ParserFactory gets file and decides which parser to initialize and use.
I have such code in ParserFactory:
clz = “ParserFormat#{@version}”
parser_impl = Kernel.const_get(clz)
and I get an error:

Test-unit version : 2.0.1 loaded

  1. Error:
    test_parsing(ParserTest):
    NameError: uninitialized constant Kernel::ParserPlanFormat1_0
    D:/Ruby_1_86_27/lib/ruby/gems/1.8/gems/rake-0.8.1/lib/rake.rb:2237:in
    `const_missing’

I don’t get how I have to specify path to class. I’ve tried it as in
Java: “parsers/SomeClass”, but it didn’t help :slight_smile:

Also i’m exploring Ruby on Rails and
Kernel.const_get(“MyFavouriteModelClass”) works there.

Please explain or give guide to read.

Ruby needs to interpret the file where the class is defined before you
can use it. Interpreting the file has the side-effect of defining the
constant, which is in turn a side-effect of the class keyword.

As you surely know that is typically done with require, and if the
parsers are unknown beforehand you can just use Dir[] with a glob to
require whatever has some filename pattern in a loop or something. I
mean, require is a method you can call it wherever you want.

Rails does not need that because it offers class/module autoloading:
if the filename is named after the class using some common sense
conventions, and lives in some of the standard directories,
app/something, lib, etc., then the dependencies mechanism goes and
loads it for you the first time the constant is used. That is
accomplished basically via a Ruby hook called const_missing that is
triggered when that initial failure happens.

Xavier N. wrote:

Ruby needs to interpret the file where the class is defined before you
can use it.
I understand it.

Interpreting the file has the side-effect of defining the
constant, which is in turn a side-effect of the class keyword.
don’t get idea with side-effects

As you surely know that is typically done with require, and if the
parsers are unknown beforehand you can just use Dir[] with a glob to
require whatever has some filename pattern in a loop or something. I
mean, require is a method you can call it wherever you want.
Something like:
require “parsers/parser_format#{@format_version.gsub(/./, ‘’)}"
clz = "ParserFormat#{@format_version.gsub(/./, '
’)}”
parser_impl = Kernel.const_get(clz)
works nicely!

Rails does not need that because it offers class/module autoloading:
if the filename is named after the class using some common sense
conventions, and lives in some of the standard directories,
app/something, lib, etc., then the dependencies mechanism goes and
loads it for you the first time the constant is used. That is
accomplished basically via a Ruby hook called const_missing that is
triggered when that initial failure happens.
Thanks!

On Thu, Feb 11, 2010 at 10:32 AM, Sergey S.
[email protected] wrote:

Interpreting the file has the side-effect of defining the
constant, which is in turn a side-effect of the class keyword.
don’t get idea with side-effects

Ah yes let me explain a bit more. Doing

class C
end

as far as constants and class names is concerned is similar to doing
this

C = Class.new

By the way, top-level constants are stored in Object. Your code works
with Kernel because it is mixed in Object and constant lookup goes up,
but Object would be the natural place to invoke const_get on.

Something like:
require “parsers/parser_format#{@format_version.gsub(/./, ‘')}"
clz = "ParserFormat#{@format_version.gsub(/./, '
’)}”
parser_impl = Kernel.const_get(clz)
works nicely!

That’s right.

Xavier N. wrote:

as far as constants and class names is concerned is similar to doing
this

C = Class.new

Nice opportunity to make silly mistakes, especially if you work in group
with low communication.
Thank you for explaining me the material.

Xavier N. wrote:

Excuse me for disturbing you again.
I have pretty close question.
I don’t get how to locate files inside ruby-project. In Rails I’ve used
‘RAILS_ROOT’ and it was clear to me how does it work.
And what about usual ruby app?

  1. NetBeans generated TestUnit gets this string:
    $:.unshift File.join(File.dirname(FILE),’…’,‘lib’)
    What for?

  2. My project structure:
    project:
    \lib
    \lib\main.rb
    \lib\some_file.rb
    \lib\parsers\yaml_parser.rb
    \data
    \data\yaml_parser_config.yml
    \data\source.scv
    \data\Format_1.0.yml

I keep config files in ‘data’ catalog.

Is it good way to ref these resources in this way:

#Some TestUnit
def test_read
@conf = YAML.load_file( ‘data/Format_1.0.yml’ ) #Good or bad?
assert_equal(Hash, @conf.class)
assert_equal(“Dates”,@conf[‘Dates’][‘class’])
assert_equal([“id”, “descr”, “rough_est”, “accurate_est”, “spent”,
“left”, “resource_id”, “begin”, “end”],@conf[‘Tasks’][‘fields_ordered’])
end

or in some Parser class:
@parser_config = YAML.load_file(‘data/Format_1.0.yml’ ) )

Or I have to use some ‘magic’ ruby variables?

Yes you have the FILE keyword. That evaluates to the filename of
the file that contains it. So, its value is per file.

In a case like yours it is normal to rely on FILE and a known
project structure to go for stuff thus avoiding config files.