vmc 0.0.8 → 0.2.4

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