106

How can I programmatically turn a class name, FooBar, into a symbol, :foo_bar? e.g. something like this, but that handles camel case properly?

FooBar.to_s.downcase.to_sym
awendt
  • 13,195
  • 5
  • 48
  • 66
Josh Glover
  • 25,142
  • 27
  • 92
  • 129

4 Answers4

156

Rails comes with a method called underscore that will allow you to transform CamelCased strings into underscore_separated strings. So you might be able to do this:

FooBar.name.underscore.to_sym

But you will have to install ActiveSupport just to do that, as ipsum says.

If you don't want to install ActiveSupport just for that, you can monkey-patch underscore into String yourself (the underscore function is defined in ActiveSupport::Inflector):

class String
  def underscore
    word = self.dup
    word.gsub!(/::/, '/')
    word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
    word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
    word.tr!("-", "_")
    word.downcase!
    word
  end
end
kikito
  • 51,734
  • 32
  • 149
  • 189
85

Rails 4 .model_name

In Rails 4, it returns an ActiveModel::Name object which contains many useful more "semantic" attributes such as:

FooBar.model_name.param_key
#=> "foo_bar"

FooBar.model_name.route_key
#=> "foo_bars"

FooBar.model_name.human
#=> "Foo bar"

So you should use one of those if they match your desired meaning, which is likely the case. Advantages:

  • easier to understand your code
  • your app will still work even in the (unlikely) event that Rails decides to change a naming convention.

BTW, human has the advantage of being I18N aware.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • `model_name` also works for instance objects. Ex: `foo_bar.model_name`. If you want the full list write `foo_bar.model_name.inspect` in rails console or your debugger – ivanxuu Sep 08 '16 at 09:31
  • and I was looking for the opposite/reverse, `classify`, `modelize` didn't seem to work. – Pysis Jul 16 '20 at 19:49
9

first: gem install activesupport

require 'rubygems'
require 'active_support'
"FooBar".underscore.to_sym
ipsum
  • 1,042
  • 7
  • 17
  • Should be `FooBar.to_s.underscore.to_sym`, since I'm trying to convert a class (which is a constant) into a symbol, which I can then feed to [Factory Girl](https://github.com/thoughtbot/factory_girl). :) – Josh Glover Apr 11 '11 at 14:31
2

Here's what I went for:

module MyModule
  module ClassMethods
    def class_to_sym  
      name_without_namespace = name.split("::").last
      name_without_namespace.gsub(/([^\^])([A-Z])/,'\1_\2').downcase.to_sym
    end
  end

  def self.included(base)
    base.extend(ClassMethods)
  end
end

class ThisIsMyClass
  include MyModule
end 

ThisIsMyClass.class_to_sym #:this_is_my_class
Louis Sayers
  • 2,162
  • 3
  • 30
  • 53
  • 3
    ActiveSupport has `String#demodulize` which will remove the part before `::`. – amoebe Sep 29 '15 at 11:07
  • 1
    @amoebe - thanks for the info. Depends on what your working on though - you may not be working on a rails app (or even a web app). – Louis Sayers Sep 29 '15 at 20:14