Refactored how repositories are defined and loaded. We can now support probing tons of different types of repositories by loading them just in time.
This commit is contained in:
parent
147f927f84
commit
506be7f192
44
bin/qw
44
bin/qw
@ -1,38 +1,33 @@
|
|||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
# Add qwandry's library to the load path
|
|
||||||
|
# Add Qwandry's library to the load path
|
||||||
$:.unshift File.dirname(__FILE__) + '/../lib'
|
$:.unshift File.dirname(__FILE__) + '/../lib'
|
||||||
# Require it
|
|
||||||
|
# Require Qwandry's library
|
||||||
require "qwandry.rb"
|
require "qwandry.rb"
|
||||||
|
|
||||||
# Create launcher
|
# Load Qwandry's configuration files
|
||||||
@qwandry = Qwandry::Launcher.new
|
config = Qwandry::Configuration
|
||||||
|
config.load_configuration_files
|
||||||
|
|
||||||
|
# Create a launcher
|
||||||
|
@launcher = Qwandry::Launcher.new
|
||||||
|
|
||||||
opts = OptionParser.new do |opts|
|
opts = OptionParser.new do |opts|
|
||||||
opts.banner = "Usage: qwandry [options] name"
|
opts.banner = "Usage: qwandry [options] name"
|
||||||
opts.separator ""
|
opts.separator ""
|
||||||
|
|
||||||
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|
|
opts.on("-r", "--repo LABELS", Array, "Search in LABELS, default: #{config.default.join(',')}","Available Repository Types:", *config.configurations.map{|c| " #{c}"}) do |names|
|
||||||
@qwandry.active.replace(labels)
|
config.default *names
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.separator ""
|
opts.separator ""
|
||||||
opts.on("-e", "--editor EDITOR", "Use EDITOR to open the package") do |editor|
|
opts.on("-e", "--editor EDITOR", "Use EDITOR to open the package") do |editor|
|
||||||
@editor = editor
|
@launcher.editor = editor
|
||||||
end
|
end
|
||||||
|
|
||||||
opts.separator "Additional Commands"
|
opts.separator "Additional Commands"
|
||||||
|
|
||||||
opts.on("--paths", "Prints all repositories and their paths") do
|
|
||||||
@qwandry.repositories.each do |label, entries|
|
|
||||||
puts "#{label} #{"[default]" if @qwandry.active.include? label}"
|
|
||||||
entries.each do |repo|
|
|
||||||
puts "\t#{repo.path} (#{repo.class.to_s.split('::').last})"
|
|
||||||
end
|
|
||||||
puts ""
|
|
||||||
end
|
|
||||||
exit(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
opts.on("--customize", "Create and edit files for customizing Qwandry") do
|
opts.on("--customize", "Create and edit files for customizing Qwandry") do
|
||||||
dir = Qwandry.config_dir
|
dir = Qwandry.config_dir
|
||||||
if !dir
|
if !dir
|
||||||
@ -43,7 +38,7 @@ opts = OptionParser.new do |opts|
|
|||||||
Dir[File.dirname(__FILE__) + '/../templates/*'].each do |path|
|
Dir[File.dirname(__FILE__) + '/../templates/*'].each do |path|
|
||||||
FileUtils.cp(path, dir, :verbose=>true) unless File.exist?(path)
|
FileUtils.cp(path, dir, :verbose=>true) unless File.exist?(path)
|
||||||
end
|
end
|
||||||
@qwandry.launch dir
|
@launcher.launch dir
|
||||||
end
|
end
|
||||||
exit(0)
|
exit(0)
|
||||||
end
|
end
|
||||||
@ -60,13 +55,13 @@ if ARGV.length == 0
|
|||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
# Configure default values
|
# Find the packages:
|
||||||
@qwandry.editor = @editor if @editor
|
|
||||||
|
|
||||||
name = ARGV.join(' ')
|
name = ARGV.join(' ')
|
||||||
packages = @qwandry.find(*ARGV)
|
packages = @launcher.find(*ARGV)
|
||||||
ARGV.clear # for the gets below
|
ARGV.clear # for the gets below
|
||||||
|
|
||||||
|
# There may be 0, 1, or many matches. If many, then
|
||||||
|
# ask the user which one to launch.
|
||||||
package = nil
|
package = nil
|
||||||
case packages.length
|
case packages.length
|
||||||
when 0
|
when 0
|
||||||
@ -84,8 +79,9 @@ else
|
|||||||
package = packages[index]
|
package = packages[index]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If there is a package, then launch it.
|
||||||
if package
|
if package
|
||||||
if @qwandry.launch(package)
|
if @launcher.launch(package)
|
||||||
# Exit code 0 for success
|
# Exit code 0 for success
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
@ -21,6 +21,7 @@ module Qwandry
|
|||||||
autoload :FlatRepository, "qwandry/flat_repository"
|
autoload :FlatRepository, "qwandry/flat_repository"
|
||||||
autoload :LibraryRepository, "qwandry/library_repository"
|
autoload :LibraryRepository, "qwandry/library_repository"
|
||||||
autoload :Package, "qwandry/package"
|
autoload :Package, "qwandry/package"
|
||||||
|
autoload :Configuration, "qwandry/configuration"
|
||||||
end
|
end
|
||||||
|
|
||||||
# If defined, Qwandry will use XDG_CONFIG_HOME as the xdg spec. If not it
|
# If defined, Qwandry will use XDG_CONFIG_HOME as the xdg spec. If not it
|
||||||
|
135
lib/qwandry/configuration.rb
Normal file
135
lib/qwandry/configuration.rb
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
module Qwandry
|
||||||
|
class Configuration
|
||||||
|
class << self
|
||||||
|
# Regsisters a new Qwandry configuration. Use in conjunction with Configuration#add like so:
|
||||||
|
#
|
||||||
|
# register 'projects' do
|
||||||
|
# add '~/Projects/personal'
|
||||||
|
# add '~/Projects/work'
|
||||||
|
# add '~/Experiments'
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
def register name, &block
|
||||||
|
name = name.to_sym
|
||||||
|
builders[name] << block
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the default configuration to launch, if no `configurations` are passed
|
||||||
|
# in, it returns their names.
|
||||||
|
def default(*configurations)
|
||||||
|
if configurations.empty?
|
||||||
|
@default ||= []
|
||||||
|
else
|
||||||
|
@default = configurations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the registered configurations
|
||||||
|
def configurations
|
||||||
|
builders.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loads a configuration file, and executes it in the context of the Qwandry::Configuration
|
||||||
|
# class. See default_configuration.rb for an example, or run:
|
||||||
|
#
|
||||||
|
# qw --customize
|
||||||
|
#
|
||||||
|
# For a sample customization file.
|
||||||
|
def load_configuration(path)
|
||||||
|
if File.exist?(path)
|
||||||
|
begin
|
||||||
|
eval IO.read(path), nil, path, 1
|
||||||
|
rescue Exception=>ex
|
||||||
|
STDERR.puts "Warning: error in configuration file: #{path.inspect}"
|
||||||
|
STDERR.puts "Exception: #{ex.message}"
|
||||||
|
STDERR.puts ex.backtrace
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loads the Qwandry default configuration and then the user's custom
|
||||||
|
# configuration file if it is present.
|
||||||
|
def load_configuration_files
|
||||||
|
# load default configuration files
|
||||||
|
system_config_dir = File.join(File.dirname(__FILE__), 'configuration')
|
||||||
|
Dir[system_config_dir+"/*.rb"].each do |path|
|
||||||
|
load_configuration path
|
||||||
|
end
|
||||||
|
|
||||||
|
# load user custom init files
|
||||||
|
if config_dir = Qwandry.config_dir
|
||||||
|
custom_path = File.join(config_dir, 'init.rb')
|
||||||
|
load_configuration(custom_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Loads the repositories for `names` if no names are given, it loads the defaults
|
||||||
|
def repositories(*names)
|
||||||
|
names = default if names.empty?
|
||||||
|
repositories = []
|
||||||
|
|
||||||
|
names.each do |name|
|
||||||
|
name = name.to_sym
|
||||||
|
raise ArgumentError, "Unknown Repository type '#{name}'" unless builders.has_key? name
|
||||||
|
|
||||||
|
builder = builders[name]
|
||||||
|
repositories += builder.repositories
|
||||||
|
end
|
||||||
|
|
||||||
|
repositories
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def builders
|
||||||
|
@builders ||= Hash.new{|h,name| h[name] = self.new(name) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a new Configuration for building a set of Repositories, this
|
||||||
|
# should probably only be invoked by Configuration.build, it only exists
|
||||||
|
# to make the customization DSL relatively easy to work with.
|
||||||
|
def initialize(name)
|
||||||
|
@name = name
|
||||||
|
@blocks = []
|
||||||
|
@repositories = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def << (block)
|
||||||
|
@blocks << block
|
||||||
|
end
|
||||||
|
|
||||||
|
def repositories
|
||||||
|
@blocks.each{|block| instance_eval(&block) }
|
||||||
|
@blocks.clear
|
||||||
|
|
||||||
|
@repositories
|
||||||
|
end
|
||||||
|
|
||||||
|
# Adds a new Repository to the current configuration.
|
||||||
|
#
|
||||||
|
# The `options` can be used to customize the repository.
|
||||||
|
#
|
||||||
|
# [:class] Repository class, defaults to Qwandry::FlatRepository
|
||||||
|
# [:accept] Filters paths, only keeping ones matching the accept option
|
||||||
|
# [:reject] Filters paths, rejecting any paths matching the reject option
|
||||||
|
#
|
||||||
|
# `:accept` and `:reject` take patterns such as '*.py[oc]', procs, and regular expressions.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# # Add all my little ruby scripts in the scratch directory
|
||||||
|
# add '~/scratch', :accept => '*.rb'
|
||||||
|
# # Add log files in common locations for easy access, but ignore the zipped ones
|
||||||
|
# add ['/var/log/', '/usr/local/var/log/'], :reject => '*.bz2'
|
||||||
|
#
|
||||||
|
def add(paths, options={})
|
||||||
|
paths = [paths] if paths.is_a?(String)
|
||||||
|
paths.each do |path|
|
||||||
|
repository_class = options[:class] || Qwandry::FlatRepository
|
||||||
|
@repositories << repository_class.new(@name, File.expand_path(path), options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
46
lib/qwandry/configuration/default.rb
Normal file
46
lib/qwandry/configuration/default.rb
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
# Register the default ruby configuration:
|
||||||
|
register :ruby do
|
||||||
|
# Reject binary paths, and then find only the `/lib/ruby` sources:
|
||||||
|
paths = ($:).reject{|path| path =~ /#{RUBY_PLATFORM}$/}.grep(/lib\/ruby/)
|
||||||
|
|
||||||
|
# Add ruby standard libraries using the LibraryRepository:
|
||||||
|
add paths, :class=>Qwandry::LibraryRepository
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register the default ruby gems configuration:
|
||||||
|
register :gem do
|
||||||
|
# Get the gem paths from the ruby load paths:
|
||||||
|
paths = ($:).grep(/gems/).map{|p| p[/.+\/gems\//]}.uniq
|
||||||
|
|
||||||
|
# Add all the rubygems' paths:
|
||||||
|
add paths
|
||||||
|
end
|
||||||
|
|
||||||
|
# Register a perl configuration:
|
||||||
|
register :perl do
|
||||||
|
# Execute a perl script to find all of the perl load paths:
|
||||||
|
perl_paths = `perl -e 'foreach $k (@INC){print $k,"\n";}'` rescue ''
|
||||||
|
|
||||||
|
# Split on new lines, rejecting blank paths and the current directory:
|
||||||
|
perl_paths = perl_paths.split("\n").reject{|path| path == '' || path == '.'}
|
||||||
|
|
||||||
|
# Add perl paths as a LibraryRepository
|
||||||
|
add perl_paths, :class=>Qwandry::LibraryRepository
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add python repositories:
|
||||||
|
register :python do
|
||||||
|
# Execute a python script to find all of the python load paths:
|
||||||
|
python_paths = `python -c 'import sys;print(\"\\n\".join(sys.path))'` rescue ''
|
||||||
|
|
||||||
|
# Reject all the blank paths and the current directory. Also reject anything that looks like a binary
|
||||||
|
python_paths = python_paths.split("\n").reject{|path| path == '' || path == '.' || path =~ /\.zip$/ || path =~/lib-dynload$/}
|
||||||
|
|
||||||
|
# Add the python paths, instruct Qwandry to skip any compiled files when trying to match a file/library:
|
||||||
|
add python_paths, :class=>Qwandry::LibraryRepository, :reject => /\.(py[oc])|(egg-info)$/
|
||||||
|
end
|
||||||
|
|
||||||
|
# Qwandry is a ruby app after all, so activate ruby and gem by default. Other defaults can be set
|
||||||
|
# with a custom init.rb
|
||||||
|
default :ruby, :gem
|
@ -5,57 +5,19 @@ module Qwandry
|
|||||||
# The default editor to be used by Qwandry#launch.
|
# The default editor to be used by Qwandry#launch.
|
||||||
attr_accessor :editor
|
attr_accessor :editor
|
||||||
|
|
||||||
# The set of active repositories
|
|
||||||
attr_reader :active
|
|
||||||
|
|
||||||
# Returns the repositories the Launcher will use.
|
|
||||||
attr_reader :repositories
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@repositories = Hash.new{|h,k| h[k] = []}
|
|
||||||
@active = Set.new
|
|
||||||
configure_repositories!
|
|
||||||
custom_configuration!
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adds a repository path to Qwandry's Launcher. `label` is used to label packages residing in the folder `path`.
|
|
||||||
#
|
|
||||||
# The `options` can be used to customize the repository.
|
|
||||||
#
|
|
||||||
# [:class] Repository class, defaults to Qwandry::FlatRepository
|
|
||||||
# [:accept] Filters paths, only keeping ones matching the accept option
|
|
||||||
# [:reject] Filters paths, rejecting any paths matching the reject option
|
|
||||||
#
|
|
||||||
# `:accept` and `:reject` take patterns such as '*.py[oc]', procs, and regular expressions.
|
|
||||||
def add(label, path, options={})
|
|
||||||
if path.is_a?(Array)
|
|
||||||
path.each{|p| add label, p, options}
|
|
||||||
else
|
|
||||||
repository_class = options[:class] || Qwandry::FlatRepository
|
|
||||||
label = label.to_s
|
|
||||||
@repositories[label] << repository_class.new(label, File.expand_path(path), options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def activate(*labels)
|
|
||||||
labels.each{|label| @active.add label.to_s}
|
|
||||||
end
|
|
||||||
|
|
||||||
def deactivate(*labels)
|
|
||||||
labels.each{|label| @active.delete label.to_s}
|
|
||||||
end
|
|
||||||
|
|
||||||
# Searches all of the loaded repositories for `name`
|
# Searches all of the loaded repositories for `name`
|
||||||
def find(*pattern)
|
def find(*pattern)
|
||||||
|
# Create a glob pattern from the user's input, for instance
|
||||||
|
# ["rails","2.3"] => "rails*2.3*"
|
||||||
pattern = pattern.join('*')
|
pattern = pattern.join('*')
|
||||||
pattern << '*' unless pattern =~ /\*$/
|
pattern << '*' unless pattern =~ /\*$/
|
||||||
|
|
||||||
packages = []
|
packages = []
|
||||||
@repositories.select{|label,_| @active.include? label }.each do |label, repos|
|
repositories = Qwandry::Configuration.repositories
|
||||||
repos.each do |repo|
|
repositories.each do |repo|
|
||||||
packages.concat(repo.scan(pattern))
|
packages.concat(repo.scan(pattern))
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
packages
|
packages
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -76,56 +38,6 @@ module Qwandry
|
|||||||
# Launch the editor with its options and any paths that we have been passed
|
# Launch the editor with its options and any paths that we have been passed
|
||||||
system(*(editor_and_options + paths))
|
system(*(editor_and_options + paths))
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
def configure_repositories!
|
|
||||||
# Get all the paths on ruby's load path:
|
|
||||||
paths = $:
|
|
||||||
|
|
||||||
# Reject binary paths, we only want ruby sources:
|
|
||||||
paths = paths.reject{|path| path =~ /#{RUBY_PLATFORM}$/}
|
|
||||||
|
|
||||||
# Add ruby standard libraries:
|
|
||||||
paths.grep(/lib\/ruby/).each do |path|
|
|
||||||
add :ruby, path, :class=>Qwandry::LibraryRepository
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add gem repositories:
|
|
||||||
($:).grep(/gems/).map{|p| p[/.+\/gems\//]}.uniq.each do |path|
|
|
||||||
add :gem, path
|
|
||||||
end
|
|
||||||
|
|
||||||
# Add perl repositories:
|
|
||||||
perl_paths = `perl -e 'foreach $k (@INC){print $k,"\n";}'` rescue ''
|
|
||||||
perl_paths.split("\n").reject{|path| path == '' || path == '.'}.each do |path|
|
|
||||||
add :perl, path, :class=>Qwandry::LibraryRepository
|
|
||||||
end
|
|
||||||
|
|
||||||
# add python repositories:
|
|
||||||
python_paths = `python -c 'import sys;print(\"\\n\".join(sys.path))'` rescue ''
|
|
||||||
python_paths.split("\n").reject{|path| path == '' || path == '.' || path =~ /\.zip$/ || path =~/lib-dynload$/}.each do |path|
|
|
||||||
add :python, path, :class=>Qwandry::LibraryRepository, :reject => /\.(py[oc])|(egg-info)$/
|
|
||||||
end
|
|
||||||
|
|
||||||
# Qwandry is a ruby app after all, so activate ruby and rubygems by default. Other defaults can be set
|
|
||||||
# with a custom init.rb
|
|
||||||
activate :ruby, :gem
|
|
||||||
end
|
|
||||||
|
|
||||||
def custom_configuration!
|
|
||||||
if config_dir = Qwandry.config_dir
|
|
||||||
custom_path = File.join(config_dir, 'init.rb')
|
|
||||||
if File.exist?(custom_path)
|
|
||||||
begin
|
|
||||||
eval IO.read(custom_path), nil, custom_path, 1
|
|
||||||
rescue Exception=>ex
|
|
||||||
STDERR.puts "Warning: error in custom file: #{custom_path.inspect}"
|
|
||||||
STDERR.puts "Exception: #{ex.message}"
|
|
||||||
STDERR.puts ex.backtrace
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user