rVM 0.0.14 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|