Forum: Ruby Ruby as a configuration language

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Tapio K. (Guest)
on 2007-02-08 18:00
(Received via mailing list)
Hi

I'm writing an automated software building framework in Ruby. The
software
compiles software packages from source code using human-written
profiles. The
profiles should be extremely easy to read and write by humans (and easy
to
parse by a program). I wish I could write the profiles in Ruby to avoid
creating yet another advanced configuration language. But... it seems
that I
might not be able to do that. What I want is something like this:

# An example package description file. The actual building commands are
# written in a bash script.
name = "gcc"
version = "4.1.1"
title = " "GNU Compiler Collection"
description = "..."

archive {
    localname = "#{name}-#{version}.tar.bz2"
    originalname = "#{name}-#{version}.tar.gz"
    baseurl "ftp://ftp.gnu.org/gnu/gcc/#{version}/"
    baseurl "ftp.mirror.site/gnu/gcc/#{version}/"
    convert = "gz-bz2"
}

# Optional components
feature "gfortran" {
    title = "Fortran compiler"
    depend "gmp"
}

### END example

So my question is: is there a way to do this so that I would eval this
package
description file and appropriate fields  of internal objects would be
filled
by the evaluated script?

Thanks in advance!
Simon S. (Guest)
on 2007-02-08 18:35
(Received via mailing list)
On 2/8/07, Tapio K. <removed_email_address@domain.invalid> wrote:
> I'm writing an automated software building framework in Ruby. The software
> compiles software packages from source code using human-written profiles. The
> profiles should be extremely easy to read and write by humans (and easy to
[snip]

There is a Ruby replacement of make, that can give you some
inspiration on how to do it. Perhaps rake can do all what you want?
http://rake.rubyforge.org/
Gavin K. (Guest)
on 2007-02-08 18:36
(Received via mailing list)
Tapio K. wrote:
> I wish I could write the profiles in Ruby to avoid
> creating yet another advanced configuration language.

Some people will suggest YAML. That's certainly a reasonable way to
go. Don't create your own config language when a simple markup exists
that translates painlessly into Ruby data structures.

>     baseurl "ftp://ftp.gnu.org/gnu/gcc/#{version}/"
> ### END example
If you want a true DSL, here's some untested code pointing you in the
right direction. I have no idea if you can have more than one archive,
or more than one feature, or more than one baseurl per Archive...but
you can do it.

class Archive
  attr_accessor :localname, :originalname, :baseurl, :convert
end

class Feature
  attr_accessor :title, :dependency
  def depend( depends_on )
    @dependency = depends_on
  end
end

def archive( &block )
  $archive = Archive.new
  $archive.instance_eval( &block )
end

$features = []
def feature( &block )
  f = Feature.new
  $features << f
  f.instance_eval( &block )
end
Gavin K. (Guest)
on 2007-02-08 18:40
(Received via mailing list)
On Feb 8, 9:32 am, "Phrogz" <removed_email_address@domain.invalid> wrote:
> end
Oops, I missed that baseurl was a method call there. Then you might
want something like:

class Archive
  attr_accessor :localname, :originalname, :convert
  def initialize
    @baseurls = []
  end
  def baseurl( url )
    @baseurls << url
  end
end
Suraj K. (Guest)
on 2007-02-08 19:31
Simon S. wrote:
> On 2/8/07, Tapio K. <removed_email_address@domain.invalid> wrote:
>> I'm writing an automated software building framework in Ruby. The software
>> compiles software packages from source code using human-written profiles. The
>> profiles should be extremely easy to read and write by humans (and easy to
> [snip]
>
> There is a Ruby replacement of make, that can give you some
> inspiration on how to do it. Perhaps rake can do all what you want?
> http://rake.rubyforge.org/

I agree. If you're going to build a DSL for your particular domain, I
suggest building it atop Rake. Stand on the shoulders of this jolly Red
giant.
Peter B. (Guest)
on 2007-02-08 19:56
(Received via mailing list)
You should take a look at Capistrano - its pretty amazing what it does
out of the box, is base don Rake, and makes it easy to build, configure,
and deploy applications.
Brian C. (Guest)
on 2007-02-08 22:23
(Received via mailing list)
On Fri, Feb 09, 2007 at 01:00:09AM +0900, Tapio K. wrote:
>     baseurl "ftp://ftp.gnu.org/gnu/gcc/#{version}/"
> ### END example
I did something a bit like this, using Rake, for building mail software.
You
can download the source, which is basically just a bunch of package
building
scripts, from http://linnet.rubyforge.org/ (take the linnet-src package,
or
check out from cvs)

You might be able to steal some ideas. It's not as abstracted as the way
you've described above, but I found in practice that the software
packages I
was building didn't all fit into identical moulds.
Tapio K. (Guest)
on 2007-02-08 23:56
(Received via mailing list)
>I'm writing an automated software building framework in Ruby. The software
>compiles software packages from source code using human-written profiles. The
>profiles should be extremely easy to read and write by humans (and easy to
>parse by a program). I wish I could write the profiles in Ruby to avoid
>creating yet another advanced configuration language.

Thanks to all of you who responded. I thought that instance_eval would
be the
solution, but I somehow overlooked it. Now I have to simulate scoping so
that
code in archive and feature blocks may refer to things declared in the
top-level of the profile (which will not be real top-level, since the
profile
scripts get inctance_evaled, to avoid pollution of Object). Overriding
method_missing probably solves this nicely.

Ruby is a great thing (although it lacks multiple dispatching and
multiple inheritance)!
Joel VanderWerf (Guest)
on 2007-02-09 00:31
(Received via mailing list)
Tapio K. wrote:
> scripts get inctance_evaled, to avoid pollution of Object). Overriding
> method_missing probably solves this nicely.
>
> Ruby is a great thing (although it lacks multiple dispatching and
> multiple inheritance)!

You could evaluate the whole config file inside of a scope (for example,
a module), and then pull objects out of this module as you need them.

There's one way to do this in:

http://redshift.sourceforge.net/script/

You can make methods like "archive" and "feature" available by
subclassing the Script module and defining them as module methods.

$ cat config-script.rb
   name = "gcc"
   version = "4.1.1"
   title = "GNU Compiler Collection"
   description = "..."

   archive {
       localname "#{name}-#{version}.tar.bz2"
       #originalname "#{name}-#{version}.tar.gz"
       #baseurl "ftp://ftp.gnu.org/gnu/gcc/#{version}/"
       #baseurl "ftp.mirror.site/gnu/gcc/#{version}/"
       #convert "gz-bz2"
   }

   # Optional components
   feature("gfortran") {
       title "Fortran compiler"
       #depend "gmp"
   }

$ cat parser.rb
   require 'script'

   class Archive
     def localname n; @localname = n; end
     # etc.
   end

   class Feature
     def initialize arg; @arg = arg; end
     def title t; @title = t; end
     # etc.
   end

   class ConfigScript < Script
     def archive(&bl)
       @archive = Archive.new
       @archive.instance_eval(&bl)
     end

     def feature(arg, &bl)
       @feature = Feature.new(arg)
       @feature.instance_eval(&bl)
     end

     def inspect
       super + [@archive, @feature].inspect
     end
   end

   config_script = ConfigScript.load("config-script.rb")

   p config_script

$ ruby parser.rb
#<ConfigScript:/home/vjoel/tmp/scr/config-script.rb>[#<Archive:0xb7cb5ff4
@localname="gcc-4.1.1.tar.bz2">, #<Feature:0xb7cb5f7c @arg="gfortran",
@title="Fortran compiler">]

This doesn't really try to solve your parsing questions. It's just an
example of how to eval the script inside a scope and not let that leak
out into the global scope.

It might be less work if you can accept a config file of the form:

$ cat config-script2.rb
   NAME = "gcc"
   VERSION = "4.1.1"
   TITLE = "GNU Compiler Collection"
   DESCRIPTION = "..."

   ARCHIVE = {
       :localname => "#{NAME}-#{VERSION}.tar.bz2"
   }

   module Features
     GFORTRAN = {
         :title => "Fortran compiler"
     }
   end

Then the parser is just:

$ cat parser2.rb
   require 'script'

   config_script = Script.load("config-script2.rb")

   p config_script

   p config_script::NAME

   p config_script::ARCHIVE

   config_script::Features.constants.each do |c|
     p(c=>config_script::Features.const_get(c))
   end

$ ruby parser2.rb
#<Script:/home/vjoel/tmp/scr/config-script2.rb>
"gcc"
{:localname=>"gcc-4.1.1.tar.bz2"}
{"GFORTRAN"=>{:title=>"Fortran compiler"}}


Anyway, I hope this suggests some of the possibilities for scoping your
DSL. There are lots of possibilities for sweetening up the syntax.
This topic is locked and can not be replied to.