Skip to content

Instantly share code, notes, and snippets.

@flada-auxv
Created December 2, 2012 15:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flada-auxv/4189358 to your computer and use it in GitHub Desktop.
Save flada-auxv/4189358 to your computer and use it in GitHub Desktop.
モンキーパッチの影響と対応策。ActiveSupportに見られるモジュールを利用する手法とRuby2.0から導入されるrefinments
# -*- coding: utf-8 -*-
# モンキーパッチには二つの問題がある。
# ・変更の範囲が「グローバル」である事。
# ・変更が行われた事が「見えづらい」事。
#
# 変更が見えづらい点への対応策として、ActiveSupportではモジュールを利用してモンキーパッチを明示的にしている。
# rails/activesupport/lib/active_support/core_ext配下。
# モジュールにメソッドを定義して、オープンクラスでインクルードする事で、#ancestors()などで確認する事が出来る。
# もちろんこれだけでは、「グローバル」な変更への配慮は出来ていないが。
module MyApp
module CoreExtensions
module Fixnum
module TimeFormat
UNIT_SCORES = {
h: 3600,
m: 60,
s: 1
}
# @param format Time#strftimeの引数と同じ 日付に対する指定はしない事
# @param unit :h = 時間、:m = 分間、:s = 秒間
# 86400秒以上の数値に対して呼び出された場合はnilを返す
def to_time(format: "%H:%M", unit: :m)
second_notation = self * UNIT_SCORES[unit]
return nil if second_notation > 86400
h, m, s = 0, 0, 0
m, s = second_notation.divmod(60)
h, m = m.divmod(60) if m > 60
Time.now.instance_eval do
return Time.local(year, mon, day, h, m, s).strftime(format)
end
end
end
end
end
end
class Fixnum
include MyApp::CoreExtensions::Fixnum::TimeFormat
end
p 480.to_time # => "08:00"
p 14.to_time(unit: :h) # => "14:00"
p Fixnum.ancestors
# => [Fixnum,
# MyApp::CoreExtensions::Fixnum::TimeFormat,
# Integer,
# Numeric,
# Comparable,
# Object,
# PP::ObjectMixin,
# Kernel,
# BasicObject]
# -*- coding: utf-8 -*-
# refinementsを使えば、影響をmoduleやclass単位に限定する事が出来るので「グローバル」なスコープを汚染しない。
# 影響を与えるそのスコープにてusing句で明示されるので、「見えづらい」点も解消するはず。
# もともと「見えづらい」というは、グローバルな影響を及ぼす事に起因してる面もあると思う。
module TimeFormatExtensions
refine Fixnum do
UNIT_SCORES = {
h: 3600,
m: 60,
s: 1
}
# @param format Time#strftimeの引数と同じ 日付に対する指定はしない事
# @param unit :h = 時間、:m = 分間、:s = 秒間
# 86400秒以上の数値に対して呼び出された場合はnilを返す
def to_time(format: "%H:%M", unit: :m)
second_notation = self * UNIT_SCORES[unit]
return nil if second_notation > 86400
h, m, s = 0, 0, 0
m, s = second_notation.divmod(60)
h, m = m.divmod(60) if m > 60
Time.now.instance_eval do
return Time.local(year, mon, day, h, m, s).strftime(format)
end
end
end
end
module MyApp
using TimeFormatExtensions
p 480.to_time # => "08:00"
p 14.to_time(unit: :h) # => "14:00"
end
p 480.to_time # => NoMethodError
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment