From cdcec384f0854738f5764890621d6a1bbdadf1dd Mon Sep 17 00:00:00 2001 From: "Glenn Y. Rolland" Date: Sat, 28 Dec 2013 15:05:51 +0100 Subject: [PATCH] Continue ruby implementation. --- bin/kook | 9 ++- bin/kotam | 112 --------------------------- lib/base.sh | 25 ------ lib/kook.rb | 2 +- lib/kook/app.rb | 178 +++++++++++++++++++++++++++++++++++++++++++ lib/kook/cli.rb | 173 ++++++++++++++++------------------------- lib/kook/config.rb | 105 ------------------------- lib/kook/project.rb | 39 ++++++---- spec/spec_helper.rb | 0 spec/test_project.rb | 60 +++++++++++++++ 10 files changed, 336 insertions(+), 367 deletions(-) delete mode 100755 bin/kotam delete mode 100644 lib/base.sh create mode 100644 lib/kook/app.rb delete mode 100644 lib/kook/config.rb create mode 100644 spec/spec_helper.rb create mode 100755 spec/test_project.rb diff --git a/bin/kook b/bin/kook index 469902e..3b8ae52 100755 --- a/bin/kook +++ b/bin/kook @@ -19,5 +19,10 @@ $:.insert 0, File.join(DATA_DIR, 'lib') require 'thor' 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 diff --git a/bin/kotam b/bin/kotam deleted file mode 100755 index 1bbe23d..0000000 --- a/bin/kotam +++ /dev/null @@ -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 - - diff --git a/lib/base.sh b/lib/base.sh deleted file mode 100644 index 951437f..0000000 --- a/lib/base.sh +++ /dev/null @@ -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" -} - diff --git a/lib/kook.rb b/lib/kook.rb index cee1c0f..2dc4cb3 100644 --- a/lib/kook.rb +++ b/lib/kook.rb @@ -2,6 +2,6 @@ require 'kook/exceptions' require 'kook/view' require 'kook/project' -require 'kook/config' +require 'kook/app' require 'kook/cli' diff --git a/lib/kook/app.rb b/lib/kook/app.rb new file mode 100644 index 0000000..6cf815a --- /dev/null +++ b/lib/kook/app.rb @@ -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 diff --git a/lib/kook/cli.rb b/lib/kook/cli.rb index 1d9b35d..76bc21f 100644 --- a/lib/kook/cli.rb +++ b/lib/kook/cli.rb @@ -3,37 +3,38 @@ require 'thor' module Kook module CLI module KookHelper - - def fail_for exception, params=nil - STDERR.puts "ERROR(#{exception.class}) : #{exception}" - STDERR.puts exception.backtrace - exit 1 - end - end - - - class OldConfig - include Singleton - include KookHelper - - DEFAULT_CONFIG = { - 'global' => {}, # (ConfigKey_str => ConfigVal_str ) list - 'projects' => {}, # Project_str list - 'views' => {}, # (Project_str => View_str) list - 'commands' => {} # (Project_str => View_str) => Command list - } - - def initialize - # load file - @config = DEFAULT_CONFIG - load_main - @config + def before_filter options + @app = App.new + @app.load options[:config] + @app.verbose = options[:verbose] + @app.current_project = options[:project] 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_option :verbose, + type: :boolean, + default: false, + aliases: '-v', + desc: 'Whether to output informative debug' - def [] key - @config[key] + class_option :config, + type: :string, + default: nil, + aliases: '-c', + desc: 'Configuration file' + + class_option :project, + type: :string, + default: nil, + aliases: '-p', + desc: 'Target project' + end end end @@ -42,43 +43,48 @@ module Kook desc "detect", "Detect current project" def detect + before_filter options + current_project = @app.current_project project_name = current_project.nil? ? "-none-" : current_project say "Current project is #{project_name}." end desc "list", "List projects" def list - config.each_project do |project_name,project| - pp project_name - exist = File.exist? project.path + before_filter options + projects_exist = false + @app.each_project do |project_name,project_data| + projects_exist = true + exist = File.exist? project_data.path display_path = ( - path.clone + project_data.path.clone .gsub!(/#{ENV['HOME']}/,'~') .send(exist ? :green : :red) ) puts "%- 24s %s" % [project_name, display_path] end + STDERR.puts "Empty list : no project configured." if not projects_exist end - # FIXME: option for alternative path - desc "add PROJECT [PATH]", "Register new project" + option :path + desc "add PROJECT", "Register new project" def add project_name, project_path=nil + before_filter options + project_path = options[:path] if project_path.nil? then project_path = Dir.pwd end 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}." - rescue Exception => e - fail_for e, project: project_name, path: project_path end desc "rm PROJECT", "Unregister existing project" def rm project - config['projects'].delete project - config.save_main + before_filter options + @app.remove_project project say "Project #{project} unregistered." end @@ -93,45 +99,32 @@ module Kook end # TODO: editcopy project to another name + base path # TODO: copy project to another name + base path - # - private - - def config - Config.instance - end end class View < Thor include KookHelper - desc "list [PROJECT]", "List view for a project" - def list project=nil - project ||= current_project - validate_project_exists project + desc "list", "List view for a project" + def list project_name=nil + before_filter options + project_name ||= @app.current_project - if config['views'].has_key? project then - 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 + @app.list_views project_name end - desc "add PROJECT VIEW", "Register new view" - def add project, view, path=nil - if path.nil? then - path = Dir.pwd + desc "add VIEW", "Register new view" + option :path + def add view_name + before_filter options + project_name ||= @app.current_project + + view_path = options[:path] + if view_path.nil? then + view_path = Dir.pwd end + + @app.add_view project_name, view_name, view_path + project_rootdir = config['projects'][project] # simplify if current dir is a subdir of project base if path == project_rootdir then @@ -157,12 +150,6 @@ module Kook config['views'][project].delete view config.save_main end - - private - - def config - Config.instance - end end # FIXME: add helper validating project name @@ -187,12 +174,6 @@ module Kook def rm project, view, index raise NotImplementedError end - - private - - def config - Config.instance - end end class Main < Thor @@ -207,36 +188,12 @@ module Kook desc "command SUBCOMMAND [options]", "Commands for managing commands" subcommand "command", CLI::Command - desc "fire PROJECT", "Run project environment" - def fire project - validate_project_name project - validate_project_exists project - 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 + desc "fire [PROJECT]", "Run project environment" + def fire project_name=nil + before_filter options + @app.fire_project project_name end - - private - - def config - Config.instance - end end end end diff --git a/lib/kook/config.rb b/lib/kook/config.rb deleted file mode 100644 index dc977fe..0000000 --- a/lib/kook/config.rb +++ /dev/null @@ -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 diff --git a/lib/kook/project.rb b/lib/kook/project.rb index ee885f9..bf43eb6 100644 --- a/lib/kook/project.rb +++ b/lib/kook/project.rb @@ -17,6 +17,9 @@ module Kook @description = nil @path = nil @views = {} + + yield self if block_given? + self end def path= path @@ -38,31 +41,39 @@ module Kook return @view.delete(view_name) end + def each_view + @views.each do |view_name, view_data| + yield view_name, view_data + end + end + def to_hash return { - project: @name, - description: @description, - path: @path, - views: @views.values.map{ |v| v.to_hash } + 'project' => @name, + 'description' => @description, + #'path' => @path, + 'views' => @views.values.map{ |v| v.to_hash } } end - def from_hash project_hash - @name = project_hash[:project] - @description = project_hash[:description] - @path = project_hash[:path] - project_hash[:views].each do |hash_view| - view = View.new do |v| - v.from_hash hash_view - end - add_view view + def self.from_hash project_hash, project_path + project = Project.new project_hash['project'] do |p| + p.description = project_hash['description'] + p.path = project_path + + #project_hash[:views].each do |hash_view| + # view = View.new do |v| + # v.from_hash hash_view + # end + # p.add_view view + #end end end def self.validate_name name raise "TooShortProjectIdentifier" if name.size < Project::PROJECT_NAME_MIN_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}" end return true diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..e69de29 diff --git a/spec/test_project.rb b/spec/test_project.rb new file mode 100755 index 0000000..f202f69 --- /dev/null +++ b/spec/test_project.rb @@ -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