Continue ruby implementation.

This commit is contained in:
Glenn Y. Rolland 2013-12-28 15:05:51 +01:00
parent 34b3f80b3e
commit cdcec384f0
10 changed files with 336 additions and 367 deletions

View file

@ -19,5 +19,10 @@ $:.insert 0, File.join(DATA_DIR, 'lib')
require 'thor' require 'thor'
require 'kook' require 'kook'
Kook::CLI::Main.start(ARGV) begin
Kook::CLI::Main.start(ARGV)
rescue Exception => exception
STDERR.puts "ERROR(#{exception.class}) : #{exception}"
STDERR.puts exception.backtrace
exit 1
end

112
bin/kotam
View file

@ -1,112 +0,0 @@
#!/bin/sh
set -u
KOTAM_DATA_DIR=$(dirname `readlink -f "$0"`)
KOTAM_DATA_DIR=$(cd "$KOTAM_DATA_DIR/../lib" ; pwd -P)
#echo $KOTAM_DATA_DIR
KOTAM_CONFIG_DIR=$HOME/.config/kotam
. $KOTAM_DATA_DIR/base.sh
kotam_usage() {
echo "Usage : `basename "$0"` [options]"
}
kotam_fail(){
printf "ERROR: $*\n\n" >&2
kotam_usage
exit 1
}
kotam_project_list() {
local tmppwd=$(pwd)
echo "List of projects :"
cd $KOTAM_CONFIG_DIR
find -maxdepth 1 -mindepth 1 -name '*.kotam' |sed -e 's|./| |' -e 's/.kotam$//'
cd $tmppwd
}
kotam_project_ensure() {
if [ ! -e "$KOTAM_CONFIG_DIR/$PROJECT.kotam" ]; then
kotam_fail "unknown project $PROJECT" >&2
fi
}
kotam_project_edit() {
kotam_project_ensure
"$EDITOR" "$KOTAM_CONFIG_DIR/$PROJECT.kotam"
}
kotam_project_run(){
kotam_project_ensure
. "$KOTAM_CONFIG_DIR/$PROJECT.kotam"
}
ACTION=""
PROJECT=""
mkdir -p $KOTAM_CONFIG_DIR
while [ $# -gt 0 ]; do
arg=${1:-}
opt=${2:-}
case $arg in
-l|--list)
ACTION="list"
;;
-e) # edit project
shift
if [ -z "$opt" ]; then
kotam_fail "project name missing"
fi
ACTION="edit"
PROJECT="$opt"
;;
-c) # create
shift
if [ -z "$opt" ]; then
kotam_fail "project name missing"
fi
ACTION="create"
PROJECT="$opt"
;;
-d) # delete
shift
if [ -z "$opt" ]; then
kotam_fail "project name missing"
fi
ACTION="delete"
PROJECT="$opt"
;;
*) #enter into project
ACTION="run"
PROJECT="$arg"
;;
esac
shift
done
case "$ACTION" in
list)
kotam_project_list
;;
create)
kotam_project_create
;;
edit)
kotam_project_edit
;;
delete)
kotam_project_delete
;;
run)
kotam_project_run
;;
*)
kotam_fail "undefined action $ACTION"
;;
esac

View file

@ -1,25 +0,0 @@
kotam_run() {
local cmd="$*"
qdbus org.kde.konsole /Sessions/${session} sendText "$cmd"
qdbus org.kde.konsole /Sessions/${session} sendText "
"
}
kotam_newtab() {
#dbus-send --session --dest=${KONSOLE_DBUS_SERVICE} --type=method_call \
# --print-reply /konsole/MainWindow_1 org.kde.KMainWindow.activateAction string:"new-tab"
TARGET=${KONSOLE_DBUS_SERVICE:-org.kde.konsole}
session=$(qdbus $TARGET /Konsole newSession)
}
kotam_renametab() {
#sessionno=$1
tabname=$1
#session="/Sessions/${sessionno}"
#dbus-send --session --dest=${KONSOLE_DBUS_SERVICE} --type=method_call --print-reply ${session} org.kde.konsole.Session.setTitle int32:1 string:"$tabname"
qdbus org.kde.konsole /Sessions/${session} setTitle 1 "$tabname"
}

View file

@ -2,6 +2,6 @@
require 'kook/exceptions' require 'kook/exceptions'
require 'kook/view' require 'kook/view'
require 'kook/project' require 'kook/project'
require 'kook/config' require 'kook/app'
require 'kook/cli' require 'kook/cli'

178
lib/kook/app.rb Normal file
View file

@ -0,0 +1,178 @@
module Kook
class App
CONFIG_DIR = File.join ENV['HOME'], '.config', 'kook'
CONFIG_FILE = File.join CONFIG_DIR, 'config.yml'
attr_accessor :verbose
class ExistingProject < RuntimeError ; end
class MissingProject < RuntimeError ; end
def initialize
super
@projects = {}
@config_file = CONFIG_FILE
@verbose = false
@current_project = nil
end
def add_project project_name, project_path=nil
raise ExistingProject if @projects.has_key? project_name
project_data = Project.new project_name
project_data.path = project_path
@projects[project_name] = project_data
save
end
def remove_project project_name
raise MissingProject if not @projects.has_key? project_name
@projects.delete project_name
save
end
def fire_project project_name
raise MissingProject if not @projects.has_key? project_name
project_path = @projects[project_name].path
@projects[project_name].each_view do |view,view_data|
target = ENV['KONSOLE_DBUS_SERVICE'] || 'org.kde.konsole'
session=`qdbus #{target} /Konsole newSession`.strip
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"cd #{project_path}\n\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"cd #{view_path}\n\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"clear\n\""
system "qdbus org.kde.konsole /Sessions/#{session} setTitle 1 \"#{view}\""
next unless config['commands'][project].has_key? view
config['commands'][project][view].each do |command|
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"#{command}\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"\n\""
end
end
end
def each_project
@projects.each do |p,data|
yield p,data
end
end
def add_view project_name, view_name, view_path=nil
Project.validate_name project_name
View.validate_name view_name
raise MissingProject if not @projects.has_key? project_name
view = View.new view_name
@projects[project_name].add_view view
save
end
def list_views project_name
raise MissingProject if not @projects.has_key? project_name
# FIXME: return if config['views'][project].nil?
@projects[project_name].each_view do |view_name,view_data|
puts "%- 24s %s" % [view_name, view_data.path]
#next if config['commands'][project].nil? or \
# config['commands'][project][view].nil?
#config['commands'][project][view].each_index do |idx|
# puts " % 4d. %s" % [
# idx,
# config['commands'][project][view][idx]
# ]
#end
end
end
def load config_file=nil
config_file ||= @config_file
@config_file = config_file
if not File.exist? config_file then
STDERR.puts "Missing config file #{config_file}" if @verbose
return false
end
STDERR.puts "Loading main configuration #{config_file}..." if @verbose
yaml = YAML::load_file config_file
yaml['projects'].each do |project_name,project_path|
# pp project_path
#project_path = @config['projects'][project]
project_file = kook_file_for project_path
STDERR.puts "Loading sub configuration #{project_file}..." if @verbose
if File.exist? project_file then
subconfig = YAML::load_file project_file
next if not subconfig
@projects[project_name] = Project.from_hash subconfig, project_path
end
end
return true
end
def save config_file=nil
config_file ||= @config_file
config_dir = File.dirname config_file
if not File.exist? config_dir then
FileUtils.mkdir_p config_dir
end
STDERR.puts "Saving to #{config_file}" if @verbose
@projects.each do |project_name,project_data|
# FIXME: test if project configuration is dirty
project_file = File.join project_data.path, "Kookfile"
File.open(project_file, "w") do |file|
file.write project_data.to_hash.to_yaml
end
end
File.open(config_file, "w") do |file|
file.write to_yaml
end
self
end
def current_project= project_name
# FIXME: validate project name
@current_project = project_name
end
def current_project
return @current_project if not @current_project.nil?
current_dir = Dir.pwd
@projects.each do |project_name,project|
if current_dir =~ /^#{project.path}/ then
return project_name
end
end
return nil
end
private
def kook_file_for project_path
kook_files = Dir.glob(File.join(project_path, 'Kookfile'))
raise MissingProjectFile if kook_files.empty?
kook_files.first
end
def to_yaml
return {
'global' => {},
'projects' => Hash[@projects.map{ |p,v| [v.name, v.path] }]
}.to_yaml
end
end
end

View file

@ -3,37 +3,38 @@ require 'thor'
module Kook module Kook
module CLI module CLI
module KookHelper module KookHelper
def before_filter options
def fail_for exception, params=nil @app = App.new
STDERR.puts "ERROR(#{exception.class}) : #{exception}" @app.load options[:config]
STDERR.puts exception.backtrace @app.verbose = options[:verbose]
exit 1 @app.current_project = options[:project]
end
end end
# Inject our extensions in thor instances
def self.included(base)
base.class_eval do
#if ancestors.include? Thor::Group
# namespace self.name.split('::').last.downcase.to_sym
#end
class OldConfig class_option :verbose,
include Singleton type: :boolean,
include KookHelper default: false,
aliases: '-v',
desc: 'Whether to output informative debug'
DEFAULT_CONFIG = { class_option :config,
'global' => {}, # (ConfigKey_str => ConfigVal_str ) list type: :string,
'projects' => {}, # Project_str list default: nil,
'views' => {}, # (Project_str => View_str) list aliases: '-c',
'commands' => {} # (Project_str => View_str) => Command list desc: 'Configuration file'
}
def initialize class_option :project,
# load file type: :string,
@config = DEFAULT_CONFIG default: nil,
load_main aliases: '-p',
@config desc: 'Target project'
end end
def [] key
@config[key]
end end
end end
@ -42,43 +43,48 @@ module Kook
desc "detect", "Detect current project" desc "detect", "Detect current project"
def detect def detect
before_filter options
current_project = @app.current_project
project_name = current_project.nil? ? "-none-" : current_project project_name = current_project.nil? ? "-none-" : current_project
say "Current project is #{project_name}." say "Current project is #{project_name}."
end end
desc "list", "List projects" desc "list", "List projects"
def list def list
config.each_project do |project_name,project| before_filter options
pp project_name projects_exist = false
exist = File.exist? project.path @app.each_project do |project_name,project_data|
projects_exist = true
exist = File.exist? project_data.path
display_path = ( display_path = (
path.clone project_data.path.clone
.gsub!(/#{ENV['HOME']}/,'~') .gsub!(/#{ENV['HOME']}/,'~')
.send(exist ? :green : :red) .send(exist ? :green : :red)
) )
puts "%- 24s %s" % [project_name, display_path] puts "%- 24s %s" % [project_name, display_path]
end end
STDERR.puts "Empty list : no project configured." if not projects_exist
end end
# FIXME: option for alternative path option :path
desc "add PROJECT [PATH]", "Register new project" desc "add PROJECT", "Register new project"
def add project_name, project_path=nil def add project_name, project_path=nil
before_filter options
project_path = options[:path]
if project_path.nil? then if project_path.nil? then
project_path = Dir.pwd project_path = Dir.pwd
end end
project_path = File.expand_path project_path project_path = File.expand_path project_path
config.add_project project_name, project_path @app.add_project project_name, project_path
say "Project #{project_name} registered on #{project_path}." say "Project #{project_name} registered on #{project_path}."
rescue Exception => e
fail_for e, project: project_name, path: project_path
end end
desc "rm PROJECT", "Unregister existing project" desc "rm PROJECT", "Unregister existing project"
def rm project def rm project
config['projects'].delete project before_filter options
config.save_main @app.remove_project project
say "Project #{project} unregistered." say "Project #{project} unregistered."
end end
@ -93,45 +99,32 @@ module Kook
end end
# TODO: editcopy project to another name + base path # TODO: editcopy project to another name + base path
# TODO: copy project to another name + base path # TODO: copy project to another name + base path
#
private
def config
Config.instance
end
end end
class View < Thor class View < Thor
include KookHelper include KookHelper
desc "list [PROJECT]", "List view for a project" desc "list", "List view for a project"
def list project=nil def list project_name=nil
project ||= current_project before_filter options
validate_project_exists project project_name ||= @app.current_project
if config['views'].has_key? project then @app.list_views project_name
return if config['views'][project].nil?
config['views'][project].each do |view,path|
puts "%- 24s %s" % [view, path]
next if config['commands'][project].nil? or \
config['commands'][project][view].nil?
config['commands'][project][view].each_index do |idx|
puts " % 4d. %s" % [
idx,
config['commands'][project][view][idx]
]
end
end
end
end end
desc "add PROJECT VIEW", "Register new view" desc "add VIEW", "Register new view"
def add project, view, path=nil option :path
if path.nil? then def add view_name
path = Dir.pwd before_filter options
project_name ||= @app.current_project
view_path = options[:path]
if view_path.nil? then
view_path = Dir.pwd
end end
@app.add_view project_name, view_name, view_path
project_rootdir = config['projects'][project] project_rootdir = config['projects'][project]
# simplify if current dir is a subdir of project base # simplify if current dir is a subdir of project base
if path == project_rootdir then if path == project_rootdir then
@ -157,12 +150,6 @@ module Kook
config['views'][project].delete view config['views'][project].delete view
config.save_main config.save_main
end end
private
def config
Config.instance
end
end end
# FIXME: add helper validating project name # FIXME: add helper validating project name
@ -187,12 +174,6 @@ module Kook
def rm project, view, index def rm project, view, index
raise NotImplementedError raise NotImplementedError
end end
private
def config
Config.instance
end
end end
class Main < Thor class Main < Thor
@ -207,36 +188,12 @@ module Kook
desc "command SUBCOMMAND [options]", "Commands for managing commands" desc "command SUBCOMMAND [options]", "Commands for managing commands"
subcommand "command", CLI::Command subcommand "command", CLI::Command
desc "fire PROJECT", "Run project environment" desc "fire [PROJECT]", "Run project environment"
def fire project def fire project_name=nil
validate_project_name project before_filter options
validate_project_exists project @app.fire_project project_name
pp config
raise "No view defined for #{project}" if not config['views'].has_key? project
config['views'][project].each do |view,view_path|
project_path = config['projects'][project]
target = ENV['KONSOLE_DBUS_SERVICE'] || 'org.kde.konsole'
session=`qdbus #{target} /Konsole newSession`.strip
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"cd #{project_path}\n\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"cd #{view_path}\n\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"clear\n\""
system "qdbus org.kde.konsole /Sessions/#{session} setTitle 1 \"#{view}\""
next unless config['commands'][project].has_key? view
config['commands'][project][view].each do |command|
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"#{command}\""
system "qdbus org.kde.konsole /Sessions/#{session} sendText \"\n\""
end
end
end end
private
def config
Config.instance
end
end end
end end
end end

View file

@ -1,105 +0,0 @@
module Kook
class Config
include Singleton
CONFIG_DIR = File.join ENV['HOME'], '.config', 'kook'
CONFIG_FILE = File.join CONFIG_DIR, 'config.yml'
class ExistingProject < RuntimeError ; end
class MissingProject < RuntimeError ; end
def add_project project_name, project_path=nil
raise ExistingProject if @projects.has_key? project_name
project_data = Project.new project_name
project_data.path = project_path
@projects[project_name] = project_data
save
end
def each_project
@projects.each do |p,data|
yield p,data
end
end
def add_view project_name, view_name
Project.validate_name project_name
View.validate_name view_name
raise MissingProject if not @projects.has_key? project_name
view = View.new view_name
@projects[project_name].add_view view
save
end
def load
yaml = YAML::load_file CONFIG_FILE
yaml['projects'].each do |project_name,project_path|
pp project_path
#project_path = @config['projects'][project]
project_file = kook_file_for project_path
if File.exist? project_file then
subconfig = YAML::load_file project_file
next if not subconfig
@config['views'][project] = subconfig['views']
@config['commands'][project] = subconfig['commands']
end
end
end
def save
if not File.exist? CONFIG_DIR then
FileUtils.mkdir_p CONFIG_DIR
end
@projects.each do |project_name,project_data|
# FIXME: test if project configuration is dirty
#pp project_data
project_file = File.join project_data.path, "Kookfile"
File.open(project_file, "w") do |file|
file.write project_data.to_hash.to_yaml
end
end
File.open(CONFIG_FILE, "w") do |file|
file.write to_yaml
end
self
end
def current_project
current_dir = Dir.pwd
@projects.each do |project_name,project|
if current_dir =~ /^#{project.path}/ then
return project_name
end
end
return nil
end
private
def kook_file_for project_path
kook_files = Dir.glob(File.join(project_path, 'Kookfile'))
raise MissingProjectFile if kook_files.empty?
kook_files.first
end
def to_yaml
return {
'global' => {},
'projects' => @projects.values.map{ |p| { p.name => p.path } }
}.to_yaml
end
def initialize
super
@projects = {}
self.load
end
end
end

View file

@ -17,6 +17,9 @@ module Kook
@description = nil @description = nil
@path = nil @path = nil
@views = {} @views = {}
yield self if block_given?
self
end end
def path= path def path= path
@ -38,31 +41,39 @@ module Kook
return @view.delete(view_name) return @view.delete(view_name)
end end
def each_view
@views.each do |view_name, view_data|
yield view_name, view_data
end
end
def to_hash def to_hash
return { return {
project: @name, 'project' => @name,
description: @description, 'description' => @description,
path: @path, #'path' => @path,
views: @views.values.map{ |v| v.to_hash } 'views' => @views.values.map{ |v| v.to_hash }
} }
end end
def from_hash project_hash def self.from_hash project_hash, project_path
@name = project_hash[:project] project = Project.new project_hash['project'] do |p|
@description = project_hash[:description] p.description = project_hash['description']
@path = project_hash[:path] p.path = project_path
project_hash[:views].each do |hash_view|
view = View.new do |v| #project_hash[:views].each do |hash_view|
v.from_hash hash_view # view = View.new do |v|
end # v.from_hash hash_view
add_view view # end
# p.add_view view
#end
end end
end end
def self.validate_name name def self.validate_name name
raise "TooShortProjectIdentifier" if name.size < Project::PROJECT_NAME_MIN_SIZE raise "TooShortProjectIdentifier" if name.size < Project::PROJECT_NAME_MIN_SIZE
raise "TooLongProjectIdentifier" if name.size > Project::PROJECT_NAME_MAX_SIZE raise "TooLongProjectIdentifier" if name.size > Project::PROJECT_NAME_MAX_SIZE
if not name =~ /^\w+$/ then if not name =~ /^\w(\S+)$/ then
raise "BadProjectIdentifier #{name}" raise "BadProjectIdentifier #{name}"
end end
return true return true

0
spec/spec_helper.rb Normal file
View file

60
spec/test_project.rb Executable file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env ruby
require 'singleton'
require 'yaml'
require 'pp'
module Kook
class Config
include Singleton
def create_project project_name
raise "ExistingProject" if @projects.has_key? project_name
@projects[project_name] = Project.new project_name
end
def create_view project_name, view_name
Project.validate_name project_name
View.validate_name view_name
raise "MissingProject" if not @projects.has_key? project_name
view = View.new view_name
@projects[project_name].add_view view
end
def to_yaml
return {
global: {},
projects: @projects.values.map{ |p| p.to_hash }
}.to_yaml
end
private
def initialize
@projects = {}
super
end
end
end
class Test
def self.test_project_create
config = Kook::Config.instance
config.create_project 'proj'
end
def self.test_view_create
config = Kook::Config.instance
config.create_view 'proj', 'proj-root'
config.create_view 'proj', 'proj-base'
config.create_view 'proj', 'proj-3'
puts config.to_yaml
end
end
Test.test_project_create
Test.test_view_create