ポンコツ・キャンプ-シンプルなコマンド入力プログラム
世間では電子帳簿保存法の対応が迫られ、当社でも対応せねばと巷のアプリを検討することになりました。案外、使い易そうなアプリもあったのですが、全てサブスクリプション。。。毎年、永久に金をむしり取られる仕組みのようです。数年後、データが溜った後に料金の改定があったらどうなるのでしょう?悲惨な結果しか待っていないような気がします。
仕方なく自作することにしました。
幸いにも中々良いものが出来ました。
現在、稼働中です。
DOS画面上で、コマンド入力で動くソフトです。
Rubyで作ったら、とてもすっきりと仕上がりましたので、コマンドラインで駆動する部分を抜き出して、アップします。
ベテランの方からすれば、あっそ。ぐらいの物ですので、初心者向けの内容だと思って下さい。
3つのファイルで構成されています。
単純な作りになっていますが、意外に使い出があります。
main.rb
# frozen_string_literal: true
require_relative 'command_parser'
require_relative 'commander'
# -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# -+-+-+ Main routine -+-+-+
# -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
commander = Commander.new
parser = CommandParser.new(commander)
until parser.exit_command?
print "\n\ninput command => "
parser.parse(gets.chomp)
end
command_parser.rb
# frozen_string_literal: true
class CommandParser
def initialize(commander)
@commander = commander
@command = nil
end
def parse(demand)
command, _, options = demand.strip.partition(' ')
@command = command.empty? ? Commander::ENTER : command.downcase.to_sym
options = options.split(',').map(&:strip)
return print "\nunknow command: #{@command}" if @commander.incorrect?(@command)
@commander.method(@command).call(options)
end
def exit_command?
@commander.exit_command?(@command)
end
end
commander.rb
# frozen_string_literal: true
class Commander
ENTER = :enter
def initialize
@help_message = [
"\n --- exit - 保存して、終了",
"\n --- quit - 終了",
"\n --- save - 保存",
"\n",
"\n --- help - help表示",
"\n --- <enter> - nextと同じ",
"\n",
"\n --- top - 最初のページを表示する",
"\n --- bottom - 最後のページを表示する",
"\n --- next [n] - 次のページを表示するか、nページ先のページを表示",
"\n --- previous [n] - 前のページを表示するか、nページ前のページを表示",
"\n --- page n - ページn番を表示する",
"\n",
"\n --- select [kind], [price], [customer], [since date], [before date]",
"\n - 表示を絞り込む",
"\n kind: 文書の種類",
"\n price: 価格",
"\n customer: 顧客名",
"\n since date: yyyy-mm-dd以降",
"\n before date: yyyy-mm-dd以前"
]
@command_list = nil
end
def command_list
@command_list ||= @help_message.map { |line| /--- <*([a-z_]+)>* /.match(line).to_a[1] }
.compact
.map(&:to_sym)
end
private :command_list
def exit_command?(command)
command_list[0, 2].include?(command)
end
def incorrect?(command)
!command_list.include?(command)
end
def exit(_)
p 'exit'
end
def quit(_)
p 'quit'
end
def save(_)
p 'save'
end
def help(_)
print @help_message.join
end
def enter(_)
self.next('1')
end
def top(_)
page('1')
end
def bottom(_)
p 'bottom'
p '何等かの方法で、max_page_sizeを取得して、page(max_page_size)で飛ばす'
end
def next(n)
n, = n
p 'next'
p n
end
def previous(n)
n, = n
p 'previous'
p n
end
def page(n)
p 'page'
n, = n
p n
end
def select(options)
kind, price, customer, since_date, before_date = options
p 'select'
p kind
p price
p customer
p since_date
p before_date
end
end
簡単な説明
main.rb
... 終了コマンドが発行されるまで、コマンドを実行しながら、
グルグル回り続けさせています。
:exit, :quitに出会うまで、回り続けます。
command_parser.rb
... 入力されたコマンドを解析して、Commanderに投げています。
parseメソッド内で、commandとoptionsに分離しています。commandは、シンボル化し、optionsは、配列に分解しています。 @command.command_listに無いcommandはタイポとして表示されます。 チェックを通過したら、@commanderにメッセージを送ります。 @commander.method(@command).call(options)のお蔭で、膨大なcase文を 管理しなくても良くなるのでこの表記は便利だと思われます。
commander.rb
... 個々のコマンドを実行します。
@help_messageは、他の記述の仕方もあるのでしょうが、個人的な好みで 配列に納めています。@command_listは、全てのコマンドのリストです。
@help_messageからコマンドを抽出して、commanderをCommandParserに送っています。これによって、コマンドの管理はCommnanderのメソッドと@help_message内の記述のみになり、コマンド拡張などの場合のメンテナンスの軽減を図っています。
実行
3つのファイルを同じディレクトリにいれて、ruby main.rbで動きます。
まずは、helpとタイプしてみましょう。
登録してあるコマンドを色々試してみたり、間違ったコマンドも試してみて下さい。
ではでは。
この記事が気に入ったらサポートをしてみませんか?