Forum: Ruby Instance name of a class/object?

Posted by Carolyn Grant (carolyn_ann)
on 2012-11-11 05:21
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 by unknown (Guest)
on 2012-11-11 10:25
(Received via mailing list)
Am 11.11.2012 05:21, schrieb Carolyn Grant:
>
>
> 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?

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?
Posted by Brian Candler (candlerb)
on 2012-11-11 10:31
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 by unknown (Guest)
on 2012-11-11 10:34
(Received via mailing list)
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')
Posted by Carolyn Grant (carolyn_ann)
on 2012-11-11 14:16
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 by Carolyn Grant (carolyn_ann)
on 2012-11-11 14:18
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". :-)
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!
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.