Newbee question. Runtime classloading

S

Sergey Sheypak

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 :)

Also i'm exploring Ruby on Rails and
Kernel.const_get("MyFavouriteModelClass") works there.

Please explain or give guide to read.
 
X

Xavier Noria

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

Sergey Sheypak

Xavier said:
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!
 
X

Xavier Noria

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

Sergey Sheypak

Xavier said:
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.
 
S

Sergey Sheypak

Xavier Noria 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?
 
X

Xavier Noria

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.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top