Update CFBundleVersion with git - ruby

How to Update CFBundleVersion of Xcode according to GitHub commit.
I was using since yesterday the SVN with ruby Script:
#!/usr/bin/ruby
require "rubygems"
require "plist"
# fail if not run from Xcode
unless ENV['XCODE_VERSION_ACTUAL']
puts "error: Must be run from Xcode's Run Script Build Phase"
exit false
end
revision = `svnversion`.chomp
if not $?.success?
puts "error: svnversion failed"
exit false
end
if revision =~ /[^0-9]/
if ENV['CONFIGURATION'] == 'Release'
puts "error: Working copy has mixed revision or uncommitted changes, cannot build in #{ENV['CONFIGURATION']} configuration"
exit false
end
mixed_revision = revision
revision = revision[/[0-9]+/] or '0'
puts "warning: Mixed/flagged revision #{mixed_revision} corrected to #{revision}"
end
plistFile = "#{ENV['BUILT_PRODUCTS_DIR']}/#{ENV['INFOPLIST_PATH']}"
`/usr/bin/plutil -convert xml1 "#{plistFile}"`
unless pl = Plist::parse_xml(plistFile)
puts "Could parse #{plistFile}"
exit
end
freshPlFile = "#{ENV['SOURCE_ROOT']}/Resources/Info.plist"
`/usr/bin/plutil -convert xml1 "#{freshPlFile}"`
unless freshPl = Plist::parse_xml(freshPlFile)
puts "Could parse #{freshPlFile}"
exit
end
begin
file = File.new("Resources/brands/#{ENV['BRAND_NAME']}/versions.txt", "r")
while (line = file.gets)
strp_line = line.strip
strp_line.gsub!(/ /,'')
strp_line.gsub!("ver",'')
equal_pos = strp_line.index('=')
str_len = strp_line.length
if strp_line[0,equal_pos] == freshPl["CFBundleShortVersionString"]
puts "Custom version found: #{strp_line[equal_pos+1,str_len -1]}"
pl["CFBundleShortVersionString"] = strp_line[equal_pos+1,str_len -1]
next
end
end
file.close
rescue => err
puts "Exception: #{err}"
err
end
version = pl["CFBundleShortVersionString"].gsub(/ \([a-f0-9 \/:]*\)/, '')
# keep only the major and minor version number and add the revision
version.gsub!(/([^\.]*)\.([^\.]*).*/, "\\1.\\2.#{revision}");
pl["CFBundleShortVersionString"] = version
pl["CFBundleVersion"] = Time.now.utc.strftime("%Y%m%d%H")
pl.save_plist(plistFile)
`/usr/bin/plutil -convert binary1 #{plistFile}`
puts "#{plistFile}:"
puts "CFBundleVersion = #{pl["CFBundleVersion"]}"
puts "CFBundleShortVersionString = #{pl["CFBundleShortVersionString"]}"
I dont have any idea how to connect them with Github.
I tried to use this script but it doesn't work:
# add git tag + version number to Info.plist
version = `/usr/bin/env git describe`.chomp
puts "raw version "+version
version_fancy_re = /(\d*\.\d*)-?(\d*)-?/
version =~ version_fancy_re
commit_num = $2
if ( $2.empty? )
commit_num = "0"
end
fancy_version = ""+$1+"."+commit_num
puts "compatible: "+fancy_version
# backup
source_plist_path = File.join(ENV['PROJECT_DIR'], ENV['INFOPLIST_FILE'])
orig_plist = File.open( source_plist_path, "r").read;
File.open( source_plist_path+".bak", "w") { |file| file.write(orig_plist) }
# put in CFBundleVersion key
version_re = /([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>).*?(<\/string>)/
orig_plist =~ version_re
bundle_version_string = $1 + fancy_version + $2
orig_plist.gsub!(version_re, bundle_version_string)
# put in CFBundleShortVersionString key
version_re = /([\t ]+<key>CFBundleShortVersionString<\/key>\n[\t ]+<string>).*?(<\/string>)/
orig_plist =~ version_re
bundle_version_string = $1 + fancy_version + $2
orig_plist.gsub!(version_re, bundle_version_string)
# write
File.open(source_plist_path, "w") { |file| file.write(orig_plist) }
puts "Set version string to '#{fancy_version}'"
Any idea where am I doing the mistake?
I don't have experience with ruby, I will appreciate any help.

Related

Ruby: void value expression while using if else with break

I am new to Ruby language. I am working on integrating the Danger to add checks on GitHub PR. One of the use case is to check if modified files in my PR contains particular change. I have added below code in DangerFile
filesArray = ["Resources/Config.plist", "Resources/Deployment.plist"]
def modified_files_contains_change(files, change)
does_contain_required_changes = false
for file in files do
message "diff in #{file}"
diff = git.diff_for_file(file)
patch_contains_change = diff.patch =~ #{change}
if diff && patch_contains_change
does_contain_required_changes = true
else
does_contain_required_changes = false
break
end
end
message "Does contain changes in all files => #{does_contain_required_changes}"
does_contain_required_changes
end
if modified_files_contains_change(files, change)
warn "Some useful warning message goes here"
end
While running this on my CI, I am getting below error
[!] Invalid `Dangerfile` file: void value expression.
# from Dangerfile:33
# -------------------------------------------
# does_contain_required_changes = false
> break
# end
# end
# -------------------------------------------
I tried searching but didn't understand the what's going wrong. Can someone please help me understand what is the exact issue? Thanks in advance.
=~ #{change} is read as just =~ because # starts an inline comment. =~ expects a regular expression on one of sides. To fix the issue, change the line below:
patch_contains_change = diff.patch =~ #{change}
to:
patch_contains_change = diff.patch =~ Regexp.new(change)
This assumes you are passing a string as change.
Also, this would be more or less rubyish way to accomplish the task:
def modified_files_contains_change(files, change)
does_contain_required_changes =
files.all? do |file|
message "diff in #{file}"
# break will immediately exit the `all?` unless diff is present
break false unless diff = git.diff_for_file(file)
diff.patch =~ Regexp.new(change)
end
message "Does contain changes in all files => #{does_contain_required_changes}"
does_contain_required_changes
end
To return a file where we found an issue:
def modified_files_contains_change(files, change)
problematic_file =
files.detect do |file|
message "diff in #{file}"
(diff = git.diff_for_file(file)).nil? || diff.patch !~ Regexp.new(change)
end
does_contain_required_changes = problematic_file.nil?
if does_contain_required_changes
message "All good!"
else
message "We have issues with file #{problematic_file}"
end
does_contain_required_changes
end

Method to call other methods multiple times using hash length as iterator

I have 2 methods that I want to call multiple times. The number of times that I want to call them is based on the count of a hash I'm using. I'm trying to create a new method that calls the other 2 and repeats for the length of the hash count. My issue is that I'm getting an error
"findfiles2.rb:61:in` `chdir': no implicit conversion of Enumerator into String (TypeError)
from findfiles2.rb:61:in `store_directories'
from findfiles2.rb:138:in `block in repeat'
from findfiles2.rb:134:in `loop'
from findfiles2.rb:134:in `repeat'
from findfiles2.rb:153:in `<main>'"
Here's my code:
require 'date'
require "mail"
options = { :address => "smtp.gmail.com",
:port => 587,
:domain => 'gmail.com',
:user_name => 'username',
:password => 'password/',
:authentication => 'plain',
:enable_starttls_auto => true }
mail_sender = "somename#gmail.com"
mail_recipient = "somename#yahoo.com"
directories = {
"directory1" => "/path/to/folder1/",
"directory2" => "/path/to/folder2/",
"directory3" => "/path/to/folder3/",
"directory4" => "/path/to/folder4/",
"directory5" => "/path/to/folder5/"
}
directory_count = directories.count.to_i
file_output = "/path/to/output/"
exclude_folder = 'sample'
output_file_name = "directory_list"
output_file_extension = ".csv"
date_today = Date.today.to_s
log_file_path = "/path/to/output/"
log_name = "script_log_" + date_today + ".txt"
log_file_name = log_file_path + log_name
# starts log file
def start_log(file_output, log_name)
Dir.chdir(file_output)
log_output = File.open(log_name, 'a+')
$stdout = log_output
puts Time.now.to_s + " > " + "Starting Script..."
puts "_______________________________________________"
end
# stores subdirectory contents into an array
def store_directories(directory, folder_to_exclude)
# changes working directory to the directory variable
puts Time.now.to_s + " > " + "Updating search directory..."
Dir.chdir(directory)
# outputs only subdirectories with a creation date of older than 24 hours, except for folders names 'test'
Dir.glob("*.*").map(&File.method(:realpath))
puts Time.now.to_s + " > " + "Gathering subdirectories..."
subdir_list=Dir.glob("*").map(&File.method(:realpath)).reject{|files| (not File.directory?(files) && (File.mtime(files) < (Time.now - (60*1440))) && (not files == directory + folder_to_exclude)) }
return subdir_list
end
# checks to see if there are any directories in the array
def directory_check(directory_list, save_to_file, today_date, output_file, output_extension)
if directory_list.empty? == false
# changes the working directory to the file output directory for the file
Dir.chdir(save_to_file)
# writes the array contents into a new file
file_name = output_file + "_" + today_date + output_extension
puts Time.now.to_s + " > " + "Saving contents to: " + file_name
File.open(file_name, "a+") do |f|
directory_list.each { |element| f.puts(element) }
end
else
puts Time.now.to_s + " > " + "This directory does not contain any subdirectories that are older than 24 hours"
exit
end
end
# sends an email containing today's report if a file was created today
def send_email(today_date, output_file_path, output_file_name, output_file_extension, mail_options, email_sender, email_recipient)
backlog_file = output_file_path + output_file_name + "_" + today_date + output_file_extension
if File.exist?(backlog_file) == true
puts Time.now.to_s + " > " + "Sending email report to: " + email_recipient + "..."
Mail.defaults do
delivery_method :smtp, mail_options
end
Mail.deliver do
to email_recipient
from email_sender
subject 'Backlog for ' + today_date
body 'Attached is a report showing any batches that have not been processed within the last 24 hours.'
add_file backlog_file
end
else
puts Time.now.to_s + " > " + "No batches older than 24 hours to report"
exit
end
end
This is the method that is giving me trouble
def repeat(directory, times, exclude_folder)
# fail "times must be 1 or more" if times < 1
counter = 1
# counter_string = counter.to_s
# puts counter_string
# directory_counter = directory + counter_string
loop do
if counter != times
subdir_list_contents = store_directories(directory, exclude_folder)
directory_check(subdir_list_contents, file_output, date_today, output_file_name, output_file_extension)
counter = counter + 1
else
break
end
end
end
This is where I'm starting to run everything.
# Starting log file...
start_log(file_output, log_name)
repeat(directories.each, directory_count, exclude_folder)
# # outputs contents of directory 1 to the file (I want to perform this for the amount of times equal to the hash length, which is what I'm creating the repeat method for)
subdir_list_contents = store_directories(directory1, exclude_folder)
directory_check(subdir_list_contents, file_output, date_today, output_file_name, output_file_extension)
# # # If there is a new file from today, sends an email with file as attachment
send_email(date_today, file_output, output_file_name, output_file_extension, options, mail_sender, mail_recipient)
Your code is much too long. As you can see, nobody helps you.
$ ruby -w t.rb
t.rb:125: warning: mismatched indentations at 'end' with 'def' at 104
t.rb:37: warning: assigned but unused variable - log_file_name
/Users/b/.rvm/rubies/ruby-2.4.0-rc1/lib/ruby/site_ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:
in `require': cannot load such file -- mail (LoadError)
Post code that we can run. I'm not going to install a whole mail server. So I comment out
#require "mail"
then
t.rb:44:in `chdir': No such file or directory # dir_chdir - /path/to/output/ (Errno::ENOENT)
etc, etc.
Follow the error message :
t.rb:62:in `chdir': no implicit conversion of Enumerator into String (TypeError)
from t.rb:62:in `store_directories'
from t.rb:139:in `block in repeat'
from t.rb:136:in `loop'
from t.rb:136:in `repeat'
from t.rb:149:in `<main>'
All what you need to trace is repeat and store_directories, so you could have reduced the posted code to this strict minimum to reproduce the error :
directories = {
"directory1" => "/path/to/folder1/",
"directory2" => "/path/to/folder2/",
}
directory_count = directories.count.to_i
exclude_folder = 'sample'
# stores subdirectory contents into an array
def store_directories(directory, folder_to_exclude)
puts "directory=#{directory.inspect} folder_to_exclude=#{folder_to_exclude}"
Dir.chdir(directory)
end
def repeat(directory, times, exclude_folder)
store_directories(directory, exclude_folder)
end
repeat(directories.each, directory_count, exclude_folder)
Execution :
$ ruby -w t.rb
directory=#<Enumerator: {"directory1"=>"/path/to/folder1/", "directory2"=>"/path/to/folder2/"}:each> folder_to_exclude=sample
t.rb:12:in `chdir': no implicit conversion of Enumerator into String (TypeError)
from t.rb:12:in `store_directories'
from t.rb:16:in `repeat'
from t.rb:19:in `<main>'
Had you done this, you don't even had to post a question, because the cause of the error is obvious. In
repeat(directories.each, directory_count, exclude_folder)
directories.each returns an Enumerator. Remove each :
$ ruby -w t.rb
directory={"directory1"=>"/path/to/folder1/", "directory2"=>"/path/to/folder2/"} folder_to_exclude=sample
t.rb:12:in `chdir': no implicit conversion of Hash into String (TypeError)
from t.rb:12:in `store_directories'
from t.rb:16:in `repeat'
from t.rb:20:in `<main>'
I suppose that what you wanted to do is call repeat once for each directory :
def repeat(directory, exclude_folder)
puts "in repeat directory=#{directory} exclude_folder=#{exclude_folder}"
store_directories(directory, exclude_folder)
end
directories.each { | _key, directory | repeat(directory, exclude_folder) }
Execution :
$ ruby -w t.rb
in repeat directory=dir1 exclude_folder=sample
in store_directories directory="dir1" folder_to_exclude=sample
in repeat directory=dir2 exclude_folder=sample
in store_directories directory="dir2" folder_to_exclude=sample
t.rb:18:in `chdir': No such file or directory # dir_chdir - dir2 (Errno::ENOENT)
from t.rb:18:in `store_directories'
from t.rb:53:in `repeat'
chdir has a side effect : it changes the current directory, and the next time, it will start searching in the new current directory. To avoid this, you need to restore the previous state :
def store_directories(directory, folder_to_exclude)
puts "in store_directories directory=#{directory.inspect} folder_to_exclude=#{folder_to_exclude}"
current_directory = Dir.getwd
Dir.chdir(directory)
# ...
# Restore the directory that was current when entering the method.
# Without it, the next chdir will start from the directory left by the previous chdir.
Dir.chdir(current_directory)
end
Now it works :
$ ruby -w t.rb
in repeat directory=dir1 exclude_folder=sample
in store_directories directory="dir1" folder_to_exclude=sample
in repeat directory=dir2 exclude_folder=sample
in store_directories directory="dir2" folder_to_exclude=sample
After a few more changes, I end up with this :
require 'date'
directories = {
"directory1" => 'dir1',
"directory2" => 'dir2'
}
exclude_folder = 'sample'
#file_output = '.'
#date_today = Date.today.to_s
#output_file_name = 'directory_list'
#output_file_extension = '.csv'
# stores subdirectory contents into an array
def store_directories(directory, folder_to_exclude)
puts "in store_directories directory=#{directory.inspect} folder_to_exclude=#{folder_to_exclude}"
current_directory = Dir.getwd
puts Time.now.to_s + " > " + "Updating search directory..."
# changes working directory to the directory variable
Dir.chdir(directory)
# outputs only subdirectories with a creation date of older than 24 hours, except for folders names 'test'
puts Time.now.to_s + " > " + "Gathering subdirectories..."
subdir_list = Dir.glob("*").map { | file | File.realpath(file) }
puts "all files : subdir_list=#{subdir_list}"
puts "directory + folder_to_exclude=#{directory + folder_to_exclude}" # nonsense
subdir_list = subdir_list.reject do | file |
not File.directory?(file) \
&& File.mtime(file) < Time.now - 86400 \
&& (not file == folder_to_exclude)
end
puts "after reject : subdir_list=#{subdir_list}"
# Restore the directory that was current when entering the method.
# Without it, the next chdir will start from the directory left by the previous chdir.
Dir.chdir(current_directory)
puts "subdir_list=#{subdir_list.inspect}"
subdir_list
end
# checks to see if there are any directories in the array
def directory_check(directory_list, save_to_file, today_date, output_file, output_extension)
if directory_list.empty? == false
# changes the working directory to the file output directory for the file
Dir.chdir(save_to_file) # <----------------- problem !!!!
# writes the array contents into a new file
file_name = output_file + "_" + today_date + output_extension
puts Time.now.to_s + " > " + "Saving contents to: " + file_name
File.open(file_name, "a+") do |f|
directory_list.each { |element| f.puts(element) }
end
else
puts Time.now.to_s + " > " + "This directory does not contain any subdirectories that are older than 24 hours"
end
end
def repeat(directory, exclude_folder)
puts "in repeat directory=#{directory} exclude_folder=#{exclude_folder}"
subdir_list_contents = store_directories(directory, exclude_folder)
directory_check(subdir_list_contents, #file_output, #date_today, #output_file_name, #output_file_extension)
end
directories.each { | _key, directory | repeat(directory, exclude_folder) }
Execution :
$ ruby -w t.rb
in repeat directory=dir1 exclude_folder=sample
in store_directories directory="dir1" folder_to_exclude=sample
2017-10-27 08:05:24 +0200 > Updating search directory...
2017-10-27 08:05:24 +0200 > Gathering subdirectories...
all files : subdir_list=["/userdata/devl/ruby/zintlist/directories/dir1/x1.txt", "/userdata/devl/ruby/zintlist/directories/dir1/x2.txt"]
directory + folder_to_exclude=dir1sample
after reject : subdir_list=[]
subdir_list=[]
2017-10-27 08:05:24 +0200 > This directory does not contain any subdirectories that are older than 24 hours
in repeat directory=dir2 exclude_folder=sample
in store_directories directory="dir2" folder_to_exclude=sample
2017-10-27 08:05:24 +0200 > Updating search directory...
2017-10-27 08:05:24 +0200 > Gathering subdirectories...
all files : subdir_list=["/userdata/devl/ruby/zintlist/directories/dir2/x3.txt"]
directory + folder_to_exclude=dir2sample
after reject : subdir_list=[]
subdir_list=[]
2017-10-27 08:05:24 +0200 > This directory does not contain any subdirectories that are older than 24 hours

How to select the appropriate diff /patch for a commit with rugged

I try to get the commits that have been done after a date in a local copy of a git repo and then extract the related modifications on the files.
If I would like to compare this to a git command, It would be :
git log -p --reverse --after="2016-10-01"
Here is the script I use:
require "rugged"
require "date"
git_dir = "../ruby-gnome2/"
repo = Rugged::Repository.new(git_dir)
walker = Rugged::Walker.new(repo)
walker.sorting(Rugged::SORT_DATE| Rugged::SORT_REVERSE)
walker.push(repo.head.target)
walker.each do |commit|
c_time = Time.at(commit.time)
next unless c_time >= Date.new(2016,10,01).to_time
puts c_time
puts commit.diff.size
puts commit.diff.stat.inspect
end
The problem is that it looks like a lot of files are modified here is the end of the output of this script:
2016-10-22 17:33:37 +0200
2463
[2463, 0, 271332]
Which means that there are 2463 files modified/deleted/replaced. While a git log -p --reverse --after="2016-10-22" show that only 2 files are modified.
How can I get the same results than with the git command? ie How can I find the real files that are modified by this commit?
As I didn't have any answer from the rugged team, I have done a ruby gobject-introspection loader for the libgit2-glib here https://github.com/ruby-gnome2/ggit.
Now I can find the diff and the logs that corresponds to the git command line interface:
require "ggit"
PATH = File.expand_path(File.dirname(__FILE__))
repo_path = "#{PATH}/ruby-gnome2/.git"
file = Gio::File.path(repo_path)
begin
repo = Ggit::Repository.open(file)
revwalker = Ggit::RevisionWalker.new(repo)
revwalker.sort_mode = [:time, :topological, :reverse]
head = repo.head
revwalker.push(head.target)
rescue => error
STDERR.puts error.message
exit 1
end
def signature_to_string(signature)
name = signature.name
email = signature.email
time = signature.time.format("%c")
"#{name} <#{email}> #{time}"
end
while oid = revwalker.next do
commit = repo.lookup(oid, Ggit::Commit.gtype)
author = signature_to_string(commit.author)
date = commit.committer.time
next unless (date.year >= 2016 && date.month >= 11 && date.day_of_month > 5)
committer = signature_to_string(commit.committer)
subject = commit.subject
message = commit.message
puts "SHA: #{oid}"
puts "Author: #{author}"
puts "Committer: #{committer}"
puts "Subject: #{subject}"
puts "Message: #{message}"
puts "----------------------------------------"
commit_parents = commit.parents
if commit_parents.size > 0
parent_commit = commit_parents.get(0)
commit_tree = commit.tree
parent_tree = parent_commit.tree
diff = Ggit::Diff.new(repo, :old_tree => parent_tree,
:new_tree => commit_tree, :options => nil)
diff.print( Ggit::DiffFormatType::PATCH ).each do |_delta, _hunk, line|
puts "\t | #{line.text}"
0
end
end
end
When I clone ruby-gnome2/ruby-gnome2, it tells me there is 2400+ files, so for you to get 2463, that strikes me as all the files have been modified.
This differs from the normal behavior of a rugged#commit.diff, which diff by default the current commit (returned by the Walker) against the first parent commit.
Check if you have some settings like git config core.autocrlf set to true (which might change eol in your local repo).

OptParser does not return options

I have this code sample:
#!/usr/bin/env ruby
require_relative File.expand_path('../../lib/argosnap', __FILE__)
require 'optparse'
options = {}
opt_parser = OptionParser.new do |opt|
opt.banner = "argosnap #{Argosnap::VERSION} ( http://github/atmosx/argosnap )\nUsage: argosnap [OPTIONS]"
opt.separator ""
opt.separator " version: dislay version"
opt.separator " install: installs 'config.yml' and launchd script"
opt.separator " balance: check picodollars"
opt.separator ""
opt.on("-v","--version","display version") do |version|
options[:version] = version
end
opt.on("-c","--config [TYPE]", String, "install configuration files") do |config|
options[:config] = config
end
opt.on("-b","--balance","executes 'argosnap' and displayes notifications") do |balance|
options[:balance] = balance
end
opt.on("-h","--help","help") do
puts opt_parser
end
end
begin
opt_parser.parse!
rescue OptionParser::InvalidOption => e
puts "No such option! Type 'argosnap -h' for help!"
exit
end
case ARGV[0]
when "version"
puts Argosnap::VERSION
when "config"
Argosnap::Install.new.config
when "balance"
b = Argosnap::Fetch.new.balance
puts "Current balance (picodollars): #{b}"
else
puts "Type: 'argosnap -h' for help!"
end
My problem is that options hash is empty. It's like if it doesn't accept the options[:var] = var defined inside the OptParser class. I'd like to use -v and --version in my program to make it more unix-like.
I'm using ruby-2.0.
UPDATE: The way it is the code works I've tried changing when "version" with when '-v' or when options[:version] which seemed the best approach to me, but nothing worked.
when you write case ARGV[0] you are totally ignoring the opt_parser...
ARGV[0] is the first word in the command line. The whole point of opt_parser is that you don't look at ARGV:
if options[:version]
puts Argosnap::VERSION
elsif options[:config]
Argosnap::Install.new.config
elsif options[:balance]
b = Argosnap::Fetch.new.balance
puts "Current balance (picodollars): #{b}"
else
puts "Type: 'argosnap -h' for help!"
end

How can I do readline arguments completion?

I have a Ruby app which uses readline with command completion.
After the first string (the command) was typed, I would like to be able to complete its arguments. The arguments list should be based on the chosen command.
Does someone have a quick example?
These are the commands:
COMMANDS = [
'collect', 'watch'
].sort
COLLECT = [
'stuff', 'otherstuff'
].sort
comp = proc do |s|
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
Readline.completion_proc = comp
Each time I press TAB, the proc block is executed and a command from the COMMANDS array is matched.
After one of the commands was fully matched I would like to start searching for the argument only in the COLLECT array.
Since your question popped up first every time I looked for something like this I want to share my code for any one else.
#!/usr/bin/env ruby
require 'readline'
module Shell
PROMPT = "shell> "
module InputCompletor
CORE_WORDS = %w[ clear help show exit export]
SHOW_ARGS = %w[ list user ]
EXPORT_ARGS = %w[ file ]
COMPLETION_PROC = proc { |input|
case input
when /^(show|export) (.*)/
command = $1
receiver = $2
DISPATCH_TABLE[$1].call($2)
when /^(h|s|c|e.*)/
receiver = $1
CORE_WORDS.grep(/^#{Regexp.quote(receiver)}/)
when /^\s*$/
puts
CORE_WORDS.map{|d| print "#{d}\t"}
puts
print PROMPT
end
}
def self.show(receiver)
if SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"show #{SHOW_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
def self.export(receiver)
if EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length > 1
EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/)
elsif EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).length == 1
"export #{EXPORT_ARGS.grep(/^#{Regexp.quote(receiver)}/).join}"
end
end
DISPATCH_TABLE = {'show' => lambda {|x| show(x)} ,
'export' => lambda {|x| export(x)}}
end
class CLI
Readline.completion_append_character = ' '
Readline.completer_word_break_characters = "\x00"
Readline.completion_proc = Shell::InputCompletor::COMPLETION_PROC
def initialize
while line = Readline.readline("#{PROMPT}",true)
Readline::HISTORY.pop if /^\s*$/ =~ line
begin
if Readline::HISTORY[-2] == line
Readline::HISTORY.pop
end
rescue IndexError
end
cmd = line.chomp
case cmd
when /^clear/
system('clear')
when /^help/
puts 'no help here'
when /show list/
puts 'nothing to show'
when /^show\s$/
puts 'missing args'
when /export file/
puts 'nothing to export'
when /^export\s$/
puts 'missing args'
when /^exit/
exit
end
end
end
end
end
Shell::CLI.new
After thinking a while, the solution was very simple:
comp = proc do |s|
if Readline.line_buffer =~ /^.* /
COLLECT.grep( /^#{Regexp.escape(s)}/ )
else
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
end
Now I just need to turn it into something more flexible/usable.

Resources