launchy 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -57,14 +57,10 @@ module Launchy
57
57
  end
58
58
 
59
59
  def parse( argv, env )
60
- begin
61
- parser.parse!( argv )
62
- return true
63
- rescue ::OptionParser::ParseError => pe
64
- $stderr.puts "#{parser.program_name}: #{pe}"
65
- $stderr.puts "Try `#{parser.program_name} --help for more information."
66
- return false
67
- end
60
+ parser.parse!( argv )
61
+ return true
62
+ rescue ::OptionParser::ParseError => pe
63
+ error_output( pe )
68
64
  end
69
65
 
70
66
  def good_run( argv, env )
@@ -74,6 +70,18 @@ module Launchy
74
70
  else
75
71
  return false
76
72
  end
73
+ rescue StandardError => e
74
+ error_output( e )
75
+ end
76
+
77
+ def error_output( error )
78
+ $stderr.puts "ERROR: #{error}"
79
+ Launchy.log "ERROR: #{error}"
80
+ error.backtrace.each do |bt|
81
+ Launchy.log bt
82
+ end
83
+ $stderr.puts "Try `#{parser.program_name} --help' for more information."
84
+ return false
77
85
  end
78
86
 
79
87
  def run( argv = ARGV, env = ENV )
@@ -40,7 +40,7 @@ module Launchy
40
40
  # and passing all the rest of the parameters to that method in
41
41
  # each child
42
42
  def find_child( method, *args )
43
- klass = children.find do |child|
43
+ children.find do |child|
44
44
  Launchy.log "Checking if class #{child} is the one for #{method}(#{args.join(', ')})}"
45
45
  child.send( method, *args )
46
46
  end
@@ -5,6 +5,7 @@ module Launchy::Detect
5
5
 
6
6
  attr_reader :host_os
7
7
  alias to_s host_os
8
+ alias to_str host_os
8
9
 
9
10
  def initialize( host_os = nil )
10
11
  @host_os = host_os
@@ -2,7 +2,7 @@ module Launchy::Detect
2
2
  #
3
3
  # Detect the current desktop environment for *nix machines
4
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
5
+ # used by xdg-open from http://portland.freedesktop.org/
6
6
  class NixDesktopEnvironment
7
7
  class NotFoundError < Launchy::Error; end
8
8
 
@@ -49,7 +49,7 @@ module Launchy::Detect
49
49
  class Xfce < NixDesktopEnvironment
50
50
  def self.is_current_desktop_environment?
51
51
  if Launchy::Application.find_executable( 'xprop' ) then
52
- %x[ xprop -root _DT_SAVE_MODE | grep ' = \"xfce\"$' ].strip.size > 0
52
+ %x[ xprop -root _DT_SAVE_MODE].include?("xfce")
53
53
  else
54
54
  false
55
55
  end
@@ -59,6 +59,16 @@ module Launchy::Detect
59
59
  'exo-open'
60
60
  end
61
61
  end
62
+
63
+ class Fluxbox < NixDesktopEnvironment
64
+ def self.is_current_desktop_environment?
65
+ ENV['DESKTOP_SESSION'] == 'fluxbox'
66
+ end
67
+
68
+ def self.browser
69
+ 'xdg-open'
70
+ end
71
+ end
62
72
  end
63
73
  end
64
74
 
@@ -25,7 +25,6 @@ module Launchy::Detect
25
25
 
26
26
  return Windows.new if host_os_family.windows?
27
27
  if ruby_engine.jruby? then
28
- require 'spoon'
29
28
  return Jruby.new
30
29
  end
31
30
  return Forkable.new
@@ -95,6 +94,7 @@ module Launchy::Detect
95
94
 
96
95
  class Jruby < Runner
97
96
  def wet_run( cmd, *args )
97
+ require 'spoon'
98
98
  Spoon.spawnp( *shell_commands( cmd, *args ) )
99
99
  end
100
100
  end
@@ -102,11 +102,21 @@ module Launchy::Detect
102
102
  class Forkable < Runner
103
103
  def wet_run( cmd, *args )
104
104
  child_pid = fork do
105
+ close_file_descriptors unless Launchy.debug?
106
+ Launchy.log("wet_run: before exec in child process")
105
107
  exec( *shell_commands( cmd, *args ))
106
108
  exit!
107
109
  end
108
110
  Process.detach( child_pid )
109
111
  end
112
+
113
+ def close_file_descriptors
114
+ [$stdin, $stdout, $stderr].each do |io|
115
+ io.reopen( "/dev/null", "r+" )
116
+ end
117
+ end
118
+
119
+ private :close_file_descriptors
110
120
  end
111
121
  end
112
122
  end
@@ -2,4 +2,5 @@ module Launchy
2
2
  class Error < ::StandardError; end
3
3
  class ApplicationNotFoundError < Error; end
4
4
  class CommandNotFoundError < Error; end
5
+ class ArgumentError < Error; end
5
6
  end
@@ -1,5 +1,5 @@
1
1
  module Launchy
2
- VERSION = "2.1.0"
2
+ VERSION = "2.2.0"
3
3
 
4
4
  module Version
5
5
 
@@ -53,7 +53,7 @@ describe Launchy::Cli do
53
53
 
54
54
  it "prints the command on stdout when using --dry-run" do
55
55
  argv = %w[ --debug --dry-run http://github.com/copiousfreetime/launchy ]
56
- rc = Launchy::Cli.new.good_run( argv, {} )
56
+ Launchy::Cli.new.good_run( argv, {} )
57
57
  $stdout.string.must_match %r[github.com]
58
58
  end
59
59
 
@@ -22,6 +22,14 @@ describe Launchy::Detect::NixDesktopEnvironment do
22
22
  end
23
23
  end
24
24
 
25
+ it "detects the fluxbox desktop environment" do
26
+ ENV['DESKTOP_SESSION'] = 'fluxbox'
27
+ fluxbox_env = Launchy::Detect::NixDesktopEnvironment.detect
28
+ fluxbox_env.must_equal( Launchy::Detect::NixDesktopEnvironment::Fluxbox )
29
+ fluxbox_env.browser.must_equal( Launchy::Detect::NixDesktopEnvironment::Fluxbox.browser )
30
+ ENV.delete( 'DESKTOP_SESSION' )
31
+ end
32
+
25
33
  it "returns false for XFCE if xprop is not found" do
26
34
  Launchy.host_os = "linux"
27
35
  Launchy::Detect::NixDesktopEnvironment::Xfce.is_current_desktop_environment?.must_equal( false )
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require 'spec_helper'
2
3
 
3
4
  describe Launchy::Detect::Runner do
@@ -50,9 +50,14 @@ describe Launchy do
50
50
  Launchy.ruby_engine.must_equal 'myruby'
51
51
  end
52
52
 
53
- it "prints an error on stderr when no scheme is found for the given uri" do
54
- Launchy.open( "blah://something/invalid" )
55
- $stderr.string.must_match( /Failure in opening/ )
53
+ it "raises an exception if no scheme is found for the given uri" do
54
+ lambda { Launchy.open( "blah://something/invalid" ) }.must_raise Launchy::ApplicationNotFoundError
56
55
  end
57
56
 
57
+ [ 'www.example.com', 'www.example.com/foo/bar' ].each do |x|
58
+ it "picks a Browser for #{x}" do
59
+ app = Launchy.app_for_uri_string( x )
60
+ app.must_equal( Launchy::Application::Browser )
61
+ end
62
+ end
58
63
  end
@@ -1,3 +1,9 @@
1
+ if RUBY_VERSION >= '1.9.2' then
2
+ require 'simplecov'
3
+ puts "Using coverage!"
4
+ SimpleCov.start if ENV['COVERAGE']
5
+ end
6
+
1
7
  gem 'minitest'
2
8
  require 'launchy'
3
9
  require 'stringio'
@@ -0,0 +1,213 @@
1
+ # vim: syntax=ruby
2
+ require 'rake/clean'
3
+ #------------------------------------------------------------------------------
4
+ # If you want to Develop on this project just run 'rake develop' and you'll
5
+ # have all you need to get going. If you want to use bundler for development,
6
+ # then run 'rake develop:using_bundler'
7
+ #------------------------------------------------------------------------------
8
+ namespace :develop do
9
+
10
+ # Install all the development and runtime dependencies of this gem using the
11
+ # gemspec.
12
+ task :default do
13
+ require 'rubygems/dependency_installer'
14
+ installer = Gem::DependencyInstaller.new
15
+
16
+ This.set_coverage_gem
17
+
18
+ puts "Installing gem depedencies needed for development"
19
+ This.platform_gemspec.dependencies.each do |dep|
20
+ if dep.matching_specs.empty? then
21
+ puts "Installing : #{dep}"
22
+ installer.install dep
23
+ else
24
+ puts "Skipping : #{dep} -> already installed #{dep.matching_specs.first.full_name}"
25
+ end
26
+ end
27
+ puts "\n\nNow run 'rake test'"
28
+ end
29
+
30
+ # Create a Gemfile that just references the gemspec
31
+ file 'Gemfile' => :gemspec do
32
+ File.open( "Gemfile", "w+" ) do |f|
33
+ f.puts 'source :rubygems'
34
+ f.puts 'gemspec'
35
+ end
36
+ end
37
+
38
+ desc "Create a bundler Gemfile"
39
+ task :using_bundler => 'Gemfile' do
40
+ puts "Now you can 'bundle'"
41
+ end
42
+
43
+ # Gemfiles are build artifacts
44
+ CLOBBER << FileList['Gemfile*']
45
+ end
46
+ desc "Boostrap development"
47
+ task :develop => "develop:default"
48
+
49
+ #------------------------------------------------------------------------------
50
+ # Minitest - standard TestTask
51
+ #------------------------------------------------------------------------------
52
+ begin
53
+ require 'rake/testtask'
54
+ Rake::TestTask.new( :test ) do |t|
55
+ t.ruby_opts = %w[ -w -rubygems ]
56
+ t.libs = %w[ lib spec ]
57
+ t.pattern = "spec/**/*_spec.rb"
58
+ end
59
+ task :default => :test
60
+ rescue LoadError
61
+ This.task_warning( 'test' )
62
+ end
63
+
64
+ #------------------------------------------------------------------------------
65
+ # RDoc - standard rdoc rake task, although we must make sure to use a more
66
+ # recent version of rdoc since it is the one that has 'tomdoc' markup
67
+ #------------------------------------------------------------------------------
68
+ begin
69
+ gem 'rdoc' # otherwise we get the wrong task from stdlib
70
+ require 'rdoc/task'
71
+ RDoc::Task.new do |t|
72
+ t.markup = 'tomdoc'
73
+ t.rdoc_dir = 'doc'
74
+ t.main = 'README.rdoc'
75
+ t.title = "#{This.name} #{This.version}"
76
+ t.rdoc_files.include( '*.rdoc', 'lib/**/*.rb' )
77
+ end
78
+ rescue LoadError => le
79
+ This.task_warning( 'rdoc' )
80
+ end
81
+
82
+ #------------------------------------------------------------------------------
83
+ # Coverage - optional code coverage, rcov for 1.8 and simplecov for 1.9, so
84
+ # for the moment only rcov is listed.
85
+ #------------------------------------------------------------------------------
86
+ if RUBY_VERSION < "1.9.0"
87
+ begin
88
+ require 'rcov/rcovtask'
89
+ Rcov::RcovTask.new( 'coverage' ) do |t|
90
+ t.libs << 'spec'
91
+ t.pattern = 'spec/**/*_spec.rb'
92
+ t.verbose = true
93
+ t.rcov_opts << "-x ^/" # remove all the global files
94
+ t.rcov_opts << "--sort coverage" # so we see the worst files at the top
95
+ end
96
+ rescue LoadError
97
+ This.task_warning( 'rcov' )
98
+ end
99
+ else
100
+ begin
101
+ require 'simplecov'
102
+ desc 'Run tests with code coverage'
103
+ task :coverage do
104
+ ENV['COVERAGE'] = 'true'
105
+ Rake::Task[:test].execute
106
+ end
107
+ CLOBBER << FileList["coverage"]
108
+ rescue LoadError
109
+ This.task_warning( 'simplecov' )
110
+ end
111
+ end
112
+
113
+ #------------------------------------------------------------------------------
114
+ # Manifest - We want an explicit list of thos files that are to be packaged in
115
+ # the gem. Most of this is from Hoe.
116
+ #------------------------------------------------------------------------------
117
+ namespace 'manifest' do
118
+ desc "Check the manifest"
119
+ task :check => :clean do
120
+ files = FileList["**/*", ".*"].exclude( This.exclude_from_manifest ).to_a.sort
121
+ files = files.select{ |f| File.file?( f ) }
122
+
123
+ tmp = "Manifest.tmp"
124
+ File.open( tmp, 'w' ) do |f|
125
+ f.puts files.join("\n")
126
+ end
127
+
128
+ begin
129
+ sh "diff -du Manifest.txt #{tmp}"
130
+ ensure
131
+ rm tmp
132
+ end
133
+ puts "Manifest looks good"
134
+ end
135
+
136
+ desc "Generate the manifest"
137
+ task :generate => :clean do
138
+ files = %x[ git ls-files ].split("\n").sort
139
+ files.reject! { |f| f =~ This.exclude_from_manifest }
140
+ File.open( "Manifest.txt", "w" ) do |f|
141
+ f.puts files.join("\n")
142
+ end
143
+ end
144
+ end
145
+
146
+ #------------------------------------------------------------------------------
147
+ # Fixme - look for fixmes and report them
148
+ #------------------------------------------------------------------------------
149
+ desc "Look for fixmes and report them"
150
+ task :fixme => 'manifest:check' do
151
+ This.manifest.each do |file|
152
+ next if file == File.basename( __FILE__ )
153
+
154
+ puts "FIXME: Rename #{file}" if file =~ /fixme/i
155
+
156
+ IO.readlines( file ).each_with_index do |line, idx|
157
+ prefix = "FIXME: #{file}:#{idx+1}".ljust(42)
158
+ puts "#{prefix} => #{line.strip}" if line =~ /fixme/i
159
+ end
160
+ end
161
+ end
162
+
163
+ #------------------------------------------------------------------------------
164
+ # Gem Specification
165
+ #------------------------------------------------------------------------------
166
+ # Really this is only here to support those who use bundler
167
+ desc "Build the #{This.name}.gemspec file"
168
+ task :gemspec do
169
+ File.open( This.gemspec_file, "wb+" ) do |f|
170
+ f.write This.platform_gemspec.to_ruby
171
+ end
172
+ end
173
+
174
+ # the gemspec is also a dev artifact and should not be kept around.
175
+ CLOBBER << This.gemspec_file
176
+
177
+ # The standard gem packaging task, everyone has it.
178
+ require 'rubygems/package_task'
179
+ Gem::PackageTask.new( This.platform_gemspec ) do
180
+ # nothing
181
+ end
182
+
183
+ #------------------------------------------------------------------------------
184
+ # Release - the steps we go through to do a final release, this is pulled from
185
+ # a compbination of mojombo's rakegem, hoe and hoe-git
186
+ #
187
+ # 1) make sure we are on the master branch
188
+ # 2) make sure there are no uncommitted items
189
+ # 3) check the manifest and make sure all looks good
190
+ # 4) build the gem
191
+ # 5) do an empty commit to have the commit message of the version
192
+ # 6) tag that commit as the version
193
+ # 7) push master
194
+ # 8) push the tag
195
+ # 7) pus the gem
196
+ #------------------------------------------------------------------------------
197
+ task :release_check do
198
+ unless `git branch` =~ /^\* master$/
199
+ abort "You must be on the master branch to release!"
200
+ end
201
+ unless `git status` =~ /^nothing to commit/m
202
+ abort "Nope, sorry, you have unfinished business"
203
+ end
204
+ end
205
+
206
+ desc "Create tag v#{This.version}, build and push #{This.platform_gemspec.full_name} to rubygems.org"
207
+ task :release => [ :release_check, 'manifest:check', :gem ] do
208
+ sh "git commit --allow-empty -a -m 'Release #{This.version}'"
209
+ sh "git tag -a -m 'v#{This.version}' v#{This.version}"
210
+ sh "git push origin master"
211
+ sh "git push origin v#{This.version}"
212
+ sh "gem push pkg/#{This.platform_gemspec.full_name}.gem"
213
+ end
@@ -0,0 +1,202 @@
1
+ require 'pathname'
2
+
3
+ # Public: A Class containing all the metadata and utilities needed to manage a
4
+ # ruby project.
5
+ class ThisProject
6
+ # The name of this project
7
+ attr_accessor :name
8
+
9
+ # The author's name
10
+ attr_accessor :author
11
+
12
+ # The email address of the author(s)
13
+ attr_accessor :email
14
+
15
+ # The homepage of this project
16
+ attr_accessor :homepage
17
+
18
+ # The regex of files to exclude from the manifest
19
+ attr_accessor :exclude_from_manifest
20
+
21
+ # The hash of Gem::Specifications keyed' by platform
22
+ attr_accessor :gemspecs
23
+
24
+ # Public: Initialize ThisProject
25
+ #
26
+ # Yields self
27
+ def initialize(&block)
28
+ @exclude_from_manifest = %r/tmp$|\.(git|DS_Store)|^(doc|coverage|pkg)|Gemfile*|\.gemspec$|\.swp$|\.jar|\.rvmrc$|~$/
29
+ @gemspecs = Hash.new
30
+ yield self if block_given?
31
+ end
32
+
33
+ # Public: return the version of ThisProject
34
+ #
35
+ # Search the ruby files in the project looking for the one that has the
36
+ # version string in it. This does not eval any code in the project, it parses
37
+ # the source code looking for the string.
38
+ #
39
+ # Returns a String version
40
+ def version
41
+ [ "lib/#{ name }.rb", "lib/#{ name }/version.rb" ].each do |v|
42
+ path = project_path( v )
43
+ line = path.read[/^\s*VERSION\s*=\s*.*/]
44
+ if line then
45
+ return line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
46
+ end
47
+ end
48
+ end
49
+
50
+ # Internal: Return a section of an RDoc file with the given section name
51
+ #
52
+ # path - the relative path in the project of the file to parse
53
+ # section_name - the section out of the file from which to parse data
54
+ #
55
+ # Retuns the text of the section as an array of paragrphs.
56
+ def section_of( file, section_name )
57
+ re = /^=+ (.*)$/
58
+ sectional = project_path( file )
59
+ parts = sectional.read.split( re )[1..-1]
60
+ parts.map! { |p| p.strip }
61
+
62
+ sections = Hash.new
63
+ Hash[*parts].each do |k,v|
64
+ sections[k] = v.split("\n\n")
65
+ end
66
+ return sections[section_name]
67
+ end
68
+
69
+ # Internal: print out a warning about the give task
70
+ def task_warning( task )
71
+ warn "WARNING: '#{task}' tasks are not defined. Please run 'rake develop'"
72
+ end
73
+
74
+ # Internal: Return the full path to the file that is relative to the project
75
+ # root.
76
+ #
77
+ # path - the relative path of the file from the project root
78
+ #
79
+ # Returns the Pathname of the file
80
+ def project_path( relative_path )
81
+ project_root.join( relative_path )
82
+ end
83
+
84
+ # Internal: The absolute path of this file
85
+ #
86
+ # Returns the Pathname of this file.
87
+ def this_file_path
88
+ Pathname.new( __FILE__ ).expand_path
89
+ end
90
+
91
+ # Internal: The root directory of this project
92
+ #
93
+ # This is defined as being the directory that is in the path of this project
94
+ # that has the first Rakefile
95
+ #
96
+ # Returns the Pathname of the directory
97
+ def project_root
98
+ this_file_path.ascend do |p|
99
+ rakefile = p.join( 'Rakefile' )
100
+ return p if rakefile.exist?
101
+ end
102
+ end
103
+
104
+ # Internal: Returns the contents of the Manifest.txt file as an array
105
+ #
106
+ # Returns an Array of strings
107
+ def manifest
108
+ manifest_file = project_path( "Manifest.txt" )
109
+ abort "You need a Manifest.txt" unless manifest_file.readable?
110
+ manifest_file.readlines.map { |l| l.strip }
111
+ end
112
+
113
+ # Internal: Returns the gemspace associated with the current ruby platform
114
+ def platform_gemspec
115
+ gemspecs[platform]
116
+ end
117
+
118
+ def core_gemspec
119
+ Gem::Specification.new do |spec|
120
+ spec.name = name
121
+ spec.version = version
122
+ spec.author = author
123
+ spec.email = email
124
+ spec.homepage = homepage
125
+
126
+ spec.summary = summary
127
+ spec.description = description
128
+
129
+ spec.files = manifest
130
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
131
+ spec.test_files = spec.files.grep(/^spec/)
132
+
133
+ spec.extra_rdoc_files += spec.files.grep(/(txt|rdoc)$/)
134
+ spec.rdoc_options = [ "--main" , 'README.rdoc',
135
+ "--markup", "tomdoc" ]
136
+ end
137
+ end
138
+
139
+ # Internal: Return the gemspec for the ruby platform
140
+ def ruby_gemspec( core = core_gemspec, &block )
141
+ yielding_gemspec( 'ruby', core, &block )
142
+ end
143
+
144
+ # Internal: Return the gemspec for the jruby platform
145
+ def java_gemspec( core = core_gemspec, &block )
146
+ yielding_gemspec( 'java', core, &block )
147
+ end
148
+
149
+ # Internal: give an initial spec and a key, create a new gemspec based off of
150
+ # it.
151
+ #
152
+ # This will force the new gemspecs 'platform' to be that of the key, since the
153
+ # only reason you would have multiple gemspecs at this point is to deal with
154
+ # different platforms.
155
+ def yielding_gemspec( key, core )
156
+ spec = gemspecs[key] ||= core.dup
157
+ spec.platform = key
158
+ yield spec if block_given?
159
+ return spec
160
+ end
161
+
162
+ # Internal: Set the recovery gem development dependency
163
+ #
164
+ # These are dynamically set since they cannot be hard coded as there is
165
+ # no way to ship them correctly in the gemspec
166
+ #
167
+ # Returns nothing.
168
+ def set_coverage_gem
169
+ if RUBY_VERSION < "1.9.0"
170
+ platform_gemspec.add_development_dependency( 'rcov', '~> 1.0.0' )
171
+ else
172
+ platform_gemspec.add_development_dependency( 'simplecov', '~> 0.7.1' )
173
+ end
174
+ end
175
+
176
+ # Internal: Return the platform of ThisProject at the current moment in time.
177
+ def platform
178
+ (RUBY_PLATFORM == "java") ? 'java' : Gem::Platform::RUBY
179
+ end
180
+
181
+ # Internal: Return the DESCRIPTION section of the README.rdoc file
182
+ def description_section
183
+ section_of( 'README.rdoc', 'DESCRIPTION')
184
+ end
185
+
186
+ # Internal: Return the summary text from the README
187
+ def summary
188
+ description_section.first
189
+ end
190
+
191
+ # Internal: Return the full description text from the READEM
192
+ def description
193
+ description_section.join(" ").tr("\n", ' ').gsub(/[{}]/,'').gsub(/\[[^\]]+\]/,'') # strip rdoc
194
+ end
195
+
196
+ # Internal: The path to the gemspec file
197
+ def gemspec_file
198
+ project_path( "#{ name }.gemspec" )
199
+ end
200
+ end
201
+
202
+ This = ThisProject.new