vmc 0.0.8 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/LICENSE +8 -3
  2. data/README.md +83 -0
  3. data/Rakefile +11 -65
  4. data/bin/vmc +3 -2
  5. data/lib/cli/commands/admin.rb +57 -0
  6. data/lib/cli/commands/apps.rb +828 -0
  7. data/lib/cli/commands/base.rb +56 -0
  8. data/lib/cli/commands/misc.rb +99 -0
  9. data/lib/cli/commands/services.rb +84 -0
  10. data/lib/cli/commands/user.rb +60 -0
  11. data/lib/cli/config.rb +109 -0
  12. data/lib/cli/core_ext.rb +119 -0
  13. data/lib/cli/errors.rb +19 -0
  14. data/lib/cli/frameworks.rb +97 -0
  15. data/lib/cli/runner.rb +437 -0
  16. data/lib/cli/services_helper.rb +74 -0
  17. data/lib/cli/usage.rb +94 -0
  18. data/lib/cli/version.rb +5 -0
  19. data/lib/cli/zip_util.rb +61 -0
  20. data/lib/cli.rb +30 -0
  21. data/lib/vmc/client.rb +415 -0
  22. data/lib/vmc/const.rb +19 -0
  23. data/lib/vmc.rb +2 -1589
  24. data/spec/assets/app_info.txt +9 -0
  25. data/spec/assets/app_listings.txt +9 -0
  26. data/spec/assets/bad_create_app.txt +9 -0
  27. data/spec/assets/delete_app.txt +9 -0
  28. data/spec/assets/global_service_listings.txt +9 -0
  29. data/spec/assets/good_create_app.txt +9 -0
  30. data/spec/assets/good_create_service.txt +9 -0
  31. data/spec/assets/info_authenticated.txt +27 -0
  32. data/spec/assets/info_return.txt +15 -0
  33. data/spec/assets/info_return_bad.txt +16 -0
  34. data/spec/assets/login_fail.txt +9 -0
  35. data/spec/assets/login_success.txt +9 -0
  36. data/spec/assets/sample_token.txt +1 -0
  37. data/spec/assets/service_already_exists.txt +9 -0
  38. data/spec/assets/service_listings.txt +9 -0
  39. data/spec/assets/service_not_found.txt +9 -0
  40. data/spec/assets/user_info.txt +9 -0
  41. data/spec/spec_helper.rb +11 -0
  42. data/spec/unit/cli_opts_spec.rb +73 -0
  43. data/spec/unit/client_spec.rb +284 -0
  44. metadata +114 -71
  45. data/README +0 -58
  46. data/lib/parse.rb +0 -719
  47. data/lib/vmc_base.rb +0 -205
  48. data/vendor/gems/httpclient/VERSION +0 -1
  49. data/vendor/gems/httpclient/lib/http-access2/cookie.rb +0 -1
  50. data/vendor/gems/httpclient/lib/http-access2/http.rb +0 -1
  51. data/vendor/gems/httpclient/lib/http-access2.rb +0 -53
  52. data/vendor/gems/httpclient/lib/httpclient/auth.rb +0 -522
  53. data/vendor/gems/httpclient/lib/httpclient/cacert.p7s +0 -1579
  54. data/vendor/gems/httpclient/lib/httpclient/cacert_sha1.p7s +0 -1579
  55. data/vendor/gems/httpclient/lib/httpclient/connection.rb +0 -84
  56. data/vendor/gems/httpclient/lib/httpclient/cookie.rb +0 -562
  57. data/vendor/gems/httpclient/lib/httpclient/http.rb +0 -867
  58. data/vendor/gems/httpclient/lib/httpclient/session.rb +0 -864
  59. data/vendor/gems/httpclient/lib/httpclient/ssl_config.rb +0 -417
  60. data/vendor/gems/httpclient/lib/httpclient/timeout.rb +0 -136
  61. data/vendor/gems/httpclient/lib/httpclient/util.rb +0 -86
  62. data/vendor/gems/httpclient/lib/httpclient.rb +0 -1020
  63. data/vendor/gems/httpclient/lib/tags +0 -908
@@ -0,0 +1,56 @@
1
+
2
+ require 'rubygems'
3
+ require 'terminal-table/import'
4
+ require 'highline/import'
5
+
6
+ module VMC::Cli
7
+
8
+ module Command
9
+
10
+ class Base
11
+ attr_reader :no_prompt, :prompt_ok
12
+
13
+ def initialize(options={})
14
+ @options = options.dup
15
+ @no_prompt = @options[:noprompts]
16
+ @prompt_ok = !no_prompt
17
+
18
+ # Fix for system ruby and Highline (stdin) on MacOSX
19
+ if RUBY_PLATFORM =~ /darwin/ && RUBY_VERSION == '1.8.7' && RUBY_PATCHLEVEL <= 174
20
+ HighLine.track_eof = false
21
+ end
22
+
23
+ # Suppress colorize on Windows systems for now.
24
+ if !!RUBY_PLATFORM['mingw'] || !!RUBY_PLATFORM['mswin32'] || !!RUBY_PLATFORM['cygwin']
25
+ VMC::Cli::Config.colorize = false
26
+ end
27
+
28
+ end
29
+
30
+ def client
31
+ return @client if @client
32
+ @client = VMC::Client.new(target_url, auth_token)
33
+ @client.trace = true if VMC::Cli::Config.trace
34
+ @client.proxy_for @options[:proxy] if @options[:proxy]
35
+ @client
36
+ end
37
+
38
+ def client_info
39
+ return @client_info if @client_info
40
+ @client_info = client.info
41
+ end
42
+
43
+ def target_url
44
+ return @target_url if @target_url
45
+ @target_url = VMC::Cli::Config.target_url
46
+ end
47
+
48
+ def auth_token
49
+ return @auth_token if @auth_token
50
+ @auth_token = VMC::Cli::Config.auth_token
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,99 @@
1
+ module VMC::Cli::Command
2
+
3
+ class Misc < Base
4
+ def version
5
+ say "vmc #{VMC::Cli::VERSION}"
6
+ end
7
+
8
+ def target
9
+ return display JSON.pretty_generate({:target => target_url}) if @options[:json]
10
+ banner "[#{target_url}]"
11
+ end
12
+
13
+ def targets
14
+ targets = VMC::Cli::Config.targets
15
+ return display JSON.pretty_generate(targets) if @options[:json]
16
+ return display 'None specified' if targets.empty?
17
+ targets_table = table do |t|
18
+ t.headings = 'Target', 'Authorization'
19
+ targets.each { |target, token| t << [target, token] }
20
+ end
21
+ display "\n"
22
+ display targets_table
23
+ end
24
+
25
+ alias :tokens :targets
26
+
27
+ def set_target(target_url)
28
+ target_url = "http://#{target_url}" unless /^https?/ =~ target_url
29
+ client = VMC::Client.new(target_url)
30
+ unless client.target_valid?
31
+ if prompt_ok
32
+ display "Host is not valid: '#{target_url}'".red
33
+ show_response = ask "Would you like see the response [yN]? "
34
+ display "\n<<<\n#{client.raw_info}\n>>>\n" if show_response.upcase == 'Y'
35
+ end
36
+ exit(false)
37
+ else
38
+ VMC::Cli::Config.store_target(target_url)
39
+ say "Succesfully targeted to [#{target_url}]".green
40
+ end
41
+ end
42
+
43
+ def info
44
+ info = client.info
45
+ return display JSON.pretty_generate(info) if @options[:json]
46
+
47
+ display "\n#{info[:description]}"
48
+ display "For support visit #{info[:support]}"
49
+ display ""
50
+ display "Target: #{target_url} (v#{info[:version]})"
51
+ display "Client: v#{VMC::Cli::VERSION}"
52
+ if info[:user]
53
+ display ''
54
+ display "User: #{info[:user]}"
55
+ end
56
+ if usage = info[:usage] and limits = info[:limits]
57
+ tmem = pretty_size(limits[:memory]*1024*1024)
58
+ mem = pretty_size(usage[:memory]*1024*1024)
59
+ tser = limits[:services]
60
+ ser = usage[:services]
61
+ tapps = limits[:apps] || 0
62
+ apps = usage[:apps] || 0
63
+ display "Usage: Memory (#{mem} of #{tmem} total)"
64
+ display " Services (#{ser} of #{tser} total)"
65
+ display " Apps (#{apps} of #{tapps} total)" if limits[:apps]
66
+ end
67
+ end
68
+
69
+ def aliases
70
+ aliases = VMC::Cli::Config.aliases
71
+ return display JSON.pretty_generate(aliases) if @options[:json]
72
+ return display "No Aliases" if aliases.empty?
73
+ atable = table do |t|
74
+ t.headings = 'Alias', "Command"
75
+ aliases.each { |k,v| t << [v, k] }
76
+ end
77
+ display "\n"
78
+ display atable
79
+ end
80
+
81
+ def alias(k, v=nil)
82
+ k,v = k.split('=') unless v
83
+ aliases = VMC::Cli::Config.aliases
84
+ aliases[k] = v
85
+ VMC::Cli::Config.store_aliases(aliases)
86
+ display "Successfully aliased '#{k}' to '#{v}'".green
87
+ end
88
+
89
+ def unalias(key)
90
+ aliases = VMC::Cli::Config.aliases
91
+ aliases.delete(key)
92
+ VMC::Cli::Config.store_aliases(aliases)
93
+ display "Successfully unaliased '#{key}'".green
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+
@@ -0,0 +1,84 @@
1
+ module VMC::Cli::Command
2
+
3
+ class Services < Base
4
+ include VMC::Cli::ServicesHelper
5
+
6
+ def services
7
+ ss = client.services_info
8
+ ps = client.services
9
+ if @options[:json]
10
+ services = { :system => ss, :provisioned => ps }
11
+ return display JSON.pretty_generate(services)
12
+ end
13
+ display_system_services(ss)
14
+ display_provisioned_services(ps)
15
+ end
16
+
17
+ def create_service(service=nil, name=nil, appname=nil)
18
+ unless no_prompt || service
19
+ services = client.services_info
20
+ err 'No services available to provision' if services.empty?
21
+ choose do |menu|
22
+ menu.prompt = 'Please select one you wish to provision: '
23
+ menu.select_by = :index_or_name
24
+ services.each do |service_type, value|
25
+ value.each do |vendor, version|
26
+ menu.choice(vendor.to_s) { service = vendor.to_s }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ name = @options[:name] unless name
32
+ unless name
33
+ name = random_service_name(service)
34
+ picked_name = true
35
+ end
36
+ create_service_banner(service, name, picked_name)
37
+ appname = @options[:bind] unless appname
38
+ bind_service_banner(name, appname) if appname
39
+ end
40
+
41
+ def delete_service(service=nil)
42
+ unless no_prompt || service
43
+ user_services = client.services
44
+ err 'No services available to delete' if user_services.empty?
45
+ choose do |menu|
46
+ menu.prompt = 'Please select one you wish to delete: '
47
+ menu.select_by = :index_or_name
48
+ user_services.each do |s|
49
+ menu.choice(s[:name]) { service = s[:name] }
50
+ end
51
+ end
52
+ end
53
+ err "Service name required." unless service
54
+ display "Deleting service [#{service}]: ", false
55
+ client.delete_service(service)
56
+ display 'OK'.green
57
+ end
58
+
59
+ def bind_service(service, appname)
60
+ bind_service_banner(service, appname)
61
+ end
62
+
63
+ def unbind_service(service, appname)
64
+ unbind_service_banner(service, appname)
65
+ end
66
+
67
+ def clone_services(src_app, dest_app)
68
+ begin
69
+ src = client.app_info(src_app)
70
+ dest = client.app_info(dest_app)
71
+ rescue
72
+ end
73
+
74
+ err "Application '#{src_app}' does not exist" unless src
75
+ err "Application '#{dest_app}' does not exist" unless dest
76
+
77
+ services = src[:services]
78
+ err 'No services to clone' unless services && !services.empty?
79
+ services.each { |service| bind_service_banner(service, dest_app, false) }
80
+ check_app_for_restart(dest_app)
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,60 @@
1
+ module VMC::Cli::Command
2
+
3
+ class User < Base
4
+
5
+ def info
6
+ info = client.info
7
+ username = info[:user] || 'N/A'
8
+ return display JSON.pretty_generate([username]) if @options[:json]
9
+ display "\n[#{username}]"
10
+ end
11
+
12
+ def login(email=nil)
13
+ email = @options[:email] unless email
14
+ password = @options[:password]
15
+ tries = 0
16
+ email = ask("Email: ") unless no_prompt || email
17
+ password = ask("Password: ") {|q| q.echo = '*'} unless no_prompt || password
18
+ err "Need a valid email" unless email
19
+ err "Need a password" unless password
20
+ login_and_save_token(email, password)
21
+ say "Successfully logged into [#{target_url}]".green
22
+ rescue VMC::Client::TargetError
23
+ display "Problem with login, invalid account or password.".red
24
+ retry if (tries += 1) < 3 && prompt_ok
25
+ exit 1
26
+ rescue => e
27
+ display "Problem with login, #{e}, try again or register for an account.".red
28
+ exit 1
29
+ end
30
+
31
+ def logout
32
+ VMC::Cli::Config.remove_token_file
33
+ say "Successfully logged out of [#{target_url}]".green
34
+ end
35
+
36
+ def change_password(password=nil)
37
+ info = client.info
38
+ email = info[:user]
39
+ err "Need to be logged in to change password." unless email
40
+ say "Changing password for '#{email}'\n"
41
+ unless no_prompt
42
+ password = ask("New Password: ") {|q| q.echo = '*'}
43
+ password2 = ask("Verify Password: ") {|q| q.echo = '*'}
44
+ err "Passwords did not match, try again" if password != password2
45
+ end
46
+ err "Password required" unless password
47
+ client.change_password(password)
48
+ say "\nSuccessfully changed password".green
49
+ end
50
+
51
+ private
52
+
53
+ def login_and_save_token(email, password)
54
+ token = client.login(email, password)
55
+ VMC::Cli::Config.store_token(token)
56
+ end
57
+
58
+ end
59
+
60
+ end
data/lib/cli/config.rb ADDED
@@ -0,0 +1,109 @@
1
+ require "yaml"
2
+ require 'fileutils'
3
+
4
+ require 'rubygems'
5
+ require 'json/pure'
6
+
7
+ module VMC::Cli
8
+ class Config
9
+
10
+ DEFAULT_TARGET = 'api.vcap.me'
11
+ DEFAULT_SUGGEST = 'vcap.me'
12
+
13
+ TARGET_FILE = '~/.vmc_target'
14
+ TOKEN_FILE = '~/.vmc_token'
15
+ INSTANCES_FILE = '~/.vmc_instances'
16
+ ALIASES_FILE = '~/.vmc_aliases'
17
+
18
+ class << self
19
+ attr_accessor :colorize
20
+ attr_accessor :output
21
+ attr_accessor :trace
22
+ attr_accessor :nozip
23
+ attr_reader :suggest_url
24
+
25
+ def target_url
26
+ return @target_url if @target_url
27
+ target_file = File.expand_path(TARGET_FILE)
28
+ if File.exists? target_file
29
+ @target_url = File.read(target_file).strip!
30
+ ha = @target_url.split('.')
31
+ ha.shift
32
+ @suggest_url = ha.join('.')
33
+ @suggest_url = DEFAULT_SUGGEST if @suggest_url.empty?
34
+ else
35
+ @target_url = DEFAULT_TARGET
36
+ @suggest_url = DEFAULT_SUGGEST
37
+ end
38
+ @target_url = "http://#{@target_url}" unless /^https?/ =~ @target_url
39
+ @target_url
40
+ end
41
+
42
+ def store_target(target_host)
43
+ target_file = File.expand_path(TARGET_FILE)
44
+ File.open(target_file, 'w+') { |f| f.puts target_host }
45
+ FileUtils.chmod 0600, target_file
46
+ end
47
+
48
+ def all_tokens
49
+ token_file = File.expand_path(TOKEN_FILE)
50
+ return nil unless File.exists? token_file
51
+ contents = File.read(token_file).strip
52
+ JSON.parse(contents)
53
+ end
54
+
55
+ alias :targets :all_tokens
56
+
57
+ def auth_token
58
+ return @token if @token
59
+ tokens = all_tokens
60
+ @token = tokens[target_url] if tokens
61
+ end
62
+
63
+ def remove_token_file
64
+ FileUtils.rm_f(File.expand_path(TOKEN_FILE))
65
+ end
66
+
67
+ def store_token(token)
68
+ tokens = all_tokens || {}
69
+ tokens[target_url] = token
70
+ token_file = File.expand_path(TOKEN_FILE)
71
+ File.open(token_file, 'w+') { |f| f.write(tokens.to_json) }
72
+ FileUtils.chmod 0600, token_file
73
+ end
74
+
75
+ def instances
76
+ instances_file = File.expand_path(INSTANCES_FILE)
77
+ return nil unless File.exists? instances_file
78
+ contents = File.read(instances_file).strip
79
+ JSON.parse(contents)
80
+ end
81
+
82
+ def store_instances(instances)
83
+ instances_file = File.expand_path(INSTANCES_FILE)
84
+ File.open(instances_file, 'w') { |f| f.write(instances.to_json) }
85
+ end
86
+
87
+ def aliases
88
+ aliases_file = File.expand_path(ALIASES_FILE)
89
+ # bacward compatible
90
+ unless File.exists? aliases_file
91
+ old_aliases_file = File.expand_path('~/.vmc-aliases')
92
+ FileUtils.mv(old_aliases_file, aliases_file) if File.exists? old_aliases_file
93
+ end
94
+ aliases = YAML.load_file(aliases_file) rescue {}
95
+ end
96
+
97
+ def store_aliases(aliases)
98
+ aliases_file = File.expand_path(ALIASES_FILE)
99
+ File.open(aliases_file, 'wb') {|f| f.write(aliases.to_yaml)}
100
+ end
101
+
102
+ end
103
+
104
+ def initialize(work_dir = Dir.pwd)
105
+ @work_dir = work_dir
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,119 @@
1
+ module VMCExtensions
2
+
3
+ def say(message)
4
+ VMC::Cli::Config.output.puts(message) if VMC::Cli::Config.output
5
+ end
6
+
7
+ def header(message, filler = '-')
8
+ say "\n"
9
+ say message
10
+ say filler.to_s * message.size
11
+ end
12
+
13
+ def banner(message)
14
+ say "\n"
15
+ say message
16
+ end
17
+
18
+ def display(message, nl=true)
19
+ if nl
20
+ say message
21
+ else
22
+ if VMC::Cli::Config.output
23
+ VMC::Cli::Config.output.print(message)
24
+ VMC::Cli::Config.output.flush
25
+ end
26
+ end
27
+ end
28
+
29
+ def clear(size=80)
30
+ return unless VMC::Cli::Config.output
31
+ VMC::Cli::Config.output.print("\r")
32
+ VMC::Cli::Config.output.print(" " * size)
33
+ VMC::Cli::Config.output.print("\r")
34
+ #VMC::Cli::Config.output.flush
35
+ end
36
+
37
+ def err(message, prefix='Error: ')
38
+ raise VMC::Cli::CliExit, "#{prefix}#{message}"
39
+ end
40
+
41
+ def quit(message = nil)
42
+ raise VMC::Cli::GracefulExit, message
43
+ end
44
+
45
+ def blank?
46
+ self.to_s.blank?
47
+ end
48
+
49
+ def uptime_string(delta)
50
+ num_seconds = delta.to_i
51
+ days = num_seconds / (60 * 60 * 24);
52
+ num_seconds -= days * (60 * 60 * 24);
53
+ hours = num_seconds / (60 * 60);
54
+ num_seconds -= hours * (60 * 60);
55
+ minutes = num_seconds / 60;
56
+ num_seconds -= minutes * 60;
57
+ "#{days}d:#{hours}h:#{minutes}m:#{num_seconds}s"
58
+ end
59
+
60
+ def pretty_size(size, prec=1)
61
+ return 'NA' unless size
62
+ return "#{size}B" if size < 1024
63
+ return sprintf("%.#{prec}fK", size/1024.0) if size < (1024*1024)
64
+ return sprintf("%.#{prec}fM", size/(1024.0*1024.0)) if size < (1024*1024*1024)
65
+ return sprintf("%.#{prec}fG", size/(1024.0*1024.0*1024.0))
66
+ end
67
+
68
+ end
69
+
70
+ module VMCStringExtensions
71
+
72
+ def red
73
+ colorize("\e[0m\e[31m")
74
+ end
75
+
76
+ def green
77
+ colorize("\e[0m\e[32m")
78
+ end
79
+
80
+ def yellow
81
+ colorize("\e[0m\e[33m")
82
+ end
83
+
84
+ def bold
85
+ colorize("\e[0m\e[1m")
86
+ end
87
+
88
+ def colorize(color_code)
89
+ if VMC::Cli::Config.colorize
90
+ "#{color_code}#{self}\e[0m"
91
+ else
92
+ self
93
+ end
94
+ end
95
+
96
+ def blank?
97
+ self =~ /^\s*$/
98
+ end
99
+
100
+ def truncate(limit = 30)
101
+ return "" if self.blank?
102
+ etc = "..."
103
+ stripped = self.strip[0..limit]
104
+ if stripped.length > limit
105
+ stripped.gsub(/\s+?(\S+)?$/, "") + etc
106
+ else
107
+ stripped
108
+ end
109
+ end
110
+
111
+ end
112
+
113
+ class Object
114
+ include VMCExtensions
115
+ end
116
+
117
+ class String
118
+ include VMCStringExtensions
119
+ end
data/lib/cli/errors.rb ADDED
@@ -0,0 +1,19 @@
1
+ module VMC::Cli
2
+
3
+ class CliError < StandardError
4
+ def self.error_code(code = nil)
5
+ define_method(:error_code) { code }
6
+ end
7
+ end
8
+
9
+ class UnknownCommand < CliError; error_code(100); end
10
+ class TargetMissing < CliError; error_code(102); end
11
+ class TargetInaccessible < CliError; error_code(103); end
12
+
13
+ class TargetError < CliError; error_code(201); end
14
+ class AuthError < TargetError; error_code(202); end
15
+
16
+ class CliExit < CliError; error_code(400); end
17
+ class GracefulExit < CliExit; error_code(401); end
18
+
19
+ end
@@ -0,0 +1,97 @@
1
+ module VMC::Cli
2
+
3
+ class Framework
4
+
5
+ DEFAULT_FRAMEWORK = "http://b20nine.com/unknown"
6
+ DEFAULT_MEM = '256M'
7
+
8
+ FRAMEWORKS = {
9
+ 'Rails' => ['rails/1.0', { :mem => '256M', :description => 'Rails Application'}],
10
+ 'Spring' => ['spring_web/1.0', { :mem => '512M', :description => 'Java SpringSource Spring Application'}],
11
+ 'Grails' => ['grails/1.0', { :mem => '512M', :description => 'Java SpringSource Grails Application'}],
12
+ 'Roo' => ['spring_web/1.0', { :mem => '512M', :description => 'Java SpringSource Roo Application'}],
13
+ 'JavaWeb' => ['spring_web/1.0', { :mem => '512M', :description => 'Java Web Application'}],
14
+ 'Sinatra' => [DEFAULT_FRAMEWORK, { :mem => '128M', :description => 'Sinatra Application'}],
15
+ 'Node' => ['nodejs/1.0', { :mem => '64M', :description => 'NodeJS Application'}],
16
+ }
17
+
18
+ class << self
19
+
20
+ def known_frameworks
21
+ FRAMEWORKS.keys
22
+ end
23
+
24
+ def lookup(name)
25
+ return Framework.new(*FRAMEWORKS[name])
26
+ end
27
+
28
+ def detect(path)
29
+ Dir.chdir(path) do
30
+
31
+ # Rails
32
+ if File.exist?('config/environment.rb')
33
+ return Framework.lookup('Rails')
34
+
35
+ # Java
36
+ elsif Dir.glob('*.war').first
37
+ war_file = Dir.glob('*.war').first
38
+ contents = ZipUtil.entry_lines(war_file)
39
+
40
+ # Spring Variations
41
+ if contents =~ /WEB-INF\/grails-app/
42
+ return Framework.lookup('Grails')
43
+ elsif contents =~ /WEB-INF\/classes\/org\/springframework/
44
+ return Framework.lookup('Spring')
45
+ elsif contents =~ /WEB-INF\/lib\/spring-core.*\.jar/
46
+ return Framework.lookup('Spring')
47
+ else
48
+ return Framework.lookup('JavaWeb')
49
+ end
50
+
51
+ # Simple Ruby Apps
52
+ elsif !Dir.glob('*.rb').empty?
53
+ matched_file = nil
54
+ Dir.glob('*.rb').each do |fname|
55
+ next if matched_file
56
+ File.open(fname, 'r') do |f|
57
+ str = f.read # This might want to be limited
58
+ matched_file = fname if (str && str.match(/^\s*require\s*'sinatra'/))
59
+ end
60
+ end
61
+ if matched_file && !File.exist?('config.ru')
62
+ f = Framework.lookup('Sinatra')
63
+ f.exec = "ruby #{matched_file}"
64
+ return f
65
+ end
66
+
67
+ # Node.JS
68
+ elsif !Dir.glob('*.js').empty?
69
+ # Fixme, make other files work too..
70
+ if File.exist?('app.js') || File.exist?('index.js') || File.exist?('main.js')
71
+ return Framework.lookup('Node')
72
+ end
73
+ end
74
+ end
75
+ nil
76
+ end
77
+
78
+ end
79
+
80
+ attr_reader :name, :description, :memory
81
+ attr_accessor :exec
82
+
83
+ alias :mem :memory
84
+
85
+ def initialize(framework=nil, opts={})
86
+ @name = framework || DEFAULT_FRAMEWORK
87
+ @memory = opts[:mem] || DEFAULT_MEM
88
+ @description = opts[:description] || 'Unknown Application Type'
89
+ @exec = opts[:exec]
90
+ end
91
+
92
+ def to_s
93
+ description
94
+ end
95
+ end
96
+
97
+ end