dsboulder-acts_as_runnable_code 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ == 1.0.1 / 2007-10-22
2
+ * added gem dependency to acts_as_wrapped_class
3
+
4
+ == 1.0.0 / 2007-10-22
5
+
6
+ * first relase
@@ -0,0 +1,6 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/acts_as_runnable_code.rb
6
+ test/test_acts_as_runnable_code.rb
@@ -0,0 +1,72 @@
1
+ ActsAsRunnableCode
2
+ by David Stevenson
3
+ http://elctech.com/blog
4
+
5
+ == DESCRIPTION:
6
+
7
+ ActsAsRunnable attempts to make the Freaky Freaky Sandbox easier to use. It assumes that you have a class that has instances that store user uploaded code in them. It also assumes that you want use your classes in the sandbox with reduced "safe" functionality provided by acts_as_wrapped_class. Finally, it assumes that you want to evaluate an instance of user uploaded code within the context of some instance of a wrapped class.
8
+
9
+ See the example app using this gem: http://tictactoe.mapleton.net
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * Run user uploaded code in the sandbox
14
+ * Automatic use of wrapper classes inside the sandbox, and unwrapping return results
15
+
16
+ == SYNOPSIS:
17
+
18
+ require "sandbox"
19
+ require "acts_as_runnable_code"
20
+
21
+ class Algorithm < ActiveRecord::Base
22
+ # "code" is a database attribute in this case, containing user's code
23
+ acts_as_runnable_code :classes => ["Board"]
24
+ end
25
+
26
+ class Board < ActiveRecord::Base
27
+ belongs_to :algorithm
28
+ acts_as_wrapped_class, :methods => [:moves, :make_move!, :turn, :log_info, :log_debug]
29
+ def moves; end
30
+ def make_move!(x,y); end;
31
+ ...
32
+ end
33
+
34
+ # Runs a user uploaded alogrithm in the context of a board
35
+ board = Board.find(id)
36
+ board.algorithm.run_code(board, :timeout => 0.5)
37
+
38
+ == REQUIREMENTS:
39
+
40
+ * acts_as_wrapped_class (gem)
41
+ * sandbox (gem)
42
+
43
+ == INSTALL:
44
+
45
+ * sudo gem install acts_as_wrapped_class
46
+ * download and install sandbox: http://code.whytheluckystiff.net/sandbox/
47
+ * sudo gem install acts_as_runnable_code
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2007 David Stevenson
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "acts_as_runnable_code"
5
+ gemspec.summary = "Sandbox support gem to help define user-provided algorithms"
6
+ gemspec.email = "david@flouri.sh"
7
+ gemspec.homepage = "http://github.com/dsboulder/acts_as_runnable_code"
8
+ gemspec.description = "TODO"
9
+ gemspec.authors = ["David Stevenson"]
10
+ end
11
+ rescue LoadError
12
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
13
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 3
@@ -0,0 +1,103 @@
1
+ # ActsAsRunnableCode
2
+ require "rubygems"
3
+ require "acts_as_wrapped_class"
4
+
5
+ module ActsAsRunnableCode
6
+ VERSION="1.0.2"
7
+ module InstanceMethods
8
+ # Run the user's code in the context of a toplevel_object
9
+ # The toplevel_object will be automatically wrapped and copied into the sandbox
10
+ def run_code(toplevel_object = nil, options = {})
11
+ options[:timeout] ||= self.class.runnable_code_options[:timeout]
12
+ s = sandbox
13
+ if toplevel_object
14
+ s.set(:toplevel, ActsAsWrappedClass::WrapperFinder.find_wrapper_for(toplevel_object))
15
+ s.set(:code, self.send(self.class.source_code_field))
16
+ result = s.eval("eval(code, toplevel.send(:binding))", options)
17
+ else
18
+ s.set(:code, self.send(self.class.source_code_field))
19
+ result = s.eval("eval(code)", options)
20
+ end
21
+ result.class.respond_to?(:wrapper_class?) && result.class.wrapper_class? ? result._wrapped_object : result
22
+ end
23
+
24
+ # Get the sandbox used to eval user code for this class
25
+ # Makes sandbox references to the wrapper classes
26
+ def sandbox
27
+ return self.class.instance_variable_get("@sandbox") if self.class.instance_variables.include?("@sandbox")
28
+
29
+ if self.class.runnable_code_options[:unsafe_sandbox]
30
+ s = Sandbox.new
31
+ else
32
+ s = Sandbox.safe
33
+ end
34
+
35
+ classes = self.class.runnable_code_options[:classes] || ActsAsWrappedClass::WRAPPED_CLASSES
36
+
37
+ classes.each do |c|
38
+ name = "#{c}Wrapper"
39
+ s.ref eval(name)
40
+ end
41
+
42
+ self.class.instance_variable_set("@sandbox", s) if self.class.runnable_code_options[:singleton_sandbox]
43
+ s
44
+ end
45
+ end
46
+
47
+ module ClassMethods
48
+ attr :runnable_code_options, true
49
+
50
+ def source_code_field
51
+ (@runnable_code_options[:code_field] || "code").to_sym
52
+ end
53
+
54
+ def runnable_code?
55
+ true
56
+ end
57
+
58
+ # a list of runnable classes in the format ["SomeClassWrapper", "OtherClassWraper"]
59
+ def runnable_classes
60
+ (runnable_code_options[:classes] || ActsAsWrappedClass::WRAPPED_CLASSES).collect { |c| "#{c}Wrapper" }
61
+ end
62
+
63
+ # a list of runnable methods in the format {"SomeClassWrapper" => ["method_a", "method_b"], "OtherClassWraper" => ["method_c", "method_d"]]
64
+ def runnable_methods
65
+ meths = {}
66
+ runnable_classes.each { |c|
67
+ klass = eval("::#{c}")
68
+ meths[c.to_s] ||= []
69
+ meths[c.to_s] += klass.allowed_methods if klass.respond_to?(:allowed_methods)
70
+ }
71
+ meths
72
+ end
73
+
74
+ # same as runnable methods but excludes all the methods of +Object
75
+ def runnable_methods_no_object
76
+ newv = {}
77
+ runnable_methods.each do |k,v|
78
+ newv[k] = v - Object.public_instance_methods
79
+ end
80
+ newv
81
+ end
82
+ end
83
+
84
+ # Mark this class as containing a method which returns user generated code.
85
+ # Options
86
+ # * options[:classes] - contains a list of classes (unwrapped names) you want accessible in the sandbox. Default: all classes loaded and marked with acts_as_wrapped_class
87
+ # * options[:singleton_sandbox] - only create 1 sandbox, which is always used whenever code is evaled (faster), rather than a new sandbox each time. Default: false
88
+ # * options[:unsafe_sandbox] - create sandbox using Sandbox.new rather than Sandbox.safe. Default: false
89
+ # * options[:timeout] - timeout in seconds before aborting user code. Default: none
90
+ # * options[:code_field] - the name of the method to call to get the user code for running. Default: :code
91
+ def acts_as_runnable_code(options = {})
92
+ self.send(:include, InstanceMethods)
93
+ self.send(:extend,ClassMethods)
94
+
95
+ self.runnable_code_options = options
96
+ end
97
+
98
+ def runnable_code?
99
+ false
100
+ end
101
+ end
102
+
103
+ Object.send(:include, ActsAsRunnableCode)
@@ -0,0 +1,115 @@
1
+ require "sandbox"
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + "/../lib/acts_as_runnable_code"
4
+
5
+ class SampleClass
6
+ acts_as_runnable_code
7
+
8
+ attr_writer :code
9
+
10
+ def code
11
+ @code || "'hello world'"
12
+ end
13
+
14
+ end
15
+
16
+ class WrappedClass2
17
+ acts_as_wrapped_class :methods => [:safeone]
18
+
19
+ def safeone
20
+ 777
21
+ end
22
+
23
+ def unsafeone
24
+ 666
25
+ end
26
+ end
27
+
28
+ class OtherClass
29
+ acts_as_runnable_code :classes => ["WrappedClass2"], :code_field => :data2, :unsafe_sandbox => true, :singleton_sandbox => true
30
+
31
+ attr :code, true
32
+
33
+ def data2
34
+ @code || "value"
35
+ end
36
+ end
37
+
38
+ class WrappedClass
39
+ acts_as_wrapped_class
40
+
41
+ attr_reader :value
42
+
43
+ def initialize(value)
44
+ @value = value
45
+ end
46
+
47
+ def other
48
+ WrappedClass.new(rand)
49
+ end
50
+ end
51
+
52
+ class ActsAsRunnableCodeTest < Test::Unit::TestCase
53
+ def test_source_code_field
54
+ assert_equal :code, SampleClass.source_code_field
55
+ assert_equal :data2, OtherClass.source_code_field
56
+ end
57
+
58
+ def test_has_run_code
59
+ assert SampleClass.new.respond_to?(:run_code)
60
+ assert OtherClass.new.respond_to?(:run_code)
61
+ end
62
+
63
+ def test_has_sandbox
64
+ assert SampleClass.new.respond_to?(:sandbox)
65
+ assert OtherClass.new.respond_to?(:sandbox)
66
+ end
67
+
68
+ def test_sandbox
69
+ s = SampleClass.new.sandbox
70
+ assert s.is_a?(Sandbox::Safe)
71
+
72
+ s = OtherClass.new.sandbox
73
+ assert s.is_a?(Sandbox::Full)
74
+ end
75
+
76
+ def test_singleton_sandbox
77
+ s1 = OtherClass.new.sandbox
78
+ s2 = OtherClass.new.sandbox
79
+ assert s1 == s2
80
+ s1 = SampleClass.new.sandbox
81
+ s2 = SampleClass.new.sandbox
82
+ assert s1 != s2
83
+ end
84
+
85
+ def test_run_code
86
+ assert_equal "hello world", SampleClass.new.run_code
87
+
88
+ w = WrappedClass.new(5.5)
89
+ sc = SampleClass.new
90
+ sc.code = "value"
91
+ assert_equal 5.5, sc.run_code(w)
92
+
93
+ sc = SampleClass.new
94
+ sc.code = "other"
95
+ assert_equal WrappedClass, sc.run_code(w).class
96
+
97
+ w = WrappedClass2.new
98
+ oc = OtherClass.new
99
+ oc.code = "safeone"
100
+ assert_equal 777, oc.run_code(w)
101
+ end
102
+
103
+ def test_arunnable_classes
104
+ assert_equal 2, SampleClass.runnable_classes.length
105
+ assert_equal ["WrappedClass2Wrapper"], OtherClass.runnable_classes
106
+
107
+ assert_equal OtherClass.runnable_methods, {"WrappedClass2Wrapper" => ["safeone"]}
108
+ assert_contents_same ["other", "value", "safeone"], SampleClass.runnable_methods_no_object.values.flatten.uniq
109
+ end
110
+
111
+ def assert_contents_same(array1, array2)
112
+ assert_equal array1.length, array2.length, "#{array1.inspect} != #{array2.inspect}"
113
+ array1.each { |a| assert array2.include?(a), "#{array2.inspect} does not contain #{a.inspect}" }
114
+ end
115
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dsboulder-acts_as_runnable_code
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - David Stevenson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-07 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TODO
17
+ email: david@flouri.sh
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.txt
24
+ files:
25
+ - History.txt
26
+ - Manifest.txt
27
+ - README.txt
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - lib/acts_as_runnable_code.rb
31
+ - test/test_acts_as_runnable_code.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/dsboulder/acts_as_runnable_code
34
+ post_install_message:
35
+ rdoc_options:
36
+ - --charset=UTF-8
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.2.0
55
+ signing_key:
56
+ specification_version: 2
57
+ summary: Sandbox support gem to help define user-provided algorithms
58
+ test_files:
59
+ - test/test_acts_as_runnable_code.rb