rVM 0.0.14 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/README +4 -4
  2. data/bin/rvm +229 -0
  3. data/lib/rvm.rb +6 -6
  4. data/lib/rvm/acts_as_rvm_type.rb +26 -3
  5. data/lib/rvm/classes.rb +9 -15
  6. data/lib/rvm/classes/block.rb +8 -3
  7. data/lib/rvm/classes/class.rb +2 -2
  8. data/lib/rvm/classes/list.rb +1 -1
  9. data/lib/rvm/classes/null.rb +31 -0
  10. data/lib/rvm/classes/number.rb +4 -0
  11. data/lib/rvm/classes/object.rb +1 -1
  12. data/lib/rvm/classes/string.rb +6 -1
  13. data/lib/rvm/environment.rb +256 -0
  14. data/lib/rvm/functions.rb +9 -4
  15. data/lib/rvm/functions/array.rb +26 -2
  16. data/lib/rvm/functions/array/append.rb +31 -1
  17. data/lib/rvm/functions/array/at.rb +29 -1
  18. data/lib/rvm/functions/array/set_at.rb +29 -0
  19. data/lib/rvm/functions/association/assoc_get.rb +34 -0
  20. data/lib/rvm/functions/association/assoc_set.rb +32 -0
  21. data/lib/rvm/functions/bitwise.rb +3 -0
  22. data/lib/rvm/functions/bitwise/bitwise_and.rb +41 -0
  23. data/lib/rvm/functions/bitwise/bitwise_or.rb +41 -0
  24. data/lib/rvm/functions/bitwise/bitwise_xor.rb +41 -0
  25. data/lib/rvm/functions/collection/get.rb +37 -4
  26. data/lib/rvm/functions/collection/set.rb +37 -3
  27. data/lib/rvm/functions/collection/size.rb +33 -1
  28. data/lib/rvm/functions/general/cmp.rb +35 -7
  29. data/lib/rvm/functions/general/eq.rb +29 -0
  30. data/lib/rvm/functions/general/gt.rb +29 -0
  31. data/lib/rvm/functions/general/gte.rb +29 -0
  32. data/lib/rvm/functions/general/lt.rb +29 -0
  33. data/lib/rvm/functions/general/lte.rb +29 -0
  34. data/lib/rvm/functions/general/neq.rb +5 -0
  35. data/lib/rvm/functions/io/print.rb +38 -8
  36. data/lib/rvm/functions/list/align.rb +25 -1
  37. data/lib/rvm/functions/list/join.rb +27 -0
  38. data/lib/rvm/functions/list/map.rb +34 -0
  39. data/lib/rvm/functions/list/split.rb +31 -0
  40. data/lib/rvm/functions/logic/and.rb +36 -2
  41. data/lib/rvm/functions/logic/not.rb +27 -0
  42. data/lib/rvm/functions/logic/or.rb +32 -2
  43. data/lib/rvm/functions/math/add.rb +25 -0
  44. data/lib/rvm/functions/math/cos.rb +39 -0
  45. data/lib/rvm/functions/math/div.rb +25 -0
  46. data/lib/rvm/functions/math/mod.rb +41 -0
  47. data/lib/rvm/functions/math/mul.rb +25 -0
  48. data/lib/rvm/functions/math/neg.rb +25 -0
  49. data/lib/rvm/functions/math/power.rb +25 -0
  50. data/lib/rvm/functions/math/shl.rb +41 -0
  51. data/lib/rvm/functions/math/shr.rb +41 -0
  52. data/lib/rvm/functions/math/sin.rb +39 -0
  53. data/lib/rvm/functions/math/sub.rb +25 -0
  54. data/lib/rvm/functions/math/tan.rb +39 -0
  55. data/lib/rvm/functions/rails/print.rb +33 -3
  56. data/lib/rvm/interpreter.rb +405 -272
  57. data/lib/rvm/languages.rb +45 -11
  58. data/lib/rvm/languages/brainfuck.rb +15 -16
  59. data/lib/rvm/languages/ecma.rb +4 -1257
  60. data/lib/rvm/languages/ecma/compiler.rb +1353 -0
  61. data/lib/rvm/languages/ecma/core-math.js +84 -0
  62. data/lib/rvm/languages/math.rb +9 -16
  63. data/lib/rvm/languages/math/compiler.rb +9 -9
  64. data/lib/rvm/languages/math/tokenizer.rb +1 -1
  65. data/lib/rvm/languages/math/tree.rb +14 -14
  66. data/lib/rvm/library.rb +26 -18
  67. data/lib/rvm/optimisation.rb +109 -0
  68. data/lib/rvm/plugin.rb +109 -45
  69. data/lib/rvm/rails.rb +79 -54
  70. data/spec/classes/atom/association_spec.rb +8 -8
  71. data/spec/classes/atom/block_spec.rb +8 -5
  72. data/spec/classes/atom/boolean_spec.rb +1 -1
  73. data/spec/classes/atom/error_spec.rb +1 -1
  74. data/spec/classes/atom/list_spec.rb +1 -1
  75. data/spec/classes/atom/number_spec.rb +2 -2
  76. data/spec/classes/atom/string_spec.rb +1 -1
  77. data/spec/languages/ecma/ecma_spec.rb +94 -38
  78. data/spec/languages/ecma/json_spec.rb +4 -4
  79. data/spec/languages/math/compiler_spec.rb +5 -5
  80. data/spec/languages/math/tokenizer_spec.rb +1 -1
  81. data/spec/languages/math/tree_spec.rb +1 -1
  82. data/spec/{base → rvm}/class_spec.rb +2 -2
  83. data/spec/{base/interpreter → rvm}/enviroment_spec.rb +19 -9
  84. data/spec/{base → rvm}/function_spec.rb +2 -2
  85. data/spec/{functions → rvm/functions}/association/assoc_get_spec.rb +2 -2
  86. data/spec/{functions → rvm/functions}/association/assoc_set_spec.rb +2 -2
  87. data/spec/rvm/functions/collection/get_spec.rb +12 -0
  88. data/spec/rvm/functions/collection/set_spec.rb +10 -0
  89. data/spec/rvm/functions/collection/size_spec.rb +10 -0
  90. data/spec/{functions → rvm/functions}/list/split_spec.rb +3 -3
  91. data/spec/{functions → rvm/functions}/string/ansi_spec.rb +3 -3
  92. data/spec/{functions → rvm/functions}/string/capstr_spec.rb +3 -3
  93. data/spec/{functions → rvm/functions}/string/center_spec.rb +3 -3
  94. data/spec/{functions → rvm/functions}/string/ljust_spec.rb +3 -3
  95. data/spec/{functions → rvm/functions}/string/regmatch_spec.rb +3 -3
  96. data/spec/{functions → rvm/functions}/string/rjust_spec.rb +3 -3
  97. data/spec/{base → rvm}/interpreter/assignment_spec.rb +1 -1
  98. data/spec/rvm/interpreter/condition_spec.rb +103 -0
  99. data/spec/{base → rvm}/interpreter/constant_spec.rb +1 -1
  100. data/spec/rvm/interpreter/core_call_spec.rb +72 -0
  101. data/spec/{base → rvm}/interpreter/interpreter_spec.rb +1 -1
  102. data/spec/{base → rvm}/interpreter/parameter_spec.rb +1 -1
  103. data/spec/rvm/interpreter/sequence_spec.rb +47 -0
  104. data/spec/{base → rvm}/interpreter/variable_spec.rb +1 -1
  105. data/spec/{base → rvm}/plugin_spec.rb +2 -2
  106. metadata +66 -35
  107. data/lib/rake/helpers/code_statistics.rb +0 -167
  108. data/spec/base/interpreter/condition_spec.rb +0 -47
  109. data/spec/base/interpreter/function_call_spec.rb +0 -72
  110. data/spec/base/interpreter/sequence_spec.rb +0 -20
  111. data/spec/functions/collection/get_spec.rb +0 -12
  112. data/spec/functions/collection/set_spec.rb +0 -10
  113. 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
+ };
@@ -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
- module Languages
7
- # This compiler handels mathematical expressions.
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
- module Math
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
- module Math
3
+ class Math
4
4
  class Compiler
5
5
  include RVM::Interpreter
6
6
  FUNCTION_MAP = {
7
- '+' => :add,
8
- '-' => :sub,
9
- '/' => :div,
10
- '*' => :mul,
11
- '^' => :power
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
- FunctionCall.new :neg, params
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]), Interpreter.const(:block,body))
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
- FunctionCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
62
+ CoreCall.new(FUNCTION_MAP[tree[:op]], [compile(tree[:left]),compile(tree[:right])])
63
63
  end
64
64
  end
65
65
  end
@@ -1,7 +1,7 @@
1
1
  require 'strscan'
2
2
  module RVM
3
3
  module Languages
4
- module Math
4
+ class Math
5
5
  class Tokenizer
6
6
  def Tokenizer.tokenize str
7
7
  s = StringScanner.new str
@@ -1,24 +1,24 @@
1
1
  module RVM
2
2
  module Languages
3
- module Math
3
+ class Math
4
4
  class Tree
5
5
  PRIORITIES = {
6
6
  ';' => -100,
7
7
  '=' => -50,
8
- '(' => 0,
9
- '+' => 10,
10
- '-' => 10,
11
- '*' => 20,
12
- '/' => 20,
13
- '^' => 30,
8
+ '(' => 0,
9
+ '+' => 10,
10
+ '-' => 10,
11
+ '*' => 20,
12
+ '/' => 20,
13
+ '^' => 30,
14
14
  }
15
15
  ASSOSICATIONS = {
16
16
  ';' => :right,
17
- '+' => :left,
18
- '-' => :left,
19
- '*' => :left,
20
- '/' => :left,
21
- '^' => :right,
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
- p(dbgstack) if $DEBUG
76
- p(dbgoutput) if $DEBUG
75
+ p(dbgstack) if $DEBUG
76
+ p(dbgoutput) if $DEBUG
77
77
  end
78
78
  while not stack.empty?
79
79
  output << stack.pop
@@ -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 = nil, file = nil, safety = RVM::Safety.new)
26
- @env = RVM::Interpreter.env()
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(language, file) if language and file
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(language, file)
33
- @safety.execute(RVM::Languages[language].new.compile(File.new(file).read),@env)
33
+ def load_library(file)
34
+ @safety.execute(@lang.compile(File.read(file)),@env)
34
35
  end
35
36
 
36
- # This is the most important part, so to say, the ruby magic in here
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 = m.to_s
46
+ n = name.to_s
55
47
  if fun = @env.function(n)
56
- fun.call(@env, vmargs)
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
- super(m,args)
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
@@ -1,30 +1,28 @@
1
1
  module RVM
2
- # = PluginHost
3
- #
4
- # $Id: plugin.rb 69 2006-07-11 06:00:20Z murphy $
5
- #
6
- # A simple subclass plugin system.
7
- #
8
- # Example:
9
- # class Generators < PluginHost
10
- # plugin_path 'app/generators'
11
- # end
12
- #
13
- # class Generator
14
- # extend Plugin
15
- # PLUGIN_HOST = Generators
16
- # end
17
- #
18
- # class FancyGenerator < Generator
19
- # register_for :fancy
20
- # end
21
- #
22
- # Generators[:fancy] #-> FancyGenerator
23
- # # or
24
- # require_plugin 'Generators/fancy'
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 all_plugin_names and load.
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
- id = validate_id id
124
- plugin_hash[nil] = id
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
- "id must be a Symbol, but it was a #{id.class}"
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
- def has? id
147
- plugin_hash.has_key? id
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
- "#{name}[#{host_id}]#{map.inspect}"
187
+ "#{name}[#{host_id}]#{map.inspect}"
157
188
  end
158
189
 
159
- protected
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
- Hash.new do |h, plugin_id|
164
- raise "Plugin #{plugin_id} not loaded."
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
- "String or Symbol expected, but #{id.class}(#{id}) given."
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
- "PluginHost expected, but #{host.class} given."
294
+ "PluginHost expected, but #{host.class} given."
231
295
  end
232
- self.const_set :PLUGIN_HOST, host if host and not self.const_defined?(:PLUGIN_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
- "No host for #{host_id.inspect} found." unless host
333
+ "No host for #{host_id.inspect} found." unless host
271
334
  host.load plugin_id
272
335
  end
336
+
273
337
  end