Skip to content

Instantly share code, notes, and snippets.

@myabc myabc/ice_nine.rb
Created Mar 18, 2012

Embed
What would you like to do?
Spike: Extensible Deep Freeze
# I didn't want to monkey patch #deep_freeze into core classes, plus I wanted
# to have a way to easily add new classes without modifying a case statement or
# similar. Plus I wanted a way for people to be able to specify exceptions for
# their own classes without patching mine.
module IceNine
def self.deep_freeze(object)
return object if object.frozen?
f = Freezer[object.class.name]
f.deep_freeze(object)
end
class Freezer
def self.[](name)
mod, const = name.to_s.split('::', 2)
const_args = RUBY_VERSION > '1.9' ? [mod, false] : mod
mod && const_defined?(*const_args) ? const_get(*const_args)[const] : self
end
def self.deep_freeze(object)
object.instance_variables do |ivar|
IceNine.deep_freeze(object.instance_variable_get(ivar))
end
object.freeze
end
class Array < self
def self.deep_freeze(object)
super object.each { |entry| IceNine.deep_freeze(entry) }
end
end
class Hash < self
def self.deep_freeze(object)
super object.each { |key, value|
IceNine.deep_freeze(key)
IceNine.deep_freeze(value)
}
end
end
class Singleton < self
def self.deep_freeze(object)
object
end
end
class Numeric < Singleton; end
class TrueClass < Singleton; end
class FalseClass < Singleton; end
class NilClass < Singleton; end
class Symbol < Singleton; end
end
end
hash = IceNine.deep_freeze('a' => '1')
puts "Hash is frozen: #{hash.frozen?}"
puts "Hash Keys are frozen: #{hash.keys.all?(&:frozen?)}"
puts "Hash Values are frozen: #{hash.values.all?(&:frozen?)}"
array = IceNine.deep_freeze([ 'a', 'b', 'c' ])
puts "Array is frozen: #{array.frozen?}"
puts "Array Elements are frozen: #{array.all?(&:frozen?)}"
object = IceNine.deep_freeze(Object.new)
puts "Object is frozen: #{object.frozen?}"
symbol = IceNine.deep_freeze(:symbol)
puts "Symbol is frozen: #{symbol.frozen?} (should be false)"
@myabc

This comment has been minimized.

Copy link
Owner Author

myabc commented Mar 18, 2012

@dkubb I saw your gist but couldn't get it to work on 1.9 – #const_defined? and #const_get have a different method signature. By default (without the second argument set to false) will also search ancestors for the given constant. I updated the gist to reflect this behaviour change.

@dkubb

This comment has been minimized.

Copy link

dkubb commented Mar 18, 2012

@myabc I updated the original gist with something I think that will work on 1.9. Its a similar technique to something I did in virtus to work around the api differences, without introducing a separate code path to get similar behaviour.

@dkubb

This comment has been minimized.

Copy link

dkubb commented Mar 18, 2012

@myabc oh heh, I see you did the same thing here. Sorry, it's early :) I believe atm rbx needs the same work-around but I need to test against rbx-head, which I'm installing now.

@dkubb

This comment has been minimized.

Copy link

dkubb commented Mar 18, 2012

Hmm, looks like rbx has some ivars in each object that I need to exclude from freezing to avoid modifying internal state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.