Configuration Files

Discussion in 'Ruby' started by John W. Long, Dec 18, 2003.

  1. John W. Long

    John W. Long Guest

    ------=_NextPart_000_0020_01C3C4E8.FCD59B60
    Content-Type: text/plain;
    charset="Windows-1252"
    Content-Transfer-Encoding: 7bit

    A while back someone submitted some code to this list for evaluating
    Configuration files in the style of:

    % cat test.config
    editor.spacespertab = 4
    toolbar.visible = true
    statusbar.visible = false
    . . .

    One of the great things about their code was that it enabled you to use ruby
    code within your configuration file so you could do things like this:

    %cat test2.config
    timer.alarm = 30 * 60 # set the timer to go off in 30 min

    Their implementation was pretty simple. Mainly they created a hash,
    splitting each line on /=/, and evaled the right side and stored the result
    in the hash with a key of the left side.

    I like this concept, but I wanted a better implementation. So I set to work
    and created the attached files. The attached now enables you to do this:

    %cat myprog.rb
    require 'config'

    $conf = Config.new

    # set up the default values and configuration structure:
    $conf.define %{
    editor.spacespertab = 8
    toolbar.visible = false
    toolbar.caption = "Tools"
    statusbar.visible = true
    copyright = "Copyright © 2003, John W. Long"
    }

    $conf.read File.new('test.config', 'r').read

    # you can even read multi line ruby snippets:
    $conf.read %{
    copyright = "Copyright © 2003, John W. Long
    All Rights Reserved"
    alarm = 30 * 60 #seconds
    }

    # now for getting the values, note the object hierarchy
    toolbar.caption = $conf.toolbar.caption.value
    toolbar.show if $conf.toolbar.visible.value
    aboutbox.copyright = $conf.copyright.value
    . . .

    You get the idea.

    A couple of advantages that this has over the previous concept:
    -- You define the default values and create the config structure at the same
    time
    -- Because you define a config structure if someone makes a typo in the
    config file misspelling "toolbar" as "tolbar" an error will be thrown
    (eventually it will even tell you what line the error was on)
    -- You access the values from the config file in almost the same way that
    they are written into the config file on your Config object

    I didn't attach the unit tests of this version because the code still
    relatively crude. Once I get things worked out a bit I may post it on
    RubyForge.

    Feedback is appreciated.

    ___________________
    John Long
    www.wiseheartdesign.com

    ------=_NextPart_000_0020_01C3C4E8.FCD59B60
    Content-Type: application/octet-stream;
    name="cleanobject.rb"
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: attachment;
    filename="cleanobject.rb"

    class CleanObject=0A=
    alias :__extend__ :extend=0A=
    alias :__instance_eval__ :instance_eval=0A=
    def initialize=0A=
    methods.each { |s|=0A=
    symbol =3D s.intern=0A=
    next if s =3D~ /^__.*__$/=0A=
    next if [:instance_eval].include?(symbol)=0A=
    instance_eval %{ undef :#{symbol.to_s} }=0A=
    }=0A=
    instance_eval %{ undef :instance_eval }=0A=
    end=0A=
    end=0A=

    ------=_NextPart_000_0020_01C3C4E8.FCD59B60
    Content-Type: application/octet-stream;
    name="config.rb"
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: attachment;
    filename="config.rb"

    require "cleanobject"=0A=
    =0A=
    class Config=0A=
    module Node=0A=
    attr_accessor :__defining__, :__children__, :value=0A=
    def __defining__=3D(value)=0A=
    @__children__ ||=3D Hash.new=0A=
    @__children__.each{ |s, v|=0A=
    v.__defining__ =3D value=0A=
    }=0A=
    @__defining__ =3D value=0A=
    end=0A=
    def method_missing(symbol, *args)=0A=
    @__children__ ||=3D Hash.new=0A=
    if @__defining__ =0A=
    s =3D symbol.to_s=0A=
    s =3D $1 if s =3D~ /(.*)=3D$/=0A=
    __instance_eval__ <<-CODE=0A=
    def #{s}=3D(value)=0A=
    unless @__children__.has_key?:)#{s})=0A=
    n =3D CleanObject.new=0A=
    n.__extend__(Node)=0A=
    n.__defining__ =3D true=0A=
    @__children__[:#{s}] =3D n=0A=
    end=0A=
    @__children__[:#{s}].value =3D value=0A=
    end=0A=
    def #{s}=0A=
    unless @__children__.has_key?:)#{s})=0A=
    n =3D CleanObject.new=0A=
    n.__extend__(Node)=0A=
    n.__defining__ =3D true=0A=
    @__children__[:#{s}] =3D n=0A=
    end=0A=
    @__children__[:#{s}]=0A=
    end=0A=
    CODE=0A=
    __send__ symbol, *args=0A=
    else=0A=
    raise NoMethodError, "undefined method `#{symbol}'"=0A=
    end=0A=
    end=0A=
    end=0A=
    =0A=
    def initialize=0A=
    @root =3D CleanObject.new=0A=
    @root.__extend__(Node)=0A=
    end=0A=
    =0A=
    def define(object)=0A=
    @root.__defining__ =3D true=0A=
    @root.__instance_eval__(prep(object.to_s))=0A=
    @root.__defining__ =3D false=0A=
    end=0A=
    =0A=
    def read(object)=0A=
    @root.__instance_eval__(prep(object.to_s))=0A=
    end=0A=
    =0A=
    def prep(string)=0A=
    code =3D ""=0A=
    string.each { |line|=0A=
    c =3D line.strip=0A=
    c =3D "self.#{c}" unless c =3D~ /^self\./ or c !~ /=3D/=0A=
    code << "#{c}\n"=0A=
    }=0A=
    code=0A=
    end=0A=
    =0A=
    def method_missing(symbol, *args)=0A=
    @root.__send__ symbol, *args=0A=
    end=0A=
    end=0A=

    ------=_NextPart_000_0020_01C3C4E8.FCD59B60--
     
    John W. Long, Dec 18, 2003
    #1
    1. Advertising

  2. John W. Long

    Hal Fulton Guest

    Gavin Sinclair wrote:
    > I have a really cool way of specifying config files. It goes like this:
    >
    > plugin:
    > player:
    > class: CommandPlayer
    > command: mpg123
    >
    > However, the code for parsing this is a secret :)


    Why, you lucky stiff...

    Hal
     
    Hal Fulton, Dec 18, 2003
    #2
    1. Advertising

  3. Hi,

    > From: "John W. Long" <>
    > Sent: Thursday, December 18, 2003 1:01 PM


    > I like this concept, but I wanted a better implementation. So I set to work
    > and created the attached files. The attached now enables you to do this:


    Interesting.

    > You get the idea.
    >
    > A couple of advantages that this has over the previous concept:
    > -- You define the default values and create the config structure at the same
    > time
    > -- Because you define a config structure if someone makes a typo in the
    > config file misspelling "toolbar" as "tolbar" an error will be thrown
    > (eventually it will even tell you what line the error was on)
    > -- You access the values from the config file in almost the same way that
    > they are written into the config file on your Config object


    Sorry for not related to your great work, but let me introduce mine.
    From the similar point of view, I wrote my own version for soap4r, too.

    http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/lib/soap4r/lib/soap/property.rb
    http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/lib/soap4r/test/soap/test_property.rb

    My concept is; define property tree and lock to avoid typo.
    And I want to "hook" property changes to reconfigure something
    and value-space checking.


    require 'soap/property'

    ###
    ## property definition
    #
    prop = SOAP::property.new
    prop.add_hook("protocol.http.proxy") do |name, value|
    # check value here...
    puts "(proxy) #{name}: #{value}"
    end

    sitebag = prop["protocol.http.sites"] = SOAP::property.new

    prop.lock(true) # true: locks properties cascading.
    sitebag.unlock # unlock sitebag property node.

    ###
    ## assigning values
    #
    prop["protocol.http.proxy"] = "myproxy:8080"
    prop["protocol.http.sites"] << "foo"
    prop["protocol.http.sites"] << "bar"

    p prop["protocol.http.sites.0"] # => "foo"
    p prop["protocol.http.sites.1"] # => "bar"
    p prop["protocol.http.sites"].values # => ["foo", "bar"]

    # typo
    prop["protocooool.http.proxy"] = "foo" #=> error
    prop["protocol.htttttp.proxy"] = "foo" #=> error
    prop["protocol.http.proxxxxy"] = "foo" #=> error



    But mine cannot read property file after property definition for now
    though it may be able to do easily.

    How do you think hooking feature of configuration files?

    Regards,
    // NaHi
     
    NAKAMURA, Hiroshi, Dec 18, 2003
    #3
  4. John W. Long

    Ara.T.Howard Guest

    On Thu, 18 Dec 2003, Gavin Sinclair wrote:

    > Date: Thu, 18 Dec 2003 13:51:45 +0900
    > From: Gavin Sinclair <>
    > Newsgroups: comp.lang.ruby
    > Subject: Re: Configuration Files
    >
    > >
    > > That sounds like a great config file system. I rolled my own basic one
    > > not too long ago, but having seen this, I'm thinking I'd replace it. :)
    > >
    > > One thing I did in mine which I kind of like, but has drawbacks, is a
    > > sort of grouping, where:
    > > [plugin.player]
    > > class=CommandPlayer
    > > command=mpg123
    > >
    > > is the same as:
    > > plugin.player.class=CommandPlayer
    > > plugin.player.command=mpg123
    > >

    >
    > I have a really cool way of specifying config files. It goes like this:
    >
    > plugin:
    > player:
    > class: CommandPlayer
    > command: mpg123
    >
    > However, the code for parsing this is a secret :)


    i'm with you (and use it myself), but the above is problematic if you actually
    want non-programmers to be able to _configure_ a system. for example, how long
    do you think it would take a non-programmer to debug this?

    file config.rb:
    ========
    require 'yaml'
    config = <<-txt
    plugin:
    player:
    class: CommandPlayer
    command: mpg123
    txt
    YAML::load(config)


    ~/eg/ruby > ruby config.rb
    /data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load': parse error on line 3, col 12: ` command: mpg123' (ArgumentError)
    from /data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load'
    from config.rb:8

    -a
    --

    ATTN: please update your address books with address below!

    ===============================================================================
    | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
    | PHONE :: 303.497.6469
    | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
    | STP :: http://www.ngdc.noaa.gov/stp/
    | NGDC :: http://www.ngdc.noaa.gov/
    | NESDIS :: http://www.nesdis.noaa.gov/
    | NOAA :: http://www.noaa.gov/
    | US DOC :: http://www.commerce.gov/
    |
    | The difference between art and science is that science is what we
    | understand well enough to explain to a computer.
    | Art is everything else.
    | -- Donald Knuth, "Discover"
    |
    | /bin/sh -c 'for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done'
    ===============================================================================
     
    Ara.T.Howard, Dec 18, 2003
    #4
  5. On Thursday 18 December 2003 10:11 am, Ara.T.Howard wrote:
    >
    > i'm with you (and use it myself), but the above is problematic if you
    > actually want non-programmers to be able to _configure_ a system. for
    > example, how long do you think it would take a non-programmer to debug
    > this?
    >
    > file config.rb:
    > ========
    > require 'yaml'
    > config = <<-txt
    > plugin:
    > player:
    > class: CommandPlayer
    > command: mpg123
    > txt
    > YAML::load(config)
    >
    >
    > ~/eg/ruby > ruby config.rb
    > /data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load': parse error on line 3,
    > col 12: ` command: mpg123' (ArgumentError) from
    > /data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load'
    > from config.rb:8
    >
    > -a


    The above throws an error for you? Man, a lot has been fixed since 1.8.0.

    Anyhow, I'm with ya. So, let's solve the problem. Would it help if you could
    hook the parse error handler to generate your own nice errors? We can also
    start to provide more specific parse error messages, though this can be
    needly on some edges.

    ~/eg/ruby > ruby config.rb
    Error in configuration file. See line 4.

    2 player:
    3 class: CommandPlayer
    Here!! -> 4 command: mpg123

    Yeah, the indentation on that line needs to be trimmed
    a bit. Just line it up cozy with the line above it.

    _why
     
    why the lucky stiff, Dec 18, 2003
    #5
  6. John W. Long

    Gennady Guest

    why the lucky stiff wrote:
    > On Thursday 18 December 2003 10:11 am, Ara.T.Howard wrote:
    >
    >>i'm with you (and use it myself), but the above is problematic if you
    >>actually want non-programmers to be able to _configure_ a system. for
    >>example, how long do you think it would take a non-programmer to debug
    >>this?
    >>
    >>file config.rb:
    >>========
    >>require 'yaml'
    >>config = <<-txt
    >>plugin:
    >> player:
    >> class: CommandPlayer
    >> command: mpg123
    >>txt
    >>YAML::load(config)
    >>
    >>
    >>~/eg/ruby > ruby config.rb
    >>/data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load': parse error on line 3,
    >>col 12: ` command: mpg123' (ArgumentError) from
    >>/data/ruby-1.8.0//lib/ruby/1.8/yaml.rb:39:in `load'
    >> from config.rb:8
    >>
    >>-a

    >
    >
    > The above throws an error for you? Man, a lot has been fixed since 1.8.0.



    I use YAML form the latest ruby-1.8.1preview3, and when I
    copy-and-pasted the code from Ara's post, I got the same error. The
    problem was that there was a TAB character after "player:" (was it
    deliberately implanted to prove the point ;-) ?), and it caused the
    problem. If you remove TAB or convert it to spaces, everything starts
    working fine.

    _why, could you just ignore trailing spaces and tabs?

    Gennady.

    >
    > Anyhow, I'm with ya. So, let's solve the problem. Would it help if you could
    > hook the parse error handler to generate your own nice errors? We can also
    > start to provide more specific parse error messages, though this can be
    > needly on some edges.
    >
    > ~/eg/ruby > ruby config.rb
    > Error in configuration file. See line 4.
    >
    > 2 player:
    > 3 class: CommandPlayer
    > Here!! -> 4 command: mpg123
    >
    > Yeah, the indentation on that line needs to be trimmed
    > a bit. Just line it up cozy with the line above it.
    >
    > _why
    >
    >
     
    Gennady, Dec 18, 2003
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Luis Esteban Valencia

    Change configuration files after installation.

    Luis Esteban Valencia, Jul 12, 2005, in forum: ASP .Net
    Replies:
    0
    Views:
    615
    Luis Esteban Valencia
    Jul 12, 2005
  2. Ashish

    ASP.Net 2.0 Configuration files.

    Ashish, Dec 12, 2005, in forum: ASP .Net
    Replies:
    4
    Views:
    3,166
    Scott Allen
    Dec 12, 2005
  3. =?Utf-8?B?U2F0aXNoIEJhYnUgRGFzYXJp?=

    The application's configuration files must contain 'trust' section

    =?Utf-8?B?U2F0aXNoIEJhYnUgRGFzYXJp?=, Jan 11, 2006, in forum: ASP .Net
    Replies:
    0
    Views:
    1,835
    =?Utf-8?B?U2F0aXNoIEJhYnUgRGFzYXJp?=
    Jan 11, 2006
  4. Mark
    Replies:
    0
    Views:
    861
  5. Stephan Koser
    Replies:
    3
    Views:
    693
    Juha Laiho
    Dec 7, 2004
Loading...

Share This Page