Initial release of constantize_with_care
authorHenryk Gerlach <hg@littleimpact.de>
Sun Aug 10 12:36:05 2008 +0200 (2008-08-10)
changeset 0c1f1a648eb21
child 1 09ae946778c2
Initial release of constantize_with_care
README
Rakefile
init.rb
lib/constantize_with_care.rb
test/constantize_with_care_test.rb
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/README	Sun Aug 10 12:36:05 2008 +0200
     1.3 @@ -0,0 +1,40 @@
     1.4 += Constantize with Care
     1.5 +
     1.6 +== License
     1.7 +Copyright (c) 2008 Henryk Gerach, released under the MIT license
     1.8 +see lib/constantize_with_care.rb
     1.9 +
    1.10 +== Homepage
    1.11 +http://www.littleimpact.de/hg/constantize_with_care/
    1.12 +
    1.13 +== Description
    1.14 +Constantize wit Care protects the constantize against 'class injection' 
    1.15 +(i.e. the constantization of unintended classes) by checking the to be 
    1.16 +constantized string against a whitelist of +allowed_classes+. 
    1.17 +
    1.18 +The whitelist of +allowed_classes+ may be a set (optimized performance) or 
    1.19 +an array of strings, a set or an array of classes or a regular expression 
    1.20 +(disrecommend since difficult).
    1.21 +
    1.22 +If the string is not allowed to be constantized an exception is raised.
    1.23 +The +exception+ defaults to RuntimeError and can be overidden.
    1.24 +
    1.25 +The method constantize_with_care is added to the String class.
    1.26 +
    1.27 +== Examples:
    1.28 +  # A Set of strings should be the fastest implementation:
    1.29 +  # ConstantSetOfStringsOfAllowedClasses = Set.new ["String","Fixnum"]
    1.30 +  # or possibly more convenient:
    1.31 +  ConstantSetOfStringsOfAllowedClasses = Set.new [String,Fixnum].map(&:to_s)
    1.32 +
    1.33 +  "String".constantize_with_care(ConstantSetOfStringsOfAllowedClasses) #=> String
    1.34 +  "Float".constantize_with_care(ConstantSetOfStringsOfAllowedClasses)  #=> raises RuntimeError
    1.35 +
    1.36 +  # For the lazy
    1.37 +  "String".constantize_with_care([String,Fixnum])           #=> String
    1.38 +  "Float".constantize_with_care([String,Fixnum], Exception) #=> raises Exception
    1.39 +
    1.40 +  # For the daring
    1.41 +  # Everything that starts with S is okay:
    1.42 +  "String".constantize_with_care(/^S/)           #=> String
    1.43 +  "Float".constantize_with_care(/^S/)            #=> raises RuntimeError
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Rakefile	Sun Aug 10 12:36:05 2008 +0200
     2.3 @@ -0,0 +1,10 @@
     2.4 +require 'rake'
     2.5 +require 'rake/testtask'
     2.6 +
     2.7 +desc "Default task"
     2.8 +task :default => [ :test ]
     2.9 +
    2.10 +Rake::TestTask.new do |t|
    2.11 +  t.test_files = Dir["test/**/*_test.rb"]
    2.12 +  t.verbose = true
    2.13 +end
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/init.rb	Sun Aug 10 12:36:05 2008 +0200
     3.3 @@ -0,0 +1,2 @@
     3.4 +require 'constantize_with_care'
     3.5 +String.send :include, ConstantizeWithCare
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/lib/constantize_with_care.rb	Sun Aug 10 12:36:05 2008 +0200
     4.3 @@ -0,0 +1,39 @@
     4.4 +# Copyright (c) 2008 Henryk Gerlach
     4.5 +#
     4.6 +# Permission is hereby granted, free of charge, to any person obtaining
     4.7 +# a copy of this software and associated documentation files (the
     4.8 +# "Software"), to deal in the Software without restriction, including
     4.9 +# without limitation the rights to use, copy, modify, merge, publish,
    4.10 +# distribute, sublicense, and/or sell copies of the Software, and to
    4.11 +# permit persons to whom the Software is furnished to do so, subject to
    4.12 +# the following conditions:
    4.13 +#
    4.14 +# The above copyright notice and this permission notice shall be
    4.15 +# included in all copies or substantial portions of the Software.
    4.16 +#
    4.17 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    4.18 +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    4.19 +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    4.20 +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
    4.21 +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
    4.22 +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    4.23 +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    4.24 +module ConstantizeWithCare
    4.25 +  def constantize_with_care(allowed_classes, exception="I'm not allowed to constantize \`#{self}'!")
    4.26 +    ok = false
    4.27 +    if allowed_classes.respond_to? :include? and allowed_classes.include?(self)
    4.28 +      ok = true
    4.29 +    elsif not ok and allowed_classes.is_a? Regexp and allowed_classes =~ self
    4.30 +      ok = true
    4.31 +    elsif not ok and allowed_classes.respond_to? :each
    4.32 +      allowed_classes.each do |klass|
    4.33 +        if self == klass.to_s
    4.34 +          ok = true
    4.35 +          break
    4.36 +        end
    4.37 +      end
    4.38 +    end
    4.39 +    raise exception unless ok
    4.40 +    self.constantize
    4.41 +  end
    4.42 +end
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/constantize_with_care_test.rb	Sun Aug 10 12:36:05 2008 +0200
     5.3 @@ -0,0 +1,71 @@
     5.4 +require 'test/unit'
     5.5 +require 'set'
     5.6 +begin
     5.7 +  require 'active_support'
     5.8 +rescue LoadError
     5.9 +  if ENV['ACTIVESUPPORT_PATH'].nil?
    5.10 +    abort <<END
    5.11 +Please set the ACTIVESUPPORT_PATH environment variable to the directory
    5.12 +containing the active_support.rb file.
    5.13 +END
    5.14 +  else
    5.15 +    $LOAD_PATH.unshift ENV['ACTIVESUPPORT_PATH']
    5.16 +    begin
    5.17 +      require 'active_support'
    5.18 +    rescue LoadError
    5.19 +      abort "ActiveSupport could not be found."
    5.20 +    end
    5.21 +  end
    5.22 +end
    5.23 +
    5.24 +require "#{File.dirname(__FILE__)}/../lib/constantize_with_care"
    5.25 +String.send :include, ConstantizeWithCare
    5.26 +
    5.27 +class ConstantizeWithCareTest< Test::Unit::TestCase
    5.28 +
    5.29 +  class MyException < Exception
    5.30 +  end
    5.31 +
    5.32 +  def test_default_exception
    5.33 +    e = assert_raise(RuntimeError) {
    5.34 +      "Float".constantize_with_care(["String","Fixnum"])
    5.35 +    }
    5.36 +    assert_equal "I'm not allowed to constantize `Float'!", e.to_s
    5.37 +  end
    5.38 +
    5.39 +  def test_exception_override
    5.40 +    assert_raise(MyException) {
    5.41 +      "Float".constantize_with_care(["String","Fixnum"], MyException)
    5.42 +    }
    5.43 +  end
    5.44 +
    5.45 +  def test_set_of_strings
    5.46 +    set_of_strings = [String,Fixnum].map{|k| k.to_s}.to_set
    5.47 +    assert_equal String, "String".constantize_with_care(set_of_strings)
    5.48 +    assert_raise(RuntimeError) {"Float".constantize_with_care(set_of_strings)}
    5.49 +  end
    5.50 +
    5.51 +  def test_set_of_classes
    5.52 +    set_of_classes= [String,Fixnum].to_set
    5.53 +    assert_equal String, "String".constantize_with_care(set_of_classes)
    5.54 +    assert_raise(RuntimeError) {"Float".constantize_with_care(set_of_classes)}
    5.55 +  end
    5.56 +
    5.57 +  def test_array_of_strings
    5.58 +    array_of_strings = [String,Fixnum].map{|k| k.to_s}
    5.59 +    assert_equal String, "String".constantize_with_care(array_of_strings)
    5.60 +    assert_raise(RuntimeError) {"Float".constantize_with_care(array_of_strings)}
    5.61 +  end
    5.62 +
    5.63 +  def test_array_of_classes
    5.64 +    array_of_classes= [String,Fixnum]
    5.65 +    assert_equal String, "String".constantize_with_care(array_of_classes)
    5.66 +    assert_raise(RuntimeError) {"Float".constantize_with_care(array_of_classes)}
    5.67 +  end
    5.68 +
    5.69 +  def test_regexp
    5.70 +    regexp= /^Str/
    5.71 +    assert_equal String, "String".constantize_with_care(regexp)
    5.72 +    assert_raise(RuntimeError) {"Float".constantize_with_care(regexp)}
    5.73 +  end
    5.74 +end