39

I am trying to create a new class to that will inherit from ActiveRecord::Base the class needs to be dynamically generated from a string

"general_systems".camelize.singularize = Class.new < ActiveRecord::Base

However I keep getting the error:

undefined method `singularize=' for "GeneralSystems":String

I've also tried to constantize the string

>> foo = "general_systems".camelize.singularize
=> "GeneralSystem"
>> foo.constantize
NameError: uninitialized constant GeneralSystem
    from /var/lib/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/inflector/methods.rb:124:in `block in constantize'
    from /var/lib/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/inflector/methods.rb:123:in `each'
    from /var/lib/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/inflector/methods.rb:123:in `constantize'
    from /var/lib/gems/1.9.1/gems/activesupport-3.0.5/lib/active_support/core_ext/string/inflections.rb:43:in `constantize'
    from (irb):4
    from /usr/bin/irb:12:in `<main>'
>> foo.constantize = Class.new
NoMethodError: undefined method `constantize=' for "GeneralSystem":String
    from (irb):5
    from /usr/bin/irb:12:in `<main>'

Any help would be greatly appreciated.

wscourge
  • 10,657
  • 14
  • 59
  • 80
Telmo
  • 391
  • 1
  • 3
  • 3
  • you are tyring to call a method "general_systems".camelize.singularize=(Class.new) which is not there. what exactly are you trying to do? – Surya Mar 14 '11 at 17:42
  • 1
    Judging by one of your comments on someone's answer, you already solved the problem with their solution. Perhaps you could select an answer now? – Aaa Mar 15 '11 at 23:06

7 Answers7

38

If you're using Rails, it provides a method called #constantize that will work:

irb(main):017:0> Object.const_get 'House::Owns'
NameError: wrong constant name House::Owns

'House::Owns'.constantize
=> House::Owns
Jason Kim
  • 18,102
  • 13
  • 66
  • 105
Peter Ehrlich
  • 6,969
  • 4
  • 49
  • 65
  • 2
    I get: `1.9.3p392 :019 > 'House::Owns'.constantize NoMethodError: undefined method `constantize' for "House::Owns":String from (irb):19 from /Users/ryan/.rvm/rubies/ruby-1.9.3-p392/bin/irb:16:in
    '`
    – ethicalhack3r Sep 17 '13 at 12:16
  • 6
    #constantize is a Rails method. http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize – Gav Mar 09 '14 at 17:00
33

Something like this?

>> Object.const_set("general_systems".classify, Class.new)
=> GeneralSystem
>> GeneralSystem.new
=> #<GeneralSystem:0x105b0f738>
Michael Kohl
  • 66,324
  • 14
  • 138
  • 158
13
klazz = Class.new(ActiveRecord::Base) do
  def do_something_fun(param1)
    param1.have_fun!
  end
end

klazz_name = "general_systems".singularize.classify
Object.const_set(klazz_name, klazz)
yfeldblum
  • 65,165
  • 12
  • 129
  • 169
8

Look at this example from "The Book Of Ruby", included in the Ruby 1.9 installer.

puts("What shall we call this class?> ")
className = gets.strip().capitalize()
Object.const_set(className,Class.new)
puts("I'll give it a method called > 'myname'" ) 
className = Object.const_get(className)
className::module_eval{
  define_method(:myname){ 
    puts("The name of my class is '#{self.class}'" ) 
 } }
 x = className.new x.myname
AShelly
  • 34,686
  • 15
  • 91
  • 152
5

Given:

class Banana
end

You can fetch the class in plain Ruby with:

Object.const_get 'Banana'
=> Banana

or if you're using Rails:

'Banana'.constantize
=> Banana
Connor
  • 796
  • 11
  • 16
4

If your string contains a namespace, you can use:

require 'rubygems'
require 'active_support/inflector'
parent=String # using String to get a self contained example
# require 'active_record'   # uncomment for ActiveRecord::Base as parent class
# parent=ActiveRecord::Base # uncomment for ActiveRecord::Base as parent class
namespace = 'A::B::general_systems'.split('::') 
class_name = namespace.pop 
namespace = namespace.inject(Object) do |mod, name|
  if mod.constants.collect{|sym| sym.to_s}.include? name.classify
    mod.const_get name.classify
  else
    mod.const_set name.classify, Module.new
  end
end

klass = if namespace.constants.include? class_name.classify
  namespace.const_get class_name.classify
else
  namespace.const_set class_name.classify, Class.new(parent)
end

object = klass.allocate # allocate new object of klass
object.send :initialize # initialize object
object.class
object.class.superclass
Florian Feldhaus
  • 5,567
  • 2
  • 38
  • 46
1

try

>> "general_systems".classify
=> "GeneralSystem"
rm-rf
  • 1,313
  • 1
  • 15
  • 24
  • Thank you all, I ended up with:
    Object.const_set(loader.classify, Class.new(ActiveRecord::Base)) 
     db_loader = Object.const_get(loader.classify) 
     db_loader.create(input_system)    
    And that works very nicely.
    – Telmo Mar 14 '11 at 18:25
  • 1
    @Telmo: For future reference, use backticks rather than `
    `.
    – Andrew Grimm Mar 14 '11 at 22:12