Instance name of a class/object?

Hi! I'm quite new to Ruby, and am quite confused about instance names.

I am in the middle of writing an app that has a good number of
configuration files, and I'd like to write:

basic_config = MySettings.new # Picks up & processes basic_config.yml
adv_config = MySettings.new # Picks up & processes adv_config.yml

I'm using SettingsLogic to the hard work, so MySettings looks a bit like
this:

class MySettings < SettingsLogic
  source instance.name # For example

  # Do some other stuff
end

I tried my_class.name; that returns a "Method not defined" error" (I'm
using 1.9.3p194)

What I'd really like to avoid is:

basic_config = MySettings.new(basic_config).

As far as I can tell, what I want to do isn't possible. I did ask on
StackOverflow and Boris was a great help, but his code requires a :name
=> some_name flag. So I figured I'd ask here and see if anyone can help
me! Can I do what I want to do?

Thanks in advance!
Carolyn

···

--
Posted via http://www.ruby-forum.com/.

This could not work for several reasons:

- the object is created first, before it can be assigned to a variable
- a single object could be referenced by several variables
- and of course: you can create and use objects without ever
   assigning them to a variable: `puts Time.new'

What's wrong with

   config = MySettings.new('basic_config')

   [or config = MySettings.new('basic_config.yml') ]

where the initialize method accepts a file name?

···

Am 11.11.2012 05:21, schrieb Carolyn Grant:

Hi! I'm quite new to Ruby, and am quite confused about instance names.

I am in the middle of writing an app that has a good number of
configuration files, and I'd like to write:

basic_config = MySettings.new # Picks up & processes basic_config.yml
adv_config = MySettings.new # Picks up & processes adv_config.yml

I'm using SettingsLogic to the hard work, so MySettings looks a bit like
this:

class MySettings < SettingsLogic
   source instance.name # For example

   # Do some other stuff
end

I tried my_class.name; that returns a "Method not defined" error" (I'm
using 1.9.3p194)

What I'd really like to avoid is:

basic_config = MySettings.new(basic_config).

As far as I can tell, what I want to do isn't possible. I did ask on
StackOverflow and Boris was a great help, but his code requires a :name
=> some_name flag. So I figured I'd ask here and see if anyone can help
me! Can I do what I want to do?

--
<https://github.com/stomar/>

Carolyn Grant wrote in post #1083906:

basic_config = MySettings.new # Picks up & processes basic_config.yml
adv_config = MySettings.new # Picks up & processes adv_config.yml

In general it doesn't make any sense for an object to know its "name",
because an object may have zero or many names at any time. You can
assign the object reference to local variables, to global variables, to
instance variables of other objects, to constants, as elements of an
Array or Hash, etc.

In the above code, the object created by MySettings.new has no name at
the time it is created. Only after the #new function has returned the
object reference, it is assigned to the local variable.

So for the code you are writing above, I would strongly advise passing
the filename (or filename stem) as an argument:

basic_config = MySettings.new("basic_config.yaml")
adv_config = MySettings.new("adv_config.yaml")

Of course, as with many things in Ruby, there is an exception. The
exception is to do with class objects; that is, objects of class Class.
Normally classes are held in constants, and the first time a class is
assigned to a constant, it learns its name, and the name becomes an
attribute of the class itself.

A class definition like "class Foo" both creates the class and assigns
it to a constant, but you can see the behaviour if you first create an
anonymous class, and then later assign it to a constant.

foo = Class.new

=> #<Class:0x106c91650>

my_instance = foo.new

=> #<#<Class:0x106c91650>:0x106c8e6f8>

Bar = foo

=> Bar

foo

=> Bar

my_instance

=> #<Bar:0x106c8e6f8>

Baz = foo

=> Bar

foo

=> Bar

my_instance

=> #<Bar:0x106c8e6f8>

I tried my_class.name; that returns a "Method not defined" error" (I'm
using 1.9.3p194)

You'll have to show the code. I use ruby 1.8, but it works fine for me.
Notice that "name" is a method of the class, not the instance, and is
only for Class objects because of the special-case behaviour described
above.

foo.name

=> "Bar"

my_instance.class.name

=> "Bar"

my_instance.name

NoMethodError: undefined method `name' for #<Bar:0x106c8e6f8>
        from (irb):11

What I'd really like to avoid is:

basic_config = MySettings.new(basic_config).

Why? It seems you want to create "magic" which will be very difficult
for another programmer to follow. When you create an object, you should
pass all the data which will be used to create state in that object, and
you should allow the caller to decide what to do with the returned
object.

The closest I could recommend is if you have a single config object
which holds all the sub-configs e.g. a hash. Then a single method
argument can say which file to load *and* which hash member to put it
in. For example:

require 'yaml'
class Configs
  def initialize
    @configs = {}
  end
  def load(stem)
    @configs[stem] = File.open("#{stem}.yaml") do |f|
      YAML.load(f)
    end
  end
  def [](stem)
    @configs[stem]
  end
end

config = Configs.new
config.load "basic_config"
config.load "adv_config"
puts config["basic_config"].inspect

Of course, if you want the config to be globally accessible throughout
your application, then you can assign it to a constant instead of a
local variable.

Config = Configs.new
Config.load "basic_config"
Config.load "adv_config"
puts Config["basic_config"].inspect

Or you could use a global variable, but a constant is generally
preferred. Constants can be put into namespaces, e.g. MyApp::Config, to
avoid clashes with other uses of Config.

···

--
Posted via http://www.ruby-forum.com/.

If you have lots of config objects you then could also use an array
or a hash that contains all of them:

config[:basic] = MySettings.new('basic_config')
config[:adv] = MySettings.new('adv_config')

···

Am 11.11.2012 10:24, schrieb sto.mar@web.de:

Am 11.11.2012 05:21, schrieb Carolyn Grant:

Hi! I'm quite new to Ruby, and am quite confused about instance names.

basic_config = MySettings.new # Picks up & processes basic_config.yml
adv_config = MySettings.new # Picks up & processes adv_config.yml

   config = MySettings.new('basic_config')

--
<https://github.com/stomar/>

Brian Candler wrote in post #1083926:

What I'd really like to avoid is:

basic_config = MySettings.new(basic_config).

Why? It seems you want to create "magic" which will be very difficult
for another programmer to follow. When you create an object, you should
pass all the data which will be used to create state in that object, and
you should allow the caller to decide what to do with the returned
object.

It just looked cleaner to me! Passing in the same name as the instance
just seemed like another opportunity for a typo, and it just looked
redundant. (The Readme would show the correct usage.)

I tried my_class.name; that returns a "Method not defined" error" (I'm
using 1.9.3p194)

You'll have to show the code. I use ruby 1.8, but it works fine for me.
Notice that "name" is a method of the class, not the instance, and is
only for Class objects because of the special-case behaviour described
above.

Thanks for the clarification - I was trying to make it do what it can't!

Of course, if you want the config to be globally accessible throughout
your application, then you can assign it to a constant instead of a
local variable.

Config = Configs.new
Config.load "basic_config"
Config.load "adv_config"
puts Config["basic_config"].inspect

Or you could use a global variable, but a constant is generally
preferred. Constants can be put into namespaces, e.g. MyApp::Config, to
avoid clashes with other uses of Config.

I'm very leery of global constants, and I'm really nervous (literally!)
about global environment variables.

My ultimate goal was to provide a sophisticated [sic...] ValueObject
that didn't need to be told what file to parse to get its values. I was
hoping to release it as a gem, but all things considered - it certainly
looks like it's either in Ruby, or vaguely possible but really difficult
and not worth the effort. It was for a configuration system for a
Sinatra app I'm in the middle of writing; I really don't like huge,
global, configuration arrays (ala Rails), so I looked for an
alternative. SettingsLogic got me most of the way there, but the need to
specify the file, when its name was the instance name, just looked
"messy".

Oh well, thanks! I've decided that I need to rethink the problem.

···

--
Posted via http://www.ruby-forum.com/.

unknown wrote in post #1083928:

···

Am 11.11.2012 10:24, schrieb sto.mar@web.de:

Am 11.11.2012 05:21, schrieb Carolyn Grant:

Hi! I'm quite new to Ruby, and am quite confused about instance names.

basic_config = MySettings.new # Picks up & processes basic_config.yml
adv_config = MySettings.new # Picks up & processes adv_config.yml

   config = MySettings.new('basic_config')

If you have lots of config objects you then could also use an array
or a hash that contains all of them:

config[:basic] = MySettings.new('basic_config')
config[:adv] = MySettings.new('adv_config')

I started with that, and wondered if there was a "better way". :slight_smile:
There doesn't seem to be, so I'm rethinking the problem and coming up
with a different solution. (I have no idea what that might be, at the
moment... Probably the duplicate-looking syntax I was trying to get away
from.)

Thanks!

--
Posted via http://www.ruby-forum.com/.