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.
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