O.K., so with local variable assignment, there are snags, such as that assignment might occur slightly later than local variable symbol addition to the local variable list. But here is my module ConstMagicErsatz that I used to implement something similar to out-of-the box Ruby constant magic:
a = Class.new
a.name #=> nil - anonymous
ABC = a # constant magic at work
a.name #=> "ABC"
The advantage here is that you don't have to write ABC = Class.new( name: "ABC" ), name gets assigned 'magically'. This also works with Struct class:
Koko = Struct.new
Koko.name #=> "Koko"
but with no other classes. So here goes my ConstMagicErsatz that allows you to do
class MySettings < SettingsLogic
  include ConstMagicErsatz
end
ABC = MySettings.new
ABC.name #=> "ABC"
As well as
a = MySettings.new name: "ABC"
a.name #=> "ABC"
Here it goes:
module ConstMagicErsatz
  def self.included receiver
    receiver.class_variable_set :@@instances, Hash.new
    receiver.class_variable_set :@@nameless_instances, Array.new
    receiver.extend ConstMagicClassMethods
  end
  # The receiver class will obtain #name pseudo getter method.
  def name
    self.class.const_magic
    name_string = self.class.instances[ self ].to_s
    name_string.nil? ? nil : name_string.demodulize
  end
  # The receiver class will obtain #name setter method
  def name= ɴ
    self.class.const_magic
    self.class.instances[ self ] = ɴ.to_s
  end
  module ConstMagicClassMethods
    # #new method will consume either:
    # 1. any parameter named :name or :ɴ from among the named parameters,
    # or,
    # 2. the first parameter from among the ordered parameters,
    # and invoke #new of the receiver class with the remaining arguments.
    def new( *args, &block )
      oo = args.extract_options!
      # consume :name named argument if it was supplied
      ɴς = if oo[:name] then oo.delete( :name ).to_s
           elsif oo[:ɴ] then oo.delete( :ɴ ).to_s
           else nil end
      # but do not consume the first ordered argument
      # and call #new method of the receiver class with the remaining args:
      instance = super *args, oo, &block
      # having obtained the instance, attach the name to it
      instances.merge!( instance => ɴς )
      return instance
    end
    # The method will search the namespace for constants to which the objects
    # of the receiver class, that are so far nameless, are assigned, and name
    # them by the first such constant found. The method returns the number of
    # remaining nameless instances.
    def const_magic
      self.nameless_instances = 
        class_variable_get( :@@instances ).select{ |key, val| val.null? }.keys
      return 0 if nameless_instances.size == 0
      catch :no_nameless_instances do search_namespace_and_subspaces Object end
      return nameless_instances.size
    end # def const_magic
    # @@instances getter and setter for the target class
    def instances; const_magic; class_variable_get :@@instances end
    def instances= val; class_variable_set :@@instances, val end
    # @@nameless_instances getter for the target class
    def nameless_instances; class_variable_get :@@nameless_instances end
    def nameless_instances= val; class_variable_set :@@nameless_instances, val end
    private
    # Checks all the constants in some module's namespace, recursivy
    def search_namespace_and_subspaces( ɱodule, occupied = [] )
      occupied << ɱodule.object_id           # mark the module "occupied"
      # Get all the constants of ɱodule namespace (in reverse - more effic.)
      const_symbols = ɱodule.constants( false ).reverse
      # check contents of these constant for wanted objects
      const_symbols.each do |sym|
        # puts "#{ɱodule}::#{sym}" # DEBUG
        # get the constant contents
        obj = ɱodule.const_get( sym ) rescue nil
        # is it a wanted object?
        if nameless_instances.map( &:object_id ).include? obj.object_id then
          class_variable_get( :@@instances )[ obj ] = ɱodule.name + "::#{sym}"
          nameless_instances.delete obj
          # and stop working in case there are no more unnamed instances
          throw :no_nameless_instances if nameless_instances.empty?
        end
      end
      # and recursively descend into the subspaces
      const_symbols.each do |sym|
        obj = ɱodule.const_get sym rescue nil # get the const value
        search_namespace_and_subspaces( obj, occupied ) unless
          occupied.include? obj.object_id if obj.kind_of? Module
      end
    end
  end # module ConstMagicClassMethods
end # module ConstMagicErsatz
The above code implements automatic searching of whole Ruby namespace with the aim of finding which constant refers to the given instance, whenever #name method is called.
The only constraint using constants gives you, is that you have to capitalize it. Of course, what you want would be modifying the metaclass of the object after it is already born and assigned to a constant. Since, again, there is no hook, you have to finde the occasion to do this, such as when the new object is first used for its purpose. So, having
ABC = MySettings.new
and then, when the first use of your MySettings instance occurs, before doing anything else, to patch its metaclass:
class MySettings
  def do_something_useful
    # before doing it
    instance_name = self.name
    singleton_class.class_exec { source "#{instance_name}.yml" }
  end
  # do other useful things
end