DEV Community

RobL
RobL

Posted on

Fun with classes and constants

Ruby is somewhat versatile at times. Suppose you want to define a class dynamically, and I do….

class Thing; end
Enter fullscreen mode Exit fullscreen mode

Ok, that’s not dynamic

# still not dynamic but getting there.
def define_a_class
  Thing = Class.new
end

# ok then I see
def define_a_class(name)
  Object.const_set(name, Class.new)
end

define_a_class('Thing')
=> Thing
Enter fullscreen mode Exit fullscreen mode

This is great but what if you wanted to define a class within a module

define_a_class('Nigel::Thing')
NameError: wrong constant name Nigel::Thing
from (pry):19:in `const_set'
Enter fullscreen mode Exit fullscreen mode

Oh. That sucks. What you’re actually trying to do here is

Object.const_set('Thing', Class.new)
#=> Thing
Nigel.const_set('Thing', Class.new)
#=> Nigel::Thing
Enter fullscreen mode Exit fullscreen mode

Object is for root namespace, and the module name for the nested namespace.

So today we had an issue. We normally define a Permit class if one doesn’t already exist like so.

def define_permit_class
  name = "#{self.name}Permit"
  Object.const_get(name)

rescue NameError
  Object.const_set(name, Class.new(Permit))
end
Enter fullscreen mode Exit fullscreen mode

This works for

Thing.define_permit_class
Enter fullscreen mode Exit fullscreen mode

But obviously not for

Nigel::Thing.define_permit_class
Enter fullscreen mode Exit fullscreen mode

Well, easily fixed. A class can find it’s parent module, or if it is doesn’t have one it’s Object.

Thing.parent 
#=> Object
Nigel::Thing.parent
#=> Nigel
Enter fullscreen mode Exit fullscreen mode

So we just refactor this a little and bingo

def define_permit_class
  name = "#{self.name.demodulize}Permit"
  parent.const_get(name)

rescue NameError
  parent.const_set(name, Class.new(Permit))
end
Enter fullscreen mode Exit fullscreen mode

Ok, it now seems that parent is deprecated in Rails 6.1. module_parent it is.

DEPRECATION WARNING: `Module#parent` has been renamed to `module_parent`. `parent` is deprecated and will be removed in Rails 6.1.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)