Suggestions for common/global data in app (cli)

N

Nit Khair

I am writing a (my first) client side ruby app. Many programs use the
same data such as db login, logger etc. Is there a standard ruby way of
doing this? Some suggestions would be useful.

- I have used YAML for config data for single scripts earlier
- I am aware of 'load'ing a ruby file which could have config data in
it.
- A module with global constants

Would these files (yaml, load) have to loaded in each program?

Have I missed any approach? I have heard of global vars but never used.

I would appreciate a link to any ruby application best practices, too.
Thanks.
 
J

Joel VanderWerf

Nit said:
I am writing a (my first) client side ruby app. Many programs use the
same data such as db login, logger etc. Is there a standard ruby way of
doing this? Some suggestions would be useful.

- I have used YAML for config data for single scripts earlier
- I am aware of 'load'ing a ruby file which could have config data in
it.
- A module with global constants

Would these files (yaml, load) have to loaded in each program?

Have I missed any approach? I have heard of global vars but never used.

I would appreciate a link to any ruby application best practices, too.
Thanks.

One suggestion (based on yaml):

http://redshift.sourceforge.net/preferences/

It also provides a utility method to find the right user dir to store
prefs in, based on env vars.

It may be overkill for a simple program though.
 
N

Nit Khair

Joel said:
One suggestion (based on yaml):

http://redshift.sourceforge.net/preferences/

It also provides a utility method to find the right user dir to store
prefs in, based on env vars.

It may be overkill for a simple program though.
Thanks, looks good. My app should become pretty large. I like the idea
of config being saved too (easily), so next execution takes the new
config.

Btw, I am aware of Configatron, too.
 
B

Brian Candler

Nit said:
I am writing a (my first) client side ruby app. Many programs use the
same data such as db login, logger etc. Is there a standard ruby way of
doing this? Some suggestions would be useful.

- I have used YAML for config data for single scripts earlier

This is very common.
- I am aware of 'load'ing a ruby file which could have config data in
it.
- A module with global constants

That's a possibility too.
Would these files (yaml, load) have to loaded in each program?

Yes, assuming these programs are separate (e.g. separate commands
invoked from the command line) as they will get a fresh Ruby interpreter
instance.
Have I missed any approach?

You may be able to combine this with the "dependency injection" pattern.
This sounds complicated but is actually very simple.

Jim Weirich wrote an excellent depinj.rb which was published on
onestepback.org; unfortunately he is in the process of reorganising his
blog and the download is not currently available.

You basically write Ruby code to configure each of your objects:

container = DI::Container.new
container.register:)db_host) { "localhost" }
container.register:)db_user) { "root" }
container.register:)db_pass) { "xyzzy" }
container.register:)db) { |c|
DataBaseAdapter.new(c.db_host, c.db_user, c.db_pass)
}

When you first reference container.db then the database connection is
instantiated for you. The nice thing is that all the interdependencies
between objects is sorted out for you.
I have heard of global vars but never used.

That would be just another way to refer to the loaded data. You could
write:

require "yaml"
$config = YAML.load("/etc/myapp.conf")

and then anywhere else in your program you could say

$config["db_user"]

or whatever.

Remember that YAML allows nesting, so you could have

db:
host: localhost
user: root
pass: xyzzy
logger:
level: 2
file: /var/log/stuff

then you could refer to $config["db"]["host"] etc.

B.
 
N

Nit Khair

Brian said:
Would these files (yaml, load) have to loaded in each program?

Yes, assuming these programs are separate (e.g. separate commands
invoked from the command line) as they will get a fresh Ruby interpreter
instance.
I have heard of global vars but never used.

That would be just another way to refer to the loaded data. You could
write:

require "yaml"
$config = YAML.load("/etc/myapp.conf")

and then anywhere else in your program you could say

$config["db_user"]

Small question. The app is not separate programs loaded from the command
line. The main menu loads, then other classes available in other files
are required and instantiated.

So there are many separate files, but one ruby interpreter. Will
"$config["XXX"] work through all the programs ? That seems fairly easy.
Will look into dep inj soon - thanks for the tip.
 
A

ara.t.howard

I am writing a (my first) client side ruby app. Many programs use the
same data such as db login, logger etc. Is there a standard ruby way
of
doing this? Some suggestions would be useful.

- I have used YAML for config data for single scripts earlier
- I am aware of 'load'ing a ruby file which could have config data in
it.
- A module with global constants

Would these files (yaml, load) have to loaded in each program?

Have I missed any approach? I have heard of global vars but never
used.

I would appreciate a link to any ruby application best practices, too.
Thanks.


gem install configuration





NAME
configuration.rb

SYNOPSIS
pure ruby scoped configuration files

DESCRIPTION
configuration.rb provides a mechanism for configuring ruby programs
with
ruby configuration files. a configuration.rb file, for example
'config/app.rb', can be written simply as

Configuration.for('app'){
key 'value'
foo 'bar'
port 42
}

and loaded via the normal ruby require/load mechanism

Kernel.load 'config/app.rb'

or with a slightly augmented loading mechnanism which simply
searches an
extra set of paths in *addition* to the standard ones

Configuration.path = %w( config configuration )

Configuration.load 'app'

configurations are completely open

Configuration.for('app'){
object_id 'very open'
}

support arbitrarily nested values

Configuration.for('app'){
a { b { c { d 42 } } }
}

c = Configuration.for 'app'

p c.a.b.c.d #=> 42

allow POLS scoped lookup of vars

Configuration.for('config'){
outer 'bar'

inner {
value 42
}
}

c = Configuration.for 'config'

p c.outer #=> 'bar'
p c.inner.value #=> 42
p c.inner.outer #=> 'bar'


and not a whole lot else - configuration.rb is s very small library
consisting of one file and < 150 loc

SAMPLES

<========< samples/a.rb >========>

~ > cat samples/a.rb

#
# basic usage is quite, simple, load the config and use it's
values. the
# config syntax is fairly obvious, i think, but note that it *is*
ruby and any
# ruby can be included. also note that each config is named,
allowing
# multiple configs to be places in one file
#
require 'configuration'

c = Configuration.load 'a'

p c.a + c.b - c.c

~ > cat config/a.rb

Configuration.for('a'){
a 40
b 4
c 2
}

~ > ruby samples/a.rb

42


<========< samples/b.rb >========>

~ > cat samples/b.rb

#
# configuration.rb supports a very natural nesting syntax. note
how values
# are scoped in a POLS fashion
#
require 'configuration'

c = Configuration.for 'b'

p c.www.url
p c.db.url
p c.mail.url

~ > cat config/b.rb

Configuration.for('b'){
host "codeforpeople.com"

www {
port 80
url "http://#{ host }:#{ port }"
}

db {
port 5342
url "db://#{ host }:#{ port }"
}

mail {
host "gmail.com"
port 25
url "mail://#{ host }:#{ port }"
}
}

~ > ruby samples/b.rb

"http://codeforpeople.com:80"
"db://codeforpeople.com:5342"
"mail://gmail.com:25"


<========< samples/c.rb >========>

~ > cat samples/c.rb

#
# configuration.rb let's you keep code very dry.
#

require 'configuration'

Configuration.load 'c'

p Configuration.for('development').db
p Configuration.for('production').db
p Configuration.for('testing').db

~ > cat config/c.rb

%w( development production testing ).each do |environment|

Configuration.for(environment){
adapter "sqlite3"
db "db/#{ environment }"
}

end

~ > ruby samples/c.rb

"db/development"
"db/production"
"db/testing"


<========< samples/d.rb >========>

~ > cat samples/d.rb

#
# configuration.rb makes use of an external blank slate dsl, this
means that
# you Configuration objects do, in fact, have all built-in ruby
methods such
# as #inspect, etc, *unless* you configure over the top of them.
the effect
# is a configuration object that behaves like a nice ruby object,
but which
# allows *any* key to be configured
#
require 'configuration'

c = Configuration.for 'd'

p c.object_id
p c.inspect
p c.p

~ > cat config/d.rb

Configuration.for('d'){
object_id 42
inspect 'forty-two'
p 42.0
}

~ > ruby samples/d.rb

42
"forty-two"
42.0


<========< samples/e.rb >========>

~ > cat samples/e.rb

#
# configuration.rb uses a totally clean slate dsl for the block.
if you need
# to access base Object methods you can do this
#

require 'configuration'

c = Configuration.for 'e'

p c.foo
p c.bar
p c.foobar

~ > cat config/e.rb

Configuration.for('e'){
foo 42

if Send('respond_to?', 'foo')
bar 'forty-two'
end

respond_to = Method('bar')

if respond_to.call('bar')
foobar 42.0
end
}

~ > ruby samples/e.rb

42
"forty-two"
42.0


AUTHOR
(e-mail address removed)


a @ http://codeforpeople.com/
 
B

Brian Candler

Nit said:
Small question. The app is not separate programs loaded from the command
line. The main menu loads, then other classes available in other files
are required and instantiated.

So there are many separate files, but one ruby interpreter. Will
"$config["XXX"] work through all the programs ?

Yes. Your apps will be written in such a way that they're somewhat tied
to the global data structure, but if there's no intention of re-using
the component objects elsewhere that's no problem.

Otherwise you could do something like this:

[config file]
thing1:
x: abc
y: def
thing2:
a: xyz
b: tuv

[library]
class Thing1
def initialize(params)
x = params["x"]
y = params["y"]
end
end

[initialisation]
$config = YAML.load("/etc/myapp.conf")
o1 = Thing1.new($config["thing1"])

You can of course put object instances in global variables too. They can
be named:

$logger ||= Logger.new(...)

Or you can use the "object locator" pattern, which is a fancy way of
saying an object containing the other objects.

$locator ||= {}
$locator[:logger] ||= Logger.new(...)

Taking this one step further you get to dep inj, since creation of each
object can make use of other objects in the container.
Will look into dep inj soon - thanks for the tip.

I see you already found the page. I had a copy of the code lying around,
so I'll attach it to this message.

Personally I'd load the $config and then use depinj to set up the
individual objects. This code is very easy to re-use, since any objects
in the container which you don't access don't get created.

Attachments:
http://www.ruby-forum.com/attachment/2790/depinj.rb
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top