launchy 0.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.autotest +28 -0
  2. data/HISTORY +15 -0
  3. data/LICENSE +1 -1
  4. data/NOTES +1 -0
  5. data/README +6 -6
  6. data/Rakefile +47 -51
  7. data/bin/launchy +2 -10
  8. data/lib/launchy.rb +96 -30
  9. data/lib/launchy/application.rb +55 -152
  10. data/lib/launchy/applications/browser.rb +68 -0
  11. data/lib/launchy/cli.rb +70 -0
  12. data/lib/launchy/descendant_tracker.rb +49 -0
  13. data/lib/launchy/detect.rb +10 -0
  14. data/lib/launchy/detect/host_os.rb +31 -0
  15. data/lib/launchy/detect/host_os_family.rb +71 -0
  16. data/lib/launchy/detect/nix_desktop_environment.rb +60 -0
  17. data/lib/launchy/detect/ruby_engine.rb +78 -0
  18. data/lib/launchy/detect/runner.rb +96 -0
  19. data/lib/launchy/error.rb +4 -0
  20. data/lib/launchy/os_family.rb +8 -0
  21. data/lib/launchy/version.rb +8 -7
  22. data/spec/application_spec.rb +26 -47
  23. data/spec/detect/host_os_family_spec.rb +40 -0
  24. data/spec/detect/host_os_spec.rb +19 -0
  25. data/spec/detect/nix_desktop_environment_spec.rb +13 -0
  26. data/spec/detect/ruby_engine_spec.rb +37 -0
  27. data/spec/launchy_spec.rb +29 -5
  28. data/spec/mock_scheme.rb +5 -0
  29. data/spec/spec_helper.rb +3 -3
  30. data/spec/{tattle-host-os.yml → tattle-host-os.yaml} +0 -0
  31. data/spec/version_spec.rb +4 -5
  32. metadata +67 -78
  33. data/gemspec.rb +0 -46
  34. data/lib/launchy/browser.rb +0 -98
  35. data/lib/launchy/command_line.rb +0 -48
  36. data/lib/launchy/paths.rb +0 -53
  37. data/spec/browser_spec.rb +0 -62
  38. data/spec/paths_spec.rb +0 -15
  39. data/tasks/announce.rake +0 -39
  40. data/tasks/config.rb +0 -107
  41. data/tasks/distribution.rake +0 -46
  42. data/tasks/documentation.rake +0 -32
  43. data/tasks/rspec.rake +0 -25
  44. data/tasks/rubyforge.rake +0 -52
  45. data/tasks/utils.rb +0 -80
@@ -0,0 +1,68 @@
1
+ class Launchy::Application
2
+ #
3
+ # The class handling the browser application and all of its schemes
4
+ #
5
+ class Browser < Launchy::Application
6
+ def self.schemes
7
+ %w[ http https ftp file ]
8
+ end
9
+
10
+ def windows_app_list
11
+ [ 'start "Launchy" /d']
12
+ end
13
+
14
+ def cygwin_app_list
15
+ [ "cmd /C start 'launchy' /d" ]
16
+ end
17
+
18
+ def darwin_app_list
19
+ [ find_executable( "open" ) ]
20
+ end
21
+
22
+ def nix_app_list
23
+ nix_de = Launchy::Detect::NixDekstopEnvironment.browser
24
+ app_list = %w[ xdg-open ]
25
+ app_list << nix_de.browser
26
+ app_list << nix_de.fallback_browsers
27
+ app_list.flatten!
28
+ app_list.delete_if { |b| b.nil? || (b.strip.size == 0) }
29
+ app_list.collect { |bin| find_executable( bin ) }.find_all { |x| not x.nil? }
30
+ end
31
+
32
+ # use a call back mechanism to get the right app_list that is decided by the
33
+ # host_os_family class.
34
+ def app_list
35
+ host_os_family.app_list( self )
36
+ end
37
+
38
+ def browser_env
39
+ return [] unless ENV['BROWSER']
40
+ browser_env = ENV['BROWSER'].split( File::PATH_SEPARATOR )
41
+ browser_env.flatten!
42
+ browser_env.delete_if { |b| b.nil? || (b.strip.size == 0) }
43
+ return browser_env
44
+ end
45
+
46
+ # Get the full commandline of what we are going to add the uri to
47
+ def browser
48
+ possibilities = (browser_env + app_list).flatten
49
+ possibilities.each do |p|
50
+ Launchy.log "#{self.class.name} : possibility : #{p}"
51
+ end
52
+ b = possibilities.shift
53
+ Launchy.log "#{self.class.name} : Using browser value '#{b}'"
54
+ return b
55
+ end
56
+
57
+ # final assembly of the command and do %s substitution
58
+ # http://www.catb.org/~esr/BROWSER/index.html
59
+ def open( uri, options = {} )
60
+ b = browser
61
+ args = [ uri.to_s ]
62
+ if b =~ /%s/ then
63
+ b.gsub!( /%s/, args.shift )
64
+ end
65
+ run( b, args )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,70 @@
1
+ require 'optparse'
2
+
3
+ module Launchy
4
+ class Cli
5
+
6
+ attr_reader :options
7
+ def initialize
8
+ @options = {}
9
+ end
10
+
11
+ def parser
12
+ @parser ||= OptionParser.new do |op|
13
+ op.banner = "Usage: launchy [options] thing-to-launch"
14
+
15
+ op.separator ""
16
+ op.separator "Launch Options:"
17
+
18
+ op.on( "-a", "--application APPLICATION",
19
+ "Explicitly specify the application class to use in the launch") do |app|
20
+ options[:application] = app
21
+ end
22
+
23
+ op.on( "-d", "--debug",
24
+ "Force debug. Output lots of information.") do |d|
25
+ options[:debug] = 'true'
26
+ end
27
+
28
+ op.on( "-e", "--engine RUBY_ENGINE",
29
+ "Force launchy to behave as if it was on a particular ruby engine.") do |e|
30
+ options[:ruby_engine] = e
31
+ end
32
+
33
+ op.on( "-n", "--dry-run", "Don't launchy, print the command to be executed on stdout" ) do |x|
34
+ options[:dry_run] = true
35
+ end
36
+
37
+ op.on( "-o", "--host-os HOST_OS",
38
+ "Force launchy to behave as if it was on a particular host os.") do |os|
39
+ options[:host_os] = os
40
+ end
41
+
42
+
43
+ op.separator ""
44
+ op.separator "Standard Options:"
45
+
46
+ op.on( "-h", "--help", "Print this message.") do |h|
47
+ puts op.to_s
48
+ exit 0
49
+ end
50
+
51
+ op.on( "-v", "--version", "Output the version of Launchy") do |v|
52
+ puts "Launchy version #{Launchy::VERSION}"
53
+ exit 0
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ def run( argv = ARGV, env = ENV )
60
+ begin
61
+ parser.parse!( argv )
62
+ Launchy.open( argv.shift, options )
63
+ rescue ::OptionParser::ParseError => pe
64
+ $stderr.puts "#{parser.program_name}: #{pe}"
65
+ $stderr.puts "Try `#{parser.program_name} --help for more information."
66
+ exit 1
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,49 @@
1
+ require 'set'
2
+
3
+ module Launchy
4
+ #
5
+ # Use by either
6
+ #
7
+ # class Foo
8
+ # extend DescendantTracker
9
+ # end
10
+ #
11
+ # or
12
+ #
13
+ # class Foo
14
+ # class << self
15
+ # include DescendantTracker
16
+ # end
17
+ # end
18
+ #
19
+ # It will track all the classes that inherit from the extended class and keep
20
+ # them in a Set that is available via the 'children' method.
21
+ #
22
+ module DescendantTracker
23
+ def inherited( klass )
24
+ return unless klass.instance_of?( Class )
25
+ self.children << klass
26
+ end
27
+
28
+ #
29
+ # The list of children that are registered
30
+ #
31
+ def children
32
+ unless defined? @children
33
+ @children = Set.new
34
+ end
35
+ return @children
36
+ end
37
+
38
+ #
39
+ # Find one of the child classes by calling the given method
40
+ # and passing all the rest of the parameters to that method in
41
+ # each child
42
+ def find_child( method, *args )
43
+ klass = children.find do |child|
44
+ Launchy.log "Checking if class #{child} is the one for #{method}(#{args.join(', ')})}"
45
+ child.send( method, *args )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,10 @@
1
+ module Launchy
2
+ module Detect
3
+ end
4
+ end
5
+
6
+ require 'launchy/detect/host_os'
7
+ require 'launchy/detect/host_os_family'
8
+ require 'launchy/detect/ruby_engine'
9
+ require 'launchy/detect/nix_desktop_environment'
10
+ require 'launchy/detect/runner'
@@ -0,0 +1,31 @@
1
+ require 'rbconfig'
2
+
3
+ module Launchy::Detect
4
+ class HostOs
5
+
6
+ attr_reader :host_os
7
+ alias to_s host_os
8
+
9
+ def initialize( host_os = nil )
10
+ @host_os = host_os
11
+
12
+ if not @host_os then
13
+ if @host_os = override_host_os then
14
+ Launchy.log "Using LAUNCHY_HOST_OS override value of '#{Launchy.host_os}'"
15
+ else
16
+ @host_os = default_host_os
17
+ end
18
+ end
19
+ end
20
+
21
+ def default_host_os
22
+ ::RbConfig::CONFIG['host_os']
23
+ end
24
+
25
+ def override_host_os
26
+ Launchy.host_os
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,71 @@
1
+ module Launchy::Detect
2
+ # Detect the current host os family
3
+ #
4
+ # If the current host familiy cannot be detected then return
5
+ # HostOsFamily::Unknown
6
+ class HostOsFamily
7
+ class NotFoundError < Launchy::Error; end
8
+ extend ::Launchy::DescendantTracker
9
+
10
+ class << self
11
+
12
+ def detect( host_os = HostOs.new )
13
+ found = find_child( :matches?, host_os )
14
+ return found.new( host_os ) if found
15
+ raise NotFoundError, "Unknown OS family for host os '#{host_os}'. #{Launchy.bug_report_message}"
16
+ end
17
+
18
+ def matches?( host_os )
19
+ matching_regex.match( host_os.to_s )
20
+ end
21
+
22
+ def windows?() self == Windows; end
23
+ def darwin?() self == Darwin; end
24
+ def nix?() self == Nix; end
25
+ def cygwin?() self == Cygwin; end
26
+ end
27
+
28
+
29
+ attr_reader :host_os
30
+ def initialize( host_os = HostOs.new )
31
+ @host_os = host_os
32
+ end
33
+
34
+ def windows?() self.class.windows?; end
35
+ def darwin?() self.class.darwin?; end
36
+ def nix?() self.class.nix?; end
37
+ def cygwin?() self.class.cygwin?; end
38
+
39
+ #---------------------------
40
+ # All known host os families
41
+ #---------------------------
42
+ #
43
+ class Windows < HostOsFamily
44
+ def self.matching_regex
45
+ /(mingw|mswin|windows)/i
46
+ end
47
+ def app_list( app ) app.windows_app_list; end
48
+ end
49
+
50
+ class Darwin < HostOsFamily
51
+ def self.matching_regex
52
+ /(darwin|mac os)/i
53
+ end
54
+ def app_list( app ) app.darwin_app_list; end
55
+ end
56
+
57
+ class Nix < HostOsFamily
58
+ def self.matching_regex
59
+ /(linux|bsd|aix|solaris)/i
60
+ end
61
+ def app_list( app ) app.nix_app_list; end
62
+ end
63
+
64
+ class Cygwin < HostOsFamily
65
+ def self.matching_regex
66
+ /cygwin/i
67
+ end
68
+ def app_list( app ) app.cygwin_app_list; end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,60 @@
1
+ module Launchy::Detect
2
+ #
3
+ # Detect the current desktop environment for *nix machines
4
+ # Currently this is Linux centric. The detection is based upon the detection
5
+ # used by xdg-open from http://portland.freedesktop.org/wiki/XdgUtils
6
+ class NixDesktopEnvironment
7
+ class NotFoundError < Launchy::Error; end
8
+
9
+ extend ::Launchy::DescendantTracker
10
+
11
+ # Detect the current *nix desktop environment
12
+ #
13
+ # If the current dekstop environment be detected, the return
14
+ # NixDekstopEnvironment::Unknown
15
+ def self.detect
16
+ found = find_child( :is_current_desktop_environment? )
17
+ return found if found
18
+ raise NotFoundError, "Current Desktop environment not found. #{Launchy.bug_report_message}"
19
+ end
20
+
21
+ def self.fallback_browsers
22
+ %w[ firefox seamonkey opera mozilla netscape galeon ]
23
+ end
24
+
25
+ #---------------------------------------
26
+ # The list of known desktop environments
27
+ #---------------------------------------
28
+
29
+ class Kde < NixDesktopEnvironment
30
+ def self.is_current_desktop_environment?
31
+ ENV['KDE_FULL_SESSION']
32
+ end
33
+
34
+ def self.browser
35
+ 'kfmclient'
36
+ end
37
+ end
38
+
39
+ class Gnome < NixDesktopEnvironment
40
+ def self.is_current_desktop_environment?
41
+ ENV['GNOME_DESKTOP_SESSION_ID']
42
+ end
43
+
44
+ def self.browser
45
+ 'gnome-open'
46
+ end
47
+ end
48
+
49
+ class Xfce < NixDesktopEnvironment
50
+ def self.is_current_desktop_environment?
51
+ %x[ xprop -root _DT_SAVE_MODE | grep ' = \"xfce\"$' ].strip.size > 0
52
+ end
53
+
54
+ def self.browser
55
+ 'exo-open'
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,78 @@
1
+ module Launchy::Detect
2
+ class RubyEngine
3
+ class NotFoundError < Launchy::Error; end
4
+
5
+ extend ::Launchy::DescendantTracker
6
+
7
+ # Detect the current ruby engine.
8
+ #
9
+ # If the current ruby engine cannot be detected, the return
10
+ # RubyEngine::Unknown
11
+ def self.detect( ruby_engine = RubyEngine.new )
12
+ found = find_child( :is_current_engine?, ruby_engine.to_s )
13
+ return found if found
14
+ raise NotFoundError, "#{ruby_engine_error_message( ruby_engine )} #{Launchy.bug_report_message}"
15
+ end
16
+
17
+ def self.ruby_engine_error_message( ruby_engine )
18
+ msg = "Unkonwn RUBY_ENGINE "
19
+ if ruby_engine then
20
+ msg += " '#{ruby_engine}'."
21
+ elsif defined?( RUBY_ENGINE ) then
22
+ msg += " '#{RUBY_ENGINE}'."
23
+ else
24
+ msg = "RUBY_ENGINE not defined for #{RUBY_DESCRIPTION}."
25
+ end
26
+ return msg
27
+ end
28
+
29
+ def self.is_current_engine?( ruby_engine )
30
+ return ruby_engine == self.engine_name
31
+ end
32
+
33
+ def self.mri?() self == Mri; end
34
+ def self.jruby?() self == Jruby; end
35
+ def self.rbx?() self == Rbx; end
36
+ def self.macruby?() self == MacRuby; end
37
+
38
+ attr_reader :ruby_engine
39
+ alias to_s ruby_engine
40
+ def initialize( ruby_engine = Launchy.ruby_engine )
41
+ if ruby_engine then
42
+ @ruby_engine = ruby_engine
43
+ else
44
+ @ruby_engine = defined?( RUBY_ENGINE ) ? RUBY_ENGINE : "ruby"
45
+ end
46
+ end
47
+
48
+
49
+ #-------------------------------
50
+ # The list of known ruby engines
51
+ #-------------------------------
52
+
53
+ #
54
+ # This is the ruby engine if the RUBY_ENGINE constant is not defined
55
+ class Mri < RubyEngine
56
+ def self.engine_name() "ruby"; end
57
+ def self.is_current_engine?( ruby_engine )
58
+ if ruby_engine then
59
+ super( ruby_engine )
60
+ else
61
+ return true if not Launchy.ruby_engine and not defined?( RUBY_ENGINE )
62
+ end
63
+ end
64
+ end
65
+
66
+ class Jruby < RubyEngine
67
+ def self.engine_name() "jruby"; end
68
+ end
69
+
70
+ class Rbx < RubyEngine
71
+ def self.engine_name() "rbx"; end
72
+ end
73
+
74
+ class MacRuby < RubyEngine
75
+ def self.engine_name() "macruby"; end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,96 @@
1
+ require 'shellwords'
2
+
3
+ module Launchy::Detect
4
+ class Runner
5
+ class NotFoundError < Launchy::Error; end
6
+
7
+ extend ::Launchy::DescendantTracker
8
+
9
+ # Detect the current command runner
10
+ #
11
+ # If a runner cannot be detected then raise Runner::NotFoundError
12
+ def self.detect
13
+ host_os_family = Launchy::Detect::HostOsFamily.detect
14
+ ruby_engine = Launchy::Detect::RubyEngine.detect
15
+
16
+ return Windows.new if host_os_family.windows?
17
+ if ruby_engine.jruby? then
18
+ require 'spoon'
19
+ return Jruby.new
20
+ end
21
+ return Forkable.new
22
+ end
23
+
24
+ #
25
+ # cut it down to just the shell commands that will be passed to exec or
26
+ # posix_spawn. The cmd argument is split according to shell rules and the
27
+ # args are escaped according to shell rules.
28
+ #
29
+ def shell_commands( cmd, args )
30
+ cmdline = [ cmd.shellsplit ]
31
+ cmdline << args.collect{ |a| a.to_s.shellescape }
32
+ return commanddline_normalize( cmdline )
33
+ end
34
+
35
+ def commandline_normalize( cmdline )
36
+ c = cmdline.flatten!
37
+ c = c.find_all { |a| (not a.nil?) and ( a.size > 0 ) }
38
+ Launchy.log "ARGV => #{c.inspect}"
39
+ return c
40
+ end
41
+
42
+ def run( cmd, *args )
43
+ if Launchy.dry_run? then
44
+ puts dry_run( cmd, *args )
45
+ else
46
+ wet_run( cmd, *args )
47
+ end
48
+ end
49
+
50
+
51
+ #---------------------------------------
52
+ # The list of known runners
53
+ #---------------------------------------
54
+
55
+ class Windows < Runner
56
+ def dry_run( cmd, *args )
57
+ "cmd /c #{shell_commands( cmd, args).join(" " )}"
58
+ end
59
+
60
+ def shell_commands( cmd, args )
61
+ cmdline = [ cmd ]
62
+ cmdline << args.collect { |a| a.to_s.gsub("&", "^&") }
63
+ return commandline_normalize( cmdline )
64
+ end
65
+
66
+ def wet_run( cmd, *args )
67
+ system( 'cmd', '/c', *shell_commands( cmd, *args ) )
68
+ end
69
+ end
70
+
71
+ class Jruby < Runner
72
+
73
+ def dry_run( cmd, *args )
74
+ shell_commands(cmd, args).join(" ")
75
+ end
76
+
77
+ def wet_run( cmd, *args )
78
+ Spoon.spawnp( *shell_commands( cmd, args ) )
79
+ end
80
+ end
81
+
82
+ class Forkable < Runner
83
+ def dry_run( cmd, *args )
84
+ shell_commands(cmd, args).join(" ")
85
+ end
86
+
87
+ def wet_run( cmd, *args )
88
+ child_pid = fork do
89
+ exec( *shell_commands( cmd, args ))
90
+ exit!
91
+ end
92
+ Process.detach( child_pid )
93
+ end
94
+ end
95
+ end
96
+ end