qwandry 0.0.3 → 0.1.0

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.
@@ -8,6 +8,12 @@ lurking when your computer can do it faster?
8
8
  qw rails # will ask you which version of rails you want to open
9
9
  qw activerec 3.1 # will find the gem activerecord 3.1 and open it
10
10
 
11
+ You can also use Qwandry with other common languages:
12
+
13
+ qw -r python numpy # opens python's numpy library
14
+ qw -r perl URI # open perl's URI library
15
+ qw -r node express # open express if it is installed for node
16
+
11
17
  Installation
12
18
  ------------
13
19
  Qwandry is a standard ruby gem, on any system with ruby installed already
data/TODO CHANGED
@@ -1,11 +1,4 @@
1
- TODO, or perhaps not...
2
-
3
- - Differentiate multiple similar matches (show path or repository label)
4
- - Allow better customization of repositories
5
- - Customize collection phase (see LibraryRepository)
6
- - Customize naming phase
7
- - If the user is using rvm, add the ruby src directory to the search path, maybe merge it with the normal ruby
8
- - Examples: opening digest could get you both the ruby and the C sources.
9
- - For editors that don't support multi opening collections of files (ie: ./digest.rb and ./digest/) we could trick them by adding symlinks in a temp dir.
10
- - Could be an option... might be a terrible idea :)
11
- - Instead of requiring users to find and configure other languages, small scripts for perl, python, etc. could introspect on their load paths.
1
+ TODO
2
+ ----
3
+ Bugs and feature requests can be found on github:
4
+ https://github.com/adamsanderson/qwandry/issues
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.1.0
data/bin/qw CHANGED
@@ -1,38 +1,33 @@
1
1
  #!/usr/bin/env ruby
2
- # Add qwandry's library to the load path
2
+
3
+ # Add Qwandry's library to the load path
3
4
  $:.unshift File.dirname(__FILE__) + '/../lib'
4
- # Require it
5
+
6
+ # Require Qwandry's library
5
7
  require "qwandry.rb"
6
8
 
7
- # Create launcher
8
- @qwandry = Qwandry::Launcher.new
9
+ # Load Qwandry's configuration files
10
+ config = Qwandry::Configuration
11
+ config.load_configuration_files
12
+
13
+ # Create a launcher
14
+ @launcher = Qwandry::Launcher.new
9
15
 
10
16
  opts = OptionParser.new do |opts|
11
17
  opts.banner = "Usage: qwandry [options] name"
12
18
  opts.separator ""
13
19
 
14
- opts.on("-r", "--repo LABELS", Array, "Search in LABELS, default: #{@qwandry.active.to_a.join(',')}","Available Repositories:", *@qwandry.repositories.keys.map{|k| " #{k}"}) do |labels|
15
- @qwandry.active.replace(labels)
20
+ opts.on("-r", "--repo LABELS", Array, "Search in LABELS, default: #{config.default.join(',')}","Available Repository Types:", *config.configurations.map{|c| " #{c}"}) do |names|
21
+ config.default *names
16
22
  end
17
23
 
18
24
  opts.separator ""
19
25
  opts.on("-e", "--editor EDITOR", "Use EDITOR to open the package") do |editor|
20
- @editor = editor
26
+ @launcher.editor = editor
21
27
  end
22
28
 
23
29
  opts.separator "Additional Commands"
24
30
 
25
- opts.on("--paths", "Prints all repositories and their paths") do
26
- @qwandry.repositories.each do |label, entries|
27
- puts "#{label} #{"[default]" if @qwandry.active.include? label}"
28
- entries.each do |repo|
29
- puts "\t#{repo.path} (#{repo.class.to_s.split('::').last})"
30
- end
31
- puts ""
32
- end
33
- exit(0)
34
- end
35
-
36
31
  opts.on("--customize", "Create and edit files for customizing Qwandry") do
37
32
  dir = Qwandry.config_dir
38
33
  if !dir
@@ -43,7 +38,7 @@ opts = OptionParser.new do |opts|
43
38
  Dir[File.dirname(__FILE__) + '/../templates/*'].each do |path|
44
39
  FileUtils.cp(path, dir, :verbose=>true) unless File.exist?(path)
45
40
  end
46
- @qwandry.launch dir
41
+ @launcher.launch dir
47
42
  end
48
43
  exit(0)
49
44
  end
@@ -57,21 +52,21 @@ end
57
52
  opts.parse! ARGV
58
53
  if ARGV.length == 0
59
54
  puts opts
60
- exit(1)
55
+ exit 1
61
56
  end
62
57
 
63
- # Configure default values
64
- @qwandry.editor = @editor if @editor
65
-
58
+ # Find the packages:
66
59
  name = ARGV.join(' ')
67
- packages = @qwandry.find(*ARGV)
60
+ packages = @launcher.find(*ARGV)
68
61
  ARGV.clear # for the gets below
69
62
 
63
+ # There may be 0, 1, or many matches. If many, then
64
+ # ask the user which one to launch.
70
65
  package = nil
71
66
  case packages.length
72
67
  when 0
73
68
  puts "No packages matched '#{name}'"
74
- exit 404 # Package not found -- hehe, super lame.
69
+ exit 4 # Exit code 4 for No Package
75
70
  when 1
76
71
  package = packages.first
77
72
  else
@@ -84,4 +79,13 @@ else
84
79
  package = packages[index]
85
80
  end
86
81
 
87
- @qwandry.launch(package) if package
82
+ # If there is a package, then launch it.
83
+ if package
84
+ if @launcher.launch(package)
85
+ # Exit code 0 for success
86
+ exit 0
87
+ else
88
+ # Exit with the status returned by the process used to open the package
89
+ exit $?.exitstatus
90
+ end
91
+ end
@@ -21,6 +21,7 @@ module Qwandry
21
21
  autoload :FlatRepository, "qwandry/flat_repository"
22
22
  autoload :LibraryRepository, "qwandry/library_repository"
23
23
  autoload :Package, "qwandry/package"
24
+ autoload :Configuration, "qwandry/configuration"
24
25
  end
25
26
 
26
27
  # If defined, Qwandry will use XDG_CONFIG_HOME as the xdg spec. If not it
@@ -0,0 +1,135 @@
1
+ module Qwandry
2
+ class Configuration
3
+ class << self
4
+ # Regsisters a new Qwandry configuration. Use in conjunction with Configuration#add like so:
5
+ #
6
+ # register 'projects' do
7
+ # add '~/Projects/personal'
8
+ # add '~/Projects/work'
9
+ # add '~/Experiments'
10
+ # end
11
+ #
12
+ def register name, &block
13
+ name = name.to_sym
14
+ builders[name] << block
15
+ end
16
+
17
+ # Sets the default configuration to launch, if no `configurations` are passed
18
+ # in, it returns their names.
19
+ def default(*configurations)
20
+ if configurations.empty?
21
+ @default ||= []
22
+ else
23
+ @default = configurations
24
+ end
25
+ end
26
+
27
+ # Returns the registered configurations
28
+ def configurations
29
+ builders.keys
30
+ end
31
+
32
+ # Loads a configuration file, and executes it in the context of the Qwandry::Configuration
33
+ # class. See default_configuration.rb for an example, or run:
34
+ #
35
+ # qw --customize
36
+ #
37
+ # For a sample customization file.
38
+ def load_configuration(path)
39
+ if File.exist?(path)
40
+ begin
41
+ eval IO.read(path), nil, path, 1
42
+ rescue Exception=>ex
43
+ STDERR.puts "Warning: error in configuration file: #{path.inspect}"
44
+ STDERR.puts "Exception: #{ex.message}"
45
+ STDERR.puts ex.backtrace
46
+ end
47
+ end
48
+ end
49
+
50
+ # Loads the Qwandry default configuration and then the user's custom
51
+ # configuration file if it is present.
52
+ def load_configuration_files
53
+ # load default configuration files
54
+ system_config_dir = File.join(File.dirname(__FILE__), 'configuration')
55
+ Dir[system_config_dir+"/*.rb"].each do |path|
56
+ load_configuration path
57
+ end
58
+
59
+ # load user custom init files
60
+ if config_dir = Qwandry.config_dir
61
+ custom_path = File.join(config_dir, 'init.rb')
62
+ load_configuration(custom_path)
63
+ end
64
+ end
65
+
66
+ # Loads the repositories for `names` if no names are given, it loads the defaults
67
+ def repositories(*names)
68
+ names = default if names.empty?
69
+ repositories = []
70
+
71
+ names.each do |name|
72
+ name = name.to_sym
73
+ raise ArgumentError, "Unknown Repository type '#{name}'" unless builders.has_key? name
74
+
75
+ builder = builders[name]
76
+ repositories += builder.repositories
77
+ end
78
+
79
+ repositories
80
+ end
81
+
82
+ private
83
+
84
+ def builders
85
+ @builders ||= Hash.new{|h,name| h[name] = self.new(name) }
86
+ end
87
+
88
+ end
89
+
90
+ # Creates a new Configuration for building a set of Repositories, this
91
+ # should probably only be invoked by Configuration.build, it only exists
92
+ # to make the customization DSL relatively easy to work with.
93
+ def initialize(name)
94
+ @name = name
95
+ @blocks = []
96
+ @repositories = []
97
+ end
98
+
99
+ def << (block)
100
+ @blocks << block
101
+ end
102
+
103
+ def repositories
104
+ @blocks.each{|block| instance_eval(&block) }
105
+ @blocks.clear
106
+
107
+ @repositories
108
+ end
109
+
110
+ # Adds a new Repository to the current configuration.
111
+ #
112
+ # The `options` can be used to customize the repository.
113
+ #
114
+ # [:class] Repository class, defaults to Qwandry::FlatRepository
115
+ # [:accept] Filters paths, only keeping ones matching the accept option
116
+ # [:reject] Filters paths, rejecting any paths matching the reject option
117
+ #
118
+ # `:accept` and `:reject` take patterns such as '*.py[oc]', procs, and regular expressions.
119
+ #
120
+ # Examples:
121
+ # # Add all my little ruby scripts in the scratch directory
122
+ # add '~/scratch', :accept => '*.rb'
123
+ # # Add log files in common locations for easy access, but ignore the zipped ones
124
+ # add ['/var/log/', '/usr/local/var/log/'], :reject => '*.bz2'
125
+ #
126
+ def add(paths, options={})
127
+ paths = [paths] if paths.is_a?(String)
128
+ paths.each do |path|
129
+ repository_class = options[:class] || Qwandry::FlatRepository
130
+ @repositories << repository_class.new(@name, File.expand_path(path), options)
131
+ end
132
+ end
133
+
134
+ end
135
+ end
@@ -0,0 +1,57 @@
1
+
2
+ # Register the default ruby configuration:
3
+ register :ruby do
4
+ # Reject binary paths, and then find only the `/lib/ruby` sources:
5
+ paths = ($:).reject{|path| path =~ /#{RUBY_PLATFORM}$/}.grep(/lib\/ruby/)
6
+
7
+ # Add ruby standard libraries using the LibraryRepository:
8
+ add paths, :class=>Qwandry::LibraryRepository
9
+ end
10
+
11
+ # Register the default ruby gems configuration:
12
+ register :gem do
13
+ # Get the gem paths from the ruby load paths:
14
+ paths = ($:).grep(/gems/).map{|p| p[/.+\/gems\//]}.uniq
15
+
16
+ # Add all the rubygems' paths:
17
+ add paths
18
+ end
19
+
20
+ # Register a perl configuration:
21
+ register :perl do
22
+ # Execute a perl script to find all of the perl load paths:
23
+ perl_paths = `perl -e 'foreach $k (@INC){print $k,"\n";}'` rescue ''
24
+
25
+ # Split on new lines, rejecting blank paths and the current directory:
26
+ perl_paths = perl_paths.split("\n").reject{|path| path == '' || path == '.'}
27
+
28
+ # Add perl paths as a LibraryRepository
29
+ add perl_paths, :class=>Qwandry::LibraryRepository
30
+ end
31
+
32
+ # Add python repositories:
33
+ register :python do
34
+ # Execute a python script to find all of the python load paths:
35
+ python_paths = `python -c 'import sys;print(\"\\n\".join(sys.path))'` rescue ''
36
+
37
+ # Reject all the blank paths and the current directory. Also reject anything that looks like a binary
38
+ python_paths = python_paths.split("\n").reject{|path| path == '' || path == '.' || path =~ /\.zip$/ || path =~/lib-dynload$/}
39
+
40
+ # Add the python paths, instruct Qwandry to skip any compiled files when trying to match a file/library:
41
+ add python_paths, :class=>Qwandry::LibraryRepository, :reject => /\.(py[oc])|(egg-info)$/
42
+ end
43
+
44
+ # Add node.js repositories:
45
+ register :node do
46
+ # Execute a node script to find all of the load paths:
47
+ node_script_path = File.join(File.dirname(__FILE__),'probe_node.js')
48
+ node_paths = `node #{node_script_path}` rescue ''
49
+
50
+ node_paths = node_paths.split("\n")
51
+ # Add the node paths
52
+ add node_paths
53
+ end
54
+
55
+ # Qwandry is a ruby app after all, so activate ruby and gem by default. Other defaults can be set
56
+ # with a custom init.rb
57
+ default :ruby, :gem
@@ -0,0 +1,4 @@
1
+ // If anyone knows a way to pipe a one liner into node to do this,
2
+ // I'm all ears.
3
+ var sys = require('sys')
4
+ sys.puts(require.paths.join("\n"))
@@ -1,8 +1,13 @@
1
1
  module Qwandry
2
- # Directories look like:
3
- # ./lib-0.1
4
- # ./lib-0.2
2
+ # The FlatRepository assumes that each file or directory in the search path
3
+ # is a stand alone Package. For instance:
4
+ #
5
+ # rails-2.3.2
6
+ # rails-3.0.1
7
+ # thin
8
+ #
5
9
  class FlatRepository < Qwandry::Repository
10
+ # Returns a Package for each matching file or directory.
6
11
  def scan(pattern)
7
12
  results = []
8
13
  all_paths.select do |path|
@@ -5,57 +5,19 @@ module Qwandry
5
5
  # The default editor to be used by Qwandry#launch.
6
6
  attr_accessor :editor
7
7
 
8
- # The set of active repositories
9
- attr_reader :active
10
-
11
- # Returns the repositories the Launcher will use.
12
- attr_reader :repositories
13
-
14
- def initialize
15
- @repositories = Hash.new{|h,k| h[k] = []}
16
- @active = Set.new
17
- configure_repositories!
18
- custom_configuration!
19
- end
20
-
21
- # Adds a repository path to Qwandry's Launcher. `label` is used to label packages residing in the folder `path`.
22
- #
23
- # The `options` can be used to customize the repository.
24
- #
25
- # [:class] Repository class, defaults to Qwandry::FlatRepository
26
- # [:accept] Filters paths, only keeping ones matching the accept option
27
- # [:reject] Filters paths, rejecting any paths matching the reject option
28
- #
29
- # `:accept` and `:reject` take patterns such as '*.py[oc]', procs, and regular expressions.
30
- def add(label, path, options={})
31
- if path.is_a?(Array)
32
- path.each{|p| add label, p, options}
33
- else
34
- repository_class = options[:class] || Qwandry::FlatRepository
35
- label = label.to_s
36
- @repositories[label] << repository_class.new(label, File.expand_path(path), options)
37
- end
38
- end
39
-
40
- def activate(*labels)
41
- labels.each{|label| @active.add label.to_s}
42
- end
43
-
44
- def deactivate(*labels)
45
- labels.each{|label| @active.delete label.to_s}
46
- end
47
-
48
8
  # Searches all of the loaded repositories for `name`
49
9
  def find(*pattern)
10
+ # Create a glob pattern from the user's input, for instance
11
+ # ["rails","2.3"] => "rails*2.3*"
50
12
  pattern = pattern.join('*')
51
13
  pattern << '*' unless pattern =~ /\*$/
52
14
 
53
15
  packages = []
54
- @repositories.select{|label,_| @active.include? label }.each do |label, repos|
55
- repos.each do |repo|
56
- packages.concat(repo.scan(pattern))
57
- end
16
+ repositories = Qwandry::Configuration.repositories
17
+ repositories.each do |repo|
18
+ packages.concat(repo.scan(pattern))
58
19
  end
20
+
59
21
  packages
60
22
  end
61
23
 
@@ -76,42 +38,6 @@ module Qwandry
76
38
  # Launch the editor with its options and any paths that we have been passed
77
39
  system(*(editor_and_options + paths))
78
40
  end
79
-
80
- private
81
- def configure_repositories!
82
- # Get all the paths on ruby's load path:
83
- paths = $:
84
-
85
- # Reject binary paths, we only want ruby sources:
86
- paths = paths.reject{|path| path =~ /#{RUBY_PLATFORM}$/}
87
-
88
- # Add ruby standard libraries:
89
- paths.grep(/lib\/ruby/).each do |path|
90
- add :ruby, path, :class=>Qwandry::LibraryRepository
91
- end
92
-
93
- # Add gem repositories:
94
- ($:).grep(/gems/).map{|p| p[/.+\/gems\//]}.uniq.each do |path|
95
- add :gem, path
96
- end
97
-
98
- activate :ruby, :gem
99
- end
100
-
101
- def custom_configuration!
102
- if config_dir = Qwandry.config_dir
103
- custom_path = File.join(config_dir, 'init.rb')
104
- if File.exist?(custom_path)
105
- begin
106
- eval IO.read(custom_path), nil, custom_path, 1
107
- rescue Exception=>ex
108
- STDERR.puts "Warning: error in custom file: #{custom_path.inspect}"
109
- STDERR.puts "Exception: #{ex.message}"
110
- STDERR.puts ex.backtrace
111
- end
112
- end
113
- end
114
- end
115
-
41
+
116
42
  end
117
- end
43
+ end
@@ -1,10 +1,15 @@
1
1
  module Qwandry
2
- # Directories look like:
3
- # lib1.rb
4
- # lib1/...
5
- # lib2.py
6
- # lib2/...
2
+ # The LibraryRepository assumes that the search path contains files in the root mixed with
3
+ # directories of the same name which should be opened together. Ruby's date library is a good
4
+ # example of this:
5
+ #
6
+ # date.rb
7
+ # date/
8
+ #
7
9
  class LibraryRepository < Qwandry::Repository
10
+
11
+ # Returns Packages that may contain one or more paths if there are similar
12
+ # root level files that should be bundled together.
8
13
  def scan(pattern)
9
14
  results = Hash.new{|h,k| h[k] = package(k)}
10
15
  all_paths.select do |path|
@@ -1,20 +1,27 @@
1
1
  module Qwandry
2
+ # A Repository's primary responsibility is to return a set of Packages that
3
+ # match the search criteria used in Repository#scan.
4
+ #
5
+ # Subclasses are expected
2
6
  class Repository
3
7
  attr_reader :name
4
8
  attr_reader :path
5
9
  attr_reader :options
6
-
10
+
11
+ # Creates a Repository with a give name, search path, and options.
7
12
  def initialize(name, path, options={})
8
13
  @name = name
9
14
  @path = path.chomp('/')
10
15
  @options = options
11
-
12
16
  end
13
17
 
18
+ # Given a name, scan should an array with 0 or more Packages matching the
19
+ # name.
14
20
  def scan(name)
15
- []
21
+ raise NotImplementedError, "Repositories must return an Array of matching packages."
16
22
  end
17
23
 
24
+ # Returns all paths that should be tested by Qwandry#scan.
18
25
  def all_paths
19
26
  paths = Dir["#{@path}/*"]
20
27
  paths = paths.select(&matcher(options[:accept])) if options[:accept]
@@ -22,11 +29,13 @@ module Qwandry
22
29
  paths
23
30
  end
24
31
 
32
+ private
33
+ # Helper for assembling a new package which may be launched by the Launcher
25
34
  def package(name, paths=[])
26
35
  Package.new(name, paths, self)
27
36
  end
28
37
 
29
- private
38
+ # Helper for generating a predicate methods
30
39
  def matcher(pattern)
31
40
  case pattern
32
41
  when Regexp then lambda{|p| p =~ pattern}
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qwandry
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 3
10
- version: 0.0.3
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adam Sanderson
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-12-11 00:00:00 -08:00
18
+ date: 2010-12-30 00:00:00 -07:00
19
19
  default_executable: qw
20
20
  dependencies: []
21
21
 
@@ -35,6 +35,9 @@ files:
35
35
  - VERSION
36
36
  - bin/qw
37
37
  - lib/qwandry.rb
38
+ - lib/qwandry/configuration.rb
39
+ - lib/qwandry/configuration/default.rb
40
+ - lib/qwandry/configuration/probe_node.js
38
41
  - lib/qwandry/flat_repository.rb
39
42
  - lib/qwandry/launcher.rb
40
43
  - lib/qwandry/library_repository.rb