root/lang/ruby/reposh/trunk/reposh.rb @ 6934

Revision 6934, 5.2 kB (checked in by yhara, 7 years ago)

lang/ruby/reposh: configurable pathext (for windows) v0.1.5

  • Property svn:executable set to *
Line 
1#
2# reposh.rb - Reposh - Simple VCS Manager Shell
3#
4require 'readline'
5require 'yaml'
6require 'optparse'
7
8class Hash
9  def recursive_merge(other)
10    self.merge(other) do |key, my_val, other_val|
11      # for values of a same key
12      if my_val.is_a? Hash and other_val.is_a? Hash
13        my_val.recursive_merge(other_val) # XXX: hang-ups for cyclic hash?
14      else
15        other_val
16      end
17    end
18  end
19end
20
21class Reposh
22  VERSION = "0.1.5"
23  CONF_DEFAULT = {
24    "global" => {
25      "editing_mode" => nil,
26      "custom_commands" => [],
27      "pathext" => [],
28    },
29    "system" => {
30      "default" => {
31        "binpath" => nil,
32        "prompt" => "> ",
33        "default_cmd" => "status",
34      },
35      "darcs" => {
36        "default_cmd" => "whatsnew --summary",
37      }
38    }
39  }
40
41  def run
42    parse_option(ARGV)
43    @conf_path ||= File.join(ENV["HOME"], ".reposh.yaml")
44    @system_name ||= guess_system
45
46    @conf = load_config(@conf_path)
47    @editing_mode = @conf["global"]["editing_mode"]
48    pathext       = @conf["global"]["pathext"]
49    @prompt      = get_conf(@system_name, "prompt")
50    binpath      = get_conf(@system_name, "binpath") || @system_name
51    default_cmd  = get_conf(@system_name, "default_cmd")
52
53    @commands = Commands.new(binpath, default_cmd, pathext)
54    @commands.register_custom_commands(@conf["global"]["custom_commands"])
55
56    run_loop
57  end
58
59  def parse_option(args)
60    o = OptionParser.new{|opt|
61      opt.on("-c confpath",
62             "path to .reposh.yaml"){|path|
63        @confpath = path
64      }
65      opt.on("-s system",
66             "vcs command name (eg. svn, svk, hg)"){|sys|
67        @system_name = sys
68      }
69      opt.on("-h", "--help",
70             "show this message"){
71        puts opt
72        exit
73      }
74      opt.on("-v", "--version",
75             "show version information"){
76        puts VERSION
77        exit
78      }
79    }
80    o.parse(args)
81  end
82
83  def load_config(path)
84    if File.exist?(path)
85      config_hash = YAML.load(File.read(path))
86      CONF_DEFAULT.recursive_merge(config_hash)
87    else
88      CONF_DEFAULT
89    end
90  end
91
92  def guess_system
93    case
94    when File.directory?(".hg")
95      "hg"
96    when File.directory?("_darcs")
97      "darcs"
98    when File.directory?(".svn")
99      "svn"
100    else
101      "svk"
102    end
103  end
104 
105  def get_conf(system, prop)
106    (@conf["system"][system] and @conf["system"][system][prop]) or @conf["system"]["default"][prop]
107  end
108
109  def run_loop
110    if @editing_mode == "vi"
111      Readline.vi_editing_mode
112    end
113
114    puts "Welcome to reposh #{VERSION} (mode: #{@system_name})"
115    loop do
116      cmd = Readline.readline(@prompt, true)
117      @commands.dispatch(cmd, @system_name)
118    end
119  end
120
121  class Commands
122    def initialize(binpath, default_cmd, pathext)
123      @binpath, @default_cmd, @pathext = binpath, default_cmd, pathext
124      @commands = []
125      register_builtin_commands
126    end
127
128    def register_builtin_commands
129      # default command
130      register(/.*/){|match|
131        cmd = (match[0] == "") ? @default_cmd : match[0]
132        execute "#{@binpath} #{cmd}"
133      }
134
135      # system commands
136      register("%reload"){
137        load __FILE__
138      }
139      register("%env"){
140        require 'pp'
141        pp ENV
142      }
143      register("%version"){
144        puts VERSION
145      }
146      register(/\A%ruby (.*)/){|match|
147        puts "reposh: result is " + eval(match[1]).inspect
148      }
149      @trace_mode = false
150      register("%trace"){
151        @trace_mode = (not @trace_mode)
152        puts "set trace_mode to #{@trace_mode}"
153      }
154
155      # exit commands
156      exit_task = lambda{
157        puts ""
158        exit
159      }
160      register(nil,    &exit_task)
161      register("exit", &exit_task)
162      register("quit", &exit_task)
163
164      # shell execution command
165      register(/^:(.*)/){|match|
166        execute match[1]
167      }
168    end
169
170    def register_custom_commands(commands)
171      commands.each do |hash|
172        if hash["for"]
173          systems = hash["for"].split(/,/).map{|s| s.strip}
174        else
175          systems = nil
176        end
177        register(Regexp.new(hash["pattern"]), systems){|match|
178          cmd = hash["rule"].
179                  gsub(/\{system\}/, @binpath).
180                  gsub(/\{\$(\d+)\}/){ match[$1.to_i] }
181          puts cmd
182          execute cmd
183        }
184      end
185    end
186
187    def register(pattern, systems = nil, &task)
188      @commands.unshift [pattern, systems, task]
189    end
190
191    def dispatch(cmd, sys)
192      @commands.each do |pattern, systems, task|
193        next if systems && !systems.include?(sys)
194
195        if (match = match?(pattern, cmd))
196          return task.call(match)
197        end
198      end
199      raise "must not happen"
200    end
201
202    def match?(pat, value)
203      case pat
204      when Regexp
205        pat.match(value)
206      when nil
207        value == nil
208      else
209        pat.strip == value
210      end
211    end
212
213    def execute(cmd)
214      stat = false
215      ([""] + @pathext).each do |ext|
216        command = add_ext(cmd, ext)
217        puts command if @trace_mode
218        result = system(command)
219        return if result
220        stat = $?
221      end
222      puts "reposh: failed to exec '#{cmd}': status #{stat.exitstatus}"
223    end
224
225    def add_ext(cmd, ext)
226      exe, *args = cmd.split(' ')
227      "#{exe}#{ext} #{args.join ' '}"
228    end
229
230  end
231
232end
233
234if $0==__FILE__
235  Reposh.new.run
236end
237
Note: See TracBrowser for help on using the browser.