af 0.3.22 → 0.5.0.beta.1

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 (157) hide show
  1. checksums.yaml +14 -6
  2. data/LICENSE +1277 -24
  3. data/Rakefile +24 -87
  4. data/bin/af +7 -2
  5. data/lib/af/version.rb +3 -0
  6. data/lib/vmc.rb +7 -2
  7. data/lib/vmc/cli.rb +475 -0
  8. data/lib/vmc/cli/app/app.rb +45 -0
  9. data/lib/vmc/cli/app/apps.rb +105 -0
  10. data/lib/vmc/cli/app/base.rb +82 -0
  11. data/lib/vmc/cli/app/crashes.rb +46 -0
  12. data/lib/vmc/cli/app/delete.rb +95 -0
  13. data/lib/vmc/cli/app/deprecated.rb +11 -0
  14. data/lib/vmc/cli/app/env.rb +78 -0
  15. data/lib/vmc/cli/app/files.rb +137 -0
  16. data/lib/vmc/cli/app/health.rb +26 -0
  17. data/lib/vmc/cli/app/instances.rb +53 -0
  18. data/lib/vmc/cli/app/logs.rb +76 -0
  19. data/lib/vmc/cli/app/push.rb +107 -0
  20. data/lib/vmc/cli/app/push/create.rb +150 -0
  21. data/lib/vmc/cli/app/push/interactions.rb +100 -0
  22. data/lib/vmc/cli/app/push/sync.rb +64 -0
  23. data/lib/vmc/cli/app/rename.rb +39 -0
  24. data/lib/vmc/cli/app/restart.rb +20 -0
  25. data/lib/vmc/cli/app/scale.rb +71 -0
  26. data/lib/vmc/cli/app/start.rb +93 -0
  27. data/lib/vmc/cli/app/stats.rb +67 -0
  28. data/lib/vmc/cli/app/stop.rb +27 -0
  29. data/lib/vmc/cli/domain/base.rb +12 -0
  30. data/lib/vmc/cli/domain/domains.rb +40 -0
  31. data/lib/vmc/cli/domain/map.rb +55 -0
  32. data/lib/vmc/cli/domain/unmap.rb +56 -0
  33. data/lib/vmc/cli/help.rb +16 -0
  34. data/lib/vmc/cli/interactive.rb +105 -0
  35. data/lib/vmc/cli/organization/base.rb +14 -0
  36. data/lib/vmc/cli/organization/create.rb +32 -0
  37. data/lib/vmc/cli/organization/delete.rb +73 -0
  38. data/lib/vmc/cli/organization/org.rb +45 -0
  39. data/lib/vmc/cli/organization/orgs.rb +35 -0
  40. data/lib/vmc/cli/organization/rename.rb +36 -0
  41. data/lib/vmc/cli/route/base.rb +12 -0
  42. data/lib/vmc/cli/route/map.rb +80 -0
  43. data/lib/vmc/cli/route/routes.rb +26 -0
  44. data/lib/vmc/cli/route/unmap.rb +94 -0
  45. data/lib/vmc/cli/service/base.rb +8 -0
  46. data/lib/vmc/cli/service/bind.rb +44 -0
  47. data/lib/vmc/cli/service/create.rb +126 -0
  48. data/lib/vmc/cli/service/delete.rb +86 -0
  49. data/lib/vmc/cli/service/rename.rb +35 -0
  50. data/lib/vmc/cli/service/service.rb +42 -0
  51. data/lib/vmc/cli/service/services.rb +115 -0
  52. data/lib/vmc/cli/service/unbind.rb +38 -0
  53. data/lib/vmc/cli/space/base.rb +21 -0
  54. data/lib/vmc/cli/space/create.rb +56 -0
  55. data/lib/vmc/cli/space/delete.rb +95 -0
  56. data/lib/vmc/cli/space/rename.rb +39 -0
  57. data/lib/vmc/cli/space/space.rb +64 -0
  58. data/lib/vmc/cli/space/spaces.rb +55 -0
  59. data/lib/vmc/cli/space/take.rb +16 -0
  60. data/lib/vmc/cli/start/base.rb +80 -0
  61. data/lib/vmc/cli/start/colors.rb +13 -0
  62. data/lib/vmc/cli/start/info.rb +122 -0
  63. data/lib/vmc/cli/start/login.rb +92 -0
  64. data/lib/vmc/cli/start/logout.rb +13 -0
  65. data/lib/vmc/cli/start/target.rb +64 -0
  66. data/lib/vmc/cli/start/target_interactions.rb +37 -0
  67. data/lib/vmc/cli/start/targets.rb +16 -0
  68. data/lib/vmc/cli/user/base.rb +29 -0
  69. data/lib/vmc/cli/user/create.rb +39 -0
  70. data/lib/vmc/cli/user/delete.rb +25 -0
  71. data/lib/vmc/cli/user/passwd.rb +50 -0
  72. data/lib/vmc/cli/user/register.rb +42 -0
  73. data/lib/vmc/cli/user/users.rb +32 -0
  74. data/lib/vmc/constants.rb +13 -0
  75. data/lib/vmc/detect.rb +134 -0
  76. data/lib/vmc/errors.rb +17 -0
  77. data/lib/vmc/plugin.rb +56 -0
  78. data/lib/vmc/spacing.rb +89 -0
  79. data/lib/vmc/spec_helper.rb +1 -0
  80. data/lib/vmc/test_support.rb +4 -0
  81. data/lib/vmc/test_support/command_helper.rb +32 -0
  82. data/lib/vmc/test_support/common_input_examples.rb +14 -0
  83. data/lib/vmc/test_support/fake_home_dir.rb +16 -0
  84. data/lib/vmc/test_support/interact_helper.rb +29 -0
  85. data/lib/vmc/version.rb +3 -0
  86. data/spec/assets/hello-sinatra/Gemfile +3 -0
  87. data/spec/assets/hello-sinatra/main.rb +6 -0
  88. data/spec/features/new_user_flow_spec.rb +71 -0
  89. data/spec/spec_helper.rb +63 -0
  90. data/spec/vmc/cli/app/base_spec.rb +17 -0
  91. data/spec/vmc/cli/app/delete_spec.rb +188 -0
  92. data/spec/vmc/cli/app/instances_spec.rb +65 -0
  93. data/spec/vmc/cli/app/push/create_spec.rb +571 -0
  94. data/spec/vmc/cli/app/push_spec.rb +369 -0
  95. data/spec/vmc/cli/app/rename_spec.rb +104 -0
  96. data/spec/vmc/cli/app/scale_spec.rb +81 -0
  97. data/spec/vmc/cli/app/stats_spec.rb +62 -0
  98. data/spec/vmc/cli/domain/map_spec.rb +140 -0
  99. data/spec/vmc/cli/domain/unmap_spec.rb +73 -0
  100. data/spec/vmc/cli/organization/orgs_spec.rb +108 -0
  101. data/spec/vmc/cli/organization/rename_spec.rb +113 -0
  102. data/spec/vmc/cli/route/map_spec.rb +138 -0
  103. data/spec/vmc/cli/route/unmap_spec.rb +215 -0
  104. data/spec/vmc/cli/service/bind_spec.rb +25 -0
  105. data/spec/vmc/cli/service/delete_spec.rb +22 -0
  106. data/spec/vmc/cli/service/rename_spec.rb +105 -0
  107. data/spec/vmc/cli/service/service_spec.rb +23 -0
  108. data/spec/vmc/cli/service/unbind_spec.rb +25 -0
  109. data/spec/vmc/cli/space/rename_spec.rb +102 -0
  110. data/spec/vmc/cli/space/spaces_spec.rb +104 -0
  111. data/spec/vmc/cli/start/info_spec.rb +153 -0
  112. data/spec/vmc/cli/start/login_spec.rb +71 -0
  113. data/spec/vmc/cli/user/create_spec.rb +54 -0
  114. data/spec/vmc/cli/user/passwd_spec.rb +102 -0
  115. data/spec/vmc/cli/user/register_spec.rb +148 -0
  116. data/spec/vmc/cli_spec.rb +448 -0
  117. data/spec/vmc/detect_spec.rb +54 -0
  118. metadata +231 -124
  119. data/README.md +0 -155
  120. data/caldecott_helper/Gemfile +0 -10
  121. data/caldecott_helper/Gemfile.lock +0 -48
  122. data/caldecott_helper/server.rb +0 -43
  123. data/config/clients.yml +0 -17
  124. data/config/micro/offline.conf +0 -2
  125. data/config/micro/paths.yml +0 -22
  126. data/config/micro/refresh_ip.rb +0 -20
  127. data/lib/cli.rb +0 -48
  128. data/lib/cli/commands/admin.rb +0 -81
  129. data/lib/cli/commands/apps.rb +0 -1358
  130. data/lib/cli/commands/base.rb +0 -233
  131. data/lib/cli/commands/manifest.rb +0 -56
  132. data/lib/cli/commands/micro.rb +0 -115
  133. data/lib/cli/commands/misc.rb +0 -147
  134. data/lib/cli/commands/services.rb +0 -217
  135. data/lib/cli/commands/user.rb +0 -70
  136. data/lib/cli/config.rb +0 -176
  137. data/lib/cli/console_helper.rb +0 -163
  138. data/lib/cli/core_ext.rb +0 -122
  139. data/lib/cli/errors.rb +0 -19
  140. data/lib/cli/file_helper.rb +0 -123
  141. data/lib/cli/frameworks.rb +0 -265
  142. data/lib/cli/manifest_helper.rb +0 -316
  143. data/lib/cli/runner.rb +0 -633
  144. data/lib/cli/services_helper.rb +0 -104
  145. data/lib/cli/tunnel_helper.rb +0 -336
  146. data/lib/cli/usage.rb +0 -129
  147. data/lib/cli/version.rb +0 -7
  148. data/lib/cli/zip_util.rb +0 -102
  149. data/lib/vmc/client.rb +0 -574
  150. data/lib/vmc/const.rb +0 -27
  151. data/lib/vmc/micro.rb +0 -56
  152. data/lib/vmc/micro/switcher/base.rb +0 -97
  153. data/lib/vmc/micro/switcher/darwin.rb +0 -19
  154. data/lib/vmc/micro/switcher/dummy.rb +0 -15
  155. data/lib/vmc/micro/switcher/linux.rb +0 -16
  156. data/lib/vmc/micro/switcher/windows.rb +0 -31
  157. data/lib/vmc/micro/vmrun.rb +0 -158
@@ -0,0 +1,13 @@
1
+ require "vmc/cli/start/base"
2
+
3
+ module VMC::Start
4
+ class Logout < Base
5
+ desc "Log out from the target"
6
+ group :start
7
+ def logout
8
+ with_progress("Logging out") do
9
+ remove_target_info
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ require "vmc/cli/start/base"
2
+ require "vmc/cli/start/target_interactions"
3
+
4
+ module VMC::Start
5
+ class Target < Base
6
+ desc "Set or display the target cloud, organization, and space"
7
+ group :start
8
+ input :url, :desc => "Target URL to switch to", :argument => :optional
9
+ input :organization, :desc => "Organization" , :aliases => %w{--org -o},
10
+ :from_given => by_name(:organization)
11
+ input :space, :desc => "Space", :alias => "-s",
12
+ :from_given => by_name(:space)
13
+ interactions TargetInteractions
14
+ def target
15
+ unless input.has?(:url) || input.has?(:organization) || \
16
+ input.has?(:space)
17
+ display_target
18
+ display_org_and_space unless quiet?
19
+ return
20
+ end
21
+
22
+ if input.has?(:url)
23
+ target = sane_target_url(input[:url])
24
+ with_progress("Setting target to #{c(target, :name)}") do
25
+ client(target).info # check that it's valid before setting
26
+ set_target(target)
27
+ end
28
+ end
29
+
30
+ return unless v2? && client.logged_in?
31
+
32
+ if input.has?(:organization) || input.has?(:space)
33
+ info = target_info
34
+
35
+ select_org_and_space(input, info)
36
+
37
+ save_target_info(info)
38
+ end
39
+
40
+ return if quiet?
41
+
42
+ invalidate_client
43
+
44
+ line
45
+ display_target
46
+ display_org_and_space
47
+ end
48
+
49
+ private
50
+
51
+ def display_org_and_space
52
+ return unless v2?
53
+
54
+ if org = client.current_organization
55
+ line "organization: #{c(org.name, :name)}"
56
+ end
57
+
58
+ if space = client.current_space
59
+ line "space: #{c(space.name, :name)}"
60
+ end
61
+ rescue CFoundry::APIError
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ module VMC::Start
2
+ module TargetInteractions
3
+ def ask_organization
4
+ orgs = client.organizations(:depth => 0)
5
+
6
+ if orgs.empty?
7
+ unless quiet?
8
+ line
9
+ line c("There are no organizations.", :warning)
10
+ line "You may want to create one with #{c("create-org", :good)}."
11
+ end
12
+ elsif orgs.size == 1 && !input.interactive?(:organization)
13
+ orgs.first
14
+ else
15
+ ask("Organization",
16
+ :choices => orgs.sort_by(&:name),
17
+ :display => proc(&:name))
18
+ end
19
+ end
20
+
21
+ def ask_space(org)
22
+ spaces = org.spaces(:depth => 0)
23
+
24
+ if spaces.empty?
25
+ unless quiet?
26
+ line
27
+ line c("There are no spaces in #{b(org.name)}.", :warning)
28
+ line "You may want to create one with #{c("create-space", :good)}."
29
+ end
30
+ elsif spaces.size == 1 && !input.interactive?(:spaces)
31
+ spaces.first
32
+ else
33
+ ask("Space", :choices => spaces, :display => proc(&:name))
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,16 @@
1
+ require "vmc/cli/start/base"
2
+
3
+ module VMC::Start
4
+ class Targets < Base
5
+ desc "List known targets."
6
+ group :start, :hidden => true
7
+ def targets
8
+ targets_info.each do |target, _|
9
+ line target
10
+ # TODO: print org/space
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+
@@ -0,0 +1,29 @@
1
+ require "vmc/cli"
2
+
3
+ module VMC
4
+ module User
5
+ class Base < CLI
6
+ def precondition
7
+ check_logged_in
8
+ end
9
+
10
+ private
11
+
12
+ def validate_password!(password)
13
+ validate_password_verified!(password)
14
+ validate_password_strength!(password)
15
+ end
16
+
17
+ def validate_password_verified!(password)
18
+ fail "Passwords do not match." unless force? || password == input[:verify]
19
+ end
20
+
21
+ def validate_password_strength!(password)
22
+ strength = client.base.uaa.password_score(password)
23
+ msg = "Your password strength is: #{strength}"
24
+ fail msg if strength == :weak
25
+ line msg
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ require "vmc/cli/user/base"
2
+
3
+ module VMC::User
4
+ class Create < Base
5
+ desc "Create a user"
6
+ group :admin, :user, :hidden => true
7
+ input :email, :desc => "User email", :argument => :optional
8
+ input :password, :desc => "User password"
9
+ input :verify, :desc => "Repeat password"
10
+ def create_user
11
+ email = input[:email]
12
+ password = input[:password]
13
+
14
+ if !force? && password != input[:verify]
15
+ fail "Passwords don't match."
16
+ end
17
+
18
+ with_progress("Creating user") do
19
+ client.register(email, password)
20
+ end
21
+ end
22
+
23
+ alias_command :add_user, :create_user
24
+
25
+ private
26
+
27
+ def ask_email
28
+ ask("Email")
29
+ end
30
+
31
+ def ask_password
32
+ ask("Password", :echo => "*", :forget => true)
33
+ end
34
+
35
+ def ask_verify
36
+ ask("Verify Password", :echo => "*", :forget => true)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ require "vmc/cli/user/base"
2
+
3
+ module VMC::User
4
+ class Delete < Base
5
+ desc "Delete a user"
6
+ group :admin, :user, :hidden => true
7
+ input :email, :desc => "User to delete", :argument => true
8
+ input :really, :type => :boolean, :forget => true, :hidden => true,
9
+ :default => proc { force? || interact }
10
+ def delete_user
11
+ email = input[:email]
12
+ return unless input[:really, email]
13
+
14
+ with_progress("Deleting #{c(email, :name)}") do
15
+ client.user(email).delete!
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def ask_really(email)
22
+ ask("Really delete user #{c(email, :name)}?", :default => false)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ require "vmc/cli/user/base"
2
+
3
+ module VMC::User
4
+ class Passwd < Base
5
+ desc "Update a user's password"
6
+ group :admin, :user, :hidden => true
7
+ input :user, :desc => "User to update", :argument => :optional,
8
+ :default => proc { client.current_user },
9
+ :from_given => proc { |email|
10
+ if v2? && client.current_user.email != email
11
+ fail "You can only change your own password on V2."
12
+ else
13
+ client.user(email)
14
+ end
15
+ }
16
+ input :password, :desc => "Current password"
17
+ input :new_password, :desc => "New password"
18
+ input :verify, :desc => "Repeat new password"
19
+ def passwd
20
+ user = input[:user]
21
+ password = input[:password] if v2?
22
+ new_password = input[:new_password]
23
+
24
+ validate_password! new_password
25
+
26
+ with_progress("Changing password") do
27
+ if v2?
28
+ user.change_password!(new_password, password)
29
+ else
30
+ user.password = new_password
31
+ user.update!
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def ask_password
39
+ ask("Current Password", :echo => "*", :forget => true)
40
+ end
41
+
42
+ def ask_new_password
43
+ ask("New Password", :echo => "*", :forget => true)
44
+ end
45
+
46
+ def ask_verify
47
+ ask("Verify Password", :echo => "*", :forget => true)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ require "vmc/cli/user/base"
2
+
3
+ module VMC::User
4
+ class Register < Base
5
+ def precondition; end
6
+
7
+ desc "Create a user and log in"
8
+ group :admin, :user, :hidden => true
9
+ input :email, :desc => "Desired email", :argument => :optional
10
+ input :password, :desc => "Desired password"
11
+ input :verify, :desc => "Repeat password"
12
+ input :login, :desc => "Automatically log in?", :default => true
13
+ def register
14
+ email = input[:email]
15
+ password = input[:password]
16
+
17
+ validate_password!(password)
18
+
19
+ with_progress("Creating user") do
20
+ client.register(email, password)
21
+ end
22
+
23
+ if input[:login]
24
+ invoke :login, :username => email, :password => password
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def ask_email
31
+ ask("Email")
32
+ end
33
+
34
+ def ask_password
35
+ ask("Password", :echo => "*", :forget => true)
36
+ end
37
+
38
+ def ask_verify
39
+ ask("Confirm Password", :echo => "*", :forget => true)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ require "vmc/cli/user/base"
2
+
3
+ module VMC::User
4
+ class Users < Base
5
+ desc "List all users"
6
+ group :admin, :hidden => true
7
+ def users
8
+ users =
9
+ with_progress("Getting users") do
10
+ client.users(:depth => 0)
11
+ end
12
+
13
+ spaced(users) do |u|
14
+ display_user(u)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def display_user(u)
21
+ if quiet?
22
+ puts u.email
23
+ else
24
+ line "#{c(u.email, :name)}:"
25
+
26
+ indented do
27
+ line "admin?: #{c(u.admin?, u.admin? ? :yes : :no)}"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module VMC
2
+ OLD_TARGET_FILE = "~/.af_target".freeze
3
+ OLD_TOKENS_FILE = "~/.af_token".freeze
4
+
5
+ CONFIG_DIR = "~/.af".freeze
6
+
7
+ LOGS_DIR = "#{CONFIG_DIR}/logs".freeze
8
+ PLUGINS_FILE = "#{CONFIG_DIR}/plugins.yml".freeze
9
+ TARGET_FILE = "#{CONFIG_DIR}/target".freeze
10
+ TOKENS_FILE = "#{CONFIG_DIR}/tokens.yml".freeze
11
+ COLORS_FILE = "#{CONFIG_DIR}/colors.yml".freeze
12
+ CRASH_FILE = "#{CONFIG_DIR}/crash".freeze
13
+ end
@@ -0,0 +1,134 @@
1
+ require "set"
2
+
3
+ require "clouseau"
4
+
5
+ module VMC
6
+ class Detector
7
+ # "Basic" framework names for a detected language
8
+ PSEUDO_FRAMEWORKS = {
9
+ :node => "node",
10
+ :python => "wsgi",
11
+ :java => "java_web",
12
+ :php => "php",
13
+ :erlang => "otp_rebar",
14
+ :dotnet => "dotNet"
15
+ }
16
+
17
+ # Mismatched framework names
18
+ FRAMEWORK_NAMES = {
19
+ :rails => "rails3"
20
+ }
21
+
22
+ # Clouseau language symbol => matching runtime names
23
+ LANGUAGE_RUNTIMES = {
24
+ :ruby => /^ruby.*/,
25
+ :java => /^java.*/,
26
+ :node => /^node.*/,
27
+ :erlang => /^erlang.*/,
28
+ :dotnet => /^dotNet.*/,
29
+ :python => /^python.*/,
30
+ :php => /^php.*/
31
+ }
32
+
33
+ def initialize(client, path)
34
+ @client = client
35
+ @path = path
36
+ end
37
+
38
+ # detect the framework
39
+ def detect_framework
40
+ detected && frameworks[detected]
41
+ end
42
+
43
+ # detect the language and return the appropriate runtimes
44
+ def detect_runtimes
45
+ if detected && lang = detected.language_name
46
+ runtimes_for(lang)
47
+ else
48
+ []
49
+ end
50
+ end
51
+
52
+ # determine runtimes for a given framework based on the language its
53
+ # detector reports itself as
54
+ def runtimes(framework)
55
+ if detector = detectors[framework]
56
+ runtimes_for(detector.language_name)
57
+ else
58
+ []
59
+ end
60
+ end
61
+
62
+ # determine suitable memory allocation via the framework's detector
63
+ def suggested_memory(framework)
64
+ if detector = detectors[framework]
65
+ detector.memory_suggestion
66
+ end
67
+ end
68
+
69
+ # helper so that this is cached somewhere
70
+ def all_runtimes
71
+ @all_runtimes ||= @client.runtimes
72
+ end
73
+
74
+ # helper so that this is cached somewhere
75
+ def all_frameworks
76
+ @all_frameworks ||= @client.frameworks
77
+ end
78
+
79
+ # helper so that this is cached somewhere
80
+ def all_infras
81
+ @all_infras ||= @client.infras
82
+ end
83
+
84
+ private
85
+
86
+ def detected
87
+ @detected ||= Clouseau.detect(@path)
88
+ end
89
+
90
+ def map_detectors!
91
+ @framework_detectors = {}
92
+ @detector_frameworks = {}
93
+
94
+ Clouseau.detectors.each do |d|
95
+ name = d.framework_name
96
+ lang = d.language_name
97
+
98
+ framework = all_frameworks.find { |f|
99
+ f.name == name.to_s ||
100
+ f.name == FRAMEWORK_NAMES[name]
101
+ }
102
+
103
+ framework ||= all_frameworks.find { |f|
104
+ f.name == PSEUDO_FRAMEWORKS[lang]
105
+ }
106
+
107
+ next unless framework
108
+
109
+ @framework_detectors[framework] = d
110
+ @detector_frameworks[d] = framework
111
+ end
112
+
113
+ nil
114
+ end
115
+
116
+ # Framework -> Detector
117
+ def detectors
118
+ map_detectors! unless @framework_detectors
119
+ @framework_detectors
120
+ end
121
+
122
+ # Detector -> Framework
123
+ def frameworks
124
+ map_detectors! unless @detector_frameworks
125
+ @detector_frameworks
126
+ end
127
+
128
+ def runtimes_for(language)
129
+ all_runtimes.select do |r|
130
+ LANGUAGE_RUNTIMES[language] === r.name
131
+ end
132
+ end
133
+ end
134
+ end