qwandry 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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