ゆるおたノート

Tomorrow is another day.

【VBA】自分用コーディングガイドライン

自分用のツールだけでなくお仕事としてVBAを書くことが増えてきたので、自分用のコーディングガイドラインを作ってみました。

勉強中のため、全部は固まっておらずところどころ説明が空欄だったり例が適当だったりしますが、「今のところ」の話として公開します。
今後も随時更新予定です。

このページについて

想定読者

  • 最近は作ったツールを誰かに渡すことが多いため、その点を念頭に置いて作成しています。
    作成者自身で使用する場合や、熟練者同士のコーディングなどにはそぐわない部分もあるかもしれません。あらかじめご了承ください。

  • 他の誰かが作ったものを改修する場合は、当ページの内容より作業中のルールを優先することとします。

優先度

各項目の【】の部分は、下記の順に優先としています。

優先度 意味
MUST 必ず守る
BETTER 迷ったらこっち。絶対ではない。
WISH 検討中・勉強中。

クックパッド社のコーディング規約を参考にさせて頂きました()。

ただ、まだコーディングに自信があるわけではないのでSHOULD(~すべき)ではなくBETTER(~した方が良い)としています。笑

考え中のこと、疑問点など

勉強中の身なので、まだ自分の中で確立できていないこともあります。

そのような部分には、🤔(考えていること)としてコメントを記載します。

全体

【MUST】プロシージャは1画面に収める

特別な理由がない限り、80字程度×20~30行程度までにする。

  • 1行が長くなる場合は、改行(_)を積極的に使用する。
    (並行して、コードの縦のラインが揃うように必ずレイアウトに気を使うこと。)
  • 処理の分岐等でこれを超える場合は、プロシージャの切り出しを検討する。

【MUST】省略せずに明記する

  • 省略形は極力使わず、単語を明記する。
    なお、プログラミング用語(特にVBA)として普及していて、運用担当者が一瞥で誤読なく識別できるものであれば、使用しても良い。

  • ビジネス用語などの所謂「横文字」の言葉も、可能な限り別の表現に言い換える。

    • 可読性が運用担当者のバックグラウンドに左右されやすいため。
    • 間違いなく共通認識があるものは、この限りではない。
  • インテリセンス(入力補完)を積極的に使用することとし、一般に普及している語句であれば~ation系の長い単語も使用可とする。

【BETTER】結論ファースト

  • 結果として出力したいものを最初に書き、段落(コードブロック)の要約の意味も持たせる。

    • 目的の変数
    • 補助的な変数
    • 補助的な変数…
    • 中間処理
    • 中間処理
    • 中間処理…
    • 結果
▼例1
Dim shAbc As Worksheet                    '目的の変数
Dim baseBook as Workbook                  '補助的な変数(一瞬しか使わないもの)
set baseBook = ThisWorkbook               '中間処理
set shAbc = baseBook.Worksheets("シート名") '結果
▼例2
'メイン処理 ─────────────────
Sub Main()
    Call SubProcess1
    Call SubProcess2
End Sub
 
'中間処理1 ─────────────────
Sub SubProcess1()
 
    Dim 変数 As 型
    変数 = 中間の中間1
 
    Call 中間の中間2
 
End Sub
 
Private Function 中間の中間1()As '処理
End Function
 
Private Sub 中間の中間2()
    '処理
End Sub
 
'中間処理2 ─────────────────
Sub SubProcess2()
    '処理
End Sub

【BETTER】それ自体に意味を持たない単語を避ける

(日本人が)イディオムとして認識しやすい言葉を除き、その単語自体で意味を判別できない単語は、可能な限り使用しないようにする。

    • 動詞

      • do
      • make
      • take
      • have
      • set
      • get
    • 形容詞・名詞

      • target

【BETTER】数値の表現

単語 使いどころ
Count 数え上げる時
Number ~の番号、数値の計算

🤔(Number of ~はどうする?)

【BETTER】前置詞

  • 識別子1つあたり1つまでとする。
    A of Bは、B Aと表現できる(ことが多い)ので、言い換えが出来ないか検討する。

  • プロシージャやメソッド名などの末尾に前置詞を置くと、英文として読みやすくなる(気がする)。

コメント

コメント

【MUST】読めば分かることは書かない
  • コードは主に英語を使用するが、コメントが「ただの翻訳」になってしまう時は、そもそも変数名やプロシージャ名を変更した方がスマートになることが多い。

  • 数カ月後の自分が「コードを読むだけで処理の内容を把握できるもの」を目指すこと。

【MUST】操作の意図を示す
  • 同じ結果を得るのに複数の選択肢がある場合や、何らかの処理を飛ばすような場合には、「なぜそうしたのか」を残すこと。
    (後で考慮漏れを検証しやすくするため)
【BETTER】要約コメント
  • 複数行に渡る処理には、段落分けの意味で要約コメントを付与してもよい。
    (併せて処理の区切りごとに空行も必ず挟むこと。)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 「何が起こるとエラーになるのか」を記す。

  • 資料として、ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

ドキュメンテーション・コメント

【MUST】プロシージャごとに記載する
  • 再利用しやすくするため、小分けしたプロシージャごとにコメントを記載していく。

🤔(全部に書くのは冗長かな…?)

【MUST】書式

下記の書式とする(不要なものは削除しても良い)。

''
' ●概要●
'
' @note
'   ●注意事項●
'
' 【参照設定】
' [Name] ●●●●
' [note] ●●●●●●●●●●●●
'
' 【参考】
' ●記事名● | ●サイト名●
' ●URL●
'
' 【参考】
' ●著者●(●発行年●)『●タイトル●』●出版社●
' ●URL●
' 第●版第●刷(p.●●●)
'
' @param {●型●} name ●概要●
' @param {●型●} name ●概要●
' @param {●型●} name ●概要●
'
' @return {●型●} ●概要●
'
' @customfunction ●概要●
'

(記号が多いのは、個人的に書き忘れ・消し忘れを防ぐためです。)

【BETTER】必要なデータの例示
  • 「何」が「どうなっている」と「どう出るのか」を例として記す。
    (後で考慮漏れを検証しやすくするため)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 前項に関連して、「何が起こるとエラーになるのか」を記す。

  • ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

【BETTER】5W1Hの明記

曖昧な表現になりそうな時は、省略せずすべて記載する。

5W1H 意味
When いつ
Where どこで
What 何を
Who/Whom 誰が/誰に
Why 何のために
How どのように

プロジェクト

【WISH】単一プロジェクトのプロジェクト名

🤔(VBAProjectのままで良い?)

【WISH】複数プロジェクトのプロジェクト名

(勉強中)

モジュール

全体

【MUST】記法

シートモジュール(後述)を除き、パスカル記法とする。

▼例
  • OpenWorkbook
  • ImportRawData
  • SheetFormatter

標準モジュール

【WISH】オブジェクト名

(考え中)

ブックモジュール

【MUST】単一プロジェクトのオブジェクト名
  • ThisWorkbookのままとする。
【BETTER】複数プロジェクトのオブジェクト名

(勉強中)

シートモジュール

【MUST】オブジェクト名
  • sh<シート名>とする。
    ※オブジェクトブラウザでのフォルム統一のため、接頭辞をshとする。

クラスモジュール

【MUST】オブジェクト名
  • 名詞形とする。
    ~er~istなど、「~するもの」といった表現だと尚良し。

ユーザーフォーム

【WISH】オブジェクト名

(勉強中)

プロシージャ

全体

【MUST】記法

すべて「キャメルケース記法」とし、スコープにより1文字目を変える。

▼例
スコープ 1文字目
パブリック 大文字 OpenBook
プライベート 小文字 sumPopulation
【MUST】求める結果 > 処理の内容

実際の処理はプロシージャの中に閉じ込めることにして、プロシージャ名は「どんな結果になるか」が読み取れる名前を優先する。

【MUST】ネストは3つまで
  • WithブロックSelect Case文を含めて、ネストは3つまでとする。

  • ネスト1階層あたり1段のインデントとする。

  • ネストが深くなる時は、関数化や早期リターン、ガード節を積極的に使用すること。

【MUST】早期リターン/ガード節の推奨

n重ループや処理の分岐などは、If文と組み合わせて改善すること。

▼例(複数条件)
Sub improveIfNests()

    If isAbc() Then Exit Sub
    If Not d = e Then Exit Sub
    If f = g Then Exit Sub
 
    'すべてFalseのときの処理
 
End Sub
▼例(ループのスキップ)
Sub improveForNestsBySkip()
 
    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then goto Continue
 
        '繰り返し処理
 
Continue:
    Next i
 
End Sub
▼例(ループの抜け出し)
Sub improveForNestsByExitFor()
 
    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then Exit For
 
        '繰り返し処理
 
    Next i
 
End Sub
▼例(n重ループの抜け出し)
Sub improveForNestsByBreak()
 
    Dim cell As Range
    Dim i As Long
    For Each cell In searchRange
        For i = 0 to Ubound(array1d)
            If cell.Value = 1 Then goto Break
 
        Next i
    Next cell
 
Break:
    '次の処理
 
End Sub
【BETTER】「~にする」という表現

make ~などの表現は、1語で言い換えられないか検討する

  • 参考

    接頭辞・接尾辞 意味 使いどき
    ~ize <名詞>化する、<名詞>にする
    ~fy <名詞>化する、<名詞>にする
    en~ より<形容詞>にする
    make~ ~にする 上記どれも一般的な単語がない場合
【BETTER】「~を行う」という表現

do ~take ~などの表現は、1語で言い換えられないか検討する

【BETTER】補助関数
  • スニペットなどに登録する際は、抽象的な名前も可とする。
  • 実際にコード中でヘルパー関数として使用する時は、「何がどう変わるのか」を(可能な限り)明示した名前に変更する

Subプロシージャ、Functionプロシージャ

【MUST】命名

動詞始まりの1~5語程度の語句とする。

  • 文型

    • V(動詞)
    • VO(動詞、目的語)
    • VOO(動詞、目的語1、目的語2)
    • VOC(動詞、目的語、補語)
  • GAS(ないしJavaScript)のメソッド名も参考とする。
    (英文として平易な表現が多い(気がする)ため。)

【MUST】条件を判定する関数
  • 原則、下記の命名規則とする。

    • is~
    • can~
    • has~
  • If文で呼び出すときに肯定文となるように書く。

If is確認すること()[ = True] Then

🤔(中止の条件判定の場合は、否定形の形容詞の方が自然かも?)

▼例
  • bad
Sub Example()
    If checkPathWhetherValid() = False Then Exit Sub
 
    '処理
 
End Sub
 
Private Function checkPathWhetherValid() As Boolean
 
    '条件の判定処理
 
End Function
  • good
Sub Example()
    If isInvalidPath(sourcePath) Then Exit Sub
 
    '処理
 
End Sub
 
Private Function isInvalidPath(ByVal sourcePath As String) As Boolean
 
    '条件の判定処理
 
End Function

プロパティプロシージャ

【MUST】命名

名詞句とする。

【BETTER】プロパティの接頭辞を統一する
  • 接頭辞

    接頭辞 命名 意味
    なし プロパティ名 プロパティ名
    p pプロパティ名 プライベートレベルのモジュール変数
    a aプロパティ名 プロパティプロシージャの引数
  • 名前を揃えることでプロシージャごとの置き換えがしやすくなり、フォルムも揃えやすい(はず)。

▼例1
Private pPropName AsProperty Get PropName(ByVal arg1 As, _
                      ByVal arg2 As) As 型
 
    PropName = pPropName(arg1, arg2)
 
End Property
 
Property Let PropName(ByVal arg1 As, _
                      ByVal arg2 As, _
                      ByVal 代入値 As)
 
    pPropName(arg1, arg2) = 代入値
 
End Property
▼例2

ワンライナーの場合。
(横に長くなりやすいので、出来るだけ避ける。)

Private pPropName AsProperty Get PropName(ByVal arg1 As, ByVal arg2 As) As 型:               PropName = pPropName(arg1, arg2): End Property
Property Let PropName(ByVal arg1 As, ByVal arg2 As, ByVal 代入値 As): pPropName(arg1, arg2) = 代入値:     End Property

変数/定数

全体

【MUST】記法

定数のみ「スネークケース記法」とし、それ以外はすべて「キャメルケース記法」とする。

なお、VBAではハイフン-を識別子として使用できない*1ので、必要な場合はアンダースコア_等で代用する。

▼例
種類
定数 MAX_SHEET_COUNTS
AUTHOR_NAME
それ以外 abcBookName
stopTime
【MUST】スコープごとに1文字目の大文字/小文字を統一する
▼例
スコープ 1文字目
パブリック 大文字 AppointList
プライベート 小文字 appointList
【MUST】数値の型を統一する
数値
整数 Long型
小数 Currency型
【MUST】変数名は3語まで
  • 可能であれば2語。
  • 動名詞~ing、動詞の名詞形~ationなどを積極的に使用すること。
【BETTER】型は変数名ではなく宣言に任せる
  • メソッド、関数で使用するものなど、文脈で型が分かるものは型名を変数名に付与しなくても良い。
▼例
  • not worse
Dim objAbcBook As Workbook
  • better
Dim abcBook As Workbook
【BETTER】似たものを対で扱う時は、名前やフォーマットを寄せる
  • 対になる変数を扱う時や型によって操作を分けるような時は、型名を明示した方が読みやすくなることもある。

  • この場合は、コードのフォルムを整えられるように(可能な限り)文字数を統一すること。
    (一瞥で差異を見つけやすくなるため)

▼例1
  • not worse
Dim sourcePath As String
Dim destinationPath As String
  • better
Dim srcPath As String
Dim dstPath As String
▼例2
  • not worse

    🤔(例が微妙…)

Dim wbAbcBook As Workbook
Dim nameAbcBook As String
Dim pathAbcBook As String
  • better
Dim abcBookObj As Workbook
Dim abcBookName As String
Dim abcBookPath As String

変数/定数

【BETTER】配列やコレクションの命名
  • 原則、複数形を使用する。

  • 単数形と複数形の区別がない単語や、複数形にするとかえって読みづらい語句の場合は、~Array~Collectionも可とする。

オブジェクトの定義・宣言

【BETTER】固有オブジェクト型の推奨
  • Object型ではなく、各オブジェクト固有の型で宣言すること。
    (インテリセンスが適用され、コーディングのスピード、可読性ともに向上するため。)

    • Workbook
    • Worksheet
    • 独自クラス、など
【WISH】参照設定したライブラリのオブジェクトを使用する時は、条件付きコンパイルを添える
  • ユーザーの利用環境が挙動に影響する場合にマクロが使えなくなってしまうことを防ぐ。
    ExcelからOutlookを操作する場合など)

列挙体

「変数・定数の中に入れるべきか」というのはあるけど、用途が似ているので…

【MUST】型名の接尾辞はeとする

enumeratione

【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

【BETTER】引き数の選択肢として列挙体を使う
  • モードの切替など、プロシージャの引き数の選択肢として使うと、インテリセンスが使えるようになり少しだけコーディングが楽になる。
▼注意!
  • 列挙体は宣言セクションにしか宣言できない。
    つまり、呼び出し元のプロシージャと距離が離れてしまうことが多いので、出来るだけモジュール1つにつき2~3個までに抑える。

  • VBAの仕様により、クラスモジュールなどではパブリックレベルでないと使えない。
    他のモジュールのものと名前がバッティングしないように確認しながら使用する。

▼例
Public Enum eBookType
    InputBook
    SharedBook
    TemplateBook
End Enum
 
Sub Main()
 
    Call openBook(eBookType.InputBook)
 
End Sub
 
Private Sub openBook(ByVal bookType As eBookType)
 
    Dim book As Workbook
    Dim bookPath As String
    bookPath = "c:\\abc.xlsx"
    Select Case bookType
        Case eBookType.InputBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True
 
        Case eBookType.SharedBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=True, _
                                      IgnoreReadOnlyRecommended:=False
 
        Case eBookType.TemplateBook
            bookPath = "c:\\abc.xltx"
            Set book = Workbooks.Open Filename:=bookPath, _
                                      Editable:=True, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True
    End Select
 
End Sub

構造体

「変数・定数の中に入れるべきか」というのはあるけど(ry

【MUST】型名の接尾辞はtとする

typet

【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

参考

以下を参考にさせて頂きました。

記事・サイト

書籍

注釈

*1:「ケバブ形式」なる記法があるらしい(美味しそう…)

【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~

スクリプトをGitでも管理できるように準備していきます。

なお、以下Git自体は既に端末にインストール済み
かつGitHubのアカウントも作成済みであると想定して、説明します。

Git管理を始める

フォルダ構成

現在

ここまで順番通りに設定されている方は、現在下記の状態だと思います。

ローカル開発のフォルダ構成

目標

ルートフォルダリポジトリを作成して、作業フォルダと同じ階層にGit関係のファイルを作成します。

作業フォルダと同階層にGit関係を置く構成にする

リポジトリの作成

  1. プロジェクトのルート・ディレクトリに移動

    cd <プロジェクトのルート・ディレクトリ>
    

    画像の例では、ルートフォルダに移動します。

    cd ルート
    

  2. リポジトリを初期化

    Git管理を始めます。

    git init
    

除外ファイルを作成・記入

除外ファイルを作成

.claspignoreと同じ手順で、.gitignoreというファイルを作成します。

なお、.gitignore自体は、基本的にプロジェクトのルートフォルダ以下にあればOKです。

`.git`フォルダ直下に`.gitignore`を作成する

除外ファイルを記入

.claspignoreと同じように、ファイルの更新をGitで追跡しないものを記入しておきます。

JSONファイルを除外

リモートリポジトリをPublicで作成する場合は、JSONファイルもアップロードしてしまうと、スクリプトIDやら色んな情報が全世界に公開されてしまいます。

ちょっと怖いので、基本的には各種設定に使用するJSONファイルを拡張子ごと除外して、appsscript.json等の実行に必要で個人情報を含まないファイルだけを残すことをオススメします。

# 個人情報を含む可能性があるため。
*.json

# スクリプトの実行に必要なため。
!appsscript.json
Google関係のファイルを除外

ドライブファイルストリームを使用していてプロジェクト内にGoogle関係のファイルがある場合、Gitではエラーになります。
こちらもあらかじめ拡張子ごと除外しておきます。

# 拡張子ごと除外
*.gs
*.gscript
*.gsheet
︙
詳しい使い方

その他、書き方は基本的に.claspignoreと同じですが、.gitignoreの詳しい使い方は下記の記事が参考になるかと思います。

.gitignore の書き方 - Qiita

README.mdを追加

とりあえずタイトルだけ…

「何をするプロジェクトか」が分かるように、とりあえずタイトルだけつけておけばOKです。
初期設定ではルートフォルダの名前が反映されるはずです。

冒頭にバッジを追加

冒頭に下記も書いておくと、「このプロジェクトではClaspを使っています」のしるしになって、他の方がこのプロジェクトを使用する際に準備しやすくなる*1と思います。
あと、「わたしGitHub使ってます!」感が出ます。笑

[![clasp](https://img.shields.io/badge/built%20with-clasp-4285f4.svg)](https://github.com/google/clasp)
  • サンプル

    「Clasp使用中」を表すバッジ

一旦、記録する

初期設定が完了したら、一旦ここまでの情報を記録しておきます。

# リポジトリ内のファイルを(除外ファイル以外)すべてステージに上げる
git add .

# ステージに上げた編集内容を記録する
git commit -m 'init.'

# リモートリポジトリに接続
git remote add origin <リモートリポジトリの接続用アドレス>

# リモートリポジトリにプッシュする
git push origin main

ローカルで編集・記録の繰り返し

あとは、いつも通りスクリプトを編集して、ステージ、コミット、プッシュしていきます。

Gitの使い方

Git自体の使い方については、下記が参考になるかと思います。

サル先生のGit入門〜バージョン管理を使いこなそう〜【プロジェクト管理ツールBacklog】

Gitに関連する記事 | WWWクリエイターズ

また、手前味噌ですが上記を参考に勉強した記録も載せておきます…

【Git勉強中】操作に慣れてきたので、流れを整理してみました。 - ゆるおたノート

ブラウザのエディタでもGit管理する

以降、基本的にはローカルのエディタでスクリプトを書いていきますが、どうしてもブラウザ側で編集が必要になった時にブラウザからもGitHubにプッシュできるよう準備しておきます。

拡張機能をインストール

Google Apps Script Github アシスタントという拡張機能を使用します。

Git管理しやすいように設定する

少し設定を変更して、ローカルのリポジトリとスムーズに連携できるようにします。

GitHubのアカウントでログイン
  1. ログイン画面を開く

    関数を選ぶボタンの隣あたりにLogin SCMというボタンが表示されるので、それをクリックします。

  2. GASの管理で使用するGitHubアカウントでログイン

フォルダをローカルの作業フォルダと同じ構成にする

作業フォルダ(ここではsrcフォルダ)も含めて、同じ構成でファイルを作成します。

ローカルの作業フォルダと同じ構成にする

前述のように既にローカルからGitHubにプッシュしてある場合は、拡張機能Repository(もしくはRepo: ~)→使用するリポジトリの順にクリックして、プルすればOKです。

作成したリポジトリからプルしておく

プッシュする拡張子を変更

初期設定だと.gsのファイルがプッシュされます。
しかし、ローカルからは.jsファイルをプッシュするので、このままでは同じリポジトリ内で別々のファイルをGit管理している状態になってしまいます。

こうなると、ブラウザ側からGitHubにプッシュした.gsファイルをローカル側ではClaspでクローン→.jsに変換してから編集、という二度手間が都度発生したり、コンフリクトの原因になったりするので、始めから拡張子を統一しておきます。

  1. 設定画面を開く

    歯車マークをクリックします。

    歯車マークをクリック

  2. 拡張子を変更して保存

    File type to sync.jsを選択して、Saveをクリックします。

    拡張子を変更し、Saveをクリック

ブランチは検討中…

私自身Gitにあまり慣れてないのもあり、ブランチの使い方は検討中です…

今はmasterブランチ*3に直上げしています。
基本的にはローカルからGitHubに上げて、緊急事態の時だけブラウザで編集する、という流れになると思うので、とりあえずは充分かなと…*4

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

連載目次

  1. 【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 当記事【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

参考

注釈

*1:このバッジには無いですが、GitHubではプロジェクトで使用している言語やライブラリのバージョン等が掲示されることが多いようです。
他の言語だと、プロジェクトを初期化した時点で依存ライブラリ等のバージョンを自動でREADMEに記入しておいてくれるものもあります。

*2:約1年前の記事で、既に情報が古いものもありますが…

*3:今後はmainブランチですね!

*4:Gitに慣れている方であれば、browserとか分かりやすい名前で作成して後でマージする、という運用もアリかもしれません。

【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~

実際に開発を始めます。

ローカル開発の開始

pushしてみる

ローカルでスクリプトを少し編集して、GASにアップロードしてみます。

  1. ローカルエディタでスクリプトを編集する

    const myFunction = () => {
      const m = "ローカル開発の練習中。"
      
      console.log(m);
    }
    

  2. ターミナルを開き、Pushする

    clasp push
    

  3. appsscript.jsonを編集した場合は)GASに同期

    過去記事のようにローカル側でappsscript.jsonを編集した場合、ここで下記のように確認が入ります。

    ? Manifest file has been updated. Do you want to push and overwrite? (y/N)
    

    マニフェストファイルが更新されています。(GASにも)アップロードして上書きしますか?

    マニフェストファイルとは、(超ざっくり言うと)このプロジェクトの設定ファイルのことです。
    ここではappsscript.jsonを指します。

    ローカルでこれを編集した場合、GAS側にもこの情報を同期する必要があるので、ここではyと答えてEnterします。

  4. Push結果を確認

    同期が完了すると、同期したファイルの一覧が出力されます。

    # 入力
    clasp push
    
    # 出力
    └─ src/appsscript.json
    └─ src/code.js
    Pushed 2 files.
    

    今回はsrcフォルダにあるappsscript.jsoncode.jsが同期されたことが分かります。
    ここでファイルに過不足がある場合は、このスクリプトのルートフォルダの設定が間違っていないか、改めて確認してみてください。

同期できたか確認してみる

ブラウザでスクリプトファイルにアクセスして、Pushしたファイルがちゃんと同期できているか確認してみます。

  1. Chromeを複数ユーザーで運用している場合は)エディタを使うユーザーとしてChromeのウィンドウをアクティブにしておく

    このあと、直近に開いたユーザーのウィンドウを開き、GASのエディタにアクセスします。

    しかし、Claspにはログインに使用したGoogle Workspaceのアカウントが紐付きますが、ブラウザのユーザーはClaspに紐付きません
    そのため、ターミナルからブラウザを開く際にAlt+Tabなどを使って都度ウィンドウを切り替える必要があります。少しめんどくさいですが…

    Windowsの場合、指定のウィンドウをターミナルからアクティブにするコマンド自体は無いみたいですが、スクリプトを別途自作して代替することはできるようです。

  2. ターミナルからブラウザを開く指示を出す

    Claspに登録しているGoogleアカウントで、GASのエディタが開きます。

    clasp open
    

  3. ブラウザでスクリプトを確認

    さきほどと同じスクリプトがブラウザ側のエディタに同期されていることが分かります。
    GASに同期できている
    ここで同期できていない場合は、F5で画面の更新を試してみてください。

  4. マニフェストファイルも確認

    画面左上の表示マニフェストファイルを表示で「マニフェストファイル」を表示します。
    「マニフェストファイル」を表示
    スクリプトと同様、ブラウザ側に同期されていることが分かります。
    マニフェストファイルも同期できている

  5. スクリプトを実行

    画面をスクリプトに戻して、Ctrl+Rで実行してみます。
    スクリプトに戻って実行

  6. スクリプトの実行ログを確認

    画面左上の表示実行数でログの一覧を表示します。 ログの一覧を表示
    少し待ってから実行した関数をクリックしてみると、コンソールの出力もいつも通り記録されています。
    ログも記録できている

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

次回

【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

連載目次

  1. 【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 当記事【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

参考

【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~

プロジェクトやエディタの設定を確認して、楽にコーディングできるよう調整します。

GASにPushしないファイルを設定

プロジェクト内に、README.mdなどの「ClaspでPush出来ないファイル」がある場合に使用します。

特に無い場合は次へ

.claspignoreの作成

[目標]フォルダ構成

赤字のファイルを作成します。
`.clasp.json`と同じ階層に`.claspignore`を作成する

Bashの場合
  1. ターミナルで.claspignoreを保存したいフォルダに移動

    cd <テキストファイルのあるフォルダ>
    

    今回の例ではルートフォルダに移動します。

    cd ルート
    

  2. ファイルを作成

    touch <ファイル名>
    

    ここで.claspignoreを作成します。

    touch .claspignore
    

PowerShellの場合
  1. .claspignoreを保存したいフォルダにテキストファイルを作成

    test.txtなど適当な名前でOKです。

  2. エクスプローラを開き、何もない辺りでShift+右クリック

  3. PowerShellウィンドウをここで開くを選択

    「PowerShellウィンドウをここで開く」を選択

  4. ファイル名を変更

    ren <作成したファイル名> <新しいファイル名>
    

    test.txt.claspignoreに変更する場合は、こんな感じ。

    ren test.txt .claspignore
    

pushしないファイルを記入

改めてフォルダ構成を確認

現在のフォルダ構成

除外するファイルを記入

相対パスで記入と思いきや、「ちょっと違う」ので要注意です。

パスの中に*を含めると、ワイルドカードとなります。
例えば、このように書くと「末尾に.mdとつくものすべてを除外する」という意味になります。

*.md

また、文頭に!とつけると「NOTpushしないもの」、つまり「pushするもの」を指します。
ローカル側のスクリプトファイルの拡張子が.jsなので、これらをすべてpushするとしたらこのように書きます。

!src/*.js
# 「.js」とつくものは全て対象とする
!src/*.js
 
# 絶対必要!
!src/appsscript.json
 
# フォルダ分けは反映させない
**/**
 
# エラーになるので「Git関係のファイル」はすべて除外する
./.git

ローカルで編集しやすくする

GASの独自文法・用語をエディタに反映する

このままでもコーディング自体は可能ですが、ローカルエディタはGAS独自の文法を認識できないので色々と不便です。

そこで「型定義ファイル」なるデータをプロジェクトに適用します。
本来はTypescript用のものですが、JavaScriptでコーディングする場合でもその恩恵に預かることができます。

型定義ファイルの設定ファイル(package.json)を作成

まずは、型定義ファイルの設定用にpackage.jsonを作成します。

  1. プロジェクトのルートフォルダに移動

    cd ルート
    

  2. 下記のコマンドで設定ファイル(package.json)を作成

    npm init -y
    

initコマンドの意味

npmでパッケージ管理を始める時は、まずプロジェクトをinitコマンドでnpm用に初期化(=initialize)します。

npm init

実行するといろいろ聞かれますが、基本的にはすべてy(=yes)でOKのようです。
処理が完了すると、ルートフォルダ直下にpackage.jsonが作成された状態になります。

でも、どうせ全部yなら何回も答えるのは面倒です。
そこで、initの時にオプションとして-y(もしくは-yes)とつけておくと、「全部yesで答えといて」とまとめて指示するコマンドになります。

npm init -y
npm init -yes
GASの型定義ファイルをインストール

そのまま、Google社が配布しているGASの型定義ファイル(のパッケージ)をインストールします。

  1. Clasp用の型定義ファイル(@types/google-apps-script)をインストール

    npm i --save-dev @types/google-apps-script
    

コマンドの意味

iは、過去記事で記載したようにinstallのショートハンドです。

npm i <パッケージ名>
npm install <パッケージ名>

これを使って、GAS用の型定義ファイルをインストールします。

npm i @types/google-apps-script

また、このパッケージの場合は、オプションとして--save--save-devをつけるのが慣例のようです。

npm i --save @types/google-apps-script
npm i --save-dev @types/google-apps-script

それぞれの意味を調べてみると…

  • --save

    依存パッケージの場合に指定します。package.json の dependencies に記録されます。このパッケージを誰かが npm install したときに依存パッケージとしてインストールされます。

    npm入門 - とほほのWWW入門

    ここで言う「依存」とは「このパッケージが無いとスクリプト自体動かないよ!」という意味なので、型定義ファイルもこのプロジェクトを使う人全員にインストールされることになるみたいです。

  • --save-dev

    テストツールなど、開発者が使用するパッケージの場合に指定します。package.json の devDependencies に記録されます。このパッケージを npm install しても、インストールされません。ただし、パッケージを Git から clone して npm install した場合は開発者とみなされ、インストールされます。

    npm入門 - とほほのWWW入門

    つまり、「スクリプトの開発には必要なパッケージだけど、実行するだけの人は必ずしもインストールする必要は無い」という意味になるようです。

今回の型定義ファイルは、あくまでも「開発の補助データ」として使うものなので、後者の--save-devオプションを使用します。

npm i --save-dev @types/google-apps-script

VSCodeの設定

せっかくなので、ローカルエディタもカスタマイズします。

※当記事では、Visual Studio Code(通称VSCode)を例にご紹介します。

拡張機能
コーディング支援
コードの整形
  • Prettier - Code formatter

    JavaScript向けフォーマッタで特に人気なやつ。
    フォーマッタをこれに指定して、エディタ上でAlt+Shift+Fで使用できます。
    整形のルールは特にいじってませんが、とりあえずそのままでも充分な気がします。

カラーテーマ
ターミナル
ワークスペースの設定

ショートカットや、プロジェクト固有の設定に使います。

  1. プロジェクトをワークスペースとして保存

    プロジェクトを丸ごと「ワークスペース」として保存して、設定ファイル(***.code-workspace)を作成します。

    VSCodeをタスクバーにピン留めしている場合、右クリックで「最近使用したワークスペース一覧」に、このファイルのショートカットが表示されます。
    複数のプロジェクトを管理しやすくなるのでオススメです。

  2. (お好みで)ワークスペース固有の設定を記入

    ワークスペースの設定ファイル(***.code-workspace)に、プロジェクト固有のカラーテーマやフォント、フォルダのショートカット等が登録できます。

    私は下記のように使用しています。
    ※設定ファイルはルートフォルダ直下のvscodeフォルダに格納しています。

    • フォルダのショートカット

      "folders": [
        {
          "name": "VSCode",
          "path": "."
        }, {
          "name": "ProjectAll",
          "path": "../"
        }, {
          "name": "Git",
          "path": "../.git"
        }, {
          "name": "src",
          "path": "../src"
        }
      ],
      

    • それ以外の設定

      "settings": {
        "workbench.colorTheme": "Darcula",
        "editor.fontSize"     : 14,
      }
      

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

次回

【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート

連載目次

  1. 【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 当記事【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

参考

【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~

状況により選択してください。

  • 既存プロジェクトをローカルで編集する場合:ジャンプ!
  • ローカルでイチから開発を始める場合:ジャンプ!

【A】既存プロジェクトをローカルで編集する場合

ローカルにプロジェクトを作成

GASのプロジェクトをローカルにクローン
  1. ターミナルで「ローカル側のルートフォルダ」としたい場所に移動

    cd <フォルダのパス>
    

  2. GASのプロジェクトをローカルにクローン

    clasp cloneで、プロジェクトのデータをローカルにダウンロードします。

    clasp clone <スクリプトID>
    

    もしくは

    clasp clone <スクリプトファイルのURL>
    

    ※URLはhttps:///editの部分まででOK。

スクリプトIDの確認方法
  • プロジェクトのURLで確認する場合

    URLのスクリプトID部分を見ます。

    https://script.google.com/a/ドメイン/d/スクリプトID/edit

  • スクリプトの情報から確認する場合

    1. プロジェクトにアクセス
    2. 画面左上のファイルプロジェクトのプロパティの順に選択
    3. 情報タブのスクリプトID欄を確認
クローンが完了すると…

ローカル側でルートフォルダ以下にファイルが追加されます。

既存プロジェクトをローカルにクローンした場合のフォルダ構成

スクリプトファイル自身は、拡張子が.jsになります。

フォルダを整理する

[目標]フォルダ構成

Claspコマンドで必要なファイルだけを操作できるように、ファイルを整理します。

今回は格納先のフォルダ名をsrcとしますが、任意の名前で大丈夫です。
スクリプトをフォルダ分けしている場合もsrcフォルダ以下にまとめます。

整理後のイメージ

ファイルの移動

現在ルートフォルダに居るとして…

  1. スクリプト格納用のフォルダを作成

    mkdirコマンドで、フォルダを新規作成します。
    引数には、絶対パス/相対パスどちらでも指定できます。

    mkdir <新規フォルダ名>
    

    今回はルートフォルダ直下にsrcフォルダ作成します。

    mkdir src
    

  2. ファイルを移動する

    mv <現在のパス> <移動先(のフォルダ)のパス>
    

    スクリプトA.jssrcフォルダに移動する場合は、こんな感じ。

    mv ./スクリプトA.js ./src
    

スクリプトの格納先(=作業フォルダ)を登録

.clasp.jsonを編集して、Claspからも認識できるようにしておきます。

{
  "scriptId": "<スクリプトID>",
  "rootDir" : "<スクリプトのあるフォルダ>"
}

ちょっとややこしいですが、ここのrootDirスクリプトのルートフォルダを指します。
初期値は、前述の画像で言うとルートフォルダです。

今回はルートフォルダ直下のsrcフォルダにスクリプトファイルを置くので、このように変更しておきます。

"rootDir" : "./src"

スクリプトIDの探し方は、こちらをご参照ください。

完了。

これで、【A】既存プロジェクトをローカルで編集する場合の準備は完了です。

続きはこちらへ。

【B】ローカルでイチから開発を始める場合

ローカルにプロジェクトを作成

[目標]作成イメージ

【A】と同様に、srcフォルダにスクリプトを格納できるように準備します。

ローカル開発の開始イメージ

GASのプロジェクトを新規作成する
  1. ターミナルで「ローカル側のルートフォルダ」としたい場所に移動

    cd <フォルダのパス>
    

  2. Claspコマンドでプロジェクトを作成する

    clasp createでプロジェクトを新規作成します。

    オプションは色々使えますが、今回は作業フォルダのみ指定しておきます。

    clasp create --rootDir <作業フォルダのパス>
    

    今回は、こちらと同様にsrcフォルダを引数とします。

    clasp create --rootDir ./src
    

オプションは他にも…
  • プロジェクトを命名

    プロジェクトの名前も、作成と同時に付けることができます。

    clasp create --title <プロジェクト名>
    

    使用例。

    clasp create --title newProject
    

  • コンテナとするファイルを登録

    コンテナバインドスクリプトを作る場合は、紐付けるファイルのIDも登録しておくことも可能です。

    clasp create --parentId <紐付けるファイルのID>
    

  • その他のオプション

    他にもいろいろオプションがあるようです。
    全編英語ですが、詳しくはこちら↓を。
    GitHub - google/clasp: 🔗 Command Line Apps Script Projects

これらをまとめて、下記のようにも書くことが出来ます。

clasp create --rootDir <作業フォルダのパス> --title <プロジェクト名> --parentId <紐付けるファイルのID>

ただ、全部まとめて書くとコマンドとしては長いと思います。
(間違えずに書くのは結構しんどい…)

これらは後ほど.clasp.json等でも編集できるので、シチュエーションによって選択、ということで。

スクリプトの種類を選択

clasp createを実行すると、スクリプトの種類を選択するよう促されます。

? Clone which script? (Use arrow keys)
> standalone
  docs
  sheets
  slides
  ︙

スタンドアロンかコンテナバインドか、など状況に合わせて選択してください。

  1. >を移動

  2. Enterで決定

[結果]フォルダ構成
  • ローカル

    プロジェクトに赤字のファイルが追加されます。

    ●●

  • Googleドライブ

    「Gドライブのルートフォルダ」にも下記が追加されます。

    ●●

    ファイル名は、プロジェクト名がそのまま使われるようです。

ファイルを整理

ドライブ側のファイルを整理

Googleドライブの仕様上、そのままでも特に問題はありません。

ただ、後々使いづらくなると思う(し、個人的にも気持ち悪い)ので、探しやすい場所に移動しておきます。

設定を変更

タイムゾーン

ローカルから作り始めると、タイムゾーンアメリカになってしまうようです…

  • マニフェストファイル(appsscript.json)

    {
      "timeZone": "America/New_York",
      "dependencies": {
      },
      "exceptionLogging": "STACKDRIVER",
      "runtimeVersion": "V8"
    }
    

実行時刻の設定などで困らないように、日本時間(JST)へ修正しておきます。

"timeZone": "Asia/Tokyo",

完了。

これで、【B】ローカルでイチから開発を始める場合の準備は完了です。

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

次回

【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート

連載目次

  1. 【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 当記事【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

参考

【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~

Node.js、Claspの順にインストールしていきます。

Claspをインストール

node.jsを入れる

ダウンロード

OSにより選択してください。

Windowsの64bit版の場合
  1. 公式サイトにアクセス

    Node.js

  2. 左側の「推奨版」を任意の場所にダウンロード

    ※当記事の執筆時点で、推奨版は12.18.4 LTSです。
    Node.jsのダウンロード・ページ

それ以外の場合
  1. 公式サイトのバージョン一覧にアクセス

    ダウンロード | Node.js

  2. 「推奨版」タブから、自分のOSや目的に合うものを任意の場所にダウンロード

    例えばWindowsの32bit版であれば、画像中央あたりのWindows Installer (.msi)32-bitが良いようです。
    Node.jsのインストーラーの一覧

インストール
  1. インストーラを起動して、画面の指示に従いNode.jsをインストールする

    詳しい手順はこちら↓が参考になるかと思います。
    claspを使う事前準備-Node.jsをインストールする – 経理業務向けGoogle Apps Script講座

    Node.jsを入れる過程で、このあと使用するnpmというツールもインストールされます。

バージョンを確認
  1. Node.jsのインストールができたら、ターミナルを起動する

    特に理由が無ければPowerShellGit Bashがおすすめです。
    以下、原則Git Bashを使用する場合を前提に説明します。

  2. 下記のコマンドを入力して、インストールができているか確認する

    npm -v

    こんな風にnpmのバージョンが出力されればOK。

    ※バージョンは執筆時点のものです。

    6.9.0

Claspを入れる

インストール

npmというNode.js版のパッケージ管理ツールを使用してClaspをインストールします。

PowerShell用の記事ですが、「パッケージとは何ぞや?」な方はこちらへ。
【PowerShell】「そもそもパッケージって何?」っていうあなたに、少し説明します。 - ゆるおたノート

  1. 下記のコマンドを入力して、Google社が配布しているClaspの公式パッケージ(@google/clasp)をインストール
    npm i @google/clasp -g
コマンドの意味

npm i [パッケージ名]で、パッケージをダウンロードしてインストールする」という意味のコマンドです。

# パッケージをインストール
npm i [パッケージ名]

インストール先はカレントディレクトリの周辺(参考)で、基本はプロジェクト内の展開になると思います。

そこに-gというオプションをつけると、展開範囲は「OSのユーザー単位(=グローバル)」になります。

# パッケージをグローバルでインストール
npm i [パッケージ名] -g

パッケージ自体のインストール先もnpmのインストール場所」に変更されます。

ちなみにiinstallのショートハンドなので、下記のように書いてもOKです。

# パッケージをグローバルでインストール
npm install -g [パッケージ名]
バージョンを確認

インストールが出来たら、念のためClaspが正しくインストール出来ているか確認します。

  1. Claspのバージョンを確認

    ※バージョンは執筆時点のものです。

    # 入力
    clasp -v
    
    # 出力(例)
    2.1.0

「そんなコマンドは無いよ」と言われたら…

Claspのコマンドを使用する時、コマンドがシェル*1に登録できていないと下記のようなエラー文が出力されます。

command not found

コマンドが見つかりません。

この場合は、Claspのコマンド達を「Claspという名のコマンド」として端末に登録して、シェルがClaspコマンドを認識できるようにしておく必要があります。

書き方は、こんな感じ。

# コマンドを登録する
alias [コマンド名]=[ソースコード]

aliasは、別名・通称という意味です。「エイリアス」と読むことが多いと思います。

Claspを登録する場合は、こんな感じに書きます。

# Claspコマンドを登録する
alias Clasp=~/clasp/src/index.js

今回はClaspのパッケージをグローバルでインストールしているので、Claspコマンドのソースコードも端末の「ユーザーごと」に入っています。

そこで、エイリアスにもユーザー用のファイルを指定します。
Windowsの場合、ソースコードの保存場所はこちら。

/C/Users/[ユーザー名]/AppData/Roaming/npm/node_modules/\@google/clasp/src/index.js

なお、これはBash用の表記で、パスはC:\Users\~ではなく/C/Users/~のようにバックスラッシュ/で区切ることに注意してください。

これをコマンドに適用すると、こんな感じになります。

# aliasコマンドで「Claspコマンド」のソースを指定する
alias clasp=/C/Users/[ユーザー名]/AppData/Roaming/npm/node_modules/\@google/clasp/src/index.js

ターミナル上で上記のように入力し、Enterで実行します。

しかし、これを実行してもターミナルでは特に何も出力されません。(不安…)
そこで、ちゃんと登録できたか確認する意味も込めて、改めてClaspコマンドを使ってみます。

# 入力
clasp -v
 
# 出力(例)
2.1.0

このようにバージョンが出力されればOKです。

Googleアカウントの登録

GAS側の設定

GASのプロジェクトを外部から操作できるようにする

Google Apps Script APIを有効化します。

  1. Clasp作業用のGoogleアカウントでGoogleにログイン

  2. 下記の設定ページにアクセス

    Apps Script – Google Apps Script

  3. 背景が青い辺りにカーソルを合わせてクリック

    背景が青い辺りにカーソルを合わせてクリック

  4. 画面右側のスイッチをクリックして、Google Apps Script APIオンに変更

    Google Apps Script APIを「オン」にする

Clasp側の設定

作業用のChromeを「アクティブ」にしておく

※ここはChromeブラウザを複数ユーザー分作成している方向けです。
それ以外の方は次へ

  1. Claspで作業したいユーザーのブラウザで任意のページを開いてウィンドウをアクティブにしておく
ClaspにGoogleアカウントを紐付ける
  1. ターミナルに戻って下記のコマンドを入力

    clasp login

    自動でブラウザが開きます。

  2. Clasp作業用のGoogleアカウントでログイン

    アカウントが認証されると、ブラウザの上の方に下記のメッセージが表示されます。

    Logged in! You may close this page.

    ログインしました!このページは閉じて構いません。

    ここまで来たら、ブラウザは一旦閉じてもOKです。

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

次回

【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート

連載目次

  1. 【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 当記事【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

参考

注釈

*1: シェルとは?

シェル(英:shell)とは

コンピュータ株式会社の受付のおねーさんのこと。

もう少し真面目に書くと
人間様からの入力をコンピュータさんに伝えるプログラムのこと
です。

イメージしにくければ
Windowsにおける「コマンドプロンプト」みたいなもの
と考えても構いません。

シェル (shell)とは|「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典

【Clasp】ローカルでGASを編集するメリットとデメリット

Google Apps Scriptは、Webブラウザさえあれば使えるのが大きなメリットですが、Claspというツールを使うとローカルで編集することもできます。

ただ、多少準備が必要なせいもあるのか、入門者向けの情報が少ないです。
もっともっと普及してほしいので、1年ちょっと使ってみて感じたことを整理してみました。

なお、以下はローカルエディタとして「Visual Studio Code(通称VSCode)を使う場合」を例に書いています。
細かい点は違うかもしれませんが、大抵のメリットは他のエディタでもカスタマイズ等で同じように享受できるかと思います。
適宜、読み替えてご覧ください。

メリット

画面のカスタマイズ

好きな配色、フォントが使える

ブラウザ上のエディタでは拡張機能Darculaというカラーテーマを使っています。
(フォントは恐らくRoboto MonoArialだと思います。)

Chromeの拡張機能でDarculaを選択

ローカルでも、GASを編集する場合はDarculaです。
(フォントはRoboto Monoに日本語用としてMotoyaLMaru W3 monoを組み合わせています。)

VSCodeでもDarculaを選択。

同じ名前のテーマでも色付けのパターンは多少違うようですが、こちらの方がキーワード等は厳密に区別してくれている気がします。

他にもプログラミング界隈でよく使われるものを筆頭に、拡張機能として豊富に(というか大量に)提供されています。

大量のカラーテーマ

こだわる人は、自分の好みに合わせて微調整したり、イチから作成することもできます。

デバッグ中は切り替える

しかし、私が助かっているのはここだけではなくて、「うっかり人間」向けのメリットがあります。

デバッグの時に「出力内容が変だけど、どうしてもエラーの原因を見つけられない!」という時、変な値を代入していないか目視でもチェックしたりします。
そういう時は、テーマやフォントを変えてみると見つけやすくなることがあります*1

例えば、rainglowという拡張機能Rainbow Contrastを選択すると、コメントは薄くなりますが、文字列や数値の彩度が上がるので少し浮き上がったように見えます。
こうすると、そのまま目で検索するより少し楽になります。

  • 変更前:Darcula

    VSCodeでもDarculaを選択。

  • 変更後:Rainbow Contrast

    Rainbow Contrastで値を強調

どのシチュエーションでどのテーマが向いているかは個人差もあるようですが、その探す時間も愛おしいくらいです。

そもそも、それを差し置いても自分好みの配色・フォントを使えるっていうだけで結構テンションが上がります。笑

拡張機能があれば、カッコも色分け

プログラムを書いていると、複雑な処理にはどうしてもネストを多用しがちになり、カッコだらけで読みづらくなっていきます。
しかもブラウザのエディタでは、カッコは基本的に単色です。

単色のカッコ達

こんな時、VSCode拡張機能過去記事)を入れると、カッコの階層ごとに色分けとルーラーが加わります。
「今書いているコードはどの階層なのか」が視覚的に判別しやすい
ので、コーディングが楽です。

拡張機能で階層を色分け

もちろん、深すぎるネストは即解消推奨ですが、その手助けにもなります。

エディタの補助機能

自動整形で困らない

これは恐らく今だけのバグかと思いますが、ブラウザではV8版になってからCtrl+Tabの自動インデント機能が上手く機能しなくなってしまいました。*2

例えば、「テンプレート文字列」を使うとインデントがズレます…
自動整形のバグ?

ブラウザ上でもコードを編集することがあり、かなり多用していたので個人的にはこれが1番困っています。

それに対し、VSCodeは元々「モダンJavaScript」向け*3に作られているので、新しい文法でも問題なく整形してくれます。

※コロン:の位置は自分で調整しています。
インデントはズレない

また、Prettierなどの整形ツールを使えば、さらに自分やチームの独自ルールに準拠するように細かく設定することも可能です。

使ってない変数はグレーアウト

リファクタリングを繰り返していると、いつの間にか定義しただけの「ぼっち変数/定数」が残ってしまうことがあります。

動作には直接問題ないとしても「ちょっと残念なコード」になっちゃうし、コンピューターがコードを解析する時間も宣言の数だけほんの少し増えてしまいます。

どうせなら少しでもパフォーマンスは上げたいものです。
それに、今は大丈夫でも、将来的に「名前がバッティングしてエラーの元に」なんてことにもなりかねません…

VSCodeはその辺も織り込み済みで、宣言だけで実際に使っていない変数/定数は、グレーアウトと修正の提案で知らせてくれます。

※この画像では分かりづらいですが、定数mが修正の提案とともにグレーになっています。
未使用変数はグレーアウト

ファイルを分割しても認識してくれる

コードが長くなってくると、ファイルを分割することがあります。

この場合、別のファイルで定義しているものは、Alt+/(単語補完)やCtrl+Space(コンテンツ アシスト)では入力補完が呼び出せないことがあります。

インテリセンスが出ない

都度ファイルを行ったり来たりする必要があって、地味に不便です…

VSCodeなら、プロジェクト内であれば認識して選択肢に入れてくれます。
特にクラスであれば、メソッドやプロパティには画像のように個別の記号がつくので、「これはメソッドだっけ…?カッコ()要るんだっけな…?」みたいなことも減ります。

メソッドやプロパティは記号つきで選択肢

ドキュメンテーション・コメントを頑張ると…

少し面倒ですが、ドキュメンテーション・コメントも1つずつ書いておくと、入力補完に加えて自分で書いた説明も小窓で見られるようになります。

定義を見ながら編集できる
(引数の説明が微妙なのは見逃してください…)

これはつい最近気づいた機能ですが、かなり便利です。
引数の数や順番も分かるので、書き間違いによるエラーも減りました。

型名なども省略せず所定の書式に沿って書いておけば、型も判定してくれるみたいで入力補完の選択肢も更に厳密に絞ってくれます。
クラスを親子関係で使うような時は、特に有効です。

Git管理

ちゃんとコマンドが使える

Chromeの拡張機能だけでも、GitHubと連携すればGit管理は出来ます。これも便利です。
でも、これだと「ブランチ切ってプッシュ、プル」しかできないのです…

もうちょっと厳密に管理したい時は、Claspで一旦ローカルに落として管理するのが便利です。
ローカルで使えるということは、SourceTree等のGUIツールを使っている方であれば、そちらでも操作できるようになります。

正直Gitの使い方自体は全く自信がありませんが、個人的には「コミットの取り消し」や「コミット・コメントの編集」ができるだけでも、ローカルで使う価値は大いにあると思っています。

その上で上記の拡張機能と連携すれば、盤石です。
ただし、これもちょっとコツが要るみたいで…この点は後日、別途記事にする予定です。

READMEスクリプトの説明もできる

私は個人用途が多いのでこれはあまり活用できていませんが、READMEを使えば、リファレンス等の作成や運用ルールの掲示等に活用できるかと思います。

デメリット

慣れるまでは、設定とか同期・実行とか少しめんどくさいかも

GAS特有の文法には弱い?

Claspを入れただけでは、SpreadsheetサービスDriveAppサービスのような「GAS特有の文法」はエディタが認識できません。

そこで、ひと手間。
プロジェクトの中に公式が配布しているGAS専用の型定義ファイルというものを入れると、ブラウザと同じように認識/入力補完」してくれるようになります。

GAS特有の文法も使える

不安定…?

というわけで文法自体はデメリットと言うほどではないのですが、これが少し不安定なようで(?)、ときどきGAS用語がエディタに認識されないことがあります。
(もしかしたら、私が普段Typescriptを使っていないせいで無意識にどこか大事なところを弄っているとかで濡れ衣かもしれませんが…)

型定義ファイルを入れ直しても動かない時は、ちょっとめんどくさいですが「GASの文法はブラウザで、それ以外の文法やコードの整形はローカルで」がとりあえずの対処法になるかなと思います。

情報が少なくて…

便利なツールですが、基本的にCLIという黒い画面でいじることもあり、まだまだ「入門者向け」の解説が少ないです。
そのため、分からないところはどうしても時間をかけて調べながら・ときどき自分で翻訳しながら、の作業になります。

特にプログラミング経験が浅い身としては、ひとつひとつ理解できるまで時間がかかることもあります。
少しでも使い方を理解できたら、全部覚えるのは大変なのでPhrase Expressのようなスニペットツールを活用していくことをおすすめします。

でも、すごく便利なので、もっと普及してClaspの情報が増えてほしいです…

このシリーズについて

Google Apps Scriptをローカルで編集する方法について、まとめています。

誤り、分かりづらい等ありましたら、ぜひコメント欄やTwitterお問い合わせフォーム等でご教示ください。

次回

【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート

連載目次

  1. 当記事【Clasp】ローカルでGASを編集するメリットとデメリット - ゆるおたノート
  2. 【Clasp】ローカルでGASを書く準備 ~Claspをインストールする~ - ゆるおたノート
  3. 【Clasp】ローカルでGASを書く準備 ~プロジェクトを作成する~ - ゆるおたノート
  4. 【Clasp】ローカルでGASを書く準備 ~コーディングを楽にする~ - ゆるおたノート
  5. 【Clasp】ローカルでGASを書いてみる ~スクリプトを書いてアップロードする~ - ゆるおたノート
  6. 【Clasp】ローカルでGASを書いてみる ~スクリプトをGitで管理する~ - ゆるおたノート

注釈

*1:ADHD等の発達障害界隈で言われていることですが、PC上で編集した文章は「目が滑る」ことがあります。
その対処として、「(紙に印刷したりして)見た目を変えるとチェック漏れが減る」というテクニックを応用しています。

*2:そもそも公式さんが調整してからリリースしてほしかったのですが、もうそれは仕方ないとして…
Googleさんの発表(動画)によれば新しいIDEを目下開発中らしいので、そちらでは改善されていることを祈ります!

*3:厳密には、JavaScriptというかTypescript向け…?

【VBA】どこがダメ? - エラー発生時のチェックリスト(コンパイルエラー編)

同じエラー文に繰り返し遭遇してはGoogle先生にお聞きする日々。

原因の詳しい説明は既にネットの大海に広がっているのでそちらにお任せするとして、「で、どこを直せばいいの?」のヒントをもう少し具体的に列挙していきます。
※あくまでも自分の体験ベースなので、かなり偏りがあると思います。あらかじめご了承ください。

変数が定義されていません。

チェック!

  • スペル間違ってない?

修飾子が不正です。

どういう意味?

メンバーにないものを指定したとき。

チェック!

  • 参照するメンバの名前はあってる?
    • コピペで確認してみた?

functionまたは変数が必要です

チェック!

  • プロシージャの種類は合ってる?
    • 戻り値があるのにSubプロシージャになってたりしない?

参照が不正または不完全です。

チェック!

  • 呼び出したいメンバーがWithブロックからはみ出てない?
  • オブジェクトのメンバーなのにオブジェクトを指定してなかったりしない?

同じプロパティに対するプロパティ プロシージャの定義が一致していません。または、プロパティ プロシージャに省略可能な引数またはParamaArrayが含まれているか、Property Setの最後の引数が不正です。

(なんか珍しく具体的ですねw)

チェック!

  • Property Get/Let/Setプロシージャ同士で型は合ってる?
  • ParamaArrayキーワード使ってない?
  • Property Setプロシージャの引数の型は正しい?

定数式が必要です。

チェック!

  • それまで問題なく動いてたのに、突然このエラーが出てる?

対処方法

  • ソースコードは新しいモジュールにコピペして、モジュール名を上書きすると直るみたいです。

    • こちら↓が参考になりました。

パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。

チェック!

  • ユーザー定義型をDictionary型Collection型Itemプロパティに代入しようとしてない?

対処方法

  • 丸ごとクラス化して、それを代入すると良いようです。

    • 下記2点が参考になりました。

配列には割り当てられません。

チェック!

  • Array関数で配列を作るときに、変数の宣言時点で要素数を指定しちゃってない?

    Sub test()

    '要素数つきで宣言している
    Dim test(0 To 5) As Variant
    test = Array("クサ", "ツチ", "ジン", "カイ", "ビー", "イー")

    Dim m As String: Const SPACE = " "
    m = m & test(0) & SPACE
    m = m & test(1) & SPACE
    m = m & test(2) & SPACE
    m = m & test(2) & SPACE
    m = m & test(3) & SPACE
    m = m & test(0) & SPACE
    m = m & test(4) & SPACE
    m = m & test(5)

    Debug.Print m

    End Sub

対処方法

  • Array関数を使う時は、Variant型で要素数は指定せずに使うと良いようです。

    Sub test()

    Dim test() As Variant '()は無くてもOK
    test = Array("クサ", "ツチ", "ジン", "カイ", "ビー", "イー")

    Dim m As String: Const SPACE = " "
    m = m & test(0) & SPACE
    m = m & test(1) & SPACE
    m = m & test(2) & SPACE
    m = m & test(2) & SPACE
    m = m & test(3) & SPACE
    m = m & test(0) & SPACE
    m = m & test(4) & SPACE
    m = m & test(5)

    Debug.Print m

    End Sub

    • こちら↓が参考になりました。

      変数はVariant型で宣言し、また変数の後ろに要素数を入れることはしません。

      仮にString型で宣言すると「配列がありません」というエラーになり、変数の後にカッコをつけてその中に要素数を入れると「配列には割り当てられません」というエラーになります。

      配列の利用(VBA)その2 – GANASYS研修室

連載目次

  1. 【VBA】なにか忘れてる? - エラー発生時のチェックリスト(実行時エラー編) - ゆるおたノート
  2. 当記事【VBA】どこがダメ? - エラー発生時のチェックリスト(コンパイルエラー編) - ゆるおたノート

カチャカチャ音が苦手なのでPC環境を自分好みに整えてみた

巣ごもり需要もあって、最近ゲーミング端末が人気みたいですね。
でも、私個人はキーボード等のカチャカチャ音が昔から苦手です。

特に派遣社員時代に支給いただいた純正のマウスが少し苦痛で、少しでも仕事のストレスを減らすべく「買い換えよう」と決心しました。

小さいことではありますが、現在はある程度満足できるレベルになったので、レビューと合わせてご紹介します。

マウス

条件

駅前の家電量販店をハシゴして、展示品を1つずつカチカチ、ポチポチ。
どうせなら作業場を広く取りたいので「無線」も条件に追加しつつ、静音性能とサイズ感を比較して検討しました。

項目 希望 優先度 説明
有線/無線 無線 どうせなら身の回りのケーブルを減らしたい。
クリック音 静音 そもそもの目的。
カチカチ音が苦手なので、出来るだけ音が小さいものを。
サイズ感 (出来れば)
小さめのもの
手にフィットするもの希望。
かつ移動が多いので、持ち運びしやすいものを。
左右クリック - (説明無用!)
スクロールホイール - 「Web検索命!」の業務だったので。
チルトホイール - Excelもよく使う。
でも「あれば多少仕事早くなるかな?」くらいの気持ち。
その他のボタン - × マクロは少し惹かれるけど、ボタンが増えても覚えきれない気がして。
設定ソフトも会社が許してくれないだろうなと…

【即決】M331GR

当時、ミーティングが週に何度かあり、出し入れが多かったのでサイズ感も重視して探しました。
ところが、静音タイプを探している時点で選択肢はほぼ無し…(残念)

その中で購入したのがこちら↓のSILENT PLUSモデル。

結論から言うと、音は「かなり静か」です。
クリック音の表現でよくある「カチカチッ」という高音は一切感じません。
(ただし、スクロールホイールはクリックに比べると少し音が大きめで、カリカリ鳴ります。多用する方は要注意です。)

「トントン、トントン」と低めの音がほんのり聞こえる程度で、職場では「無音だけどちゃんと仕事してるの?」と周りに心配されました。笑

しかも、クリックの衝撃が弱いので、純正マウスに比べて段違いに疲れにくいです。
これは意図していなかったので嬉しい誤算でした。
ただし、隣席の方に触ってみて頂いたところ「ボタンを押してる感覚が無くて気持ち悪い」そうなので、人によっては馴染まないのかもしれません。

また、持ち運びしやすい"やや"小型サイズなのもGoodでした。
外観はちょっとボコッとするので多少の妥協は必要ですが、PCバッグのポケットにも入ります。

形も、コロンとした流線型で(エルゴノミクス系*1には劣るかもしれませんが)手にフィットしていて親指の納まりも良いです。

ちなみに、小型マウスにありがちな「手の甲が浮いて攣りそうになる」ようなことはありませんが、高さがあるので少しデメリットもあります(後述)。

現在は購入してから3年半ほど経ったところですが、電池を2回変えただけでずっと使い続けています。
よって、電池の持ちや本体の耐久性も申し分なしだと思います。

【サブ】リストレスト

ただ、上記のマウスは、小型タイプにしては少し高さがあります。
私は手の付け根を机に置いて操作するので、机のフチで手が擦れて夕方にはジンジン痛くなるのが地味に悩みでした。

マウスはまだ予算に余裕があったので、厚みのあるリストレストと合わせることにしました。

店頭・ネットショップ共に素材、サイズ展開が色々ありますが、私はぷにぷに感が好きなので「ゲル素材」を愛用しています。

できれば…

欲を言えば、マウスは機能・形をそのままに在宅向け(?)の大きいサイズも展開してほしいなぁ、と思っています。
高級マウスみたいな「手ですっぽり包み込む」くらいの大きさで、あまりゴツゴツしてないものを…

そもそも静音タイプ自体バリエーションが少ないし、価格帯的にも力を入れていないように感じるので、もしかしたら需要があまり無いのかもしれませんが。

キーボード

条件

キーボードも、最初はマウスと同じように触って比べました。
ただ、やっぱりどれも良いモノはめちゃくちゃ高い。至極当然ではあるのですが…

しかも最近ゲーミング端末が流行っているおかげで、ゴテゴテ、キラキラしたものが多くて。
これはこれでカッコイイとは思いますが、さすがに仕事用としては使いづらいので*2、そうじゃないものを探しました。

項目 希望 優先度 説明
有線/無線 (出来れば)無線 出来るだけ無線にしたいけど、価格帯によっては少ないようなので重視できない…
打鍵音 静音 カチャカチャ音が苦手なので。
打鍵の感覚 抵抗感が強すぎないもの 指筋、腕筋を大事にしたい。笑
Mac系の薄いやつ*3は、叩く時点で痛いのでイヤ。
テンキー - Excelをよく使うので。
でもプログラミングのお陰で上部の数字キーでも慣れてきたので、最悪無くても良し。
Ctrlキー 絶対、左下 Fnキーが左下」は絶対イヤ!
Homeキー
Endキー
- プログラミングを学び始めて便利さを知ったので。
Page Upキー
Page Downキー
- ExcelのCtrl+Page Up/Downをよく使うので。
アプリケーションキー - 右クリックの代わり。マウス操作を減らす救世主。

【第1候補】G512R-TC(※)

いきなりゲーミングキーボードですが、実機を触った中で感触が良かったのはこれです。
※型番のメモを紛失してしまったので、記憶を頼りに「似ているもの」を掲載しています。

「ボタンを押しました」感適度な跳ね返りがあって、気持ちいい。
キーボードの用語で言うと「茶軸」というらしいです。

ただ、キラキラ光るLEDライトは消せるから良いとして、「カチャカチャ音が好きになれないなぁ…」と二の足を踏みました。
しかも私は普段かなり強めに打ち込んでしまうので、これだとガチャガチャガチャガチャ…ッダーーーーン!!と(職場で)近所迷惑になりかねないかな、と…
この時点で「メカニカル方式」の線は消えました。

そもそも、いわゆるブランド物なので、入門編には高すぎるのですが。

【第2候補】K275

予算があまり無かったので、買うならまずは安いものから試そうと考えて、5,000円未満で探しました。

感触と音を比べた結果、コレ↓が1番良さそうだなとそのまま購入。

「静音タイプ」と謳っていて、フルキーボードで、しかも無線*4と来て、「こりゃ私の目的にピッタリだ!」とウキウキで持ち帰りました。

ところが部屋で試したところ、ちょっと違和感が。
量販店で試した時はBGMがあったからなのか感じなかったのですが、自部屋で使うと「カチャカチャ、カチャカチャ」と大きめの音がしました。
弱めを意識して打ってみると、確かに静かではありますが…

また、使っていくうちに気付きましたが、キーの1つ1つを真上から押さないと引っ掛かります。
長文を打っているときに限って発生させてしまうので、これも地味にストレスでした。

買い直す余裕はなかったのでしばらく注意しながら使っていましたが、(あまり使わないファンクションキー以外は)ますます引っ掛かるようになり、それに伴って音も余計に気になるようになりました。
そうは言っても、これはあくまで廉価モデルだと思うので、消耗品と考えれば経年劣化くらいは仕方ないのかなとは考えています。

ただ、どうしてもデメリットが気になりますが、メリットもあって。

無線の遅延は全く気にならないレベル*5でした。
レシーバーも、上述のマウスでもlogicoolの無線タイプを使っていたので、合わせて1つでOKなのも助かります。
混線するようなこともありませんでした。
お陰で、PC本体から伸びるケーブルは、電源とモニタ用の2本に減りました。

また、本体重量もあまり無いし、裏面のスタンドも立てたり仕舞ったりが簡単です。
そのお陰で、本体が入るバッグであれば持ち運びも気軽にできました。

最終的に2年間使いましたが、1日中ひたすら文章やコードを打ち続けて、電池交換は1回だけ
単4なのでオフィス街のコンビニでも手に入りやすいのが助かりました。

でもやっぱり「静かなのが欲しいなぁ」という気持ちは捨てられず、 折を見てちょくちょく調べては、欲しい物リストに新しい候補を貯めていました。

…と、使い続けるうちに、キーとタイプする文字の対応(?)が正しく機能しなくなってきました。
「これは寿命かな…」ということで、ここで、静音性をもっとちゃんと求めようと決心しました。

【第3候補】PD-KB820WS

欲しい物リストを貯めていた頃に周囲で流行っていたのが、HHKBです。

※当時は下記リンクのような静音Bluetooth接続を兼ねたモデルはまだ発表されていませんでしたが、「私の希望に近かったもの」として掲載します。

いわゆる高級キーボードの部類で、打鍵感やスタイリッシュなデザイン*6が人気のブランドです。
上記のように、無線モデルや静音モデルもあります。

近くに使っている方もいらっしゃったので、高級キーボードの入り口としては筆頭候補でした。

ただ、独特なキー配列で初心者にはハードルが高いのと、「コンパクトキーボード」が未体験なのとで、躊躇いました。
何よりお値段が高めなので、気軽に試せなくて
東京であれば試打できる場所もあるみたいですが、地方民は遠くから想像するしかできません…

また、赤字のように、検討していた当時は「静音」と「Bluetooth接続」を兼ねたモデルは未発表でした。
個人的に「コンパクトタイプは気軽に持ち運びできてこそ」と考えているので、「無線でないなら急いで買う必要は無いかな…」と今回は我慢しました。強がりとも言います。

【第4候補】SKB-KG3BK

「急いで買う必要は…」とは言いつつ、部屋に遊びに来た父に「でもいつかはコンパクトキーボードも使ってみたい」と愚痴っていたところ、こちら↓を貸してくれました。

ファンクションキー以外は、キーの並びが「HHKBの日本語配列」に似ています
キー自体の機構は違うと思いますが、コンパクト系の体験になりました。

感想としては、「慣れるのに時間がかかりそう」でした…
当然ながらキーの間隔が狭く、打ちたいところにキーが無くて「あぁ、もう!」の連続です。
また、矢印キーが文字キー達と同列に並んでいるので、感覚で見つけられず誤操作が増えてしまいました…

…このあたりは慣れれば問題無いのかもしれませんが。

また、これはHHKBから離れますが、キーの感触が少し重いです。
仕事で1日使うと、夕方には指の付け根に乳酸が溜まっているのがよく分かります。笑
この辺はもともとの筋力もあるかもしれないので、好みが分かれそうです。

でも打鍵音はかなり好きな部類です。
カチャカチャ音が控えめで、低音系の「コトコト」というか「クリクリ」みたいな音がします。

打鍵の重ささえ耐えられれば触っていて気持ちいいので、現時点ではサブのキーボードとしてたまに使いたいなと思っています。

【第5候補】PZ-R2TLSA-JP4-IV-Z

ここまでで、各所のレビュー通り「キーボードの性能は『ほとんど値段相応』なんだろうな」というのが実感としてもありました。
ということは、やっぱりどこかで「勢い」を発揮しなくちゃいけないなと。

でも、最近在宅ワークを始めたのでスペースはあまり使えない…
でもでも、矢印キーやHome/Endキー、PageUp/PageDownキーは独立したものが欲しい…
それに第1候補の気持ちよさと第3候補のコンパクトさのどちらか、もしくは両方の良いとこ取りしたものを…

とか妄想しながら考えながらレビューを見て回った結果、見つけたのがこちらです。

HHKBのPFU社とRealForceの東プレ社がコラボしたモデル*7です。
キーの機構はHHKBと同じ静電容量無接点方式*8で、フルキーボードから「テンキーを外しただけ」のキー配列、しかも静音タイプもある!!!
「あれ、もしや今度こそ私のためにあるのでは?」と錯覚するほど、理想の商品がありました。

まず、音は本当に静かです。
無音ではありませんが、カチャカチャとした高音域の音はほぼありません。
速めに打っても気になるほど大きい音にはならず、低音系の「コトコトコトコト…」という感じです。

また、Nキーロールオーバーなる仕様のおかげで複数キーを同時押ししても認識してくれるので、急いで打ち込んでもほぼ思った通りに出力されます。

そして何より、キーを押した感覚が気持ちいいです。
ボタンを押した感覚はしっかりあって、でもカチカチ感は無く、シリコンゴムをふにふに押しているような感覚です。
買って10ヶ月経ちましたが、今だにこれだけで結構テンションが上がります。笑

ところで、人気の高級キーボードではありますが、デメリットもいくつかあります。

今回はテンキーレスを購入しましたが、表計算の仕事も多いので入力作業はどうしても不便に感じます。
スペースの都合で敢えて、だったので仕方ないところですが、スプレッドシートを仕事でも家庭でも使っている以上、数値入力は早く操作できた方が快適だと思います。

もちろん別売りのテンキーもありますが、同じRealForceブランドだと当然高いです…
常に必要というわけでは無いし、追加で買うとしたらこれは安物でも良いかも…(迷い中)

それに、今回のモデルは無線タイプが無く、USB接続が必須です。
今のところ噛み合わせがゆるいとかはありませんが、最近は気分転換にちょこちょこ部屋を移動するので、そのたびに挿したり抜いたりがちょっと不便かつ不安です…出来れば消耗したくないし…

使うときは必然的にUSBの穴も塞がってしまうので、その辺のやりくりも少し必要です。

また、結構重いし、厚みもあります
これはキー機構の格納スペースや重心の影響なのかなと重いますが、持ち運びには少し注意が必要です。
とはいえ、このタイプのキーボードを使う方はそもそも長距離移動のお供として使う目的ではないと思うので、デメリットと言うのかは微妙なところでしょうか。

そしてやっぱり、値段が高い・敷居が高いです。
「仕事で使う」とかこつけて買いましたが、やっぱり清水の舞台から飛び降りるような気持ちです。

まだ収入も安定していないので、万が一どこか故障・破損してしまったらメンテナンスできるんだろうかと思うと、かなりビクビクしています。
そのぶん大事に使うので良いことではあるのですが…

一応「他のキー機構と違って半永久的に使える」のが謳い文句みたいなので、それを信じています。

【サブ】耐震ジェル

さて、キーボード本体は静かですが、もう1つ大きな問題があります。それは振動です。

私の部屋では、スペースの都合上「キーボードスライダー」を使用しています。

この上でキーボードを打っていると、スライダーのテーブル面が共振して、低音で「ドドドド…」と響いているのが分かります。
キーボードの重さと私の打鍵の強さのせいだと思いますが、この音はちょっと気になるレベルです…

しかも、スライダーが揺れているということは、机も揺れるし、そうなれば床にも伝わります。
一応カーペットも敷いていますが、賃貸で暮らす身としては見過ごせないポイントです。

今回は、キーボードというよりはスライダー「も」振動していることが問題なので、間に何か挟もうと考えました。

選択肢としてはキーボードマットもありますが、これは表面の素材で好みが分かれるらしく、既に大金(※個人比)を使っている私としては「出来れば数千円でも失敗したくない」と思っていました。

それに、夏に敷いたままにするのも憚られる*9ので、マット以外の選択肢を探しました。

結果、100均の耐震ジェルが大当たりでした!

※私が買ったのはダイソー専用商品なので商品リンクは無いみたいです。画像だけでごめんなさい。

100均の耐震ジェル
※画像出典:衝撃吸収パッド 角型M | ヒャッキング

「ドドドド」といった共振音は明らかになくなり、聞こえるのはキーボードの打鍵音だけになりました。

また、意図しなかったメリットですが、やわらかめのジェルを選んだお陰で、キーを押したときの反発もさらに柔らかくなりました。
腱鞘炎気味だったのがだいぶ和らいだので、手首の痛みに悩んでいる方にもおすすめです。

結論

ここまでをまとめると、現在使用しているのは下記の通りです。

分類 品目 型番 説明
マウス 本体 M331GR
(SILENT PLUS)
無線小型静音
リストレスト MOH-FTRWH ゲル素材
キーボード 本体 PZ-R2TLSA-JP4-IV-Z 有線テンキーレス静音
キーボードスライダー 100-KB003
パームレスト MOH-FTPWH ゲル素材
(リストレストと同じシリーズのロングタイプ)
耐震ジェル 4984355057703
(※JANコード)

しつこいですが、一般的にはカチカチ音の方が好まれるらしく、静音モデルはもともと選択肢があまり多くありません。ちょっと悲しい…

ただ、マウスはともかく、キーボードを選ぶ場合は似たような機能なら値段で妥協しないことが大事みたいです。
優柔不断過ぎて、少しもったいないことをしました。

しばらくはもう出費できないので買い替えは控えますが、「もっと良いものあるよー!」とかありましたら、是非コメント欄Twitter@yuricks7)等で教えて下さい。
将来の楽しみとして参考にさせていただきます!


*1:「人間工学に基づいて~(云々)」とか書かれているやつです。

*2:自宅用に買う手もありましたが、余裕で予算を超えるので保留としました

*3:「パンタグラフ方式」と言うそうです。

*4:レシーバーも先に買ったマウスと共用できます。

*5:ゲーミングデバイスとしては使ってないのでそこまでは評価できませんが…

*6:真っ黒アリ、無刻印アリ、英字のみ刻印アリ、など。黒は厳密には「墨色」と呼ぶみたいです。
ちなみに、「薄暗い部屋でも使うなら墨モデルは印字がほぼ見えなくなるので避けた方が良い」そうです。
こちらもかなり参考になりました。貴重なアドバイスありがとうございました!

*7:ちょっと前にTBS系の「マツコの知らない世界」(2020年7月21日(火)放送)でも紹介されていたので、記憶に新しいかもしれません

*8:セブン銀行のATMも採用されているそうです。

*9:超ズボラ人間なので、自分にこまめな掃除・洗濯は期待しないことにしています…

【VBA】環境変数の中身をVBAで見てみる

パーフェクトExcel VBAを写経していて楽しい関数を見つけたので、記録しておきます。

プログラミング入門者の壁

「パスを通す」って何?

プログラミングを始めるときに大抵つまづく環境構築。
Google先生に聞いてみると、大体「パスを通せばOKよ」的な説明がされている。

  • 「パス」って?"ファイルの場所"とかのパス?
  • それを「通す」ってどういうこと?何をすれば?

「解説してもらっても言葉の定義から調べることになって結局挫折…」みたいなことはよくあるけど、
要は、特定のソフトウェアで使うパスを、OS全体の変数(=環境変数)に
代入するように設定しよう
という意味らしい。

それで、この環境変数の状態を「VBAを使えばExcelからも見られる」というのを先ほど知った。

Excelから見るには?

VBAライブラリのInteractionモジュールに含まれていて、いわゆる「組み込み関数」のひとつ。
その名もEnviron関数(エンバイロン関数)。

環境変数の値を取得して状態を確認することができる。
「確認」だけで、値の設定まではこれだけでは出来ないみたいだけど…(残念)

これを使って、環境変数の値を取得してみる。

環境変数を出力してみる

引き数の選択肢

今回は、下記の表からPATHを使用する。

実行(イミディエイトウィンドウ)

イミディエイトウィンドウで下記を入力してEnterしてみる。

? Environ("PATH")

結果(イミディエイトウィンドウ)

こんな感じで出力された。

C:\Program Files\Microsoft Office\Root\Office16\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\iCLS\;C:\Program Files\Intel\Intel(R) Management Engine Components\iCLS\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\WiFi\bin\;C:\Program Files\Common Files\Intel\WirelessCommon\;C:\Program Files\Microsoft VS Code\bin;C:\Program Files\Google\Google Apps Sync\;C:\Program Files\Google\Google Apps Migration\;C:\Program Files\nodejs\;C:\Program Files\Git\cmd;C:\Users\<ユーザー名>\AppData\Local\Microsoft\WindowsApps;C:\Users\<ユーザー名>\AppData\Roaming\npm;;C:\Program Files\Microsoft Office\root\Client

1行で出るのね…読みづらい(^^;)

でも、よく見るとパス1点ごとに;で区切られているみたい。

C:\Program Files\~~~;C:\Program Files (x86)\~~~;C:\Program Files\~~~;…

ならば、そのまま区切って一覧にしてみよう。

一覧で出してみる

実行

今度は標準モジュールなどで下記を実行してみる。

Sub MySub()

    Dim environmentVariables() As String
    environmentVariables = Split(Environ("PATH"), ";")

    Dim i As Long
    For i = 0 To UBound(environmentVariables)
        Debug.Print environmentVariables(i)
    Next i

End Sub

結果(イミディエイトウィンドウ)

こんな感じで出ました(2020/03/22(日)現在)。

C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\iCLS\
C:\Program Files\Intel\Intel(R) Management Engine Components\iCLS\
C:\WINDOWS\system32
C:\WINDOWS
C:\WINDOWS\System32\Wbem
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
C:\WINDOWS\System32\OpenSSH\
C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL
C:\Program Files\Intel\Intel(R) Management Engine Components\DAL
C:\Program Files\Intel\WiFi\bin\
C:\Program Files\Common Files\Intel\WirelessCommon\
C:\Program Files\Microsoft VS Code\bin
C:\Program Files\Google\Google Apps Sync\
C:\Program Files\Google\Google Apps Migration\
C:\Program Files\nodejs\
C:\Program Files\Git\cmd
C:\Users\<ユーザー名>\AppData\Local\Microsoft\WindowsApps
C:\Users\<ユーザー名>\AppData\Roaming\npm
C:\Program Files\Microsoft Office\root\Client

おぉー、思ったよりたくさん設定されている…
(こんな感じで一瞬でシュババッと色々出てくるのが好きなんですけど、共感してくれる人いるかな…)

まとめ

これをExcelに出力すれば、必要なパスが登録されているかチェックする、とかなら使えるかも。

「パスを通す」までをVBAでやるとしたら、ターミナルを呼び出さなきゃいけないみたい。
ちょっとめんどくさいけど、Environ関数と同じInteractionモジュールのShell関数で出来るようだ。

そもそも「環境構築ならターミナルとかで見なさいよ」と言われそうな気もするけど笑、
Excelが使えるならこんな選択肢もあるんだなー」ってことで!

参照

【VBA】自分用コーディングガイドライン

自分用のツールだけでなくお仕事としてVBAを書くことが増えてきたので、自分用のコーディングガイドラインを作ってみました。

勉強中のため、全部は固まっておらずところどころ説明が空欄だったり例が適当だったりしますが、「今のところ」の話として公開します。
今後も随時更新予定です。

このページについて

想定読者

  • 最近は作ったツールを誰かに渡すことが多いため、その点を念頭に置いて作成しています。
    作成者自身で使用する場合や、熟練者同士のコーディングなどにはそぐわない部分もあるかもしれません。あらかじめご了承ください。

  • 他の誰かが作ったものを改修する場合は、当ページの内容より作業中のルールを優先することとします。

優先度

各項目の【】の部分は、下記の順に優先としています。

優先度 意味
MUST 必ず守る
BETTER 迷ったらこっち。絶対ではない。
WISH 検討中・勉強中。

クックパッド社のコーディング規約を参考にさせて頂きました()。

ただ、まだコーディングに自信があるわけではないのでSHOULD(~すべき)ではなくBETTER(~した方が良い)としています。笑

考え中のこと、疑問点など

勉強中の身なので、まだ自分の中で確立できていないこともあります。

そのような部分には、🤔(考えていること)としてコメントを記載します。

全体

【MUST】プロシージャは1画面に収める

特別な理由がない限り、80字程度×20~30行程度までにする。

  • 1行が長くなる場合は、改行(_)を積極的に使用する。
    (並行して、コードの縦のラインが揃うように必ずレイアウトに気を使うこと。)
  • 処理の分岐等でこれを超える場合は、プロシージャの切り出しを検討する。

【MUST】省略せずに明記する

  • 省略形は極力使わず、単語を明記する。
    なお、プログラミング用語(特にVBA)として普及していて、運用担当者が一瞥で誤読なく識別できるものであれば、使用しても良い。

  • ビジネス用語などの所謂「横文字」の言葉も、可能な限り別の表現に言い換える。

    • 可読性が運用担当者のバックグラウンドに左右されやすいため。
    • 間違いなく共通認識があるものは、この限りではない。
  • インテリセンス(入力補完)を積極的に使用することとし、一般に普及している語句であれば~ation系の長い単語も使用可とする。

【BETTER】結論ファースト

  • 結果として出力したいものを最初に書き、段落(コードブロック)の要約の意味も持たせる。

    • 目的の変数
    • 補助的な変数
    • 補助的な変数…
    • 中間処理
    • 中間処理
    • 中間処理…
    • 結果
▼例1
Dim shAbc As Worksheet                    '目的の変数
Dim baseBook as Workbook                  '補助的な変数(一瞬しか使わないもの)
set baseBook = ThisWorkbook               '中間処理
set shAbc = baseBook.Worksheets("シート名") '結果
▼例2
'メイン処理 ─────────────────
Sub Main()
    Call SubProcess1
    Call SubProcess2
End Sub
 
'中間処理1 ─────────────────
Sub SubProcess1()
 
    Dim 変数 As 型
    変数 = 中間の中間1
 
    Call 中間の中間2
 
End Sub
 
Private Function 中間の中間1()As '処理
End Function
 
Private Sub 中間の中間2()
    '処理
End Sub
 
'中間処理2 ─────────────────
Sub SubProcess2()
    '処理
End Sub

【BETTER】それ自体に意味を持たない単語を避ける

(日本人が)イディオムとして認識しやすい言葉を除き、その単語自体で意味を判別できない単語は、可能な限り使用しないようにする。

    • 動詞

      • do
      • make
      • take
      • have
      • set
      • get
    • 形容詞・名詞

      • target

【BETTER】数値の表現

単語 使いどころ
Count 数え上げる時
Number ~の番号、数値の計算

🤔(Number of ~はどうする?)

【BETTER】前置詞

  • 識別子1つあたり1つまでとする。
    A of Bは、B Aと表現できる(ことが多い)ので、言い換えが出来ないか検討する。

  • プロシージャやメソッド名などの末尾に前置詞を置くと、英文として読みやすくなる(気がする)。

コメント

コメント

【MUST】読めば分かることは書かない
  • コードは主に英語を使用するが、コメントが「ただの翻訳」になってしまう時は、そもそも変数名やプロシージャ名を変更した方がスマートになることが多い。

  • 数カ月後の自分が「コードを読むだけで処理の内容を把握できるもの」を目指すこと。

【MUST】操作の意図を示す
  • 同じ結果を得るのに複数の選択肢がある場合や、何らかの処理を飛ばすような場合には、「なぜそうしたのか」を残すこと。
    (後で考慮漏れを検証しやすくするため)
【BETTER】要約コメント
  • 複数行に渡る処理には、段落分けの意味で要約コメントを付与してもよい。
    (併せて処理の区切りごとに空行も必ず挟むこと。)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 「何が起こるとエラーになるのか」を記す。

  • 資料として、ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

ドキュメンテーション・コメント

【MUST】プロシージャごとに記載する
  • 再利用しやすくするため、小分けしたプロシージャごとにコメントを記載していく。

🤔(全部に書くのは冗長かな…?)

【MUST】書式

下記の書式とする(不要なものは削除しても良い)。

''
' ●概要●
'
' @note
'   ●注意事項●
'
' 【参照設定】
' [Name] ●●●●
' [note] ●●●●●●●●●●●●
'
' 【参考】
' ●記事名● | ●サイト名●
' ●URL●
'
' 【参考】
' ●著者●(●発行年●)『●タイトル●』●出版社●
' ●URL●
' 第●版第●刷(p.●●●)
'
' @param {●型●} name ●概要●
' @param {●型●} name ●概要●
' @param {●型●} name ●概要●
'
' @return {●型●} ●概要●
'
' @customfunction ●概要●
'

(記号が多いのは、個人的に書き忘れ・消し忘れを防ぐためです。)

【BETTER】必要なデータの例示
  • 「何」が「どうなっている」と「どう出るのか」を例として記す。
    (後で考慮漏れを検証しやすくするため)
【BETTER】エラーの可能性の示唆
  • 分かる範囲で。

  • 前項に関連して、「何が起こるとエラーになるのか」を記す。

  • ベン図やパターンの列挙があると尚良し。
    (後で考慮漏れを検証しやすくするため)

【BETTER】5W1Hの明記

曖昧な表現になりそうな時は、省略せずすべて記載する。

5W1H 意味
When いつ
Where どこで
What 何を
Who/Whom 誰が/誰に
Why 何のために
How どのように

プロジェクト

【WISH】単一プロジェクトのプロジェクト名

🤔(VBAProjectのままで良い?)

【WISH】複数プロジェクトのプロジェクト名

(勉強中)

モジュール

全体

【MUST】記法

シートモジュール(後述)を除き、パスカル記法とする。

▼例
  • OpenWorkbook
  • ImportRawData
  • SheetFormatter

標準モジュール

【WISH】オブジェクト名

(考え中)

ブックモジュール

【MUST】単一プロジェクトのオブジェクト名
  • ThisWorkbookのままとする。
【BETTER】複数プロジェクトのオブジェクト名

(勉強中)

シートモジュール

【MUST】オブジェクト名
  • sh<シート名>とする。
    ※オブジェクトブラウザでのフォルム統一のため、接頭辞をshとする。

クラスモジュール

【MUST】オブジェクト名
  • 名詞形とする。
    ~er~istなど、「~するもの」といった表現だと尚良し。

ユーザーフォーム

【WISH】オブジェクト名

(勉強中)

プロシージャ

全体

【MUST】記法

すべて「キャメルケース記法」とし、スコープにより1文字目を変える。

▼例
スコープ 1文字目
パブリック 大文字 OpenBook
プライベート 小文字 sumPopulation
【MUST】求める結果 > 処理の内容

実際の処理はプロシージャの中に閉じ込めることにして、プロシージャ名は「どんな結果になるか」が読み取れる名前を優先する。

【MUST】ネストは3つまで
  • WithブロックSelect Case文を含めて、ネストは3つまでとする。

  • ネスト1階層あたり1段のインデントとする。

  • ネストが深くなる時は、関数化や早期リターン、ガード節を積極的に使用すること。

【MUST】早期リターン/ガード節の推奨

n重ループや処理の分岐などは、If文と組み合わせて改善すること。

▼例(複数条件)
Sub improveIfNests()

    If isAbc() Then Exit Sub
    If Not d = e Then Exit Sub
    If f = g Then Exit Sub
 
    'すべてFalseのときの処理
 
End Sub
▼例(ループのスキップ)
Sub improveForNestsBySkip()
 
    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then goto Continue
 
        '繰り返し処理
 
Continue:
    Next i
 
End Sub
▼例(ループの抜け出し)
Sub improveForNestsByExitFor()
 
    Dim i As Long
    For i = 0 to Ubound(array1d)
        If array1d(i) = 1 Then Exit For
 
        '繰り返し処理
 
    Next i
 
End Sub
▼例(n重ループの抜け出し)
Sub improveForNestsByBreak()
 
    Dim cell As Range
    Dim i As Long
    For Each cell In searchRange
        For i = 0 to Ubound(array1d)
            If cell.Value = 1 Then goto Break
 
        Next i
    Next cell
 
Break:
    '次の処理
 
End Sub
【BETTER】「~にする」という表現

make ~などの表現は、1語で言い換えられないか検討する

  • 参考

    接頭辞・接尾辞 意味 使いどき
    ~ize <名詞>化する、<名詞>にする
    ~fy <名詞>化する、<名詞>にする
    en~ より<形容詞>にする
    make~ ~にする 上記どれも一般的な単語がない場合
【BETTER】「~を行う」という表現

do ~take ~などの表現は、1語で言い換えられないか検討する

【BETTER】補助関数
  • スニペットなどに登録する際は、抽象的な名前も可とする。
  • 実際にコード中でヘルパー関数として使用する時は、「何がどう変わるのか」を(可能な限り)明示した名前に変更する

Subプロシージャ、Functionプロシージャ

【MUST】命名

動詞始まりの1~5語程度の語句とする。

  • 文型

    • V(動詞)
    • VO(動詞、目的語)
    • VOO(動詞、目的語1、目的語2)
    • VOC(動詞、目的語、補語)
  • GAS(ないしJavaScript)のメソッド名も参考とする。
    (英文として平易な表現が多い(気がする)ため。)

【MUST】条件を判定する関数
  • 原則、下記の命名規則とする。

    • is~
    • can~
    • has~
  • If文で呼び出すときに肯定文となるように書く。

If is確認すること()[ = True] Then

🤔(中止の条件判定の場合は、否定形の形容詞の方が自然かも?)

▼例
  • bad
Sub Example()
    If checkPathWhetherValid() = False Then Exit Sub
 
    '処理
 
End Sub
 
Private Function checkPathWhetherValid() As Boolean
 
    '条件の判定処理
 
End Function
  • good
Sub Example()
    If isInvalidPath(sourcePath) Then Exit Sub
 
    '処理
 
End Sub
 
Private Function isInvalidPath(ByVal sourcePath As String) As Boolean
 
    '条件の判定処理
 
End Function

プロパティプロシージャ

【MUST】命名

名詞句とする。

【BETTER】プロパティの接頭辞を統一する
  • 接頭辞

    接頭辞 命名 意味
    なし プロパティ名 プロパティ名
    p pプロパティ名 プライベートレベルのモジュール変数
    a aプロパティ名 プロパティプロシージャの引数
  • 名前を揃えることでプロシージャごとの置き換えがしやすくなり、フォルムも揃えやすい(はず)。

▼例1
Private pPropName AsProperty Get PropName(ByVal arg1 As, _
                      ByVal arg2 As) As 型
 
    PropName = pPropName(arg1, arg2)
 
End Property
 
Property Let PropName(ByVal arg1 As, _
                      ByVal arg2 As, _
                      ByVal 代入値 As)
 
    pPropName(arg1, arg2) = 代入値
 
End Property
▼例2

ワンライナーの場合。
(横に長くなりやすいので、出来るだけ避ける。)

Private pPropName AsProperty Get PropName(ByVal arg1 As, ByVal arg2 As) As 型:               PropName = pPropName(arg1, arg2): End Property
Property Let PropName(ByVal arg1 As, ByVal arg2 As, ByVal 代入値 As): pPropName(arg1, arg2) = 代入値:     End Property

変数/定数

全体

【MUST】記法

定数のみ「スネークケース記法」とし、それ以外はすべて「キャメルケース記法」とする。

なお、VBAではハイフン-を識別子として使用できない*1ので、必要な場合はアンダースコア_等で代用する。

▼例
種類
定数 MAX_SHEET_COUNTS
AUTHOR_NAME
それ以外 abcBookName
stopTime
【MUST】スコープごとに1文字目の大文字/小文字を統一する
▼例
スコープ 1文字目
パブリック 大文字 AppointList
プライベート 小文字 appointList
【MUST】数値の型を統一する
数値
整数 Long型
小数 Currency型
【MUST】変数名は3語まで
  • 可能であれば2語。
  • 動名詞~ing、動詞の名詞形~ationなどを積極的に使用すること。
【BETTER】型は変数名ではなく宣言に任せる
  • メソッド、関数で使用するものなど、文脈で型が分かるものは型名を変数名に付与しなくても良い。
▼例
  • not worse
Dim objAbcBook As Workbook
  • better
Dim abcBook As Workbook
【BETTER】似たものを対で扱う時は、名前やフォーマットを寄せる
  • 対になる変数を扱う時や型によって操作を分けるような時は、型名を明示した方が読みやすくなることもある。

  • この場合は、コードのフォルムを整えられるように(可能な限り)文字数を統一すること。
    (一瞥で差異を見つけやすくなるため)

▼例1
  • not worse
Dim sourcePath As String
Dim destinationPath As String
  • better
Dim srcPath As String
Dim dstPath As String
▼例2
  • not worse

    🤔(例が微妙…)

Dim wbAbcBook As Workbook
Dim nameAbcBook As String
Dim pathAbcBook As String
  • better
Dim abcBookObj As Workbook
Dim abcBookName As String
Dim abcBookPath As String

変数/定数

【BETTER】配列やコレクションの命名
  • 原則、複数形を使用する。

  • 単数形と複数形の区別がない単語や、複数形にするとかえって読みづらい語句の場合は、~Array~Collectionも可とする。

オブジェクトの定義・宣言

【BETTER】固有オブジェクト型の推奨
  • Object型ではなく、各オブジェクト固有の型で宣言すること。
    (インテリセンスが適用され、コーディングのスピード、可読性ともに向上するため。)

    • Workbook
    • Worksheet
    • 独自クラス、など
【WISH】参照設定したライブラリのオブジェクトを使用する時は、条件付きコンパイルを添える
  • ユーザーの利用環境が挙動に影響する場合にマクロが使えなくなってしまうことを防ぐ。
    ExcelからOutlookを操作する場合など)

列挙体

「変数・定数の中に入れるべきか」というのはあるけど、用途が似ているので…

【MUST】型名の接尾辞はeとする

enumeratione

【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

【BETTER】引き数の選択肢として列挙体を使う
  • モードの切替など、プロシージャの引き数の選択肢として使うと、インテリセンスが使えるようになり少しだけコーディングが楽になる。
▼注意!
  • 列挙体は宣言セクションにしか宣言できない。
    つまり、呼び出し元のプロシージャと距離が離れてしまうことが多いので、出来るだけモジュール1つにつき2~3個までに抑える。

  • VBAの仕様により、クラスモジュールなどではパブリックレベルでないと使えない。
    他のモジュールのものと名前がバッティングしないように確認しながら使用する。

▼例
Public Enum eBookType
    InputBook
    SharedBook
    TemplateBook
End Enum
 
Sub Main()
 
    Call openBook(eBookType.InputBook)
 
End Sub
 
Private Sub openBook(ByVal bookType As eBookType)
 
    Dim book As Workbook
    Dim bookPath As String
    bookPath = "c:\\abc.xlsx"
    Select Case bookType
        Case eBookType.InputBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True
 
        Case eBookType.SharedBook
            Set book = Workbooks.Open Filename:=bookPath, _
                                      ReadOnly:=True, _
                                      IgnoreReadOnlyRecommended:=False
 
        Case eBookType.TemplateBook
            bookPath = "c:\\abc.xltx"
            Set book = Workbooks.Open Filename:=bookPath, _
                                      Editable:=True, _
                                      ReadOnly:=False, _
                                      IgnoreReadOnlyRecommended:=True
    End Select
 
End Sub

構造体

「変数・定数の中に入れるべきか」というのはあるけど(ry

【MUST】型名の接尾辞はtとする

typet

【BETTER】メンバーは大文字始まりとする

🤔(たまに変数と被るのがちょっと…)
🤔(メンバーにも接頭辞つけたらうるさい…?)

参考

以下を参考にさせて頂きました。

記事・サイト

書籍

注釈

*1:「ケバブ形式」なる記法があるらしい(美味しそう…)

【VBA】なにか忘れてる? - エラー発生時のチェックリスト(実行時エラー編)

VBAのプログラムを書く機会が増えるにつれて、何度も何度も同じエラー文に遭遇することも増えてきました。
調べてみると、私は焦りやすいせいか何かを見落としている「うっかりケース」が多いみたいです。

そこで、チェックリストとして少し整理することで対策してみます。
※自分の体験ベースなのでかなり偏りがあるはずです。そのあたりは目をつぶっていただけたら幸いです!

実行時エラー(1004): 申し訳ございません。****が見つかりません。名前が変更されたか、移動や削除が行われた可能性があります。

チェック!

  • パスは合ってる?

  • パスの区切り文字(\)は足りてる?

  • フォルダの中は空っぽになってない?

実行時エラー(1004): "****"にアクセスできません。読み取り専用または暗号化されています。

チェック!

  • 「読み取り専用」で開いてる?

  • 不要なら「読み取り専用」解除した?

  • パスの表記は合ってる?

    良くあるミスとして、ファイル名を指定するときに
    Workbooks.Open Filename:="C:¥Book1.xls"
    と、すべきところで
    Workbooks.Open Filename:="Book1.xls"
    としてしまう場合があります。
    それと、ネットワークドライブの場合、
    (¥¥共有DISKなどの指定で)ドライブ名が確定していない場合にもあります。

    Excelのマクロでブックを開こうとするとエラーが出て開けません。長文ですが... - Yahoo!知恵袋

実行時エラー(1004): アプリケーション定義またはオブジェクト定義のエラーです。

チェック!

実行時エラー(9): インデックスが有効範囲にありません。

チェック!

実行時エラー(70): 書き込みできません

どんな時に出る?

ブックを別のフォルダへ移動しようとしたら発生しました。

チェック!

  • ブックは閉じてる?

実行時エラー(91): オブジェクト変数またはWithブロック変数が設定されていません。

チェック!

  • Setキーワードはある?
  • 参照先、ぜんぶ中身ある?
  • プロシージャの戻り値はオブジェクト型で指定できてる?
  • インスタンスInitializeできてる?(クラスのNewできてる?)
    • 「親クラス」が空っぽになってない?
    • 「子クラス」も確認した?
  • コンストラクタ(Initializeメソッド)の引き数は合ってる?
  • 値のプロパティにオブジェクトの参照値入れようとしてない?

実行時エラー(424): オブジェクトが必要です。

どういう意味?

オブジェクトがNothingらしいです。

チェック!

  • どこか別の場所で破棄してない?
  • 参照値は代入してある?
  • 本当に参照値代入できてる?
  • 呼び出す前にNewしてある?
  • Newできてる?(親子関係で中身が空っぽだったりしない?)

実行時エラー(438): オブジェクトは、このプロパティまたはメソッドをサポートしていません。

チェック!

  • プロパティ名やメソッド名は合ってる?
  • 自作プロパティや自作メソッドの名前変えたりした?
  • インテリセンス使えた?
    • 列挙体や構造体の上に変数やプロシージャがあったりしない?
  • Withブロックの依存関係、ズレてない?
    • インデント合ってる?
  • プロパティ呼び出してるだけになってない?
    • 何かに代入してる?
    • 値を見てる?

実行時エラー(-2147221080): オートメーションエラーです。

どんな時に出る?

個人的には、オブジェクトを呼び出すときに遭遇することが多い気がします。

チェック!

連載目次

  1. 当記事【VBA】なにか忘れてる? - エラー発生時のチェックリスト(実行時エラー編) - ゆるおたノート
  2. 【VBA】どこがダメ? - エラー発生時のチェックリスト(コンパイルエラー編) - ゆるおたノート

【バッチファイル】何やらSTARTできてないモノがあるような…?

前回、バッチファイルの作成について書きました。

【バッチファイル】指定した順番にアプリケーションを開くスクリプトを書いてみた - ゆるおたノート

ただ、分からないことがあって、「動く」ものが出来るまで3ヶ月も掛かってしまいました。
…こういうのは独学あるあるでしょうか。

まだ全部は解決してないですが、分かったことを記録しておこうと思います。

Google Chromeの便利機能も自動化するには…?

Chromeのユーザーごとに開きたい

Google Chromeでは、アカウントごとにブラウザを分けて使うことが出来る。
(今の仕事で知りました)

プロフィールを使用すると、自分の Chrome 情報(ブックマーク、履歴、パスワード、その他の設定)のすべてを他のユーザーと別々に保持することができます。

次の場合はプロフィールの使用をおすすめします。

複数の人とパソコンを共有する。
仕事用と個人用などで別々のアカウントを使用する。

Chrome を他のユーザーと共有する | Google Chromeヘルプ

これを利用してChromeの「ユーザー」を指定して起動しようとしたら、少し苦労した。

パスを確認してみる

まず何も考えずChromeのパスを指定して開くと、最後に開いていたユーザーとしてブラウザを起動する。

start "" /max "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"

これはこれで便利なのだけど、「このアカウントで!」って出来るともっと嬉しい。

そこで、試しに適当なユーザーのショートカットで右クリックして、プロパティを見てみる。

ユーザー別ショートカットの「リンク先」を確認する

--profile-directoryのあとの部分に、ユーザーを特定する情報が入っているらしい。

デフォルトのユーザーは、画像のようにDefault
その後、ユーザーを追加するごとにProfile 1Profile 2Profile 3、…と増えていく。

今回指定したいユーザーは「Profile 13」だったので、リンク先は下記のようになっている。

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 13"

ユーザーを指定して開きたい

パスが分かったので、まずは下記のように指定してみた。

@rem Chromeのパス
set userA="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --profile-directory=Profile 13"
 
start "" /max %userA%
echo %userA%

実行してみると…
C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --profile-directory=Profile 13'が見つかりません。名前を正しく入力したかどうかを確認してから、やり直してください。
怒られた。

「♪そこに~ファイルあ~りません~残ってなんか~いません~」ですと??
…分かる人からしたら、この時点で「ダメだこいつ」だと思う。

🤔「あ、これじゃ開けないんだ…」

🤔「パスは…ん、合ってるな?」

🤔「合ってるのに開けないの???なぜ???」

少し考えて、分解して変数にしてみた。

@rem Chromeのパス
@rem (`--profile-directory=`まで変数にしてみる)
set chromeExePath="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --profile-directory="
 
@rem ユーザーのプロファイル番号
@rem (ユーザーを変える時はここを変える)
set myProfile="Profile 13"
 
@rem ターゲットのフルパス
set myPath=%chromeExePath%%myProfile%
 
@rem "Profile 13"のブラウザを開く
start "" /max %myPath%

そして、実行。
C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --profile-directory=Profile 13'が見つかりません。名前を正しく入力したかどうかを確認してから、やり直してください。
…あれ、変わらない。

😫「なんでよーーーパスは合ってるじゃんーーーーー。」

小一時間調べてみても、それらしき記事が見つけられず…

その頃のつぶやき。

「私にはバッチ処理なんてまだ早いか…」と、この時は諦めた。
ドヤ顔でこんな記事も書いたくせに…

その時は突然に!

しばらく放置していたところ、最近シンノユウキさんのブログ*1を拝見していてやっと気づいた。

--profile-directoryも含めてパスだと思ってたけど、これは「パス」ではなく「オプション」なんだ。
だから、「文字列」として変数に格納すると、動かない。

(ただ、「何のオプション」なのかはまだ良く分かってない…
--profile-directoryはパスのオプション?startコマンドのオプション?)

改めてさっきのエラーメッセージを見てみると、C:\~.exeProfile 13""がついてないことに気付いた。
ダブル・クォーテーションが足りなかった…

C:\からProfile 13まで1つの文字列になっちゃってるってことかー。

というわけで、正しくはこう書くみたい。

@rem Chromeのパス
set chromeExePath="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
 
@rem ユーザーのプロファイル番号
set myProfile="Profile 13"
 
@rem "Profile 13"のブラウザを開く
start "" /max %chromeExePath% --profile-directory=%myProfile%

実行してみると…

Profile 13で開けた~

😭「できたーーーーーーーー!」

これで好きなユーザーでChromeを開ける!!

Slackも追加したいんだけどな…

「開くことは出来る」けど…

日課の予定チェックにSlackを使っているので、これもスタートアップにしたい。
…と思ったら、実行するユーザーによっては意図通りに動いてくれない。

Slackのウィンドウを開いてくれるのは確かなんだけど、コマンドプロンプトCreating Slack Applicationと出たまま、ウィンドウが閉じない。

`Creating Slack Application`と出たまま…

「どこかで失敗してるのかな?」と思いつつ、「まぁ、クリック1回くらいなら…」と手動でウィンドウを閉じると、つられて(?)Slackも閉じられてしまう…

🤔(やっぱり何か途中なんだろうなぁ…)

😨(毎日強制終了してるようなのもちょっと怖いなぁ…)

🙄(それに毎回手動で閉じては開き直すってのも…意味ないしめんどくさいな…)

「管理者ユーザー」ならちゃんと開いて終了してくれるんだけどな。
今のところ、これと「それ以外」の2件しかサンプルが無いけど。

またあとで考える。

そういうわけで、ちゃんと起動できないユーザーの分はスクリプトで起動するのを一旦諦めて、また時間を置いて考えることにした。
とりあえずスタートアップフォルダにショートカットを戻して、元通り勝手に開いてもらおう。

コードもコメントアウトしておいて…

REM rem Slack.exe
REM START "" /max "C:\Program Files\Slack\Slack.exe"
REM call :sleep

不本意だし消化不良だけど、たぶんまた知識が足りないんだろうな。
管理者で問題なく開けるんなら権限の問題だったりするのかなぁ…

このシリーズについて

バッチファイルの書き方を自分なりに勉強しています。

もし間違いや改善のアイディア等ありましたら、コメント欄やTwitter等でご意見いただけたら嬉しいです。

連載目次

  1. 【バッチファイル】指定した順番にアプリケーションを開くスクリプトを書いてみた - ゆるおたノート

  2. 当記事【バッチファイル】何やらSTARTできてないモノが…? - ゆるおたノート


*1:いつもありがとうございます!
私のための記事でも何でもありませんが笑、何故かとてもタイミングが良いのです!!

【バッチファイル】指定した順番にアプリケーションを開くスクリプトを書いてみた

スタートアップフォルダにいくつかショートカットを入れておいて、PCを立ち上げて、アプリケーションの起動を待つ…

これだけでも結構便利なのですが、ふと「これ、順番も指定できるといいのにな…」と思いました。

少し調べてみたところ、バッチファイルの練習にも良いらしいので、スクリプトを書いてみました。

スタートアップの起動順を指定するスクリプト

:::
:: メイン処理
:Main
  @rem ▼前処理
  @rem ウィンドウ出力を消す
  @echo off
 
  @rem ▼アプリを順次起動
  call :openApps
 
@rem ▼コマンドプロンプトを閉じて終了
exit
  
::  ~以下、関数シーケンス~
  
:::
:: アプリを指定順に起動する
::
:: 【参考】
:: Windows、バッチファイル(.bat)を終了するときは「exit」ではなく「exit /B」を使おう|マコトのおもちゃ箱 ~ぼへぼへ自営業者の技術メモ~
:: http://piyopiyocs.blog115.fc2.com/blog-entry-884.html
::
:: @note
::  パスにスペースがあるとエラーが出て実行できないので、パスも""で囲むこと。
:: 【例】────────────────────────
:: start "" /max "C:\xxx.xxx"
:: call :sleep
:: ────────────────────────────
:openApps
 
  rem GoogleChrome.exe(ユーザーごとに開く)
  set chromeExe="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
 
    @rem userA
    set userA="Default"
    start "" /max %chromeExe% --profile-directory=%userA%
 
    @rem 別ウィンドウでChatworkのアプリも開く
    set chatworkId="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    start "" /max %chromeExe% --profile-directory=%userA% --app-id=%chatworkId%
    call :sleep
 
    @rem userB
    set userB="Profile 1"
    start "" /max %chromeExe% --profile-directory=%userB%
    call :sleep
 
  rem Slack.exe
  set slackExe="C:\Program Files\Slack\Slack.exe"
  start "" /max %slackExe%
  call :sleep
 
  rem Evernote Clipper.exe
  start "" "C:\Program Files (x86)\Evernote\Evernote\EvernoteClipper.exe"
  call :sleep
 
  @rem 関数シーケンスの終了
exit /B
 
:::
:: 処理と処理の間に休憩を入れる
:sleep
  @rem 3秒待つ(`/nobreak`でキャンセル待ちをoff)
  timeout /t 3 /nobreak > null
 
  @rem 関数シーケンスの終了
exit /B

バッチファイルの使い方をざっくりと

コマンドの実行順

基本的に上から順に実行され、最後に指定したウィンドウが最前面の状態で終了する。
今回はアプリケーションを複数起動するスクリプトなので、上から順に開かれていく。

そのため、性能の良いのPCなら「すぐ使いたいもの」を先頭に置くと良いかも。
例えば、最初にExcelの勤怠シートを開いて、出勤時間を入力している間に他のアプリも開かれているようにする、とか。

また、PCを起動→ログインだけしておいてコーヒーを淹れに行くようなルーティンがある方であれば、「最初に使いたいもの」を最後の処理にすると良さそう。

ファイルの拡張子

スクリプトが書けたら、.batという拡張子で保存する。
はてなブログの仕様上、当記事ではコードの紹介部分でdosbatchと指定していますが、あまり深く考えないでください。)

これを好きなところに置いておいて、ダブルクリックで開くと処理が実行される。

文字化けに注意!

何も考えずVS Codeで書いて、そのまま実行してみたらビックリ。
文字化けするわ、「そんなコマンドはありません」とか出るわ…

VS Code文字コードUTF-8が基本*1だけど、Windowsコマンドプロンプトcmd.exeShift-JISにする必要がある。

というわけで、バッチファイルを作る時は文字コードを確認してから作業に入るのをお忘れなく!

※例えばVS Codeなら、setting.jsonワークスペースの設定の中であらかじめ指定しておくとラクになります。

"[bat]" : {
  "files.encoding": "shiftjis",
},

スクリプトの書き方

出力を消す

【例】基本の書き方
@rem ウィンドウ出力を消す
@echo off
コマンドを分解
処理の内容を見せない

echo offで、コマンドプロンプトのウィンドウ上にズラズラズラ~っと処理内容やコメントが表示されるのを止める。

echo off
命令文も見せない

また、コマンドの先頭に@とつけることで、命令文そのものの表示も止める。

@echo off

(*・ノェ・)コッソリ { 出力オフにしといてね~

これは、echo offだけでなく他のコマンドでも使える。

アプリケーションやファイルを開く

【例】基本の書き方

例えば、このような処理をしたい場合。

こんな時は、下記のように書く。

@rem (例)"C:\xxx.xxx"のファイルを開く
start "" /max "C:\xxx.xxx"
新規ウィンドウで開くコマンド

startコマンドで、指定したパスのファイルを新しいウィンドウで開く。
今回の場合は、下記のような構文。

start [ウィンドウのタイトル] [オプション] [パス]
引数 使い方
タイトル コマンドプロンプトのウィンドウに表示するタイトル。
""と指定すると、処理が終わった時にコマンドプロンプトのウィンドウも自動で閉じられる。
オプション /maxと指定すると、ウィンドウサイズを最大に指定できる。
パス パスにスペースが含まれている場合、エラーが出て実行できなくなる(後述)。
""で囲むとこのエラーを回避できる。

※他にもオプションがあるようなので、詳しく知りたい方はこちらへ。

変数を使う
変数の定義

変数は、下記のように定義する。

set 変数名=

例えば文字列を代入する場合は下記のように。

set 変数名="文字列"

""は無くても良い。
…のだけど、パスのようなスペースを含む文字列があるときは注意が必要になる。

startコマンドのように引数を複数使えるコマンドを実行する時に、別々の引数として認識されてしまい実行時にエラーが起きるもとになる。

とりあえず、クセとして"文字列"で指定するようにしておく。
そもそも、ファイル名やフォルダ名にスペースを使わないようにした方が良いかも…

変数の呼び出し

呼び出す時は、変数名を%で挟む。

%変数名%

複数の変数を連結する時は、こんな感じに詰めて並べる

%変数名1%%変数名2%

変数の間のスペースも文字列とみなされる
先ほどのパスの話と少し混乱するけど、文字列の連結の場合は間を詰める。

【例】Chromeをユーザーごとに開く

ここまでを踏まえて、例えばGoogle Chromeをユーザー別やWebアプリ別に開く場合は、下記のように書ける。

@rem Chromeのパス
set chromeExePath="C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
 
@rem UserAとしてChromeを開く
set userA="Profile 1"
start "" /max %chromeExePath% --profile-directory=%userA%
 
@rem 別ウィンドウでChatworkのアプリも開く
set chatworkId="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
start "" /max %chromeExePath% --profile-directory=%userA% --app-id=%chatworkId%
call :sleep
 
@rem UserBとしてChromeを開く
set userB="Profile 2"
start "" /max %chromeExePath% --profile-directory=%userB%
ちなみに…

Chromeスクリプトで起動する方法(と失敗談)について、別途記事にしています。

【バッチファイル】何やらSTARTできてないモノが…? - ゆるおたノート

処理を一時停止する

【例】基本の書き方

例えば、いま2つのファイルがあるとして、ファイルA(重め)・ファイルB(軽め)と呼ぶとする。
これらをファイルAファイルBの順に開く時、ファイルAを開いている間はファイルBを開く命令をうまく処理できないらしく、ファイルBは開いてくれないことがある。

その対策として、処理の準備をする意味で命令と命令の間に少しだけ待ち時間を設けることにする。

@rem sleep関数の定義
:sleep
  @rem 3秒待つ(キャンセル待ちはしない)
  timeout /t 3 /nobreak > null
exit /B

今回はこれをsleep関数としておく。

※なお、本来はバッチファイルに「関数」という概念は無く、正しくは「サブルーチン」というらしい。
当記事ではこのまま「関数」と呼ぶことにする。使い方は後述

コマンドを分解

timeoutコマンドで、一旦処理を止める。

@rem
timeout /t 3 /nobreak > null
待ち時間を指定する

/t 3で「3秒間待つ」という意味になる。

@rem 3秒間待つ
timeout /t 3

ただ、この場合、timeoutコマンドの実行中にコマンドプロンプトの画面に触れてしまうと、「キャンセル待ち」の状態になる。
この状態で何かキーを入力すると、「キャンセル」となって処理が中止されてしまう。

強制的に処理を続行させる

画面に触れても処理を止めないためには、/nobreakを追加して下記のように書く。

@rem キー操作に関わらず、3秒間待つ
timeout /t 3 /nobreak
出力をシンプルにする

さらに、> nullは「カウントダウンの表示を消す」という意味になる。

@rem カウントダウンを出さずに3秒間待つ
timeout /t 3 /nobreak > null

出力内容をシンプルにしたい時などに使える。

参考記事

それぞれの出力内容など、詳しくはこちらが参考になると思います。

処理をまとめる
関数の定義

:[関数名]からexit /Bまでを1つのシーケンス(=ひとつづきの処理)として定義する。

@rem ↓関数のラベル
:[関数名]
 
  @rem ~処理~
 
exit /B
関数の終わり方

関数の末尾でexitコマンドに/Bオプションをつけると、「現在実行中のシーケンスを終了して呼び出し元に戻り、次の処理に移る」という命令になる。

exit /B
関数の呼び出し

定義した関数を呼び出す時は、こんな感じ。

call :[関数名]

VBAに似てる。というか、VBA「が」似てるんだろうけど。
(歴史は苦手なので今は深く考えないことにする)

ただし、関数名の頭に:が必要なので忘れないように注意する。

実行の準備

保存

ここまでを.batで保存して、スタートアップフォルダに入れておく。
(ファイル名は任意のものでOK。)

格納場所

スタートアップフォルダの場所はこちら。

@rem ユーザーごと
"C:\Users\[ユーザー名]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup"
 
@rem 全ユーザー共通
"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp"

以上。

これで、起動やログインのたびに好きな順番で開いてくれるようになった!

このシリーズについて

バッチファイルの書き方を自分なりに勉強しています。

もし間違いや改善のアイディア等ありましたら、コメント欄やTwitter等でご意見いただけたら嬉しいです。

連載目次

  1. 当記事【バッチファイル】指定した順番にアプリケーションを開くスクリプトを書いてみた - ゆるおたノート

  2. 【バッチファイル】何やらSTARTできてないモノが…? - ゆるおたノート

参考記事


*1:Unix系は試したことないので分からないですが…

【VBA】クラスの作り方を整理してみた(基本用語編)

前回の記事が長すぎたので、良きところで分割してみました。

【VBA】クラスの作り方を整理してみた(移行手順編) - ゆるおたノート

クラス

意味

今回のメイン。
「Aというオブジェクトは、BプロパティやCメソッドを持っている」といったオブジェクトの定義を行う。
本やWebページでは、「設計書」や「鋳型」と説明されていることが多い。

VBAでは、クラスモジュールを使ってユーザー自身が新しいクラスを作成できる。

インスタンス

実際にクラスを利用する時は、下記どちらかの段階でNewキーワード*1を使って「クラス(=設計図)からオブジェクト(=実体)を生成」する。

この生成されたオブジェクトのことをインスタンスと呼ぶ。

モジュール

処理の集まり。
VBAでは、標準モジュールクラスモジュールシートモジュール等がある。

今回は「クラスモジュール」にクラスの設計を記入していく。

スコープ

データを参照可能な範囲。「●●の中ならアクセスOK」の●●の部分。
プライベートとか、パブリックとか、グローバルとかを使って、「~~レベル」と呼ばれる。

過去記事に、掲載当時に私が理解していた範囲でまとめています。

プロパティ

インスタンスの内容や状態に関する情報*2。多くは「属性」と訳される。
VBAでは、ブックの名前やセルの値、場所、オブジェクトの参照値、など。

情報をインスタンス自身に持たせて、「●●の値を取得する」という時や「値を設定する」といった時に使う。
スコープをPublicにすることで、外のモジュールからインスタンス.プロパティ名で呼び出せるようになる。

プロシージャで定義する場合、使用するプロシージャは機能別にGetLet/Setの3種類に分かれている。

名前は「大文字始まり」の名詞が多い(気がする)。

値を取得する

Property Getプロシージャ

プロパティの値を返すことのみ出来る。
VBAの仕様上、Property Getプロシージャ内で直接「プロパティへの代入・設定」することはできないので、「変数や定数を経由」して値を設定する必要がある。

使用例
'[クラスモジュール] Exampleクラス
Private 仮変数 AsProperty Get プロパティ名()As 型
    仮変数 = 123
    プロパティ名 = 仮変数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
    'インスタンスexのプロパティの中身を確認
    Debug.Print ex.プロパティ名 '「123」と表示される
End Sub

値を代入する

Property Letプロシージャ

プロパティに値を設定するためのプロシージャ。
値を代入するときのLetキーワード*3が入っている。

使用例
'[クラスモジュール] Exampleクラス
Private 仮変数 AsProperty Let プロパティ名(仮引数 As)
    仮変数 = 仮引数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
    ex.プロパティ名 = 123 'プロパティに値を代入
End Sub
Property Setプロシージャ

参照するオブジェクトをプロパティに設定するためのプロシージャ。
Subプロシージャ等でオブジェクトを代入するときのSetと同じ。

使用例
'[クラスモジュール] Exampleクラス
Private 仮変数 As オブジェクト系の型

Property Set プロパティ名(仮引数 As オブジェクト系の型)
    Set 仮変数 = 仮引数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
     'プロパティにオブジェクトの参照値を代入
    Set ex.プロパティ名 = オブジェクト
End Sub

引数の正しい使い方?

リファレンスによると、同じ名前のプロパティプロシージャは、引数名とデータ型をほぼ同じにしなければならないらしい。

The first argument through the next to last argument (1, …, n) must share the same names and data types in all property procedures with the same name.

プロパティ プロシージャ (VBA) を作成する | Microsoft Docs

同じ名前のPropertyプロシージャ間では、最初の引数~最後から2番目の引数は、同じ引数名とデータ型を共有していなければならない。

ただ、そうなっているクラスをあまり見たことがない気が。
…初心者の私が知っている限りでは。

「実際はどちらでも良い(ことにしてある)」ってことなのかな…?

<2019/10/15追記>
前回記事のコメントにて、imihito(id:imihito)さんより下記補足いただきました。 ありがとうございます!

VBAでも常に守られています(条件を満たさないPropertyを作成するとコンパイルエラーになります)。

Property Getの返り値は、Property Set/Letの最後の引数と同じ型にする必要がありますし、
Property Getの引数は、Property Set/Letの最後から1個前の引数までと、名前・型が一致する必要があります(Getに引数が無ければ、Set/Letの引数はGetの返り値と同じ型の1個のみ)

前回の記事のコメント欄より

個人的には、最後の1文が重要そうです!

メソッド

命令。
SubプロシージャFunctionプロシージャが使える。

名前は「大文字始まり」で、「●●する」のように動詞にすることが多い(気がする)。

スコープをPublicにすることで、外のモジュールからインスタンス.メソッド名で呼び出せるようになる。

'[クラスモジュール(Exampleクラス)]
Sub メソッド名(仮引数 As)
    '処理
End Sub
'[標準モジュール]
Sub 使用例()
    Dim ex As New Example
    ex.メソッド名 引数 'メソッドの呼び出し
End Sub

引数を「省略可」にするには…?

VBAでは、引数の初期値には定数(もしくは定数式)しか指定できない
ということは、Optionalな引数にプロパティの値を指定したい時は一工夫が必要そう…(調べ中)

いま頭にあるのは、下記の2つ。

  • プロパティ用の値を変数ではなくモジュールレベルの定数で定義する
  • プロシージャの中で引数の有無を判定して値を設定する
    → 数値だと初期値が0なので少し面倒かも…

あとがき

ついつい適当な憶測で書いてしまうので、記事をアップする時はこんな弱小ブログでも変な認識・知識を広めちゃったりしないか毎回結構ドキドキです…

でも、今回VBAブロガーの方々に色々ご指摘いただけて嬉しいです。とっても勉強になります!
私の頭で全部理解できてるかと言ったら微妙ですが、精進します!!

…自分でも、もっとよく調べてから書かないとですね。

このシリーズについて

初心者の頭でVBAでクラスを作成する方法の整理に挑戦しています。

連載目次

  1. オブジェクト指向って、なんだ? - ゆるおたノート
  2. 【VBA】クラスの作り方を整理してみた(移行手順編) - ゆるおたノート
  3. 当記事【VBA】クラスの作り方を整理してみた(基本用語編) - ゆるおたノート

注釈

*1:<2019/10/14追記>
当初「Newステートメント」と書いていましたが、「Newキーワード」の誤りでした。誤解を招く表現、大変失礼いたしました…

▼参考

そして、ご指摘頂いたチン☆テクラ(id:akashi_keirin)さん・鵜原パソコンソフト研究所さん、ありがとうございます!

*2:テレビゲームで言うところの火属性とか、ステータス異常とか、能力値とか。

*3:本来は変数などに代入する時も必要だけど、普段は省略しても動くように作られているらしい。

【VBA】クラスの作り方を整理してみた(移行手順編)

標準モジュールに書いていた処理を、何とかクラスモジュールに移行出来るようになってきました。

まだ世界がごちゃごちゃですが、記憶の新しいうちにメモを残してみます。

基本用語・概念については、こちらからどうぞ。 【VBA】クラスの作り方を整理してみた(基本用語編) - ゆるおたノート

手順

手続きの流れを考える

処理のテーマ

今回は、例として「フォルダ内の複数のブックで、シートの値を置き換えて名前を変えて保存する」場合を考える。

[例]手続きの流れ

だいたいこんな処理を書くと思う。(日本語でごめんなさい)


  • フォルダ内に処理対象のブックがあるか確認する
  • 拡張子付きファイル名を取得
  • パスを取得
  • パスを指定してブックを開く
  • シートを取得
  • テーブルを取得
  • セルの値を取得
  • 文字列に変換
  • 文字列を置き換える
  • ブックの拡張子付きファイル名を取得
  • 現在のファイル名から新しいファイル名を取得
  • 格納先のフォルダを取得
  • ファイル名とフォルダの場所からフルパスを取得
  • 新しいパスでブックを保存する
  • ここまでの処理を同じフォルダのブックの数だけ繰り返す
  • メッセージボックスで「完了」をお知らせ。

処理の分解

処理を区切ってプロシージャを分ける

「〜して、〜して、〜して、…」のように、「『〜して』毎」*1に1つのプロシージャにしてみる。
これを、一般に部品化と呼ぶ。

※詳しくはこちらが参考になります*2 VBA 中級者を悩ませるプロシージャ分割をマスターする極意 - t-hom’s diary

ただし、あまり細かくし過ぎると行数が増えてかえって管理が大変になるばかりなので、内容によっては2~3個の処理を1つのプロシージャにまとめることもある。

目安としては、1プロシージャあたりPCのディスプレイ1枚分(20~30行)くらい。
…区切るサイズや場所には個人差もあると思うので、絶対ではない。

[例]処理をざっくり分割

上記のの場合、ざっくり下記のように分けられると思う。


  • フォルダを確認
  • フォルダ内に処理対象のブックがあるか確認する
  • パスを取得
    • 1つ目のファイル名を取得
    • ファイル名とフォルダの場所からフルパスを取得
    • パスを取得
  • 値を取得
    • パスを指定してブックを開く
    • シートを取得
    • テーブルを取得
    • データ範囲を取得
    • 二次元配列として値を取得
  • 値を置き換える
    • セルの値を取得
    • 文字列に変換
    • 文字列を置き換える
    • セルの値を置き換える
    • シートの数だけ繰り返す
  • ブックを保存する
    • ブックの拡張子付きファイル名を取得
    • 現在のファイル名から新しいファイル名を取得
    • 格納先のフォルダを取得
    • ファイル名とフォルダの場所からフルパスを取得
    • 新しいパスでブックを保存する
  • ブックの数だけ繰り返す
  • メッセージボックスで「完了」をお知らせ。

出来るだけ共通の処理を抽象化する

「他のブックでもこのマクロを使うとしたら?」と考えながら、プロシージャや変数、引数の名前を考える。

[例]処理の抽象化

たとえば…

変更 関数名
⚫️⚫️社Bookの全シートの文字列を▼▼に置き換える
全文字列を置き換える(ワークブック置き換え前置き換え後

処理を分類してメソッド化

似たようなプロシージャをまとめる

今回の例であれば、「ファイル名とフォルダの場所からフルパスを取得」という処理が複数回登場していて、やることは共通している。

その前後にある「フォルダのパスやファイル名を『なんやかんや』する」処理も、「パスの操作」という意味で似ている。
このように、似たものを操作するプロシージャを分類して、いくつかのカテゴリに分けてみる。

さらに、標準モジュール(もしくはシートモジュール)にメイン処理用のプロシージャを作って、こちらから部品化・カテゴリ分けしたプロシージャを呼び出すことにする。

[例]プロシージャの分類

今回の例であれば、ざっくりこんな感じに分けられそう。

  • メイン・プロシージャ
  • 処理対象のブックの判定
  • パスの操作
  • ブックを開く、保存する
  • シートの値の取得
  • 文字列の操作

※「パスの操作」は、プロシージャのサイズによっては「文字列の操作」にまとめても良いかもしれない。

メイン処理以外をオブジェクトモジュール化する

メインのプロシージャ以外を、プロシージャのカテゴリごとに別々のクラスモジュール(やシートモジュール)として独立させる。

プロシージャのスコープを分ける

モジュール内専用の処理外部から呼び出せる処理(=メソッド)をそれぞれ区別し、スコープを設定しておく。
こうすることで、パブリックのものだけ入力補完が効くようになり、この後のコーディングが楽になる。

スコープのキーワード
スコープ キーワード 呼び方
全モジュール共通 Public パブリック・モジュール・レベル
モジュール内専用 Private プライベート・モジュール・レベル
クラス化のメリット

クラスの中で処理や値のスコープをPrivate以下にすると、外部のモジュールからは内部処理やデータの状態が見えなくなる。
このことをオブジェクト指向的には隠蔽(いんぺい)と呼び、特に「クラス」といった枠の中に閉じ込めることカプセル化という。

こうすることで、外部から値の変更などの干渉を受けづらくなり、コードが少し安全になる(はず)。

※「隠蔽」や「カプセル化」の目的について、詳しくはこちらもご参照いただけたら幸いです。 オブジェクト指向って、なんだ? - ゆるおたノート

プロパティの設定

共通の引数を探す

同じものを参照・利用しているのであれば、まとめて「プロパティ」にする。
値や参照はプロパティに代入して、メソッド内ではプロパティから呼び出すようにすることで引数を減らせる。

また、クラスモジュール内でも、メソッドの中からMe.プロパティ名で呼び出せるようになる(後述)。
入力補完も効くので、コーディングの効率や可読性も上がる*3

プロパティの値をモジュールレベル変数とする

プロパティの中身を参照する時はProperty Getプロシージャを呼び出す。
しかし、Property Getプロシージャの戻り値には値を直接代入できないので、下記のような書き方はできない
<2019/10/14追記>
「戻り値に代入」はできます。作例も差し替えております。失礼いたしました…

Property Getプロシージャには値を直接代入できないので、下記のような書き方はできない。

'[クラスモジュール] Exampleクラス
Property Get プロパティ名()As 型
    プロパティ名 = '←直接の代入は出来ない
End Property
'[標準モジュール]
Sub test()
    Dim ex As New Example
    ex.プロパティ名 = 新しい値
End Sub

実行すると、下記のようなコンパイルエラーが発生する。

値の取得のみ可能なプロパティに値を設定することはできません。

このため、Property LetプロシージャProperty Setプロシージャを使って「値を設定」できるようにする(詳細は後述)。

このときに、プロパティではなく「変数」を経由して「値」を代入することで、間接的にプロパティの中身を変更できるようになる。
そんなわけで、今の段階でProperty Getプロシージャも変数の値を返すようにしておく。

[例]変数を経由するよう変更
'[クラスモジュール] Exampleクラス
Private 変数 AsProperty Get プロパティ名()As 型
    プロパティ名 = 変数
End Property
変数のスコープを分ける

この時に、プロシージャと同じように「プロジェクト全体で使用するもの」と「モジュール内で完結するもの」、「プロシージャ内で完結するもの」を整理して、スコープを調整しておく。

プロパティ用の変数でスコープを「プライベート・モジュール・レベル」にしておくことで、「このクラス専用」の変数とすることができる。

つまり、「このクラス専用」の変数は、プライベートなプロシージャと同じように他のモジュールからは呼び出せなくなり、「隠蔽」されていることになる。
(以下、当記事ではこの変数のことを便宜上「隠し変数」と呼ぶことにする。)

スコープのキーワード
スコープ キーワード 呼び方
全モジュール共通 Public パブリック・モジュール・レベル
モジュール内専用 Private/Dim プライベート・モジュール・レベル
プロシージャ内専用 Dim プロシージャレベル
コンストラクで「隠し変数」に代入する

Class_Initializeという特別なプロシージャを作ると、インスタンスの生成時に必ず呼び出され、自動でプロパティに値を設定するように出来る*4

ただし、上述のようにプロパティではなく隠し変数に値を代入するように書いておく。

[例]コンストラクで隠し変数に代入
Private 隠し変数1 AsPrivate 隠し変数2 AsPrivate 隠し変数3 As オブジェクト系の型
Private 隠し変数4 As オブジェクト系の型
 
Public Sub Class_Initialize()
    隠し変数1 =Call 必ず実行する処理 '追加処理を入れても良い
    隠し変数2 =Set 隠し変数3 = オブジェクト
    Set 隠し変数4 = 値の準備とか(引数) '関数も使える
End Sub
コンストラクを使いやすくする

Class_Initializeプロシージャは「引数を設定できない仕様」になっているので、インスタンスの初期値は固定になってしまう。
いつも同じ値にしたいとは限らないので、これでは少し使いづらいことがある。

そこで一工夫。

Initializeメソッド(名前は自分で分かりやすいように変えてもOK)を別途作って、インスタンス生成時に引数として値を渡しつつ呼び出すことにする。
※この場合は、Class_Initializeプロシージャは作成しなくても良い。

これで、インスタンスの生成時にまとめて任意の値を設定できる。
(別の言語では、このような処理をするメソッドを「コンストラク」と言う。)

[例]引数が使えるコンストラク
'[クラスモジュール] Exampleクラス
Private 隠し変数1 AsPrivate 隠し変数2 AsPrivate 隠し変数3 As オブジェクト系の型
Private 隠し変数4 As オブジェクト系の型
Private 隠し変数5 As オブジェクト系の型
 
Public Sub Initialize(ByVal 仮引数1 As, _
                      ByRef 仮引数2 As オブジェクト系の型)
    隠し変数1 = 仮引数1
    
    Call 必ず実行する処理
    隠し変数2 = '引数ではなく値を指定しても大丈夫
    
    Set 隠し変数3 = 仮引数2
    Set 隠し変数4 = 値の準備とか(引数)
    Set 隠し変数5 = オブジェクト 'オブジェクトも大丈夫
    
End Sub
'[標準モジュール]
Public Sub Main()
    
    Dim test As New Example
    'Initializeメソッドを呼び出してプロパティの初期化
    test.Initialize 仮引数1:=引数1, _
                    仮引数2:=引数2    
End Sub
終了処理を追加する

モジュールレベル以上の変数は、マクロの実行が完了しても値が破棄されないので、マクロを複数回実行する時などは注意が必要になってしまう。
Class_Terminateという特別なプロシージャを作ると、インスタンスが不要になった時に自動で終了処理(=Terminate)されるようにできる。

<2019/10/14追記>
当初、上記例の中で変数の初期化・破棄処理を書いておりましたが、ことりちゅん (id:Kotori-ChunChun)さんより下記の通りアドバイスを頂きました。

クラスはインスタンスを破棄した時点でメンバの変数は一緒に消失しますから、サンプルのTerminateのように変数の初期化は意味が無いです。

ことりちゅん (id:Kotori-ChunChun)さんのツイートより

これに従い、作例は下記に修正いたしました。ご指摘ありがとうございます!

[例]終了処理
Public Sub Class_Terminate()
    Call 必ず実行する処理 'ゴミデータのお掃除など
End Sub
Property Getプロシージャでプロパティに隠し変数を代入する

ここまで準備ができたら、プロパティの値を設定していく。

上述のように隠し変数を経由してプロパティに値を設定することで、外部のプロシージャからはインスタンス名.プロパティ名でプロパティの中身を見られるようになる。

[例]値を代入
Private 隠し変数 AsProperty Get プロパティ名()As '~処理~
    隠し変数 = 値
    プロパティ名 = 隠し変数
    
End Property
[例]オブジェクトの参照値を代入
Private 隠し変数 As オブジェクト系の型
 
Property Get プロパティ名()As オブジェクト系の型
    
    '~処理~
    Set 隠し変数 = オブジェクト
    Set プロパティ名 = 隠し変数
    
End Property
ちなみに…

外部ではなく同クラス内で呼び出す時は、Me.プロパティ名と書くことができる。

[例]同クラス内で呼び出し
'[クラスモジュール] Exampleクラス
'▼プロパティ
Private 変数 AsProperty Get プロパティA()As 型
    プロパティA = 変数
End Property
 
'▼メソッド
Public Function test()
    Debug.Print "このインスタンスのプロパティAは、現在" & Me.プロパティA & "の状態です。"
End Function
Property Let/Setプロシージャで、プロパティに代入出来るようにする

プロパティは、中身を見るだけでなく処理の途中で値を変えたくなるときもある。
そういう時は、Property Let/Setプロシージャで値を設定できるようにしておく。

これで、外部のモジュールからプロパティに値を代入できるようになるので、実用に耐える(はず)。

[例]プロパティに代入するプロシージャを追加
Private 隠し変数 AsProperty Let プロパティ名(仮引数 As)
    '処理
    隠し変数 = 仮引数
End Property

繰り返しになるが、プロパティの設定は隠し変数を経由する。

そうしないと、プロパティに代入後も「隠し変数」の値は変わらず、改めてプロパティを呼び出して中身を見たら「元の値に戻ってる?」なんて混乱することに…

あとがき

VBAの神々によれば、ここまでがクラス入門編だそうです。
私はここに至るまで半年以上かかってしまいました…

「分かったような分かんないような…」で何度も書いては消してを繰り返してるので、キレイな文章はもう放棄です。
話の整理は未来の私に託します。笑

慣れてきたら、プロパティを先に作り込んでからメソッドを作り始める方が、入力補完の助けも得られてもっと書きやすいかもですね。
「どの基準・どの単位でクラスにまとめるか?」もまだ感覚が掴めてないです。
数をこなして慣れるしかないんですかね…

このシリーズについて

初心者の頭でVBAでクラスを作成する方法の整理に挑戦しています。

連載目次

  1. オブジェクト指向って、なんだ? - ゆるおたノート
  2. 当記事【VBA】クラスの作り方を整理してみた(移行手順編) - ゆるおたノート
  3. 【VBA】クラスの作り方を整理してみた(基本用語編) - ゆるおたノート

注釈

*1:日本語の文法的に言うと「文節単位」?

*2:いつもお世話になっております!!
この記事に限らず、VBA使いの人は読んで損はないブログです。

*3:入力は補完に任せられるから、多少長い名前をつけても大丈夫。

*4:Initializeは「初期化」のこと