--- /dev/null
+## MAC OS
+.DS_Store
+
+## TEXTMATE
+*.tmproj
+tmtags
+
+## EMACS
+*~
+\#*
+.\#*
+
+## VIM
+*.swp
+
+## Rubinius
+*.rbc
+.rbx
+
+## PROJECT::GENERAL
+*.gem
+coverage
+profiling
+turbulence
+rdoc
+pkg
+tmp
+doc
+log
+.yardoc
+measurements
+
+## BUNDLER
+.bundle
+Gemfile.lock
+
+## PROJECT::SPECIFIC
--- /dev/null
+sources: lib/**/*.rb
+lints:
+ InstanceVariables:
+ limit: 1
+ LineRestriction:
+ limit: 87
+ ElseClauses:
+ exclude:
+ - 'IceNine::Freezer'
--- /dev/null
+--color
+--format progress
+--profile
+--warnings
+--order random
--- /dev/null
+language: ruby
+cache: bundler
+bundler_args: --without yard guard benchmarks
+script: "bundle exec rake ci:metrics"
+rvm:
+ - 1.9.3
+ - 2.0.0
+ - ruby-head
+ - rbx
+matrix:
+ include:
+ - rvm: jruby-19mode
+ env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
+ - rvm: jruby-20mode
+ env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
+ - rvm: jruby-head
+ env: JRUBY_OPTS="$JRUBY_OPTS --debug" # for simplecov
+ fast_finish: true
+notifications:
+ irc:
+ channels:
+ - irc.freenode.org#rom-rb
+ on_success: never
+ on_failure: change
--- /dev/null
+- README.md LICENSE
--- /dev/null
+Contributing
+------------
+
+* If you want your code merged into the mainline, please discuss the proposed changes with me before doing any work on it. This library is still in early development, and the direction it is going may not always be clear. Some features may not be appropriate yet, may need to be deferred until later when the foundation for them is laid, or may be more applicable in a plugin.
+* Fork the project.
+* Make your feature addition or bug fix.
+ * Follow this [style guide](https://github.com/dkubb/styleguide).
+* Add specs for it. This is important so I don't break it in a future version unintentionally. Tests must cover all branches within the code, and code must be fully covered.
+* Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
+* Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged.
+* Send me a pull request. Bonus points for topic branches.
--- /dev/null
+# encoding: utf-8
+
+source 'https://rubygems.org'
+
+gemspec
+
+platform :rbx do
+ gem 'racc', '~> 1.4.10'
+ gem 'rubinius-coverage', '~> 2.0.3'
+ gem 'rubysl-bigdecimal', '~> 2.0.2'
+ gem 'rubysl-coverage', '~> 2.0.3'
+ gem 'rubysl-json', '~> 2.0.2'
+ gem 'rubysl-logger', '~> 2.0.0'
+ gem 'rubysl-singleton', '~> 2.0.0'
+end
+
+group :development, :test do
+ gem 'devtools', git: 'https://github.com/rom-rb/devtools.git'
+end
+
+eval_gemfile 'Gemfile.devtools'
--- /dev/null
+# encoding: utf-8
+
+group :development do
+ gem 'rake', '~> 10.1.0'
+ gem 'rspec', '~> 2.14.1'
+ gem 'yard', '~> 0.8.7'
+end
+
+group :yard do
+ gem 'kramdown', '~> 1.2.0'
+end
+
+group :guard do
+ gem 'guard', '~> 1.8.1'
+ gem 'guard-bundler', '~> 1.0.0'
+ gem 'guard-rspec', '~> 3.0.2'
+ gem 'guard-rubocop', '~> 0.2.0'
+ gem 'guard-mutant', '~> 0.0.1'
+
+ # file system change event handling
+ gem 'listen', '~> 1.3.0'
+ gem 'rb-fchange', '~> 0.0.6', require: false
+ gem 'rb-fsevent', '~> 0.9.3', require: false
+ gem 'rb-inotify', '~> 0.9.0', require: false
+
+ # notification handling
+ gem 'libnotify', '~> 0.8.0', require: false
+ gem 'rb-notifu', '~> 0.0.4', require: false
+ gem 'terminal-notifier-guard', '~> 1.5.3', require: false
+end
+
+group :metrics do
+ gem 'coveralls', '~> 0.7.0'
+ gem 'flay', '~> 2.4.0'
+ gem 'flog', '~> 4.2.0'
+ gem 'reek', '~> 1.3.2'
+ gem 'rubocop', '~> 0.14.1'
+ gem 'simplecov', '~> 0.7.1'
+ gem 'yardstick', '~> 0.9.7', git: 'https://github.com/dkubb/yardstick.git'
+
+ platforms :ruby_19, :ruby_20 do
+ gem 'mutant', '~> 0.3.0.rc3', git: 'https://github.com/mbj/mutant.git'
+ gem 'unparser', '~> 0.1.3', git: 'https://github.com/mbj/unparser.git'
+ gem 'yard-spellcheck', '~> 0.1.5'
+ end
+end
+
+group :benchmarks do
+ gem 'rbench', '~> 0.2.3'
+end
+
+platform :jruby do
+ group :jruby do
+ gem 'jruby-openssl', '~> 0.8.5'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+guard :bundler do
+ watch('Gemfile')
+end
+
+guard :rspec, :cli => File.read('.rspec').split.join(' '), :keep_failed => false do
+ # run all specs if configuration is modified
+ watch('Guardfile') { 'spec' }
+ watch('Gemfile.lock') { 'spec' }
+ watch('spec/spec_helper.rb') { 'spec' }
+
+ # run all specs if supporting files files are modified
+ watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
+
+ # run unit specs if associated lib code is modified
+ watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
+ watch(%r{\Alib/(.+)/support/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}/#{m[2]}"] }
+ watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
+
+ # run a spec if it is modified
+ watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
+end
--- /dev/null
+Copyright (c) 2012-2013 Dan Kubb
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+ice_nine
+========
+
+Deep freeze ruby objects
+
+[][gem]
+[][travis]
+[][gemnasium]
+[][codeclimate]
+[][coveralls]
+
+[gem]: https://rubygems.org/gems/ice_nine
+[travis]: https://travis-ci.org/dkubb/ice_nine
+[gemnasium]: https://gemnasium.com/dkubb/ice_nine
+[codeclimate]: https://codeclimate.com/github/dkubb/ice_nine
+[coveralls]: https://coveralls.io/r/dkubb/ice_nine
+
+Examples
+--------
+
+```ruby
+# Deep freezes most kinds of objects
+hash = IceNine.deep_freeze('a' => '1')
+array = IceNine.deep_freeze([ 'a', 'b', 'c' ])
+range = IceNine.deep_freeze('a'..'z')
+struct = IceNine.deep_freeze(Struct.new(:a, :b).new('a', 'b'))
+object = IceNine.deep_freeze(Object.new)
+user = IceNine.deep_freeze(User.new(name: 'dkubb'))
+
+# Add core extension for Object#deep_freeze (not required by default)
+require 'ice_nine/core_ext/object'
+
+object = Object.new
+object.deep_freeze
+
+# Faster deep freeze that skips walking frozen objects
+object = IceNine.deep_freeze!(Object.new)
+```
+
+Contributing
+------------
+
+See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
+
+Copyright
+---------
+
+Copyright © 2012-2013 Dan Kubb. See LICENSE for details.
--- /dev/null
+# encoding: utf-8
+
+require 'devtools'
+Devtools.init_rake_tasks
--- /dev/null
+* Add a utility method that allows "NoFreeze" subclasses to be easily added for
+ any constant.
+ * Refactor the existing NoFreeze subclasses to use the method.
+
+* Consider adding support for OpenStruct
+ * should freeze the singleton class too in order to prevent no new members
+ * being added (by new_ostruct_member)
--- /dev/null
+#!/usr/bin/env ruby
+
+# encoding: utf-8
+
+# benchmark speed of deep freeze
+
+$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
+
+require 'rbench'
+require 'ice_nine'
+
+# @return [Hash]
+def self.nested(depth, width, array_length)
+ hash = {}
+
+ 1.upto(width) do |n|
+ hash[n.to_s] = n.to_s
+ end
+
+ unless depth == 1
+ hash[(width - 1).to_s] = array_length.times.map { nested(depth - 1, width, array_length) }
+ hash[width.to_s] = nested(depth - 1, width, array_length)
+ end
+
+ hash
+end
+
+hash = nested(3, 5, 500)
+hash2 = nested(3, 5, 500)
+
+RBench.run do
+ report('deep_freeze') { IceNine.deep_freeze(hash) }
+ report('deep_freeze!') { IceNine.deep_freeze!(hash2) }
+end
--- /dev/null
+---
+unit_test_timeout: 0.1
--- /dev/null
+---
+threshold: 8
+total_score: 50.0
--- /dev/null
+---
+threshold: 5.5
--- /dev/null
+---
+name: ice_nine
+namespace: IceNine
--- /dev/null
+---
+Attribute:
+ enabled: true
+ exclude: []
+BooleanParameter:
+ enabled: true
+ exclude: []
+ClassVariable:
+ enabled: true
+ exclude: []
+ControlParameter:
+ enabled: true
+ exclude: []
+DataClump:
+ enabled: true
+ exclude: []
+ max_copies: 2
+ min_clump_size: 2
+DuplicateMethodCall:
+ enabled: true
+ exclude: []
+ max_calls: 1
+ allow_calls: []
+FeatureEnvy:
+ enabled: true
+ exclude:
+ - IceNine::RecursionGuard::Frozen#guard
+IrresponsibleModule:
+ enabled: true
+ exclude: []
+LongParameterList:
+ enabled: true
+ exclude: []
+ max_params: 2
+ overrides:
+ initialize:
+ max_params: 3
+LongYieldList:
+ enabled: true
+ exclude: []
+ max_params: 2
+NestedIterators:
+ enabled: true
+ exclude: []
+ max_allowed_nesting: 1
+ ignore_iterators: []
+NilCheck:
+ enabled: true
+ exclude: []
+RepeatedConditional:
+ enabled: true
+ exclude: []
+ max_ifs: 1
+TooManyInstanceVariables:
+ enabled: true
+ exclude: []
+ max_instance_variables: 3
+TooManyMethods:
+ enabled: true
+ exclude: []
+ max_methods: 10
+TooManyStatements:
+ enabled: true
+ exclude:
+ - IceNine::Freezer::Hash#self.freeze_key_value_pairs
+ - IceNine::Freezer::Hash#self.guarded_deep_freeze
+ - IceNine::Freezer::Hash#self.deep_freeze
+ - IceNine::Freezer::Object#self.guarded_deep_freeze
+ - IceNine::Freezer::Range#self.deep_freeze
+ - IceNine::Freezer::Range#self.guarded_deep_freeze
+ - IceNine::RecursionGuard#guard
+ - IceNine::RecursionGuard::ObjectSet#guard
+ - each
+ max_statements: 2
+UncommunicativeMethodName:
+ enabled: true
+ exclude: []
+ reject:
+ - !ruby/regexp /^[a-z]$/
+ - !ruby/regexp /[0-9]$/
+ - !ruby/regexp /[A-Z]/
+ accept: []
+UncommunicativeModuleName:
+ enabled: true
+ exclude: []
+ reject:
+ - !ruby/regexp /^.$/
+ - !ruby/regexp /[0-9]$/
+ accept: []
+UncommunicativeParameterName:
+ enabled: true
+ exclude: []
+ reject:
+ - !ruby/regexp /^.$/
+ - !ruby/regexp /[0-9]$/
+ - !ruby/regexp /[A-Z]/
+ accept: []
+UncommunicativeVariableName:
+ enabled: true
+ exclude: []
+ reject:
+ - !ruby/regexp /^.$/
+ - !ruby/regexp /[0-9]$/
+ - !ruby/regexp /[A-Z]/
+ accept: []
+UnusedParameters:
+ enabled: true
+ exclude: []
+UtilityFunction:
+ enabled: true
+ exclude:
+ - IceNine::RecursionGuard::Frozen#guard
+ max_helper_calls: 0
--- /dev/null
+---
+AbcMetricMethodCheck: { score: 1 }
+AssignmentInConditionalCheck: { }
+CaseMissingElseCheck: { }
+ClassLineCountCheck: { line_count: 88 }
+ClassNameCheck: { pattern: !ruby/regexp '/\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/' }
+ClassVariableCheck: { }
+CyclomaticComplexityBlockCheck: { complexity: 3 }
+CyclomaticComplexityMethodCheck: { complexity: 1 }
+EmptyRescueBodyCheck: { }
+ForLoopCheck: { }
+MethodLineCountCheck: { line_count: 1 }
+MethodNameCheck: { pattern: !ruby/regexp '/\A(?:[a-z\d](?:_?[a-z\d])+[?!=]?|\[\]=?|==|<=>|<<|[+*&|-])\z/' }
+ModuleLineCountCheck: { line_count: 92 }
+ModuleNameCheck: { pattern: !ruby/regexp '/\A(?:[A-Z]+|[A-Z][a-z](?:[A-Z]?[a-z])+)\z/' }
+ParameterNumberCheck: { parameter_count: 0 }
--- /dev/null
+AllCops:
+ Includes:
+ - '**/*.rake'
+ - 'Gemfile'
+ Excludes:
+ - '**/vendor/**'
+ - '**/benchmarks/**'
+
+# Avoid parameter lists longer than five parameters.
+ParameterLists:
+ Max: 3
+ CountKeywordArgs: true
+
+# Avoid more than `Max` levels of nesting.
+BlockNesting:
+ Max: 3
+
+# Align with the style guide.
+CollectionMethods:
+ PreferredMethods:
+ collect: 'map'
+ inject: 'reduce'
+ find: 'detect'
+ find_all: 'select'
+
+# Do not force public/protected/private keyword to be indented at the same
+# level as the def keyword. My personal preference is to outdent these keywords
+# because I think when scanning code it makes it easier to identify the
+# sections of code and visually separate them. When the keyword is at the same
+# level I think it sort of blends in with the def keywords and makes it harder
+# to scan the code and see where the sections are.
+AccessControl:
+ Enabled: false
+
+# Limit line length
+LineLength:
+ Max: 79
+
+# Disable documentation checking until a class needs to be documented once
+Documentation:
+ Enabled: false
+
+# Do not always use &&/|| instead of and/or.
+AndOr:
+ Enabled: false
+
+# Do not favor modifier if/unless usage when you have a single-line body
+IfUnlessModifier:
+ Enabled: false
+
+# Allow case equality operator (in limited use within the specs)
+CaseEquality:
+ Enabled: false
+
+# Constants do not always have to use SCREAMING_SNAKE_CASE
+ConstantName:
+ Enabled: false
+
+# Not all trivial readers/writers can be defined with attr_* methods
+TrivialAccessors:
+ Enabled: false
--- /dev/null
+---
+threshold: 100
--- /dev/null
+ruby-ice-nine (0.11.0-1) UNRELEASED; urgency=medium
+
+ * Initial release (Closes: #nnnn)
+
+ -- MAINTAINER <valtri@myriad14.zcu.cz> Tue, 11 Mar 2014 23:35:41 +0100
--- /dev/null
+Source: ruby-ice-nine
+Section: ruby
+Priority: optional
+Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers@lists.alioth.debian.org>
+Uploaders: <>
+Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.6.1~)
+Standards-Version: 3.9.4
+#Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/ruby-ice-nine.git
+#Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-ruby-extras/ruby-ice-nine.git;a=summary
+Homepage: https://github.com/dkubb/ice_nine
+XS-Ruby-Versions: all
+
+Package: ruby-ice-nine
+Architecture: all
+XB-Ruby-Versions: ${ruby:Versions}
+Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter
+# bundler (>= 1.3.5, ~> 1.3, development)
+Description: Deep Freeze Ruby Objects
+ Deep Freeze Ruby Objects
--- /dev/null
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: ice_nine
+Source: FIXME <http://example.com/>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+ <years> <likewise for another author>
+License: GPL-2+ (FIXME)
+
+Files: debian/*
+Copyright: 2014 <>
+License: GPL-2+ (FIXME)
+Comment: the Debian packaging is licensed under the same terms as the original package.
+
+License: GPL-2+ (FIXME)
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE. See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
--- /dev/null
+# FIXME: READMEs found
+# README.md
--- /dev/null
+---
+- spec/integration/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/integration/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/array/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/class_methods/element_reader_spec.rb
+- spec/unit/ice_nine/freezer/false_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/hash/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/module/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/nil_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/no_freeze/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/numeric/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/object/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/range/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/struct/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/symbol/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/true_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/recursion_guard/frozen/guard_spec.rb
+- spec/unit/ice_nine/recursion_guard/object_set/guard_spec.rb
+- spec/unit/object/deep_freeze_spec.rb
--- /dev/null
+#!/usr/bin/make -f
+#export DH_VERBOSE=1
+#
+# Uncomment to ignore all test failures (but the tests will run anyway)
+#export DH_RUBY_IGNORE_TESTS=all
+#
+# Uncomment to ignore some test failures (but the tests will run anyway).
+# Valid values:
+#export DH_RUBY_IGNORE_TESTS=ruby1.9.1 ruby2.0 require-rubygems
+#
+# If you need to specify the .gemspec (eg there is more than one)
+#export DH_RUBY_GEMSPEC=gem.gemspec
+
+%:
+ dh $@ --buildsystem=ruby --with ruby
--- /dev/null
+3.0 (quilt)
--- /dev/null
+version=3
+http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/ice_nine .*/ice_nine-(.*).tar.gz
--- /dev/null
+# encoding: utf-8
+
+require File.expand_path('../lib/ice_nine/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.name = 'ice_nine'
+ gem.version = IceNine::VERSION.dup
+ gem.authors = ['Dan Kubb']
+ gem.email = %w[dan.kubb@gmail.com]
+ gem.description = 'Deep Freeze Ruby Objects'
+ gem.summary = gem.description
+ gem.homepage = 'https://github.com/dkubb/ice_nine'
+ gem.license = 'MIT'
+
+ gem.require_paths = %w[lib]
+ gem.files = `git ls-files`.split($/)
+ gem.test_files = `git ls-files -- spec/{unit,integration}`.split($/)
+ gem.extra_rdoc_files = %w[LICENSE README.md TODO]
+
+ gem.add_development_dependency('bundler', '~> 1.3', '>= 1.3.5')
+end
--- /dev/null
+# encoding: utf-8
+
+require 'ice_nine/support/recursion_guard'
+
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/object'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/array'
+
+require 'ice_nine/freezer/false_class'
+require 'ice_nine/freezer/hash'
+require 'ice_nine/freezer/hash/state'
+require 'ice_nine/freezer/nil_class'
+require 'ice_nine/freezer/module'
+require 'ice_nine/freezer/numeric'
+require 'ice_nine/freezer/range'
+require 'ice_nine/freezer/rubinius'
+require 'ice_nine/freezer/struct'
+require 'ice_nine/freezer/symbol'
+require 'ice_nine/freezer/true_class'
+
+require 'ice_nine/version'
+
+# Base IceNine module
+module IceNine
+
+ # Deep Freeze an object
+ #
+ # @example
+ # object = IceNine.deep_freeze(object)
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ #
+ # @api public
+ def self.deep_freeze(object)
+ Freezer.deep_freeze(object)
+ end
+
+ # Deep Freeze an object
+ #
+ # This method uses a faster algorithm that will assume objects that are
+ # `frozen?` do not need to be frozen deeply. Use this method when `object`
+ # contains no shallowly frozen objects that need deep freezing.
+ #
+ # @example
+ # IceNine.deep_freeze!(['a', 'b']).map(&:frozen?) # [true, true]
+ #
+ # @example
+ # IceNine.deep_freeze!(['a', 'b'].freeze).map(&:frozen?) # [false, false]
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ #
+ # @api public
+ def self.deep_freeze!(object)
+ Freezer.deep_freeze!(object)
+ end
+
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+
+ # Core Ruby extensions
+ module CoreExt
+
+ # Extend Object with deep freezing
+ module Object
+
+ # Deep freeze an object
+ #
+ # @example
+ # object = object.deep_freeze
+ #
+ # @return [self]
+ #
+ # @api public
+ def deep_freeze
+ IceNine.deep_freeze(self)
+ end
+
+ # Deep freeze an object
+ #
+ # @see IceNine.deep_freeze!
+ #
+ # @example
+ # object = object.deep_freeze!
+ #
+ # @return [self]
+ #
+ # @api public
+ def deep_freeze!
+ IceNine.deep_freeze!(self)
+ end
+
+ end # Object
+ end # CoreExt
+end # IceNine
+
+# Add Object#deep_freeze
+Object.instance_eval { include IceNine::CoreExt::Object }
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+
+ # The default class that handles freezing objects
+ class Freezer
+
+ # Cache the Freezer classes returned for each type
+ @freezer_cache = Hash.new do |cache, mod|
+ cache[mod] = nil
+ mod.ancestors.each do |ancestor|
+ freezer = find(ancestor.name.to_s)
+ break cache[mod] = freezer if freezer
+ end
+ cache[mod]
+ end
+
+ # Look up the Freezer descendant by object type
+ #
+ # @example
+ # freezer_class = IceNine::Freezer[mod]
+ #
+ # @param [Module] mod
+ #
+ # @return [Class<Freezer>]
+ #
+ # @api public
+ def self.[](mod)
+ @freezer_cache[mod]
+ end
+
+ # Deep freeze an object with a particular Freezer
+ #
+ # @see IceNine.deep_freeze
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ #
+ # @api public
+ def self.deep_freeze(object)
+ guarded_deep_freeze(object, RecursionGuard::ObjectSet.new)
+ end
+
+ # Deep freeze an object with a particular Freezer
+ #
+ # @see IceNine.deep_freeze!
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ #
+ # @api public
+ def self.deep_freeze!(object)
+ guarded_deep_freeze(object, RecursionGuard::Frozen.new)
+ end
+
+ # Find a Freezer descendant by name
+ #
+ # @param [String] name
+ #
+ # @return [Class<Freezer>]
+ # returned if a matching freezer is found
+ # @return [nil]
+ # returned if no matching freezer is found
+ #
+ # @api private
+ def self.find(name)
+ freezer = name.split('::').reduce(self) do |mod, const|
+ mod.const_lookup(const) or break mod
+ end
+ freezer if freezer < self # only return a descendant freezer
+ end
+
+ private_class_method :find
+
+ # Look up a constant in the namespace
+ #
+ # @param [String] namespace
+ #
+ # @return [Module]
+ # returned if a matching constant is found
+ # @return [nil]
+ # returned if no matching constant is found
+ #
+ # @api private
+ def self.const_lookup(namespace)
+ const_get(namespace) if const_defined?(namespace, nil)
+ end
+
+ # Deep freeze an object with a particular Freezer and RecursionGuard
+ #
+ # @param [Object] object
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [Object]
+ #
+ # @api private
+ def self.guarded_deep_freeze(object, recursion_guard)
+ recursion_guard.guard(object) do
+ Freezer[object.class].guarded_deep_freeze(object, recursion_guard)
+ end
+ end
+
+ class << self
+ protected :const_lookup, :guarded_deep_freeze
+ end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class for handling Array objects
+ class Array < Object
+
+ # Deep Freeze an Array
+ #
+ # @example
+ # array = IceNine:Freezer::Array.deep_freeze(%w[a b c])
+ # array.select(&:frozen?) # => ['a', 'b', 'c']
+ #
+ # @param [Array] array
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [Array]
+ def self.guarded_deep_freeze(array, recursion_guard)
+ super
+ array.each do |entry|
+ Freezer.guarded_deep_freeze(entry, recursion_guard)
+ end
+ end
+
+ end # Array
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing false objects
+ class FalseClass < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class for handling Hash objects
+ class Hash < Object
+
+ # Deep Freeze a Hash
+ #
+ # @example
+ # hash = IceNine::Freezer::Hash.deep_freeze('a' => '1', 'b' => '2')
+ # hash.keys.select(&:frozen?) # => ['a', 'b']
+ # hash.values.select(&:frozen?) # => ['1', '2']
+ #
+ # @param [Hash] hash
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [Hash]
+ def self.guarded_deep_freeze(hash, recursion_guard)
+ super
+ default = hash.default_proc || hash.default
+ Freezer.guarded_deep_freeze(default, recursion_guard)
+ freeze_key_value_pairs(hash, recursion_guard)
+ end
+
+ # Handle freezing the key/value pairs
+ #
+ # @param [Hash] hash
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [undefined]
+ #
+ # @api private
+ def self.freeze_key_value_pairs(hash, recursion_guard)
+ hash.each do |key, value|
+ Freezer.guarded_deep_freeze(key, recursion_guard)
+ Freezer.guarded_deep_freeze(value, recursion_guard)
+ end
+ end
+
+ private_class_method :freeze_key_value_pairs
+
+ end # Hash
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+ class Hash
+
+ # Skip freezing Hash::State objects on Rubinius
+ class State < NoFreeze; end
+
+ end # Hash
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing Module objects
+ class Module < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing nil objects
+ class NilClass < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class that does not freeze anything
+ class NoFreeze < self
+
+ # Pass through the object without freezing it
+ #
+ # @example
+ # object = IceNine::Freezer::NoFreeze.deep_freeze(object)
+ # object.frozen? # => false
+ #
+ # @param [Object] object
+ # @param [RecursionGuard] _recursion_guard
+ #
+ # @return [Object]
+ def self.guarded_deep_freeze(object, _recursion_guard)
+ object
+ end
+
+ end # NoFreeze
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing Numeric objects
+ class Numeric < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class for handling Object instances
+ class Object < self
+
+ # Deep Freeze an object
+ #
+ # @example
+ # object = IceNine.deep_freeze(Object.new)
+ #
+ # @param [Object] object
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [Object]
+ def self.guarded_deep_freeze(object, recursion_guard)
+ object.freeze
+ freeze_instance_variables(object, recursion_guard)
+ object
+ end
+
+ # Handle freezing the object's instance variables
+ #
+ # @param [Object] object
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [undefined]
+ #
+ # @api private
+ def self.freeze_instance_variables(object, recursion_guard)
+ object.instance_variables.each do |ivar_name|
+ Freezer.guarded_deep_freeze(
+ object.instance_variable_get(ivar_name),
+ recursion_guard
+ )
+ end
+ end
+
+ private_class_method :freeze_instance_variables
+
+ end # Object
+
+ BasicObject = Object
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class for handling Range objects
+ class Range < Object
+
+ # Deep Freeze a Range
+ #
+ # @example
+ # range = IceNine:Freezer::Range.deep_freeze('a'..'z')
+ # range.begin.frozen? # => true
+ # range.end.frozen? # => true
+ #
+ # @param [Range] range
+ # @param [RecursionGuard] recursion_guard
+ #
+ # @return [Range]
+ def self.guarded_deep_freeze(range, recursion_guard)
+ super
+ Freezer.guarded_deep_freeze(range.begin, recursion_guard)
+ Freezer.guarded_deep_freeze(range.end, recursion_guard)
+ range
+ end
+
+ end # Range
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing Rubinius objects
+ class Rubinius < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # A freezer class for handling Struct objects
+ class Struct < Array; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing Symbol objects
+ class Symbol < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+ class Freezer
+
+ # Skip freezing true objects
+ class TrueClass < NoFreeze; end
+
+ end # Freezer
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+
+ # Protect against infinite recursion
+ #
+ # @private
+ class RecursionGuard
+
+ # Protects against infinite recursion by never yielding with the same
+ # object more than once.
+ class ObjectSet < self
+
+ # Initialize a recursion guard
+ #
+ # @return [undefined]
+ def initialize
+ @object_ids = {}
+ end
+
+ # Guard against recursively calling a block with the same object
+ #
+ # @example
+ # recursion_guard = IceNine::RecursionGuard::ObjectSet.new
+ # recursion_guard.guard(object) do
+ # logic_which_may_be_recursively_called_with_object(recursion_guard)
+ # end
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ def guard(object)
+ caller_object_id = object.__id__
+ return object if @object_ids.key?(caller_object_id)
+ @object_ids[caller_object_id] = nil
+ yield
+ end
+
+ end # ObjectSet
+
+ # Protects against infinite recursion by not yielding with frozen objects
+ class Frozen < self
+
+ # Guard against recursively calling a block with the same frozen object
+ #
+ # @param [Object] object
+ #
+ # @return [Object]
+ def guard(object)
+ return object if object.frozen?
+ yield
+ end
+
+ end # Frozen
+
+ end # RecursionGuard
+end # IceNine
--- /dev/null
+# encoding: utf-8
+
+module IceNine
+
+ # Gem version
+ VERSION = '0.11.0'.freeze
+
+end # IceNine
--- /dev/null
+--- !ruby/object:Gem::Specification
+name: ice_nine
+version: !ruby/object:Gem::Version
+ version: 0.11.0
+platform: ruby
+authors:
+- Dan Kubb
+autorequire:
+bindir: bin
+cert_chain: []
+
+date: 2013-12-19 00:00:00 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: bundler
+ prerelease: false
+ requirement: &id001 !ruby/object:Gem::Requirement
+ requirements:
+ - - ~>
+ - !ruby/object:Gem::Version
+ version: "1.3"
+ - - ">="
+ - !ruby/object:Gem::Version
+ version: 1.3.5
+ type: :development
+ version_requirements: *id001
+description: Deep Freeze Ruby Objects
+email:
+- dan.kubb@gmail.com
+executables: []
+
+extensions: []
+
+extra_rdoc_files:
+- LICENSE
+- README.md
+- TODO
+files:
+- .gitignore
+- .pelusa.yml
+- .rspec
+- .ruby-gemset
+- .travis.yml
+- .yardopts
+- CONTRIBUTING.md
+- Gemfile
+- Gemfile.devtools
+- Guardfile
+- LICENSE
+- README.md
+- Rakefile
+- TODO
+- benchmarks/speed.rb
+- config/devtools.yml
+- config/flay.yml
+- config/flog.yml
+- config/mutant.yml
+- config/reek.yml
+- config/roodi.yml
+- config/rubocop.yml
+- config/yardstick.yml
+- ice_nine.gemspec
+- lib/ice_nine.rb
+- lib/ice_nine/core_ext/object.rb
+- lib/ice_nine/freezer.rb
+- lib/ice_nine/freezer/array.rb
+- lib/ice_nine/freezer/false_class.rb
+- lib/ice_nine/freezer/hash.rb
+- lib/ice_nine/freezer/hash/state.rb
+- lib/ice_nine/freezer/module.rb
+- lib/ice_nine/freezer/nil_class.rb
+- lib/ice_nine/freezer/no_freeze.rb
+- lib/ice_nine/freezer/numeric.rb
+- lib/ice_nine/freezer/object.rb
+- lib/ice_nine/freezer/range.rb
+- lib/ice_nine/freezer/rubinius.rb
+- lib/ice_nine/freezer/struct.rb
+- lib/ice_nine/freezer/symbol.rb
+- lib/ice_nine/freezer/true_class.rb
+- lib/ice_nine/support/recursion_guard.rb
+- lib/ice_nine/version.rb
+- spec/integration/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/integration/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/shared/array_deep_freeze_shared_spec.rb
+- spec/shared/hash_deep_freeze_shared_spec.rb
+- spec/shared/ice_nine_deep_freeze_shared_spec.rb
+- spec/shared/no_freeze_deep_freeze_shared_spec.rb
+- spec/shared/object_deep_freeze_shared_spec.rb
+- spec/shared/range_deep_freeze_shared_spec.rb
+- spec/spec_helper.rb
+- spec/support/config_alias.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/array/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/class_methods/element_reader_spec.rb
+- spec/unit/ice_nine/freezer/false_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/hash/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/module/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/nil_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/no_freeze/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/numeric/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/object/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/range/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/struct/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/symbol/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/true_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/recursion_guard/frozen/guard_spec.rb
+- spec/unit/ice_nine/recursion_guard/object_set/guard_spec.rb
+- spec/unit/object/deep_freeze_spec.rb
+homepage: https://github.com/dkubb/ice_nine
+licenses:
+- MIT
+metadata: {}
+
+post_install_message:
+rdoc_options: []
+
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ requirements:
+ - &id002
+ - ">="
+ - !ruby/object:Gem::Version
+ version: "0"
+required_rubygems_version: !ruby/object:Gem::Requirement
+ requirements:
+ - *id002
+requirements: []
+
+rubyforge_project:
+rubygems_version: 2.1.10
+signing_key:
+specification_version: 4
+summary: Deep Freeze Ruby Objects
+test_files:
+- spec/integration/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/integration/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_bang_spec.rb
+- spec/unit/ice_nine/core_ext/object/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/array/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/class_methods/element_reader_spec.rb
+- spec/unit/ice_nine/freezer/false_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/hash/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/module/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/nil_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/no_freeze/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/numeric/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/object/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/range/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/struct/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/symbol/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/freezer/true_class/class_methods/deep_freeze_spec.rb
+- spec/unit/ice_nine/recursion_guard/frozen/guard_spec.rb
+- spec/unit/ice_nine/recursion_guard/object_set/guard_spec.rb
+- spec/unit/object/deep_freeze_spec.rb
+has_rdoc:
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+require 'delegate'
+
+describe IceNine, '.deep_freeze!' do
+ subject { object.deep_freeze!(value) }
+
+ let(:object) { IceNine }
+
+ context 'with a shallowly frozen value' do
+ let(:value) { %w[a b].freeze }
+
+ it 'does not deep freeze' do
+ expect(subject.select(&:frozen?)).to be_empty
+ end
+ end
+
+ it_should_behave_like 'IceNine.deep_freeze'
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+require 'delegate'
+
+describe IceNine, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { IceNine }
+
+ context 'with a shallowly frozen value' do
+ let(:value) { ['a', %w[b c]].freeze }
+
+ it 'does a deep freeze' do
+ expect(subject.select(&:frozen?)).to eql(value)
+ end
+ end
+
+ it_should_behave_like 'IceNine.deep_freeze'
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine::Freezer::Array.deep_freeze' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+
+ it 'freezes each entry' do
+ expect(subject.select(&:frozen?)).to eql(subject.to_a)
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine::Freezer::Hash.deep_freeze' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+
+ it 'freezes each key' do
+ expect(subject.keys.select(&:frozen?)).to eql(subject.keys)
+ end
+
+ it 'freezes each value' do
+ expect(subject.values.select(&:frozen?)).to eql(subject.values)
+ end
+
+ if RUBY_VERSION >= '1.9' && RUBY_ENGINE == 'rbx'
+ it 'does not freeze the state' do
+ expect(subject.instance_variable_get(:@state)).to_not be_frozen
+ end
+
+ it 'does not freeze the entries' do
+ expect(subject.instance_variable_get(:@entries)).to_not be_frozen
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine.deep_freeze' do
+
+ context 'with an Object' do
+ let(:value) { Object.new }
+
+ before do
+ value.instance_eval { @a = '1' }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the Object' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+
+ context 'with a circular reference' do
+ before do
+ value.instance_eval { @self = self }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the Object' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+ end
+ end
+
+ context 'with an Array' do
+ let(:value) { %w[a] }
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each element in the Array' do
+ expect(subject.select(&:frozen?)).to eql(subject)
+ end
+
+ context 'with a circular reference' do
+ before do
+ value << value
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each element in the Array' do
+ expect(subject.select(&:frozen?)).to eql(subject)
+ end
+ end
+ end
+
+ context 'with a Hash' do
+ let(:value) { { Object.new => Object.new } }
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each key in the Hash' do
+ expect(subject.keys.select(&:frozen?)).to eql(subject.keys)
+ end
+
+ it 'freezes each value in the Hash' do
+ expect(subject.values.select(&:frozen?)).to eql(subject.values)
+ end
+
+ context 'with a circular reference' do
+ before do
+ value[value] = value
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each key in the Hash' do
+ expect(subject.keys.select(&:frozen?)).to eql(subject.keys)
+ end
+
+ it 'freezes each value in the Hash' do
+ expect(subject.values.select(&:frozen?)).to eql(subject.values)
+ end
+ end
+ end
+
+ context 'with a Range' do
+ let(:value) { 'a'..'z' }
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freeze the first object in the Range' do
+ expect(subject.begin).to be_frozen
+ end
+
+ it 'freeze the last object in the Range' do
+ expect(subject.end).to be_frozen
+ end
+ end
+
+ context 'with a String' do
+ let(:value) { '' }
+
+ before do
+ value.instance_eval { @a = '1' }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the String' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+
+ context 'with a circular reference' do
+ before do
+ value.instance_eval { @self = self }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the String' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+ end
+ end
+
+ context 'with a Struct' do
+ let(:value) { klass.new(%w[ 1 2 ]) }
+ let(:klass) { Struct.new(:a) }
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each value in the Struct' do
+ expect(subject.values.select(&:frozen?)).to eql(subject.values)
+ end
+
+ context 'with a circular reference' do
+ before do
+ value.a = value
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes each value in the Struct' do
+ expect(subject.values.select(&:frozen?)).to eql(subject.values)
+ end
+ end
+ end
+
+ context 'with an SimpleDelegator' do
+ let(:value) { SimpleDelegator.new(nil) }
+
+ before do
+ value.instance_eval { @a = '1' }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the SimpleDelegator' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+
+ context 'with a circular reference' do
+ before do
+ value.instance_eval { @self = self }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes the instance variables in the SimpleDelegator' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+ end
+ end
+
+ [0.0, 0, 0x7fffffffffffffff, true, false, nil, :symbol].each do |value|
+ context "with a #{value.class}" do
+ let(:value) { value }
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'does not freeze the object' do
+ expect { subject }.to_not change(value, :frozen?).from(false)
+ end
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine::Freezer::NoFreeze.deep_freeze' do
+ before do
+ value.instance_eval { @a = '1' } unless value.frozen?
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'does not freeze the object' do
+ expect { subject }.to_not change(value, :frozen?).from(false)
+ end
+
+ it 'does not freeze instance variables' do
+ expect(subject.instance_variable_get(:@a)).to_not be_frozen
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine::Freezer::Object.deep_freeze' do
+ before do
+ value.instance_eval { @a = '1' }
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'freezes the object' do
+ expect { subject }.to change(value, :frozen?).from(false).to(true)
+ end
+
+ it 'freezes instance variables' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+shared_examples 'IceNine::Freezer::Range.deep_freeze' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+
+ it 'freeze the first element' do
+ expect(subject.begin).to be_frozen
+ end
+
+ it 'freeze the last element' do
+ expect(subject.end).to be_frozen
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+if ENV['COVERAGE'] == 'true'
+ require 'simplecov'
+ require 'coveralls'
+
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
+ SimpleCov::Formatter::HTMLFormatter,
+ Coveralls::SimpleCov::Formatter
+ ]
+
+ SimpleCov.start do
+ command_name 'spec:unit'
+
+ add_filter 'config'
+ add_filter 'spec'
+ add_filter 'vendor'
+
+ minimum_coverage 100
+ end
+end
+
+require 'ice_nine'
+require 'devtools/spec_helper'
+
+RSpec.configure do |config|
+ config.expect_with :rspec do |expect_with|
+ expect_with.syntax = :expect
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'rbconfig'
+
+::Config = RbConfig unless defined?(::Config)
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine, '.deep_freeze!' do
+ subject { object.deep_freeze!(value) }
+
+ let(:object) { IceNine }
+ let(:value) { Object.new }
+
+ context 'when the object is not frozen' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+ end
+
+ context 'when the object is frozen' do
+ before do
+ value.instance_eval { @a = '1' }
+ value.freeze
+ end
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { IceNine }
+ let(:value) { Object.new }
+
+ context 'when the object is not frozen' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+ end
+
+ context 'when the object is frozen' do
+ before do
+ value.instance_eval { @a = '1' }
+ value.freeze
+ end
+
+ it 'returns the object' do
+ should be(value)
+ end
+
+ it 'leaves the object frozen' do
+ expect { subject }.not_to change(value, :frozen?).from(true)
+ end
+
+ it 'freezes instance variables' do
+ expect(subject.instance_variable_get(:@a)).to be_frozen
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+require 'ice_nine/core_ext/object'
+
+describe IceNine::CoreExt::Object, '#deep_freeze!' do
+ subject { value.deep_freeze! }
+
+ let(:value) { Object.new.extend(IceNine::CoreExt::Object) }
+
+ context 'when the object is not frozen' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+ end
+
+ context 'when the object is frozen' do
+ before do
+ value.instance_eval { @a = '1' }
+ value.freeze
+ end
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+require 'ice_nine/core_ext/object'
+
+describe IceNine::CoreExt::Object, '#deep_freeze' do
+ subject { value.deep_freeze }
+
+ let(:value) { Object.new.extend(IceNine::CoreExt::Object) }
+
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine::Freezer::Array, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with an Array object' do
+ let(:value) { %w[a] }
+
+ context 'without a circular reference' do
+ it_behaves_like 'IceNine::Freezer::Array.deep_freeze'
+ end
+
+ context 'with a circular reference' do
+ before { value << value }
+
+ it_behaves_like 'IceNine::Freezer::Array.deep_freeze'
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/object'
+require 'ice_nine/freezer/array'
+require 'ice_nine/freezer/struct'
+
+describe IceNine::Freezer, '.[]' do
+ subject { object[mod] }
+
+ let(:object) { described_class }
+ let(:freezer) { object::Object }
+
+ describe 'when the module matches a descendant' do
+ let(:freezer) { Class.new(object) }
+ let(:mod) { Class }
+
+ before do
+ object.const_set(mod.name, freezer)
+ end
+
+ after do
+ object.send(:remove_const, mod.name)
+ end
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module matches a descendant inside a namespace' do
+ let(:namespace) { Class.new(object) }
+ let(:freezer) { Class.new(object) }
+ let(:mod) { Application::User }
+
+ before :all do
+ module ::Application
+ class User; end
+ end
+ end
+
+ after :all do
+ ::Application.send(:remove_const, :User)
+ Object.send(:remove_const, :Application)
+ end
+
+ around do |example|
+ namespace.const_set(:User, freezer)
+ object.const_set(:Application, namespace)
+
+ example.run
+
+ namespace.send(:remove_const, :User)
+ object.send(:remove_const, :Application)
+ end
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module is a struct' do
+ let(:mod) { Struct.new(:a) }
+ let(:freezer) { IceNine::Freezer::Struct }
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module does not match a descendant' do
+ let(:mod) { Object }
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module is an anonymous class' do
+ let(:mod) { Class.new }
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module is an anonymous module' do
+ let(:mod) { Module.new }
+
+ it 'returns the freezer' do
+ should be_nil
+ end
+ end
+
+ describe 'when the module is under a freezer namespace' do
+ let(:mod) { Hash::Test }
+ let(:freezer) { IceNine::Freezer::Hash }
+
+ around do |example|
+ class Hash::Test; end
+ example.run
+ Hash.send(:remove_const, :Test)
+ end
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+
+ describe 'when the module has a name of a freezer in another namespace' do
+ let(:mod) { Mash::State }
+ let(:freezer) { Class.new(IceNine::Freezer::Hash) }
+
+ before :all do
+ module ::Mash
+ class State; end
+ end
+ end
+
+ after :all do
+ ::Mash.send(:remove_const, :State)
+ Object.send(:remove_const, :Mash)
+ end
+
+ around do |example|
+ object.const_set(:Mash, freezer)
+
+ example.run
+
+ object.send(:remove_const, :Mash)
+ end
+
+ it 'returns the freezer' do
+ should be(freezer)
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/false_class'
+
+describe IceNine::Freezer::FalseClass, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a false object' do
+ let(:value) { false }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine::Freezer::Hash, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a Hash object having a default proc' do
+ let(:value) do
+ Hash.new { }.update(Object.new => Object.new)
+ end
+
+ it_behaves_like 'IceNine::Freezer::Hash.deep_freeze'
+
+ it 'freezes the default proc' do
+ expect(subject.default_proc).to be_frozen
+ end
+ end
+
+ context 'with a Hash object having a default value' do
+ let(:value) do
+ Hash.new('').update(Object.new => Object.new)
+ end
+
+ it_behaves_like 'IceNine::Freezer::Hash.deep_freeze'
+
+ it 'freezes the default value' do
+ expect(subject.default).to be_frozen
+ end
+
+ context 'that is a circular reference' do
+ before { value.default = value }
+
+ it_behaves_like 'IceNine::Freezer::Hash.deep_freeze'
+
+ it 'freezes the default value' do
+ expect(subject.default).to be_frozen
+ end
+ end
+ end
+
+ context 'with a Hash object containing itself as a key' do
+ let(:value) do
+ value = {}
+ value[value] = '1'
+ value
+ end
+
+ it_behaves_like 'IceNine::Freezer::Hash.deep_freeze'
+ end
+
+ context 'with a Hash object containing itself as a value' do
+ let(:value) do
+ value = {}
+ value['a'] = value
+ value
+ end
+
+ it_behaves_like 'IceNine::Freezer::Hash.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/module'
+
+describe IceNine::Freezer::Module, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a Module object' do
+ let(:value) { Module.new }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/nil_class'
+
+describe IceNine::Freezer::NilClass, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a nil object' do
+ let(:value) { nil }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer/no_freeze'
+
+describe IceNine::Freezer::NoFreeze, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+ let(:value) { double('value') }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/numeric'
+require 'bigdecimal'
+
+describe IceNine::Freezer::Numeric, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ [0.0, 0, 0x7fffffffffffffff, BigDecimal('0')].each do |value|
+ context "with a #{value.class} object" do
+ let(:value) { value }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine::Freezer::Object, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with an Object' do
+ let(:value) { Object.new }
+
+ context 'without a circular reference' do
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+ end
+
+ context 'with a circular reference' do
+ before do
+ value.instance_eval { @b = self }
+ end
+
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine::Freezer::Range, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ let(:element_class) do
+ Class.new do
+ attr_reader :number, :range
+ protected :number, :range
+
+ def initialize(number, range = nil)
+ @number = number
+ @range = nil
+ end
+
+ def succ
+ self.class.new(number.succ, range)
+ end
+
+ def <=>(other)
+ range <=> other.range && number <=> other.number
+ end
+
+ # allow for circular references
+ def range=(range)
+ @range = range
+ end
+ end
+ end
+
+ context 'with a Range' do
+ let(:value) { element_class.new(1)..element_class.new(100) }
+
+ context 'without a circular reference' do
+ it_behaves_like 'IceNine::Freezer::Range.deep_freeze'
+ end
+
+ context 'with a circular reference in the first element' do
+ before { value.begin.range = value }
+
+ it_behaves_like 'IceNine::Freezer::Range.deep_freeze'
+ end
+
+ context 'with a circular reference in the last element' do
+ before { value.end.range = value }
+
+ it_behaves_like 'IceNine::Freezer::Range.deep_freeze'
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+
+describe IceNine::Freezer::Struct, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a Struct' do
+ let(:value) { klass.new('1') }
+ let(:klass) { Struct.new(:a) }
+
+ it_behaves_like 'IceNine::Freezer::Array.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/symbol'
+
+describe IceNine::Freezer::Symbol, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a Symbol object' do
+ let(:value) { :symbol }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/freezer'
+require 'ice_nine/freezer/object'
+require 'ice_nine/freezer/no_freeze'
+require 'ice_nine/freezer/true_class'
+
+describe IceNine::Freezer::TrueClass, '.deep_freeze' do
+ subject { object.deep_freeze(value) }
+
+ let(:object) { described_class }
+
+ context 'with a true object' do
+ let(:value) { true }
+
+ it_behaves_like 'IceNine::Freezer::NoFreeze.deep_freeze'
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/support/recursion_guard'
+
+describe IceNine::RecursionGuard::Frozen, '#guard' do
+ subject { object.guard(object_arg) { return_value } }
+
+ let(:object) { IceNine::RecursionGuard::Frozen.new }
+ let(:object_arg) { Object.new }
+ let(:return_value) { double('return_value') }
+
+ context 'when the object_arg is not frozen' do
+ it 'returns the expected value' do
+ should be(return_value)
+ end
+ end
+
+ context 'when the object_arg is frozen' do
+ before do
+ object_arg.freeze
+ end
+
+ it 'returns the expected value' do
+ should be(object_arg)
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine/support/recursion_guard'
+
+describe IceNine::RecursionGuard::ObjectSet, '#guard' do
+ let(:object) { IceNine::RecursionGuard::ObjectSet.new }
+ let(:object_arg) { Object.new }
+ let(:return_value) { double('return_value') }
+
+ context 'when the block is not recursive' do
+ subject { object.guard(object_arg) { return_value } }
+
+ it 'returns the expected value' do
+ should be(return_value)
+ end
+ end
+
+ context 'when the block is recursive' do
+ subject do
+ object.guard(object_arg) do
+ expect(subject).to be(object_arg)
+ return_value
+ end
+ end
+
+ it 'returns the expected value' do
+ should be(return_value)
+ end
+ end
+end
--- /dev/null
+# encoding: utf-8
+
+require 'spec_helper'
+require 'ice_nine'
+require 'ice_nine/core_ext/object'
+
+describe Object, '#deep_freeze' do
+ subject { value.deep_freeze }
+
+ let(:value) { described_class.new }
+
+ it_behaves_like 'IceNine::Freezer::Object.deep_freeze'
+end