Forum: Ruby A Ruby class is never closed

Posted by Rubyist Rohit (rpk2006)
on 2012-08-16 19:18
Is it true that a Ruby class definition is never closed? Even after
using the 'end' keyword, the class is available for some dynamic
operations?
Posted by Jan E. (jacques1)
on 2012-08-16 19:43
Hi,

That's generally true. You can reopen a class at any time (even the 
built-in ones) or modify it through metaprogramming.

However, you can freeze a class to prevent modifications:

class A
  def f
    puts 1
  end
end

# reopen class
class A
  def g
    puts 2
  end
end

# freeze class
A.freeze

# raises an error
class A
  def g
    puts 2
  end
end
Posted by Rubyist Rohit (rpk2006)
on 2012-08-16 19:49
This means I can add a method anytime and anywhere in the project? Is 
there something similar to a Sealed Class? A class that cannot be 
instantiated.
Posted by Jan E. (jacques1)
on 2012-08-16 19:59
What you do mean? A sealed class like in C# which doesn't allow
subclasses? Or the singleton pattern where a class can only be
instantiated once?

You can actually do both. For a sealed/final class you can use the
"inherited" hook which gets called every time a new class is derived
from this class. If it raises an error, the class is "sealed" (the
question is if this makes sense).

class A
  def self.inherited subclass
    raise "class #{subclass} cannot be derived from sealed class 
#{self}"
  end
end

# this doesn't work
class B < A; end

For the singleton pattern, there's the singleton module in the standard
library.
Posted by Tony Arcieri (Guest)
on 2012-08-16 20:23
(Received via mailing list)
On Thu, Aug 16, 2012 at 10:48 AM, Rubyist Rohit 
<lists@ruby-forum.com>wrote:

> How to unfreeze it again?


One way is:

A = A.dup

Although this will warn you:

(irb):23: warning: already initialized constant A
Posted by Peter Zotov (Guest)
on 2012-08-16 21:21
(Received via mailing list)
Jan E. писал 16.08.2012 21:59:
>
> For the singleton pattern, there's the singleton module in the
> standard
> library.

For the singleton pattern, there are singleton methods in standard
Ruby.

Singleton = Object.new
class << Singleton
   def get_var
     42
   end
end

Singleton.get_var # => 42
Posted by Jan E. (jacques1)
on 2012-08-16 22:09
You can do that, but why would you want to?

Instead of having a clean class definition in a separate file, you'd 
need to include this is a kind of "initializing script". Not very 
pretty.

Also the "class" method doesn't tell you anything useful, and 
inheritance isn't possible.

So I'd rather call that a hack than an actual implementation.
Posted by Josh Cheek (josh-cheek)
on 2012-08-19 08:27
(Received via mailing list)
On Thu, Aug 16, 2012 at 12:59 PM, Jan E. <lists@ruby-forum.com> wrote:

>   def self.inherited subclass
> --
> Posted via http://www.ruby-forum.com/.
>
>
I can't think of any reason you would want to do this, but I don't think
you can ever *force* this sort of situation. e.g. if I decided that I
wanted to subclass it, I could just do this

class << A
  remove_method :inherited
end

I'd look very skeptically at any piece of code which did had such a 
hook,
though.
Posted by Marc Heiler (shevegen)
on 2012-08-25 23:36
I think he basically wants to have a class that can never ever again be 
changed.

As far as I know, this is not possible in ruby.
Posted by Robert Klemme (robert_k78)
on 2012-08-27 14:00
(Received via mailing list)
On Sat, Aug 25, 2012 at 11:36 PM, Marc Heiler <lists@ruby-forum.com> 
wrote:
> I think he basically wants to have a class that can never ever again be
> changed.
>
> As far as I know, this is not possible in ruby.

In what ways can you still change a frozen class?

Cheers

robert
Posted by Ryan Davis (Guest)
on 2012-08-27 14:04
(Received via mailing list)
On Aug 27, 2012, at 01:47 , Robert Klemme <shortcutter@googlemail.com> 
wrote:

> On Sat, Aug 25, 2012 at 11:36 PM, Marc Heiler <lists@ruby-forum.com> wrote:
>> I think he basically wants to have a class that can never ever again be
>> changed.
>>
>> As far as I know, this is not possible in ruby.
>
> In what ways can you still change a frozen class?

class X; freeze; end
X = X.dup
class X; def y; end; end # works fine

In what ways will ruby ever actually stop us from doing something we 
want to do?

private isn't private... frozen never lasts...

The people looking for these assurances have to be educated that it just 
isn't something that is worthwhile trying here.
Posted by Robert Klemme (robert_k78)
on 2012-08-27 14:06
(Received via mailing list)
On Mon, Aug 27, 2012 at 10:58 AM, Ryan Davis <ryand-ruby@zenspider.com> 
wrote:
>
> class X; freeze; end
> X = X.dup

The constant reassignment will prompt a warning.

> class X; def y; end; end # works fine

You are not actually modifying the original class X.  Instead you
create a copy and modify that.

> The people looking for these assurances have to be educated that it just isn't 
something that is worthwhile trying here.

Well, at least particular measures help others recognize that it is a
bad idea what they are trying (i.e. doing the dup reassign trick you
present above can be done but will prompt a warning).

Cheers

robert
Posted by Ryan Davis (Guest)
on 2012-08-27 22:05
(Received via mailing list)
On Aug 27, 2012, at 02:25 , Robert Klemme <shortcutter@googlemail.com> 
wrote:

>>> In what ways can you still change a frozen class?
>>
>> class X; freeze; end
>> X = X.dup
>
> The constant reassignment will prompt a warning.

Oh come one. You know as well as I do how to avoid the warning. That's 
not the point.

>> class X; def y; end; end # works fine
>
> You are not actually modifying the original class X.  Instead you
> create a copy and modify that.

So what? How is that relevant? The thread is about how to make a locked 
down system that isn't trivially thwarted. Guess what... it was 
trivially thwarted.

>> The people looking for these assurances have to be educated that it just isn't 
something that is worthwhile trying here.
>
> Well, at least particular measures help others recognize that it is a
> bad idea what they are trying (i.e. doing the dup reassign trick you
> present above can be done but will prompt a warning).

Again. Not the point. You're being (intentionally?) obtuse... It doesn't 
matter how recognizable it is... if the OP wants a locked down system, 
ruby is NOT the place to attempt it.
Posted by Robert Klemme (robert_k78)
on 2012-08-27 23:03
(Received via mailing list)
On Mon, Aug 27, 2012 at 10:05 PM, Ryan Davis <ryand-ruby@zenspider.com> 
wrote:
>>>>>
>>>>> As far as I know, this is not possible in ruby.
>>>>
>>>> In what ways can you still change a frozen class?
>>>
>>> class X; freeze; end
>>> X = X.dup

Please read again: Marc talked about a class which can never be
changed.  A frozen class is actually such a class AFAIK.  Hence I
asked (Marc) what modifications to a frozen class would be possible (I
could have overlooked something).  So far I haven't seen any.

>> The constant reassignment will prompt a warning.
>
> Oh come one. You know as well as I do how to avoid the warning. That's not the 
point.
>
>>> class X; def y; end; end # works fine
>>
>> You are not actually modifying the original class X.  Instead you
>> create a copy and modify that.
>
> So what? How is that relevant? The thread is about how to make a locked down 
system that isn't trivially thwarted. Guess what... it was trivially thwarted.

Well, you may find the point subtle but the class you assign to X the
second time is not the same as the one which was assigned the first
time.  The important bit here is that they do not share identity even
though they can be accessed via the same constant (but at different
times).  This has serious implications especially if there are
instances around of class #1: If you ask them for their class and try
to modify it, the program will fail (unless there are some
modifications possible which I have overlooked - see above).

irb(main):001:0> class X;def f;1 end end
=> nil
irb(main):002:0> o = X.new
=> #<X:0x2027d434>
irb(main):003:0> o.f
=> 1
irb(main):004:0> X.freeze
=> X
irb(main):005:0> X = X.dup
(irb):5: warning: already initialized constant X
=> X
irb(main):006:0> class X; def f;2 end end
=> nil
irb(main):007:0> X.new.f
=> 2
irb(main):008:0> o.f
=> 1
irb(main):009:0> o.class.class_eval { def f; 3 end }
RuntimeError: can't modify frozen Class
        from (irb):9:in `block in irb_binding'
        from (irb):9:in `class_eval'
        from (irb):9
        from /usr/local/bin/irb19:12:in `<main>'
irb(main):010:0> o.f
=> 1
irb(main):011:0> o.class
=> X
irb(main):012:0> o.class.equal? X
=> false

>>> The people looking for these assurances have to be educated that it just isn't 
something that is worthwhile trying here.
>>
>> Well, at least particular measures help others recognize that it is a
>> bad idea what they are trying (i.e. doing the dup reassign trick you
>> present above can be done but will prompt a warning).
>
> Again. Not the point. You're being (intentionally?) obtuse... It doesn't matter 
how recognizable it is...

Please spare your ad hominems.

> if the OP wants a locked down system, ruby is NOT the place to attempt it.

Note, that the message which started this thread read:

> Is it true that a Ruby class definition is never closed? Even after
> using the 'end' keyword, the class is available for some dynamic
> operations?

This is not about a "locked down system" but the question whether Ruby
classes can always and under all circumstances be changed.  Freezing
is a way to prevent such change.  So, yes, as long as a class is not
frozen it can be changed almost at will (super class will never
change).  But once it's frozen no modifications are possible that I am
aware of.

Cheers

robert
Posted by Ryan Davis (Guest)
on 2012-08-27 23:19
(Received via mailing list)
On Aug 27, 2012, at 14:03 , Robert Klemme <shortcutter@googlemail.com> 
wrote:

> Well, you may find the point subtle but the class you assign to X the
> second time is not the same as the one which was assigned the first
> time.  The important bit here is that they do not share identity even
> though they can be accessed via the same constant (but at different
> times).  This has serious implications especially if there are
> instances around of class #1: If you ask them for their class and try
> to modify it, the program will fail (unless there are some
> modifications possible which I have overlooked - see above).

gem install change_class

o.class = SomeOtherClass

done
Posted by Josh Rendek (Guest)
on 2012-08-28 06:19
(Received via mailing list)
A really simple way to ensure a class doesn't get modified:

class Foo
   def Foo.method_added(name)
     raise "This class is closed for modification"
   end
end

class Foo
   def testing
   end
end
Posted by Matthew Kerwin (mattyk)
on 2012-08-28 06:56
(Received via mailing list)
On 28 August 2012 14:18, Josh Rendek <josh@joshrendek.com> wrote:
>   end
> end
>

Almost.

 class Foo
   def Foo.method_added(name)
     raise "This class is closed for modification"
   end
 end

 class Foo
   class << self
     undef method_added
     def method_added m
     end
   end
   def testing
   end
 end


--
  Matthew Kerwin, B.Sc (CompSci) (Hons)
  http://matthew.kerwin.net.au/
  ABN: 59-013-727-651

  "You'll never find a programming language that frees
  you from the burden of clarifying your ideas." - xkcd
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.