51

I am developing a new Rails app based on a similar existing one. In my old app, I have Coupon class, which is very similar to Ticket in my new app. I want to reuse all code in Coupon, but with a new class name.

Since refactoring is cumbersome in Rails, I wonder if there is a way to create alias for a class in Ruby (similar to alias for attributes and methods).

Nakilon
  • 34,866
  • 14
  • 107
  • 142
AdamNYC
  • 19,887
  • 29
  • 98
  • 154

5 Answers5

102

Classes don't have names in Ruby. They are just objects assigned to variables, just like any other object. If you want to refer to a class via a different variable, assign it to a different variable:

Foo = String
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Jörg, thanks for that gem: it is perfectly obvious, still I never thought about it before! – p4010 Oct 17 '11 at 16:26
  • Thanks, Jorg. It's totally logical, yet surprising solution. :) – AdamNYC Oct 17 '11 at 23:06
  • 3
    It's not variable. It's constant (all identifiers that begins with capital letter are constants). – Hauleth Nov 03 '13 at 23:04
  • This really is not a good option because if String changes, Foo will not...Foo doesn't point to String, it clones it. – boulder_ruby Jun 26 '14 at 17:20
  • 2
    @boulder_ruby: what are you talking about? `Foo = []; Bar = Foo; Foo << 1; p Bar # [1]` – Jörg W Mittag Jun 26 '14 at 17:30
  • It might just be a rails thing or even superstition on my part. I just ran this test and got similar results. `class Demo; def hey; puts "text1"; end; end;; Test = Demo;; class Demo; def hey; puts "text 2"; end; end;;Demo.new.hey #=> "text 2"` – boulder_ruby Jun 26 '14 at 17:55
  • From `rails console`, I tried running `Foo` which thrown error `NameError: uninitialized constant Foo`. If I run `String` and then `Foo`, it works. – RAJ Mar 14 '19 at 18:30
19

in file coupon.rb:

class Coupon 
  #...
end

# add this line of code to make alias for class names
# option1. if you haven't defined a "Ticket" class: 
Ticket = Coupon   

# option2. if Ticket has been defined, you have to redefine it: 
Object.send :remove_const, "Ticket"
const_set "Ticket", Coupon

"Any referrence that begins with an uppercase letter, including the names of classes and modules, is a constant" -- << metaprogramming ruby>>, page38, Constant section

Siwei
  • 19,858
  • 7
  • 75
  • 95
8

Anyone coming here looking for how to alias a rails model class to have a new name:

I was able to simply do Foo = Bar, but had to put Foo inside it's own model file so that I wouldn't get a Uninitialized Constant Error. e.g.

# models/foo.rb
Foo = Bar

Also you may find weirdness trying to use the alias in associations like has_many, has_one etc. I've found you can usually get around those by using the root namespace (or appropriate namespace depending on how your models are structured) to make sure Rails is trying to autoload the right constant:

has_many :foo, class_name: '::Foo'
Mark G.
  • 3,176
  • 1
  • 27
  • 29
3

You've got to be careful with this, because if your class undergoes any state change (added functions, changed constants, class variables, etc) the state that your class was in when the alias was instantiated will not reflect the updated changes in your class.

In order to avoid carpal tunnel without sacrificing readability, you can store a lambda in your alias object rather than the actual class. Of course, the lambda contains the class but this assures your alias will call up the latest version of your class.

I put this in my supermanpatches.rb rails initializer (inside of config/initializers/) ‡

LAP = lambda { LosAngelesParcel }

Now you can call this using LAP[] and a freshly minted version of your class will be loaded. (Allowing you to create instances, for example, by l = LAP[].new)

runs once when rails is loaded & then is pervasive through your app, callable anywhere kind of like a global variable but 'read-only', so to speak.

boulder_ruby
  • 38,457
  • 9
  • 79
  • 100
  • 1
    This isn't correct 'class A end; B= A; A.class_eval do def foo puts("test"); end end; B.foo' This will print test because both A and B refer to same Object in memory. also B.object_id is equal to A.object_id So you don't have to use lambda – Moustafa Samir Mar 05 '15 at 11:59
  • I already updated it using A.class_eval above, is there another way to update it ? – Moustafa Samir Mar 05 '15 at 18:53
  • 1
    "If you update your classes' methods you do" -- That's not true for plain old Ruby. Are referring to Rails and its ability to reload changed classes during development? – Wayne Conrad Jul 01 '15 at 13:06
2

I agree with warhog, more or less - but I would subclass ticket from your coupon class - that way if you need to do any data munging, you can put the code in your ticket class

chrispanda
  • 3,204
  • 1
  • 21
  • 23
  • Subclassing means IS-A relationship. I feel unless it is true in the problem domain, we should not do it in software. – Wand Maker Jul 01 '13 at 01:24