プログラマ・アゲイン blog

還暦を過ぎたけどプログラマ復帰を目指してブログ始めました

自分なりのRubyプログラムの構造

Rubyのプログラムを開発するにあたり、先ずは仕様として大まかな開発スタイルを決めました。

というのも、他の人が開発したRubyプログラムを見た時、余りにもオブジェクト指向が強すぎて読みずらかったのと、自分のレベルではそのコーディングはできないと感じたからです。

そこで、後から読んでも分かり易く、バグった時に追いかけやすく、シンプルだけど冗長でない、自分のレベルにあったプログラム構造とコーディング・スタイルを決めました。

ただ、これはあくまでも私の理解レベルでの仕様なので、Rubyプログラムとしては適正でないとか、こういうコーディングは効率が悪いとか、いろいろご指摘事項はあろうかと思いますが、そこはご容赦ください。

また、私のレベルが上がれば、当然この仕様は見直されると思います。

 

プログラム構造

プログラム・モジュール

2種類のプログラム・モジュールで構成することとしました。

ここで言うプログラム・モジュールとは、.rb のファイルの事を指しています。

それぞれの位置づけは、以下のようになります。

メイン

メイン・モジュールは、以下のようにRubyの実行環境から最初に呼び出されるモジュールです。

 ruby メイン・モジュール名.rb 引数 ・・・

以下の内容を含みます。

  • メイン・ルーチン
  • require
  • logファイルのオープン/書込み/クローズ
  • 全体で使用するファイル(例:Excel)のオープン/作成/書込み
  • クラス・オブジェクトの作成
  • クラス・メソッドの呼び出し

このメイン・モジュールの、メイン・ルーチンの中で処理の流れを制御します。

実際の処理はクラス・メソッドに任せるので、メイン・モジュールからは処理の流れに従ってクラス・メソッドを呼び出すだけです。

ライブラリー

ライブラリー・モジュールは、独立したオブジェクトのイメージで、メイン・モジュールから呼ばれる以下のものを含みます。

  • クラス
  • 関数

1クラス 1モジュール、複数関数 1モジュールとします。

1クラス 1モジュールにすると、モジュール数(.rbファイル数)が多くなるので管理が大変ではないかと思われますが、バグった時に該当のクラスを見つけやすいのが利点です。

また、1クラスは 1処理データの単位に紐づけるので、処理データからもどのライブラリー・モジュールの問題かわかりやすくなります。

複数関数 1モジュールとしたのは、複数関数と言っているものが、いろいろなところで使われる共通関数だからです。関数の内容によっていくつかのモジュールに分けることも考えられますが、数が多くなかったので 1モジュールにまとめました。

 

メインとライブラリーの配置

メイン・モジュールとライブラリー・モジュールを配置するディレクトリーですが、基本的には分けることとします。

理由としては、後からディレクトリーを見た時に、メイン・モジュールなのかライブラリー・モジュールなのかがハッキリするからです。

例えば、

 メイン          :c:/ruby/proj1

 ライブラリー:c:/ruby/proj1/lib

のように、メイン・モジュールのディレクトリー直下に .\libディレクトリーを作って、そこにライブラリー・モジュールを配置します。

ただ、ライブラリー・モジュールはメイン・モジュールに依存しているわけではないので、ライブラリー・モジュールのディレクトリーをどこに作るかはその時次第かもしれません。

 

コーディング・スタイル

メイン・モジュール

メイン・モジュールのコーディングは、以下のようなパターンで行います。

#! ruby -Ku
# get directory list and count the number of sources and put excel sheet Version 2
# c:\DATA\Web_study\ruby\source-count>ruby count-number-source.rb xxxxxxxx (xxxxxxxx : directory)

require 'open3'
・・・
# ユーザー・ライブラリー
require './lib/put-dirlist.rb'
・・・
# Dir[File.expand_path('..', __FILE__) << '/lib/*.rb'].each do |library|
#   require library
# end

log = LogFile.new
log.log_info("count-number-source : count-number-source has started")
・・・
# MAIN routine
begin
・・・
rescue => exception
・・・
ensure
・・・
end
log.log_info("count-number-source : count-number-source has ended")
log.log_close
1行目

#から始まる行は、コメント行です。

この1行目の書き方「#! ruby -Ku」は、お作法のようなものだとよく言われます。そのまま書けばよいと。

意味としては、`#!` に続く文字列が `ruby` という文字列を含む行を見つけたら、Rubyインタプリタはその行以下を Ruby スクリプトとして実行することを宣言しています。

そのあとに続く「-Ku」は、あまり意味はないかもしれません。一応の意味としては、rubyコマンドライン文字コードを指定するオプションです。

-Kに続く文字[kcode]で文字コードを指定し、以下のものがあります。

kcode 文字コード
s or S Shift_JIS
e or E EUC-JP
u or U UTF-8
n or N NONE(ASCII)

因みに、Rubyはソースの解析を始める前に、マジックコメント(例えば、coding: utf-8)を読み取って、スクリプトエンコーディング(簡単に言うとソースコード文字コード)を決定します。このマジックコメントは、ソースコードの最初の行に記述しなければいけません。ただし、「#! ・・・」で始まっている場合には、2行目に記述します。また、UTF-8ソースコードであれば、マジックコメントは必要ありません。

2行目

2行目もコメント行ですが、このプログラムの処理概要を超簡単に記述します。

3行目

3行目もコメント行で、このメイン・モジュールを起動する時のコマンドを記述します。特に、引数については、簡単に意味を( )で記述しておきます。

時間がたってから再度このメイン・モジュールを実行しようとした時、実行の仕方を忘れていることが多いのと、コマンドを手入力するのが大変なので、このコメント行を入れます。

4行目~11行目

requireメソッドを使って、必要なライブラリーを読み込みます。

ユーザー・ライブラリーの読み込みについては、7行目のように個別にファイルを指定することもできますし、9行目から11行目のように、./libディレクトリー配下の .rbファイルを全て読み込むという書き方もできます。

12,13,23,24行目

logは、実行時の進捗や途中の処理状況などを必ず記録するようにします。

メイン・ルーチンの前で作成し、終了時にクローズします。

以下のような形でログされるので、後で実行状況や時間などを確認することができます。

I, [2021/08/19T13:05:31#10244] INFO -- : count-number-source : count-number-source has started

15行目~23行目

メイン・ルーチンを記述します。

今回作成したRubyプログラムでは、Excelファイルへの書き出しを行うのがほとんどなので、途中で障害が発生しても例外処理が続けられるように、begin ~ rescue ~ ensure ~ end文を使用します。

beginで、本来実施したい処理をを記述します。

rescueで、例外が起こった場合の処理を記述します。

ensureで、例外の有無に限らず実行される処理を記述します。

 

ライブラリー・モジュール

ライブラリー・モジュールのコーディングは、以下のようなパターンです。

# put directory list to print or txt file or Excel file
class DIRLIST
  def initialize(target_dir,log)
    ・・・
  end

  # ファイルの一覧を、サブディレクトリーも含めて出力するメソッドの定義
  def put_list
    ・・・
  end

  # ファイルの一覧を、txtファイルに出力するメソッドの定義
  def put_file(dir_file)
    ・・・
  end

  # ファイルの一覧を、Excelファイルに出力するメソッドの定義
  def put_excel(worksheet)
    ・・・
  end
end
1行目

1行目には、このクラスで行う処理の概要を超簡単に記述します。

2行目

class文で、クラスの宣言をします。クラス名は必ず大文字で始めないといけないので、全て大文字とすることにします。

3行目~5行目

initializeメソッドは特別なクラス・メソッドで、newメソッドでクラス・オブジェクトを生成した時に呼ばれます。

その時、newメソッドに渡した引数がそのまま渡されるので、クラス・オブジェクトにとって必要な初期化の処理を行います。

この初期化の処理で、クラス・インスタンスとして、処理対象データを読み込んでインスタンス変数にセットします。

6-9、10-13、14-17行目

これらは、クラス・メソッドを定義します。

18行目

最後に、忘れないようにclass文のendを記述します。

 

 

今後、作成したRubyプログラムの各処理のコーディングの仕方について、個別にブログにまとめていきたいと思います。