Eval'ing a config file

Hi,

I have a number of LDAP-related programs that I want to share a common
config file in which certain variables are stored, such as server, port,
password, etc. in the following form.

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

I want the parser to ignore blank lines and those that start with a ‘#’.

The code that I’ve come up with is this:

File.open("/etc/ldaprc") do |f|
f.each do |line|
next if line =~ /^(#|$)/
var, val = /^(.+?)\s*=\s*(.+)$/.match(line)[1,2]
eval "#{var} = ‘#{val}’"
end
end

It works, but looking at it, it looks too much like code I might have
written for Perl some years ago. Is there a more Rubyesque way to do
this? In particular, is there a cleaner way than eval?

Ian

···


Ian Macdonald | Parts that positively cannot be assembled
System Administrator | in improper order will be.
ian@caliban.org |
http://www.caliban.org |
>

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

Change this to:

LDAP_HOST = ‘foo.bar.com
LDAP_PORT = 389
LDAP_PASS = ‘foobar’

File.open(“/etc/ldaprc”) do |f|
f.each do |line|
next if line =~ /^(#|$)/
var, val = /^(.+?)\s*=\s*(.+)$/.match(line)[1,2]
eval “#{var} = ‘#{val}’”
end
end

Change this to:

require ‘/etc/ldaprc’

It’s not the best solution for everything, but it’s about as simple as
roading a configuration file can get!

Paul

···

On Wed, Apr 02, 2003 at 02:15:15AM +0900, Ian Macdonald wrote:

Hi,

I have a number of LDAP-related programs that I want to share a common
config file in which certain variables are stored, such as server, port,
password, etc. in the following form.

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

I want the parser to ignore blank lines and those that start with a ‘#’.

The code that I’ve come up with is this:

File.open(“/etc/ldaprc”) do |f|
f.each do |line|
next if line =~ /^(#|$)/
var, val = /^(.+?)\s*=\s*(.+)$/.match(line)[1,2]
eval “#{var} = ‘#{val}’”
end
end

It works, but looking at it, it looks too much like code I might have
written for Perl some years ago. Is there a more Rubyesque way to do
this? In particular, is there a cleaner way than eval?

Just for the eval part (IMHO the whole problem would be handled better
by some module in the RAA ;-):

Object.const_set(“AA”, 12)
=> 12
AA
=> 12

···

On Wed, Apr 02, 2003 at 02:15:15AM +0900, Ian Macdonald wrote:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Turn right here. No! NO! The OTHER right!

dear ian,
there is a very good project on raa called xml-config i suggest you check that out.

regards
warren brian noronha
warren@freedomink.org

···

On Wed, 2 Apr 2003 02:15:15 +0900 Ian Macdonald ian@caliban.org wrote:

Hi,

I have a number of LDAP-related programs that I want to share a common
config file in which certain variables are stored, such as server, port,
password, etc. in the following form.

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

I want the parser to ignore blank lines and those that start with a ‘#’.

The code that I’ve come up with is this:

File.open(“/etc/ldaprc”) do |f|
f.each do |line|
next if line =~ /^(#|$)/
var, val = /^(.+?)\s*=\s*(.+)$/.match(line)[1,2]
eval “#{var} = ‘#{val}’”
end
end

It works, but looking at it, it looks too much like code I might have
written for Perl some years ago. Is there a more Rubyesque way to do
this? In particular, is there a cleaner way than eval?

Ian

Ian Macdonald | Parts that positively cannot be assembled
System Administrator | in improper order will be.
ian@caliban.org |
http://www.caliban.org |
>

Well I am new to Ruby, been playing around for about three weeks now
(well, whenever I can think after work anyway) and while playing around
I put together a very primitive (in my opinion) way of making plugins.
Well calling it plugins probably is the wrong thing to do - let’s say
that I like distributing my code in small files all over the place
because I hate scrolling and I like putting it together again
programmatically.
It was usefull for what I’m building (a dyndns refresh utility) but I
want to ask for your ideas on how to do this better (especially on
clean-up).
Here’s the deal:

By convention I define a directory (say ./lib, where . is the
installation directory of the app - find me a way to always calculate it
correctly - wherever the script is called - and I will be happy) where I
dump my “plugins”.
What the so called “plugin” is, is actually a file (with a
pick-and-choose extension, call it .module) with Ruby code in it.
Needs to be well formed, and not be a class or module definition (I’m
not sure about that, I haven’t tried it actually).
What I actually do, is define a method in there like this:

def detect()
#some really nifty stuff
end

In the actual app I read all files in the ./lib directory with a .module
extension and use the filenames as the “plugin” names.
So each file is a way of detection (to use the semantics of my utility)
that defines a detect() method and in the app I have a method that reads
the file and builds a module out of it.

def address(methodname)
#blabla
#construct the full pathname to the module file (fullname)
if @methods.include?(fullname)
#@methods is just an array where the contents of ./lib were read
cntnt=IO.readlines(fullname).join
IPDetector.module_eval(cntnt)#IPDetector is the app
rtrn = detect()
else
raise IPDetectorError,"Unsupported method “#{methodname}”!"
end
return rtrn
end

Now, this works, but if I call address a couple of times with the
different methods I’ll end up with a couple of anonymous modules.
Is there any way to clean them up? Some way to remove a mixin from a
class? From what I saw in Module the method I would use (
Module#remove_method ) is private so no cleaning up.
It would be nice to find a way to remove the module - then I can define
constants in the module file without fear of redefinitions.
(Should not forget: I’m still using Ruby 1.6.8)
This whole thing is rather slow - but some kind of module pooling can be
done if I use an empty class (just like in the module_eval example ) for
every module file and keep them around, tracking which modules have been
created.
Any comments?
V.-

···


http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.

Thanks, I didn’t know about that method.

And yes, such a module is exactly what I have in mind.

Ian

···

On Thu 03 Apr 2003 at 02:34:44 +0900, Mauricio Fernández wrote:

Just for the eval part (IMHO the whole problem would be handled better
by some module in the RAA ;-):

Object.const_set(“AA”, 12)
=> 12
AA
=> 12


Ian Macdonald | Always remember that you are unique. Just
System Administrator | like everyone else.
ian@caliban.org |
http://www.caliban.org |
>

Personally, I’d sacrifice the equals sign, make it a colon, and call it a yaml
file. YAML would then take care of the primitives. You could even iterate
through and create constants if you don’t feel like using a configuration
hash, or create a type to handle it upon loading. You’d even have things
like aliases/anchors, without having to homebrew them.

A matter of taste, I suppose.

// Bruce

···

On Wednesday 02 April 2003 12:34 pm, Mauricio Fernández wrote:

On Wed, Apr 02, 2003 at 02:15:15AM +0900, Ian Macdonald wrote:

Hi,

I have a number of LDAP-related programs that I want to share a common
config file in which certain variables are stored, such as server, port,
password, etc. in the following form.

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

I want the parser to ignore blank lines and those that start with a ‘#’.

The code that I’ve come up with is this:

File.open(“/etc/ldaprc”) do |f|
f.each do |line|
next if line =~ /^(#|$)/
var, val = /^(.+?)\s*=\s*(.+)$/.match(line)[1,2]
eval “#{var} = ‘#{val}’”
end
end

It works, but looking at it, it looks too much like code I might have
written for Perl some years ago. Is there a more Rubyesque way to do
this? In particular, is there a cleaner way than eval?

Just for the eval part (IMHO the whole problem would be handled better

by some module in the RAA ;-):

Wed, 2 Apr 2003 03:00:54 +0900, Paul Brannan wrote:

LDAP_HOST = foo.bar.com
LDAP_PORT = 389
LDAP_PASS = foobar

Change this to:

LDAP_HOST = ‘foo.bar.com
LDAP_PORT = 389
LDAP_PASS = ‘foobar’
[snip]
Change this to:

require ‘/etc/ldaprc’

Shouldn’t it be load' rather than require’? `require’ is looking for a
file ending with .rb (as of 1.6.8)

···


Three shalt be the number thou shalt count, and the number
of the counting shalt be three.

This may not address your question-- but certainly the easiest way I’ve ever
seen plugins accomplished in Ruby follows the following (fairly popular)
pattern:

–snip–

class Plugin
AVAILABLE =
def Plugin.inherited plugin_class
AVAILABLE << plugin_class
end
end

class PluginA < Plugin; end
class PluginB < Plugin; end
class PluginC < Plugin; end

–snip–

If you’re looking for an advanced plugin framework for Ruby, I’d suggest you
check out FreeBase (rubyide.org), which
is really sweet.

Hope that helps,
// Bruce
( who’s odd enough to think that a YAML type for ruby-lang posts/replies would
be awesome )

···

On Thursday 03 April 2003 06:25 pm, Damphyr wrote:

Well I am new to Ruby, been playing around for about three weeks now
(well, whenever I can think after work anyway) and while playing around
I put together a very primitive (in my opinion) way of making plugins.
Well calling it plugins probably is the wrong thing to do - let’s say
that I like distributing my code in small files all over the place
because I hate scrolling and I like putting it together again
programmatically.

I’m not sure how relevant this is to your application, but you could have a
look at the way ruby-dbi handles loading of DBD drivers for different
databases, which are effectively dbi “plugins”. The user chooses the
appropriate DBD driver with a selection string:

db = DBI.connect(“dbi:Mysql:test”,“root”,“”)
^^^^^

That string locates the file (DBD/Mysql/Mysql.rb) which contains a module
(DBI::DBD::Mysql). You don’t worry about cross-pollution of the namespaces,
because each module for each database driver has its own namespace.

The other thing which might be useful for you is singleton classes:

class Foo; end
module Bar; def test; puts “hello” end end

a = Foo.new # some object
a.extend Bar # mixin module to this instance only
a.test

When you have finished with object ‘a’ you just forget the reference and it
gets garbage collected. Then you can create a new object and mixin a
different module next time. Perhaps this helps get over the need to remove a
mixin.

Regards,

Brian.

···

On Fri, Apr 04, 2003 at 08:25:25AM +0900, Damphyr wrote:

In the actual app I read all files in the ./lib directory with a .module
extension and use the filenames as the “plugin” names.
So each file is a way of detection (to use the semantics of my utility)
that defines a detect() method and in the app I have a method that reads
the file and builds a module out of it.

def address(methodname)
#blabla
#construct the full pathname to the module file (fullname)
if @methods.include?(fullname)
#@methods is just an array where the contents of ./lib were read
cntnt=IO.readlines(fullname).join
IPDetector.module_eval(cntnt)#IPDetector is the app
rtrn = detect()
else
raise IPDetectorError,“Unsupported method "#{methodname}"!”
end
return rtrn
end

Now, this works, but if I call address a couple of times with the
different methods I’ll end up with a couple of anonymous modules.
Is there any way to clean them up? Some way to remove a mixin from a
class?

Brian Candler wrote:

In the actual app I read all files in the ./lib directory with a .module
…Removing unnecessary traffic volume…
class?

I’m not sure how relevant this is to your application, but you could have a
look at the way ruby-dbi handles loading of DBD drivers for different
databases, which are effectively dbi “plugins”. The user chooses the
appropriate DBD driver with a selection string:

db = DBI.connect(“dbi:Mysql:test”,“root”,“”)
^^^^^

That string locates the file (DBD/Mysql/Mysql.rb) which contains a module
(DBI::DBD::Mysql). You don’t worry about cross-pollution of the namespaces,
because each module for each database driver has its own namespace.

I’ll need to check it out, thanks for the reference.

The other thing which might be useful for you is singleton classes:

class Foo; end
module Bar; def test; puts “hello” end end

a = Foo.new # some object
a.extend Bar # mixin module to this instance only
a.test

When you have finished with object ‘a’ you just forget the reference and it
gets garbage collected. Then you can create a new object and mixin a
different module next time. Perhaps this helps get over the need to remove a
mixin.

That would be similar to the idea of creating a pool by creating empty
classes.
My original requirement was to be able to drop in new modules at any
time and be able to detect the change even at runtime (my example does
not need runtime detection of drop-in modules, being a script so I
restrained itself to startup detection :slight_smile: )
So that means probably a clever mechanism for require-ing all files in a
plugin/ directory or somesuch hack (and detecting changes to that
directory at runtime). But then, how do we change the parameter to
extend to be able to say something like

module = myclass.very_clever_method_takes_string_returns_module(modulename)
a = Foo.new
a.extend module

In essence what I want is to be able to evaluate a string and find out
if it matches a symbol (or the texual representation of a symbol to be
more accurate) in my class space, and then return that symbol or the
object/class/module for that symbol
V.-

···

On Fri, Apr 04, 2003 at 08:25:25AM +0900, Damphyr wrote:


http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.

But then, how do we change the parameter to
extend to be able to say something like

module = myclass.very_clever_method_takes_string_returns_module(modulename)
a = Foo.new
a.extend module

That should work just fine - you can pass modules around like any other
object.

In essence what I want is to be able to evaluate a string and find out
if it matches a symbol (or the texual representation of a symbol to be
more accurate) in my class space, and then return that symbol or the
object/class/module for that symbol

symbols are global, so “symbol in my class space” doesn’t quite make sense.

Do you mean “does object X have a method of name S”? There are a number of
introspection techniques such as
self.class.instance_methods
self.singleton_methods

but maybe all you want is something simple like

sym = :foo
meth = begin
self.method(sym)
rescue NameError
nil
end

This will give you a method object if the object ‘self’ contains a method of
name . You can later call it using meth.call(args…)

Regards,

Brian.

···

On Sat, Apr 05, 2003 at 08:36:12PM +0900, Damphyr wrote:

Brian Candler wrote:

But then, how do we change the parameter to
extend to be able to say something like

module = myclass.very_clever_method_takes_string_returns_module(modulename)
a = Foo.new
a.extend module

That should work just fine - you can pass modules around like any other
object.

In essence what I want is to be able to evaluate a string and find out
if it matches a symbol (or the texual representation of a symbol to be
more accurate) in my class space, and then return that symbol or the
object/class/module for that symbol

symbols are global, so “symbol in my class space” doesn’t quite make sense.
well a symbol I know that belongs (should belong? wants to belong? will
belong? :slight_smile: ) to my classes - I keep mixing words and definitions !very bad!.
Do you mean “does object X have a method of name S”? There are a number of
introspection techniques such as
self.class.instance_methods
self.singleton_methods
Actually what I mean is:

I have a file MyModule.rb with content:

Module MyModule
def some_nice_method()
end
end

and another file MyModule2.rb with content

Module MyModule2
def some_nice_method()
end
end

and somewhere else I have another script that should (or that I would
like it to) be able to take in these files, and any later additions to
that pre-specified “plugin repository” and do something like:

Class Experiment
def doMethod(nameofmethod)
self.extend nameofmethod
end
end

the above does not execute (I know, I tried :)) ) and it would be silly
if it did (I guess I’m a bit silly for trying :slight_smile: ).
And that is what that

very_clever_method_takes_string_returns_module(modulename)

should do. It’s me as a programmer who knows that MyModule.rb has
implicitly the same name as MyModule - now, how can I get the module by
the string of it’s name? (this sounds nice :slight_smile: the string of it’s name :slight_smile: )
Probably impossible, (but I haven’t looked in DBI or Freebase yet to see
how they do their plugins) so I went ahead and did module_eval on raw
code. So yes, I’m essentially adding a new method to my class, not a
module. What happens when I call module_eval again? From what I gather,
the method gets overwritten.

OK, I have another description for my question:
I know beforehand that there is going to be a module, I have a way to
determine the module’s name as a string in runtime, I have a way to load
that module into memory, but I have no way to call/extend/include the
module because I don’t know it’s name (or I can’t create a Module object
from it’s name).
Did I confuse everyone? I’ll go away now and look some more…
:slight_smile:
V.-

···

On Sat, Apr 05, 2003 at 08:36:12PM +0900, Damphyr wrote:


http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.

implicitly the same name as MyModule - now, how can I get the module by
the string of it's name? (this sounds nice :slight_smile: the string of it's name :slight_smile: )

Object::const_get

pigeon% ruby -e 'p Object.const_get("Kernel").class'
Module
pigeon%

Guy Decoux

Ah - I thought you were going to return the module itself, not a string of
the module name. See Guy’s response about const_get.

There is always the string version of eval:

eval “extend #{nameofmodule}”

but I feel that to be rather kludgy, if not downright dangerous.

Something else which might be useful in your toolbox is Kernel#autoload:

file1.rb

module Foo
def bar
puts “hello”
end
end

main.rb

autoload :Foo, “file1.rb”
a=“abc”
b=“def”
a.extend Foo
b.extend Foo
a.bar #>> “hello”
b.bar #>> “hello”

This has the advantage of only pulling in the source files when required, as
long as you know in advance what the module names are.

Regards,

Brian.

···

On Sat, Apr 05, 2003 at 11:09:50PM +0900, Damphyr wrote:

Class Experiment
def doMethod(nameofmethod)
self.extend nameofmethod
end
end

the above does not execute (I know, I tried :)) ) and it would be silly
if it did (I guess I’m a bit silly for trying :slight_smile: ).
And that is what that

very_clever_method_takes_string_returns_module(modulename)

should do.

Brian Candler wrote:

Class Experiment
def doMethod(nameofmethod)
self.extend nameofmethod
end
end

the above does not execute (I know, I tried :)) ) and it would be silly
if it did (I guess I’m a bit silly for trying :slight_smile: ).
And that is what that

very_clever_method_takes_string_returns_module(modulename)

should do.

Ah - I thought you were going to return the module itself, not a string of
the module name. See Guy’s response about const_get.

And yes, I think I will subscribe to the belief that has Guy talk in
Ruby almost exclusively :slight_smile:

Something else which might be useful in your toolbox is Kernel#autoload:

Back to the manual…and back again.
This is very interesting. Combining const_get, autoload and a couple of
naming restrictions one can build some pretty flexible structures.
It’s nice that my initial enthusiasm about Ruby is being so handsomely
rewarded - I guess this’ll be one Saturday spent coding :))).
Thanks guys (although my girlfriend probably will hate you all).
V.-
P.S.
Guy says:

Object.const_get(“MyModule”)

and he’s right. The reference in the PP book puts const_get in Module
though - is there something hidden in there, or is it just an error in
the reference?

···

On Sat, Apr 05, 2003 at 11:09:50PM +0900, Damphyr wrote:


http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.

Object.const_get("MyModule")

and he's right. The reference in the PP book puts const_get in Module
though - is there something hidden in there, or is it just an error in
the reference?

#const_get is a method of Module, this mean that the class Object inherit
this method

Now the module is defined in Object, this mean that you must retrieve this
constant in Object. For example, if you want to retrieve the class
File::Stat you must search the constant "Stat" in the class File

pigeon% ruby -e 'p File.const_get("Stat")'
File::Stat
pigeon%

Guy Decoux

ts wrote:

“D” == Damphyr damphyr@freemail.gr writes:

Object.const_get(“MyModule”)

and he’s right. The reference in the PP book puts const_get in Module
though - is there something hidden in there, or is it just an error in
the reference?

#const_get is a method of Module, this mean that the class Object inherit
this method
But I thought it was the other way around: Module inherits from Object
and not Object from Module. Object is the root of our hierarchy.

var@dfnnb023 ~
$ ruby -e ‘puts Module.superclass’
Object

var@dfnnb023 ~
$ ruby -e ‘puts Object.superclass’
nil

Now the module is defined in Object, this mean that you must retrieve this
constant in Object. For example, if you want to retrieve the class
File::Stat you must search the constant “Stat” in the class File

This was clear as soon as I realized that Classes and Modules are
constants and applied some namespacing logic.
Doing

puts Object.methods

gives a whole lot more than that listed in the reference, including the
const_get method.
And then I thought that Object is a Class and Class is derived from
Module and somewhere there I got confused because Module is derived from
Object and Object…this is an endless cycle (I’m only semi-joking :slight_smile: )
V.-

···


http://www.freemail.gr - äùñåÜí õðçñåóßá çëåêôñïíéêïý ôá÷õäñïìåßïõ.

$ ruby -e 'puts Module.superclass'
Object

pigeon% ruby -e 'p Object.class.ancestors'
[Class, Module, Object, Kernel]
pigeon%

Guy Decoux