to_robust 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +39 -0
  3. data/lib/global/fix.rb +72 -0
  4. data/lib/global/fix/add_atom.rb +28 -0
  5. data/lib/global/fix/atom.rb +51 -0
  6. data/lib/global/fix/init.rb +4 -0
  7. data/lib/global/fix/remove_atom.rb +16 -0
  8. data/lib/global/fix/swap_atom.rb +26 -0
  9. data/lib/global/global.rb +155 -0
  10. data/lib/global/init.rb +10 -0
  11. data/lib/global/kernel/argument_error.rb +22 -0
  12. data/lib/global/kernel/exception.rb +9 -0
  13. data/lib/global/kernel/init.rb +4 -0
  14. data/lib/global/kernel/no_method_error.rb +15 -0
  15. data/lib/global/kernel/zero_division_error.rb +21 -0
  16. data/lib/global/report.rb +42 -0
  17. data/lib/global/robust_proc.rb +48 -0
  18. data/lib/global/strategies/divide_by_zero_error_strategy.rb +277 -0
  19. data/lib/global/strategies/init.rb +5 -0
  20. data/lib/global/strategies/no_method_error_strategy.rb +92 -0
  21. data/lib/global/strategies/wrong_arguments_error_strategy.rb +174 -0
  22. data/lib/global/strategy.rb +67 -0
  23. data/lib/local/init.rb +6 -0
  24. data/lib/local/kernel/init.rb +1 -0
  25. data/lib/local/kernel/module.rb +43 -0
  26. data/lib/local/local.rb +43 -0
  27. data/lib/local/strategies/bignum_division_strategy.rb +11 -0
  28. data/lib/local/strategies/bignum_modulus_strategy.rb +11 -0
  29. data/lib/local/strategies/fixnum_coerce_strategy.rb +11 -0
  30. data/lib/local/strategies/fixnum_division_strategy.rb +12 -0
  31. data/lib/local/strategies/fixnum_modulus_strategy.rb +12 -0
  32. data/lib/local/strategies/float_division_strategy.rb +11 -0
  33. data/lib/local/strategies/float_modulus_strategy.rb +12 -0
  34. data/lib/local/strategies/init.rb +37 -0
  35. data/lib/local/strategies/numeric_division_strategy.rb +11 -0
  36. data/lib/local/strategies/numeric_modulus_strategy.rb +12 -0
  37. data/lib/local/strategies/soft_binding_strategy.rb +108 -0
  38. data/lib/local/strategies/swap_method_strategy.rb +32 -0
  39. data/lib/local/strategy.rb +18 -0
  40. data/lib/to_robust.rb +4 -0
  41. data/test/local/float_division.rb +28 -0
  42. data/test/local/integer_division.rb +28 -0
  43. data/test/local/method_binding.rb +45 -0
  44. metadata +109 -0
@@ -0,0 +1,11 @@
1
+ # Ensures that integer division returns 0 if the denominator is zero.
2
+ class ToRobust::Local::Strategies::NumericDivisionStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
3
+
4
+ # Constructs a new NumericDivisionStrategy.
5
+ def initialize
6
+ super(Numeric, :div, :__div) do |other|
7
+ other.zero? ? 0 : __div(other)
8
+ end
9
+ end
10
+
11
+ end
@@ -0,0 +1,12 @@
1
+ # Ensures that the modulus operator returns 0 if a division by zero
2
+ # error occurs.
3
+ class ToRobust::Local::Strategies::NumericModulusStrategy < ToRobust::Local::Strategies::SwapMethodStrategy
4
+
5
+ # Constructs a new NumericModulusStrategy.
6
+ def initialize
7
+ super(Numeric, :divmod, :__divmod) do |other|
8
+ other.zero? ? 0 : __divmod(other)
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,108 @@
1
+ require 'levenshtein'
2
+
3
+ # Exploits the 'method_missing' hook to implement a custom message passing protocol.
4
+ #
5
+ # All methods for each associated context are hidden by prepending their names with "__" and
6
+ # making them private. These hidden methods can later be accessed using the "hidden_methods"
7
+ # method on an associated context.
8
+ #
9
+ # All method calls will fire the 'method_missing' callback, since the method is no longer
10
+ # attached to the object at the original address. The method missing callback then inspects the
11
+ # method call and attempts to find the nearest method to that which was requested (based
12
+ # on the Levenshtein distance between the requested method and a candidate method).
13
+ #
14
+ # This way method calls like 'ad(3,2)' and 'adda(3,2)' would map to 'add(3,2)', giving methods
15
+ # a soft binding.
16
+ #
17
+ # If the nearest method has an arity which does not match the number of parameters supplied to
18
+ # the method call, then:
19
+ # * If there are too few parameters, pad the list with zeroes (arbitrary choice!).
20
+ # * If there are too many parameters, restrict the length of the list to the arity of the method.
21
+ class ToRobust::Local::Strategies::SoftBindingStrategy < ToRobust::Local::Strategy
22
+
23
+ # Allow the maximum acceptable Levenshtein distance to a candidate method to be
24
+ # adjusted.
25
+ @max_distance = 5
26
+ class << self
27
+ attr_accessor :max_distance
28
+ end
29
+
30
+ # Caches the list of contexts for later use.
31
+ def prepare!(contexts)
32
+ @contexts = contexts
33
+ end
34
+
35
+ # Hides all methods attached to each context (by prepending their names with "__" and making
36
+ # them private) before attaching a method missing handler to each of the contexts.
37
+ def enable!
38
+ @contexts.each do |c|
39
+ c.hide_methods!
40
+ c.singleton_class.send(:define_method, :method_missing) do |method_name, *args, &block|
41
+
42
+ # Convert the method symbol to a string, ready for lookup.
43
+ method_name = method_name.to_s
44
+
45
+ # Maximum levenshtein distance allowed between a candidate and the requested method.
46
+ max_distance = RubyToRobust::Local::Strategies::SoftBindingStrategy.max_distance
47
+
48
+ # Cache the list of candidate methods.
49
+ candidates = hidden_methods
50
+
51
+ # If no method exists with the given name then find the method with the closest
52
+ # name (measured by levenshtein distance). Only consider candidates whose distance
53
+ # to the requested method is less or equal to the maximum distance.
54
+ unless candidates.key? method_name
55
+
56
+ best_candidate = nil
57
+ best_score = nil
58
+ candidates.each_key do |cname|
59
+
60
+ # Only calculate the distance if the difference in length between the requested
61
+ # and candidate methods is less or equal to the maximum distance.
62
+ unless (cname.length - method_name.length).abs > max_distance
63
+ distance = Levenshtein.distance(cname, method_name)
64
+ if distance <= max_distance and (best_score.nil? or distance < best_score)
65
+ best_candidate = cname
66
+ best_score = distance
67
+ end
68
+ end
69
+ end
70
+
71
+ # Attempt to call the best candidate.
72
+ # If no appropriate candidate is found, raise a NoMethodError.
73
+ raise NoMethodError if best_candidate.nil?
74
+ method_name = best_candidate
75
+
76
+ end
77
+
78
+ # Retrieve the method object for the selected method.
79
+ method = candidates[method_name]
80
+
81
+ # If no arguments are provided and the arity of this method is greater than zero,
82
+ # then return zero.
83
+ return 0 if method.arity > 0 and args.length == 0
84
+
85
+ # If fewer arguments than necessary are provided then pad the arguments with zeros.
86
+ args = args.fill(0, args.length...method.arity) if method.arity > args.length
87
+
88
+ # If more arguments than necessary are provided, restrict them to the function's
89
+ # arity.
90
+ args = args.first(method.arity) if method.arity != -1 and args.length > method.arity
91
+
92
+ # Call the method with the filtered arguments.
93
+ return method.call(*args)
94
+
95
+ end
96
+ end
97
+ end
98
+
99
+ # Restores all the hidden methods to their previous state and removes the method missing
100
+ # handler from each associated context.
101
+ def disable!
102
+ @contexts.each do |c|
103
+ c.unhide_methods!
104
+ c.singleton_class.send(:remove_method, :method_missing)
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,32 @@
1
+ # The swap method strategy is an abstract base strategy used by
2
+ # other strategies which operate by swapping a given method with
3
+ # an alternative which utilises soft semantics.
4
+ class ToRobust::Local::Strategies::SwapMethodStrategy < ToRobust::Local::Strategy
5
+
6
+ # Constructs a new swap method strategy.
7
+ #
8
+ # *Parameters:*
9
+ # * binding, the class or module of the method that should be patched.
10
+ # * name, the name of the method to be patched.
11
+ # * backup, the name to store the old method under.
12
+ # * &replacement, the replacement method itself.
13
+ def initialize(binding, name, backup, &replacement)
14
+ @binding = binding
15
+ @name = name
16
+ @backup = backup
17
+ @replacement = replacement
18
+ end
19
+
20
+ # Swaps the target method with its "softer" counterpart.
21
+ def enable!
22
+ @binding.send(:alias_method, @backup, @name)
23
+ @binding.send(:define_method, @name, @replacement)
24
+ end
25
+
26
+ # Restores the target method to its original "hard" semantics.
27
+ def disable!
28
+ @binding.send(:define_method, @name, @binding.instance_method(@backup))
29
+ @binding.send(:remove_method, @backup)
30
+ end
31
+
32
+ end
@@ -0,0 +1,18 @@
1
+ # Implements a local robustness strategy.
2
+ #
3
+ # These strategies are used to implement "softer" semantics in the program.
4
+ class ToRobust::Local::Strategy
5
+
6
+ # Prepares this strategy.
7
+ #
8
+ # *Parameters:*
9
+ # * contexts, a list of contexts that Local robustness is operating under.
10
+ def prepare!(contexts); end
11
+
12
+ # Enables this patch.
13
+ def enable!; end
14
+
15
+ # Disables this patch.
16
+ def disable!; end
17
+
18
+ end
data/lib/to_robust.rb ADDED
@@ -0,0 +1,4 @@
1
+ module ToRobust; end
2
+
3
+ require_relative 'global/init.rb'
4
+ require_relative 'local/init.rb'
@@ -0,0 +1,28 @@
1
+ require 'to_robust'
2
+ require 'test/unit'
3
+
4
+ class TestFloatDivision < Test::Unit::TestCase
5
+
6
+ def test_standard_behaviour
7
+ program = lambda { |x, y| x / y }
8
+ assert_equal(5.0, ToRobust::Local.protected { program[50.0, 10.0] })
9
+ assert_equal(5.0, program[50.0, 10.0])
10
+ end
11
+
12
+ def test_hard_dbz
13
+ program = lambda { |x, y| x / y }
14
+ assert_equal(Float::INFINITY, program[1.0, 0.0])
15
+ assert_equal(Float::INFINITY, program[2.0, 0.0])
16
+ assert_equal(Float::INFINITY, program[10.0, 0.0])
17
+ assert_equal(Float::INFINITY, program[255.0, 0.0])
18
+ end
19
+
20
+ def test_soft_dbz
21
+ program = lambda { |x, y| x / y }
22
+ assert_equal(0.0, ToRobust::Local.protected { program[1.0, 0.0] })
23
+ assert_equal(0.0, ToRobust::Local.protected { program[2.0, 0.0] })
24
+ assert_equal(0.0, ToRobust::Local.protected { program[10.0, 0.0] })
25
+ assert_equal(0.0, ToRobust::Local.protected { program[255.0, 0.0] })
26
+ end
27
+
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'to_robust'
2
+ require 'test/unit'
3
+
4
+ class TestIntegerDivision < Test::Unit::TestCase
5
+
6
+ def test_standard_behaviour
7
+ program = lambda { |x, y| x / y }
8
+ assert_equal(5, ToRobust::Local.protected { program[50, 10] })
9
+ assert_equal(5, program[50, 10])
10
+ end
11
+
12
+ def test_hard_dbz
13
+ program = lambda { |x, y| x / y }
14
+ assert_raise(ZeroDivisionError) { program[1, 0] }
15
+ assert_raise(ZeroDivisionError) { program[2, 0] }
16
+ assert_raise(ZeroDivisionError) { program[10, 0] }
17
+ assert_raise(ZeroDivisionError) { program[255, 0] }
18
+ end
19
+
20
+ def test_soft_dbz
21
+ program = lambda { |x, y| x / y }
22
+ assert_equal(0, ToRobust::Local.protected { program[1, 0] })
23
+ assert_equal(0, ToRobust::Local.protected { program[2, 0] })
24
+ assert_equal(0, ToRobust::Local.protected { program[10, 0] })
25
+ assert_equal(0, ToRobust::Local.protected { program[255, 0] })
26
+ end
27
+
28
+ end
@@ -0,0 +1,45 @@
1
+ require 'to_robust'
2
+ require 'test/unit'
3
+
4
+ module TestMethods
5
+ def self.add(x,y); x+y; end
6
+ def self.sub(x,y); x-y; end
7
+ def self.mul(x,y); x*y; end
8
+ end
9
+
10
+ class TestMethodBinding < Test::Unit::TestCase
11
+
12
+ def test_standard_bad_method_name
13
+ assert_raise(NoMethodError) { TestMethods.ad(3,2) }
14
+ assert_raise(NoMethodError) { TestMethods.addd(3,2) }
15
+ assert_raise(NoMethodError) { TestMethods.su(3,2) }
16
+ assert_raise(NoMethodError) { TestMethods.sab(3,2) }
17
+ assert_raise(NoMethodError) { TestMethods.mula(3,2) }
18
+ assert_raise(NoMethodError) { TestMethods.mu(3,2) }
19
+ end
20
+
21
+ def test_good_method_call
22
+
23
+ # Without Local robustness.
24
+ assert_equal(6, TestMethods.add(3,3))
25
+ assert_equal(9, TestMethods.mul(3,3))
26
+ assert_equal(0, TestMethods.sub(3,3))
27
+
28
+ # Using Local robustness.
29
+ assert_equal(6, RubyToRobust::Local.protected(TestMethods) { TestMethods.add(3,3) })
30
+ assert_equal(9, RubyToRobust::Local.protected(TestMethods) { TestMethods.mul(3,3) })
31
+ assert_equal(0, RubyToRobust::Local.protected(TestMethods) { TestMethods.sub(3,3) })
32
+
33
+ end
34
+
35
+
36
+ def test_soft_method_call
37
+
38
+ # Enable local robustness.
39
+ assert_equal(6, RubyToRobust::Local.protected(TestMethods) { TestMethods.ad(3,3) })
40
+ assert_equal(9, RubyToRobust::Local.protected(TestMethods) { TestMethods.ml(3,3) })
41
+ assert_equal(0, RubyToRobust::Local.protected(TestMethods) { TestMethods.sab(3,3) })
42
+
43
+ end
44
+
45
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: to_robust
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Chris Timperley
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-12-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: levenshtein
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.2.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.2.2
30
+ description: ! " Soft semantics for Ruby to automatically recover from fatal exceptions
31
+ and to repair damaged programs.\n Used to improve robustness, expressiveness
32
+ and performance in Grammatical Evolution.\n"
33
+ email: christimperley@gmail.com
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - LICENSE
39
+ - README.md
40
+ - lib/to_robust.rb
41
+ - lib/global/fix.rb
42
+ - lib/global/global.rb
43
+ - lib/global/init.rb
44
+ - lib/global/report.rb
45
+ - lib/global/robust_proc.rb
46
+ - lib/global/strategy.rb
47
+ - lib/global/fix/add_atom.rb
48
+ - lib/global/fix/atom.rb
49
+ - lib/global/fix/init.rb
50
+ - lib/global/fix/remove_atom.rb
51
+ - lib/global/fix/swap_atom.rb
52
+ - lib/global/kernel/argument_error.rb
53
+ - lib/global/kernel/exception.rb
54
+ - lib/global/kernel/init.rb
55
+ - lib/global/kernel/no_method_error.rb
56
+ - lib/global/kernel/zero_division_error.rb
57
+ - lib/global/strategies/divide_by_zero_error_strategy.rb
58
+ - lib/global/strategies/init.rb
59
+ - lib/global/strategies/no_method_error_strategy.rb
60
+ - lib/global/strategies/wrong_arguments_error_strategy.rb
61
+ - lib/local/init.rb
62
+ - lib/local/local.rb
63
+ - lib/local/strategy.rb
64
+ - lib/local/kernel/init.rb
65
+ - lib/local/kernel/module.rb
66
+ - lib/local/strategies/bignum_division_strategy.rb
67
+ - lib/local/strategies/bignum_modulus_strategy.rb
68
+ - lib/local/strategies/fixnum_coerce_strategy.rb
69
+ - lib/local/strategies/fixnum_division_strategy.rb
70
+ - lib/local/strategies/fixnum_modulus_strategy.rb
71
+ - lib/local/strategies/float_division_strategy.rb
72
+ - lib/local/strategies/float_modulus_strategy.rb
73
+ - lib/local/strategies/init.rb
74
+ - lib/local/strategies/numeric_division_strategy.rb
75
+ - lib/local/strategies/numeric_modulus_strategy.rb
76
+ - lib/local/strategies/soft_binding_strategy.rb
77
+ - lib/local/strategies/swap_method_strategy.rb
78
+ - test/local/float_division.rb
79
+ - test/local/integer_division.rb
80
+ - test/local/method_binding.rb
81
+ homepage: https://github.com/ChrisTimperley/ruby_to_robust
82
+ licenses:
83
+ - MIT
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 1.8.6
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>'
98
+ - !ruby/object:Gem::Version
99
+ version: 1.3.1
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.28
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Adds optional soft semantics and self-repair to methods.
106
+ test_files:
107
+ - test/local/float_division.rb
108
+ - test/local/integer_division.rb
109
+ - test/local/method_binding.rb