Skip to content

Instantly share code, notes, and snippets.

@myabc
Created March 18, 2012 12:08
Show Gist options
  • Save myabc/2070996 to your computer and use it in GitHub Desktop.
Save myabc/2070996 to your computer and use it in GitHub Desktop.
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
Copy link
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
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
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
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