rVM 0.0.14 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -4
- data/bin/rvm +229 -0
- data/lib/rvm.rb +6 -6
- data/lib/rvm/acts_as_rvm_type.rb +26 -3
- data/lib/rvm/classes.rb +9 -15
- data/lib/rvm/classes/block.rb +8 -3
- data/lib/rvm/classes/class.rb +2 -2
- data/lib/rvm/classes/list.rb +1 -1
- data/lib/rvm/classes/null.rb +31 -0
- data/lib/rvm/classes/number.rb +4 -0
- data/lib/rvm/classes/object.rb +1 -1
- data/lib/rvm/classes/string.rb +6 -1
- data/lib/rvm/environment.rb +256 -0
- data/lib/rvm/functions.rb +9 -4
- data/lib/rvm/functions/array.rb +26 -2
- data/lib/rvm/functions/array/append.rb +31 -1
- data/lib/rvm/functions/array/at.rb +29 -1
- data/lib/rvm/functions/array/set_at.rb +29 -0
- data/lib/rvm/functions/association/assoc_get.rb +34 -0
- data/lib/rvm/functions/association/assoc_set.rb +32 -0
- data/lib/rvm/functions/bitwise.rb +3 -0
- data/lib/rvm/functions/bitwise/bitwise_and.rb +41 -0
- data/lib/rvm/functions/bitwise/bitwise_or.rb +41 -0
- data/lib/rvm/functions/bitwise/bitwise_xor.rb +41 -0
- data/lib/rvm/functions/collection/get.rb +37 -4
- data/lib/rvm/functions/collection/set.rb +37 -3
- data/lib/rvm/functions/collection/size.rb +33 -1
- data/lib/rvm/functions/general/cmp.rb +35 -7
- data/lib/rvm/functions/general/eq.rb +29 -0
- data/lib/rvm/functions/general/gt.rb +29 -0
- data/lib/rvm/functions/general/gte.rb +29 -0
- data/lib/rvm/functions/general/lt.rb +29 -0
- data/lib/rvm/functions/general/lte.rb +29 -0
- data/lib/rvm/functions/general/neq.rb +5 -0
- data/lib/rvm/functions/io/print.rb +38 -8
- data/lib/rvm/functions/list/align.rb +25 -1
- data/lib/rvm/functions/list/join.rb +27 -0
- data/lib/rvm/functions/list/map.rb +34 -0
- data/lib/rvm/functions/list/split.rb +31 -0
- data/lib/rvm/functions/logic/and.rb +36 -2
- data/lib/rvm/functions/logic/not.rb +27 -0
- data/lib/rvm/functions/logic/or.rb +32 -2
- data/lib/rvm/functions/math/add.rb +25 -0
- data/lib/rvm/functions/math/cos.rb +39 -0
- data/lib/rvm/functions/math/div.rb +25 -0
- data/lib/rvm/functions/math/mod.rb +41 -0
- data/lib/rvm/functions/math/mul.rb +25 -0
- data/lib/rvm/functions/math/neg.rb +25 -0
- data/lib/rvm/functions/math/power.rb +25 -0
- data/lib/rvm/functions/math/shl.rb +41 -0
- data/lib/rvm/functions/math/shr.rb +41 -0
- data/lib/rvm/functions/math/sin.rb +39 -0
- data/lib/rvm/functions/math/sub.rb +25 -0
- data/lib/rvm/functions/math/tan.rb +39 -0
- data/lib/rvm/functions/rails/print.rb +33 -3
- data/lib/rvm/interpreter.rb +405 -272
- data/lib/rvm/languages.rb +45 -11
- data/lib/rvm/languages/brainfuck.rb +15 -16
- data/lib/rvm/languages/ecma.rb +4 -1257
- data/lib/rvm/languages/ecma/compiler.rb +1353 -0
- data/lib/rvm/languages/ecma/core-math.js +84 -0
- data/lib/rvm/languages/math.rb +9 -16
- data/lib/rvm/languages/math/compiler.rb +9 -9
- data/lib/rvm/languages/math/tokenizer.rb +1 -1
- data/lib/rvm/languages/math/tree.rb +14 -14
- data/lib/rvm/library.rb +26 -18
- data/lib/rvm/optimisation.rb +109 -0
- data/lib/rvm/plugin.rb +109 -45
- data/lib/rvm/rails.rb +79 -54
- data/spec/classes/atom/association_spec.rb +8 -8
- data/spec/classes/atom/block_spec.rb +8 -5
- data/spec/classes/atom/boolean_spec.rb +1 -1
- data/spec/classes/atom/error_spec.rb +1 -1
- data/spec/classes/atom/list_spec.rb +1 -1
- data/spec/classes/atom/number_spec.rb +2 -2
- data/spec/classes/atom/string_spec.rb +1 -1
- data/spec/languages/ecma/ecma_spec.rb +94 -38
- data/spec/languages/ecma/json_spec.rb +4 -4
- data/spec/languages/math/compiler_spec.rb +5 -5
- data/spec/languages/math/tokenizer_spec.rb +1 -1
- data/spec/languages/math/tree_spec.rb +1 -1
- data/spec/{base → rvm}/class_spec.rb +2 -2
- data/spec/{base/interpreter → rvm}/enviroment_spec.rb +19 -9
- data/spec/{base → rvm}/function_spec.rb +2 -2
- data/spec/{functions → rvm/functions}/association/assoc_get_spec.rb +2 -2
- data/spec/{functions → rvm/functions}/association/assoc_set_spec.rb +2 -2
- data/spec/rvm/functions/collection/get_spec.rb +12 -0
- data/spec/rvm/functions/collection/set_spec.rb +10 -0
- data/spec/rvm/functions/collection/size_spec.rb +10 -0
- data/spec/{functions → rvm/functions}/list/split_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/ansi_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/capstr_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/center_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/ljust_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/regmatch_spec.rb +3 -3
- data/spec/{functions → rvm/functions}/string/rjust_spec.rb +3 -3
- data/spec/{base → rvm}/interpreter/assignment_spec.rb +1 -1
- data/spec/rvm/interpreter/condition_spec.rb +103 -0
- data/spec/{base → rvm}/interpreter/constant_spec.rb +1 -1
- data/spec/rvm/interpreter/core_call_spec.rb +72 -0
- data/spec/{base → rvm}/interpreter/interpreter_spec.rb +1 -1
- data/spec/{base → rvm}/interpreter/parameter_spec.rb +1 -1
- data/spec/rvm/interpreter/sequence_spec.rb +47 -0
- data/spec/{base → rvm}/interpreter/variable_spec.rb +1 -1
- data/spec/{base → rvm}/plugin_spec.rb +2 -2
- metadata +66 -35
- data/lib/rake/helpers/code_statistics.rb +0 -167
- data/spec/base/interpreter/condition_spec.rb +0 -47
- data/spec/base/interpreter/function_call_spec.rb +0 -72
- data/spec/base/interpreter/sequence_spec.rb +0 -20
- data/spec/functions/collection/get_spec.rb +0 -12
- data/spec/functions/collection/set_spec.rb +0 -10
- data/spec/functions/collection/size_spec.rb +0 -10
@@ -0,0 +1,84 @@
|
|
1
|
+
Math = {
|
2
|
+
sin: function(x) {
|
3
|
+
__RVM__.sin(x);
|
4
|
+
},
|
5
|
+
cos: function(x) {
|
6
|
+
__RVM__.cos(x);
|
7
|
+
},
|
8
|
+
pow: function(x,y) {
|
9
|
+
__RVM__.power(x,y);
|
10
|
+
},
|
11
|
+
abs: function(x) {
|
12
|
+
__RVM__.abs(x);
|
13
|
+
},
|
14
|
+
acos: function(x) {
|
15
|
+
__RVM__.acos(x);
|
16
|
+
},
|
17
|
+
asin: function(x) {
|
18
|
+
__RVM__.asin(x);
|
19
|
+
},
|
20
|
+
atan: function(x) {
|
21
|
+
__RVM__.atan(x);
|
22
|
+
},
|
23
|
+
atan2: function(x, y) {
|
24
|
+
__RVM__.atan(x, y);
|
25
|
+
},
|
26
|
+
ceil: function(x) {
|
27
|
+
__RVM__.ceil(x);
|
28
|
+
},
|
29
|
+
exp: function(x) {
|
30
|
+
__RVM__.exp(x);
|
31
|
+
},
|
32
|
+
floor: function(x) {
|
33
|
+
__RVM__.floor(x);
|
34
|
+
},
|
35
|
+
log: function(x) {
|
36
|
+
__RVM__.log(x);
|
37
|
+
},
|
38
|
+
random: function() {
|
39
|
+
__RVM__.random();
|
40
|
+
//return 4;
|
41
|
+
},
|
42
|
+
round: function(x) {
|
43
|
+
__RVM__.round(x)
|
44
|
+
},
|
45
|
+
round: function(x) {
|
46
|
+
//__RVM__.sqrt(x); //Do we want that or do we just use power?
|
47
|
+
__RVM__.power(x,0.5);
|
48
|
+
},
|
49
|
+
tan: function(x) {
|
50
|
+
__RVM__.tan(x);
|
51
|
+
},
|
52
|
+
/*NOT IMPLEMENTED! TODO: Implement functions with a variable number of arguments, or rather how to handle them.
|
53
|
+
max: function(*x) {
|
54
|
+
__RVM__.max(x);
|
55
|
+
},
|
56
|
+
min: function(*x) {
|
57
|
+
__RVM__.min(x)
|
58
|
+
}*/
|
59
|
+
E: 2.7182818284590452354,
|
60
|
+
LN10: 2.302585092994046,
|
61
|
+
LN2: 0.6931471805599453,
|
62
|
+
LOG2E: 1.4426950408889634,
|
63
|
+
LOG10E: 0.4342944819032518,
|
64
|
+
PI: 3.1415926535897932,
|
65
|
+
SQRT1_2: 0.7071067811865476,
|
66
|
+
SQRT2: 1.4142135623730951
|
67
|
+
};
|
68
|
+
|
69
|
+
function Array(n) {
|
70
|
+
a = [];
|
71
|
+
if (n && (n >= 1)) {
|
72
|
+
a = a + [null];
|
73
|
+
while (n > 1) {
|
74
|
+
if ((n % 2) == 0) {
|
75
|
+
a = a + a;
|
76
|
+
n = n/2;
|
77
|
+
} else {
|
78
|
+
a = a + [null];
|
79
|
+
n--;
|
80
|
+
};
|
81
|
+
};
|
82
|
+
};
|
83
|
+
return a;
|
84
|
+
};
|
data/lib/rvm/languages/math.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/math/tokenizer'
|
2
|
-
require File.dirname(__FILE__) + '/math/tree'
|
3
|
-
require File.dirname(__FILE__) + '/math/compiler'
|
4
1
|
require 'rvm/functions/math'
|
5
2
|
module RVM
|
6
|
-
|
7
|
-
|
3
|
+
module Languages
|
4
|
+
# This compiler handels mathematical expressions.
|
8
5
|
#
|
9
6
|
# It is limited to smiple terms, as in for example integration of functions is not offered.
|
10
7
|
# Yet it is hopefully enough to serve most mathematical needs.
|
@@ -17,17 +14,13 @@ module RVM
|
|
17
14
|
# * Support for variables (getting and setting)
|
18
15
|
# * Multiple concatinated statemetns sepperated by ';'
|
19
16
|
# * Custom functions (as in defining as well as calling)
|
20
|
-
|
17
|
+
class Math < RVM::Languages::Language
|
18
|
+
register_for :math
|
19
|
+
helper :tokenizer, :tree, :compiler
|
21
20
|
|
21
|
+
def compile text
|
22
|
+
Compiler.compile(Tree.generate(Tokenizer.tokenize(text)))
|
23
|
+
end
|
22
24
|
end
|
23
|
-
|
24
|
-
# See the Math module for a description of this compiler.
|
25
|
-
class MathLanguage < RVM::Languages::Language
|
26
|
-
include Math
|
27
|
-
def compile text
|
28
|
-
Compiler.compile(Tree.generate(Tokenizer.tokenize(text)))
|
29
|
-
end
|
30
|
-
register_for :math
|
31
|
-
end
|
32
|
-
end
|
25
|
+
end
|
33
26
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
module RVM
|
2
2
|
module Languages
|
3
|
-
|
3
|
+
class Math
|
4
4
|
class Compiler
|
5
5
|
include RVM::Interpreter
|
6
6
|
FUNCTION_MAP = {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
'+' => :add,
|
8
|
+
'-' => :sub,
|
9
|
+
'/' => :div,
|
10
|
+
'*' => :mul,
|
11
|
+
'^' => :power
|
12
12
|
}
|
13
13
|
def Compiler.compile tree
|
14
14
|
if tree.is_a? Array
|
@@ -21,7 +21,7 @@ module RVM
|
|
21
21
|
params = tree[:params].map {|p| compile(p)}
|
22
22
|
case tree[:op]
|
23
23
|
when '-'
|
24
|
-
|
24
|
+
CoreCall.new :neg, params
|
25
25
|
else
|
26
26
|
FunctionCall.new tree[:op], params
|
27
27
|
end
|
@@ -54,12 +54,12 @@ module RVM
|
|
54
54
|
i += 1
|
55
55
|
end
|
56
56
|
body << compile(tree[:right])
|
57
|
-
Interpreter::FunctionDefinition.new(Interpreter.const(:string, tree[:left][:op]),
|
57
|
+
Interpreter::FunctionDefinition.new(Interpreter.const(:string, tree[:left][:op]), RVM::Classes::Block.new(body))
|
58
58
|
else
|
59
59
|
raise ArgumentError, "Bad name for a variable!"
|
60
60
|
end
|
61
61
|
else
|
62
|
-
|
62
|
+
CoreCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
@@ -1,24 +1,24 @@
|
|
1
1
|
module RVM
|
2
2
|
module Languages
|
3
|
-
|
3
|
+
class Math
|
4
4
|
class Tree
|
5
5
|
PRIORITIES = {
|
6
6
|
';' => -100,
|
7
7
|
'=' => -50,
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
'(' => 0,
|
9
|
+
'+' => 10,
|
10
|
+
'-' => 10,
|
11
|
+
'*' => 20,
|
12
|
+
'/' => 20,
|
13
|
+
'^' => 30,
|
14
14
|
}
|
15
15
|
ASSOSICATIONS = {
|
16
16
|
';' => :right,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
'+' => :left,
|
18
|
+
'-' => :left,
|
19
|
+
'*' => :left,
|
20
|
+
'/' => :left,
|
21
|
+
'^' => :right,
|
22
22
|
'=' => :right
|
23
23
|
}
|
24
24
|
def Tree.generate tokens
|
@@ -72,8 +72,8 @@ module RVM
|
|
72
72
|
else
|
73
73
|
raise "Unknown element in parser tree: #{token.inspect}"
|
74
74
|
end
|
75
|
-
|
76
|
-
|
75
|
+
p(dbgstack) if $DEBUG
|
76
|
+
p(dbgoutput) if $DEBUG
|
77
77
|
end
|
78
78
|
while not stack.empty?
|
79
79
|
output << stack.pop
|
data/lib/rvm/library.rb
CHANGED
@@ -22,27 +22,19 @@ module RVM
|
|
22
22
|
# Initializes library, if anguage and file are given, they are loaded
|
23
23
|
# Optionally a saftey object can be passed to handle securing the
|
24
24
|
# interpreted code
|
25
|
-
def initialize(language
|
26
|
-
@
|
25
|
+
def initialize(language, file = nil, safety = RVM::Safety.new)
|
26
|
+
@lang = RVM::Languages[language].new
|
27
|
+
@env = RVM::Interpreter.env #@lang.env
|
27
28
|
@safety = safety
|
28
|
-
load_library(
|
29
|
+
load_library(file) if file
|
29
30
|
end
|
30
31
|
|
31
32
|
#Loads a file into the Library, attention it is executed!
|
32
|
-
def load_library(
|
33
|
-
@safety.execute(
|
33
|
+
def load_library(file)
|
34
|
+
@safety.execute(@lang.compile(File.read(file)),@env)
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
-
# If a unknown method is called this gets executed to try to resole it form the
|
38
|
-
# loaded libraires
|
39
|
-
#
|
40
|
-
# Priorities are:
|
41
|
-
# 1) check the functions
|
42
|
-
# 2) check if it was a variable
|
43
|
-
# 3) check if the call ended with a '=' then set it's as variable
|
44
|
-
# 4) call super
|
45
|
-
def method_missing(m, *args)
|
37
|
+
def call_function(name, *args)
|
46
38
|
# First we convert the args
|
47
39
|
vmargs = args.map do |arg|
|
48
40
|
if c = CLASS_MAP[arg.class]
|
@@ -51,15 +43,31 @@ module RVM
|
|
51
43
|
arg
|
52
44
|
end
|
53
45
|
end
|
54
|
-
n =
|
46
|
+
n = name.to_s
|
55
47
|
if fun = @env.function(n)
|
56
|
-
fun.call(@env
|
48
|
+
fun.call(vmargs, @env)
|
57
49
|
elsif v = @env[n]
|
58
50
|
v.val
|
59
51
|
elsif n =~ /=$/
|
60
52
|
@env[n.gsub(/=$/,'')] = vmargs.first
|
61
53
|
else
|
62
|
-
|
54
|
+
raise "oops!"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# This is the most important part, so to say, the ruby magic in here
|
58
|
+
# If a unknown method is called this gets executed to try to resole it form the
|
59
|
+
# loaded libraires
|
60
|
+
#
|
61
|
+
# Priorities are:
|
62
|
+
# 1) check the functions
|
63
|
+
# 2) check if it was a variable
|
64
|
+
# 3) check if the call ended with a '=' then set it's as variable
|
65
|
+
# 4) call super
|
66
|
+
def method_missing(m, *args)
|
67
|
+
begin
|
68
|
+
call_function(m, args)
|
69
|
+
rescue Exception => e
|
70
|
+
super(m, args)
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module RVM
|
2
|
+
module Interpreter
|
3
|
+
# The Simple assignment is a version of the RVM::Interpreter::Assignment that
|
4
|
+
# is added for optimisation purpose, it does not evaluate the name and expects
|
5
|
+
# it to be a string from the beginning
|
6
|
+
class SimpleAssignment < RVM::Interpreter::Assignment
|
7
|
+
|
8
|
+
# A Assignment is initialized wiht 2 to 3 parameters.
|
9
|
+
#
|
10
|
+
# name:: The name of the variable to store, it will be executed, usually
|
11
|
+
# this will be a Constant, unless dynamic naming is needed by the
|
12
|
+
# implemented language.
|
13
|
+
#
|
14
|
+
# value:: The value that will be assigned to the variable, for a = 1 + 1
|
15
|
+
# '1+1' would be the value to assign, so as this already suggests
|
16
|
+
# the value will be executed.
|
17
|
+
#
|
18
|
+
# pos:: The position within the source code of the definition - for
|
19
|
+
# deugging purpose.
|
20
|
+
def initialize name, value, pos = nil
|
21
|
+
@pos = pos
|
22
|
+
@name = name
|
23
|
+
@value = value
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# The simple assignemnt only evaluates the value and sets the variable
|
28
|
+
# with the given name in the Environment.
|
29
|
+
def execute env
|
30
|
+
RVM::debug "Executing SimpleAssignment at #{@pos}..." if $DEBUG
|
31
|
+
env[@name] = @value.execute env
|
32
|
+
end
|
33
|
+
|
34
|
+
def optimize
|
35
|
+
@value = @value.optimize
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Version of the RVM::Interpreter::Variable that handles static variable names
|
41
|
+
class SimpleVariable < Variable
|
42
|
+
# A SimpleVariable is initialized wiht 1 to 2 parameters.
|
43
|
+
#
|
44
|
+
# name:: The name of the variable to get, it will be executed as long as
|
45
|
+
# it is no Sybol in which case it is treated as a special variable.
|
46
|
+
#
|
47
|
+
# pos:: The position within the source code of the definition - for
|
48
|
+
# deugging purpose.
|
49
|
+
def initialize name, pos = nil
|
50
|
+
super(name.to_s,pos)
|
51
|
+
end
|
52
|
+
|
53
|
+
# The name is a sting and we simple get what is written in the env
|
54
|
+
def execute env
|
55
|
+
RVM::debug "Executing SimpleVariable at #{@pos}..." if $DEBUG
|
56
|
+
begin
|
57
|
+
r = env.read_var_val(@name)
|
58
|
+
@type = r.data_type if r.respond_to?(:data_type)
|
59
|
+
r
|
60
|
+
rescue Exception => e
|
61
|
+
raise RuntimeError.new("Failed to get Varialbe #{e}", @pos[0], @pos[1], @pos[2])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def optimize
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# For details see RVM::Interpreter::Declration, this is a
|
71
|
+
# optimized version vor simple cases.
|
72
|
+
#
|
73
|
+
# Only #value# is evaluated before the assignment is done.
|
74
|
+
|
75
|
+
class SimpleDeclaration < Declaration
|
76
|
+
# A Declaration is initialized wiht 2 to 3 parameters.
|
77
|
+
#
|
78
|
+
# name:: The name of the variable to store, it will be executed, usually
|
79
|
+
# this will be a Constant, unless dynamic naming is needed by the
|
80
|
+
# implemented language.
|
81
|
+
#
|
82
|
+
# value:: The value that will be assigned to the variable, for a = 1 + 1
|
83
|
+
# '1+1' would be the value to assign, so as this already suggests
|
84
|
+
# the value will be executed.
|
85
|
+
#
|
86
|
+
# pos:: The position within the source code of the definition - for
|
87
|
+
# deugging purpose.
|
88
|
+
def initialize name, value, pos = nil
|
89
|
+
@pos = pos
|
90
|
+
@name = name.to_s
|
91
|
+
@value = value
|
92
|
+
end
|
93
|
+
|
94
|
+
# Only the value is evaluated before assigning it, the name is taken
|
95
|
+
# to be a string.
|
96
|
+
#
|
97
|
+
# For details see RVM::Interpreter::Declration
|
98
|
+
def execute env
|
99
|
+
RVM::debug "Executing SimpleAssignment at #{@pos}..." if $DEBUG
|
100
|
+
env.declare(@name,@value.execute(env)).val
|
101
|
+
end
|
102
|
+
|
103
|
+
def optimize
|
104
|
+
@value = @value.optimize
|
105
|
+
self
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/rvm/plugin.rb
CHANGED
@@ -1,30 +1,28 @@
|
|
1
1
|
module RVM
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# Thanks a lot to Murphy, this code is borrowed from coderay.
|
27
|
-
#
|
2
|
+
|
3
|
+
# = PluginHost
|
4
|
+
#
|
5
|
+
# $Id: plugin.rb 255 2008-09-21 16:25:44Z murphy $
|
6
|
+
#
|
7
|
+
# A simple subclass plugin system.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# class Generators < PluginHost
|
11
|
+
# plugin_path 'app/generators'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# class Generator
|
15
|
+
# extend Plugin
|
16
|
+
# PLUGIN_HOST = Generators
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# class FancyGenerator < Generator
|
20
|
+
# register_for :fancy
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Generators[:fancy] #-> FancyGenerator
|
24
|
+
# # or
|
25
|
+
# RVM.require_plugin 'Generators/fancy'
|
28
26
|
module PluginHost
|
29
27
|
|
30
28
|
# Raised if Encoders::[] fails because:
|
@@ -36,8 +34,12 @@ module RVM
|
|
36
34
|
PLUGIN_HOSTS = []
|
37
35
|
PLUGIN_HOSTS_BY_ID = {} # dummy hash
|
38
36
|
|
39
|
-
# Loads all plugins using
|
40
|
-
|
37
|
+
# Loads all plugins using list and load.
|
38
|
+
def load_all
|
39
|
+
for plugin in list
|
40
|
+
load plugin
|
41
|
+
end
|
42
|
+
end
|
41
43
|
|
42
44
|
# Returns the Plugin for +id+.
|
43
45
|
#
|
@@ -54,6 +56,15 @@ module RVM
|
|
54
56
|
# Alias for +[]+.
|
55
57
|
alias load []
|
56
58
|
|
59
|
+
def require_helper plugin_id, helper_name
|
60
|
+
path = path_to File.join(plugin_id, helper_name)
|
61
|
+
require path
|
62
|
+
end
|
63
|
+
|
64
|
+
def has? id
|
65
|
+
plugin_hash.has_key? validate_id(id)
|
66
|
+
end
|
67
|
+
|
57
68
|
class << self
|
58
69
|
|
59
70
|
# Adds the module/class to the PLUGIN_HOSTS list.
|
@@ -82,6 +93,15 @@ module RVM
|
|
82
93
|
|
83
94
|
end
|
84
95
|
|
96
|
+
# The path where the plugins can be found.
|
97
|
+
def plugin_path *args
|
98
|
+
unless args.empty?
|
99
|
+
@plugin_path = File.expand_path File.join(*args)
|
100
|
+
load_map
|
101
|
+
end
|
102
|
+
@plugin_path or raise 'No plugin_path given for %p' % [self]
|
103
|
+
end
|
104
|
+
|
85
105
|
# The host's ID.
|
86
106
|
#
|
87
107
|
# If PLUGIN_HOST_ID is not set, it is simply the class name.
|
@@ -119,9 +139,13 @@ module RVM
|
|
119
139
|
# map :navy => :dark_blue
|
120
140
|
# default :gray
|
121
141
|
# end
|
122
|
-
def default id
|
123
|
-
|
124
|
-
|
142
|
+
def default id = nil
|
143
|
+
if id
|
144
|
+
id = validate_id id
|
145
|
+
plugin_hash[nil] = id
|
146
|
+
else
|
147
|
+
plugin_hash[nil]
|
148
|
+
end
|
125
149
|
end
|
126
150
|
|
127
151
|
# Every plugin must register itself for one or more
|
@@ -132,7 +156,7 @@ module RVM
|
|
132
156
|
for id in ids
|
133
157
|
unless id.is_a? Symbol
|
134
158
|
raise ArgumentError,
|
135
|
-
|
159
|
+
"id must be a Symbol, but it was a #{id.class}"
|
136
160
|
end
|
137
161
|
plugin_hash[validate_id(id)] = plugin
|
138
162
|
end
|
@@ -143,8 +167,15 @@ module RVM
|
|
143
167
|
@plugin_hash ||= create_plugin_hash
|
144
168
|
end
|
145
169
|
|
146
|
-
|
147
|
-
|
170
|
+
# Returns an array of all .rb files in the plugin path.
|
171
|
+
#
|
172
|
+
# The extension .rb is not included.
|
173
|
+
def list
|
174
|
+
Dir[path_to('*')].select do |file|
|
175
|
+
File.basename(file)[/^(?!_)\w+\.rb$/]
|
176
|
+
end.map do |file|
|
177
|
+
File.basename file, '.rb'
|
178
|
+
end
|
148
179
|
end
|
149
180
|
|
150
181
|
# Makes a map of all loaded plugins.
|
@@ -153,15 +184,44 @@ module RVM
|
|
153
184
|
map.each do |id, plugin|
|
154
185
|
map[id] = plugin.to_s[/(?>[\w_]+)$/]
|
155
186
|
end
|
156
|
-
|
187
|
+
"#{name}[#{host_id}]#{map.inspect}"
|
157
188
|
end
|
158
189
|
|
159
|
-
|
190
|
+
protected
|
160
191
|
# Created a new plugin list and stores it to @plugin_hash.
|
161
192
|
def create_plugin_hash
|
162
193
|
@plugin_hash =
|
163
|
-
|
164
|
-
|
194
|
+
Hash.new do |h, plugin_id|
|
195
|
+
id = validate_id(plugin_id)
|
196
|
+
path = path_to id
|
197
|
+
begin
|
198
|
+
require path
|
199
|
+
rescue LoadError => boom
|
200
|
+
if h.has_key? nil # default plugin
|
201
|
+
h[id] = h[nil]
|
202
|
+
else
|
203
|
+
raise PluginNotFound, 'Could not load plugin %p: %s' % [id, boom]
|
204
|
+
end
|
205
|
+
else
|
206
|
+
# Plugin should have registered by now
|
207
|
+
unless h.has_key? id
|
208
|
+
raise PluginNotFound,
|
209
|
+
"No #{self.name} plugin for #{id.inspect} found in #{path}."
|
210
|
+
end
|
211
|
+
end
|
212
|
+
h[id]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Loads the map file (see map).
|
217
|
+
#
|
218
|
+
# This is done automatically when plugin_path is called.
|
219
|
+
def load_map
|
220
|
+
mapfile = path_to '_map'
|
221
|
+
if File.exist? mapfile
|
222
|
+
require mapfile
|
223
|
+
elsif $DEBUG
|
224
|
+
warn 'no _map.rb found for %s' % name
|
165
225
|
end
|
166
226
|
end
|
167
227
|
|
@@ -174,6 +234,10 @@ module RVM
|
|
174
234
|
plugin_hash.fetch validate_id(id), *args, &blk
|
175
235
|
end
|
176
236
|
|
237
|
+
# Returns the expected path to the plugin file for the given id.
|
238
|
+
def path_to plugin_id
|
239
|
+
File.join plugin_path, "#{plugin_id}.rb"
|
240
|
+
end
|
177
241
|
|
178
242
|
# Converts +id+ to a Symbol if it is a String,
|
179
243
|
# or returns +id+ if it already is a Symbol.
|
@@ -191,7 +255,7 @@ module RVM
|
|
191
255
|
end
|
192
256
|
else
|
193
257
|
raise ArgumentError,
|
194
|
-
|
258
|
+
"String or Symbol expected, but #{id.class} given."
|
195
259
|
end
|
196
260
|
end
|
197
261
|
|
@@ -227,9 +291,9 @@ module RVM
|
|
227
291
|
def plugin_host host = nil
|
228
292
|
if host and not host.is_a? PluginHost
|
229
293
|
raise ArgumentError,
|
230
|
-
|
294
|
+
"PluginHost expected, but #{host.class} given."
|
231
295
|
end
|
232
|
-
self.const_set :PLUGIN_HOST, host if host
|
296
|
+
self.const_set :PLUGIN_HOST, host if host
|
233
297
|
self::PLUGIN_HOST
|
234
298
|
end
|
235
299
|
|
@@ -256,18 +320,18 @@ module RVM
|
|
256
320
|
|
257
321
|
end
|
258
322
|
|
259
|
-
|
260
323
|
# Convenience method for plugin loading.
|
261
324
|
# The syntax used is:
|
262
325
|
#
|
263
|
-
# require_plugin '<Host ID>/<Plugin ID>'
|
326
|
+
# RVM.require_plugin '<Host ID>/<Plugin ID>'
|
264
327
|
#
|
265
328
|
# Returns the loaded plugin.
|
266
|
-
def require_plugin path
|
329
|
+
def self.require_plugin path
|
267
330
|
host_id, plugin_id = path.split '/', 2
|
268
331
|
host = PluginHost.host_by_id(host_id)
|
269
332
|
raise PluginHost::HostNotFound,
|
270
|
-
|
333
|
+
"No host for #{host_id.inspect} found." unless host
|
271
334
|
host.load plugin_id
|
272
335
|
end
|
336
|
+
|
273
337
|
end
|