Help us understand the problem. What is going on with this article?

Rubyでインクルードガード的なものを作る

More than 1 year has passed since last update.

TL;DR (長い3行で)

・同じスコープに同じ定義値が復数あると警告がでる
・直接requireとシンボリックリンクのrequireが同居しているのが原因だと修正できない
・C/C++でおなじみのインクルードガード相当のことをRubyでもやる

困りごと

同じRubyファイルを直接requireする子と,シンボリックリンク経由でrequireする子が同居すると,↓ のような警告がでます.

warning: already initialized constant

本質的には同じものをrequireしているので,警告がでても困ることはさして無いのですが,Console上に出てくるのでちょっと目障り.

C/C++であるようなインクルードガードがRubyでも欲しい.

まずは問題の再現

↓ 用意するのは3ファイル.

main.rb          → requireをcallする側
required.rb      → requireされる側
required_link.rb → required.rbへのシンボリックリンク

requireされる側のファイル

required.rb
TOP_LEVEL_DEF = "top level def"
puts "## require DONE"

requireする側のファイル

main.rb
# 直接require
require_relative './required.rb'
# シンボリックリンク経由でrequire
require_relative './required_linked.rb'

puts "## TOP_LEVEL_DEF = #{top level def}"

ここでmain.rbを実行すると,↓ のような結果になります.

## require DONE
required_linked.rb:1: warning: already initialized constant TOP_LEVEL_DEF
required.rb:1: warning: previous definition of TOP_LEVEL_DEF was here
## require DONE
## TOP_LEVEL_DEF = top level def

required.rbが2回読み込まれ,TOP_LEVEL_DEFの2回目の初期化のときに警告が出ていることがわかります.

Ruby版インクルードガード

C/C++のように#ifndefが使えれば良いのですが,定数が宣言されているかどうか,を調べるような関数は見つけられませんでした.

ただ,カレントスコープでアクセス可能な定義値の一覧を取得する関数はあるようです.

Module.constants
https://docs.ruby-lang.org/ja/latest/method/Module/s/constants.html

これを使えば,定数の未宣言/宣言済の判定ができそうです.

先ほどのrequired.rbを ↓ のように改造します.

required.rb
if !Module.constants.include?(:IS_REQUIRED)
  IS_REQUIRED = true # 値は特に意味なし

  TOP_LEVEL_DEF = "top level def"
  puts "## require DONE"
end

この状態でmain.rbを実行すると,

## require DONE
## TOP_LEVEL_DEF = top level def

という出力になり,警告が出ないようになりました.
required.rbが1回しか読み込まれていないことが確認できますし,そのあとの定義値の使用も問題ありません.

まとめると

if !Module.constants.include?(:IS_REQUIRED)
  IS_REQUIRED = true

  # Do something.

end

という構文でRubyでもC/C++のようなインクルードガードが実現できそうです.
あまり使う機会は無さそうですが,直接とシンボリックリンク経由で同一ファイルをrequireすることがあったので調べてみました.

この半端ではない車輪の再発明感

追記 (2017/11/22)

コメントでdefined?関数を教えていただきました.
こちらを使うほうが短くて直感的な書き方にできるのでよさそうです.

if !defined? IS_REQUIRED
  IS_REQUIRED = true

  # Do something.

end

---///

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away