본문으로 건너뛰기
김신건의 로그

[Rails] Active Support: Ruby 확장

· 수정 · 📖 약 1분 · 303자/단어 #rails #active-support #ruby #helper
active support, rails ruby extension, concern, Time helpers, blank present

정의

Active Support는 Ruby 표준 라이브러리에 Rails가 추가하는 유틸리티 + 코어 확장. 1.day.ago, "hello".pluralize, blank? 등 모든 Rails 코드에서 사용. Rails 없는 Ruby 프로젝트도 require "active_support/all"로 사용 가능.

주요 카테고리

  • Time / Date 헬퍼
  • 문자열 변환
  • Hash / Array 유틸
  • Object 메서드 (blank?, present?)
  • Concern / Callbacks
  • Cache
  • Notifications (instrumentation)
  • 동시성 헬퍼

Time / Date

1.day.ago               # 1일 전
3.hours.from_now        # 3시간 후
1.month.from_now
2.weeks.ago.beginning_of_week
Date.today.end_of_month
Time.current            # 현재 (시간대 인식)
Date.tomorrow
Time.zone.parse("2026-06-20 14:00")

# Duration
1.day + 3.hours         # 27시간
2.days.in_seconds       # 172800
1.year.in_days          # 365.25

# 비교
1.day.ago < Time.current    # true

# 포맷
Time.current.to_s(:db)       # "2026-06-20 14:30:00"
Time.current.to_fs(:long)    # "June 20, 2026 14:30"

문자열 확장

"hello".pluralize          # "hellos"
"hellos".singularize       # "hello"
"helloWorld".underscore    # "hello_world"
"hello_world".camelize     # "HelloWorld"
"hello world".titleize     # "Hello World"
"hello world".parameterize # "hello-world"
"hello".starts_with?("he") # true (Ruby 2.5+ 표준)
"hello".ends_with?("lo")

# 잘라내기
"hello world".truncate(8)         # "hello..."
"hello world".truncate(8, omission: "…")
"hello world".truncate_words(2)   # "hello world..."

# 코드 변환
"some_class_name".classify     # "SomeClassName"
"SomeClass".tableize           # "some_classes"
"SomeClass".foreign_key        # "some_class_id"
"posts/comment".singularize    # "posts/comment"

# 변환
"5".to_i; "5.0".to_f; "true".to_b    # to_b는 ActiveModel
"<p>Hello</p>".html_safe?      # false (자동 escape)
ActionController::Base.helpers.strip_tags("<p>Hi</p>")    # "Hi"

Object 메서드

blank? / present?

nil.blank?         # true
"".blank?          # true
"  ".blank?        # true
[].blank?          # true
{}.blank?          # true
0.blank?           # false

"hello".present?   # true
"".present?        # false
"  ".present?      # false

present? = !blank?. nil-safe로 사용.

presence

""    || "default"      # "" (빈 문자열이지만 truthy)
"".presence || "default"    # "default" (blank면 nil)

name = params[:name].presence || "anonymous"

try

user.try(:name)              # user가 nil이면 nil, 아니면 user.name
user&.name                   # Ruby 2.3+ safe navigation (더 명시적)

user.try(:profile).try(:bio)
user&.profile&.bio

try는 정의되지 않은 메서드도 안전. &.는 NoMethodError.

Hash 확장

{a: 1, b: 2}.symbolize_keys      # {a: 1, b: 2}
{"a" => 1, "b" => 2}.symbolize_keys  # {a: 1, b: 2}
{a: 1, b: 2}.stringify_keys      # {"a" => 1, "b" => 2}

{a: 1, b: 2}.except(:a)          # {b: 2}
{a: 1, b: 2}.slice(:a)            # {a: 1}

{a: 1, b: 2}.deep_merge(b: 3, c: 4)    # {a: 1, b: 3, c: 4}
# 중첩 hash 재귀 merge

{a: 1, b: 2}.deep_transform_keys(&:to_s)
{a: 1, b: nil}.compact     # {a: 1}

# 기본값
hash.fetch(:key, "default")
hash.dig(:a, :b, :c)       # nested 안전 접근

HashWithIndifferentAccess

h = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
h[:a]      # 1
h["a"]     # 1 (string/symbol 동일)

# Rails params도 같은 동작
params[:user][:name]
params["user"]["name"]

Array 확장

[1, 2, 3].in_groups_of(2)               # [[1, 2], [3, nil]]
[1, 2, 3, 4, 5].in_groups(2)            # [[1, 2, 3], [4, 5, nil]]
[1, 2, 3].split(2)                       # [[1], [3]]
[1, 2, 3, 4].second                      # 2
[1, 2, 3, 4].third
[1, 2, 3, 4].fourth ... fifth, forty_two   # 농담? 진짜 있음
[1, 2, 3].to_sentence                    # "1, 2, and 3"
%w[apple banana].to_sentence(words_connector: ", ", two_words_connector: " 또는 ")

# Pluck (배열 of hash)
[{a: 1}, {a: 2}].pluck(:a)   # [1, 2]

Concern

extend ActiveSupport::Concern. rails-concerns 참고.

Configurable

class MyClass
  include ActiveSupport::Configurable

  config_accessor :api_key, :timeout
  config.timeout = 30
end

MyClass.config.api_key = "..."
MyClass.config.api_key

class-level 설정.

Notifications (Instrumentation)

Rails 내부도 사용. 임의 이벤트 publish/subscribe.

# 구독
ActiveSupport::Notifications.subscribe("user.signup") do |name, started, finished, unique_id, payload|
  Rails.logger.info("User signed up: #{payload[:user_id]}")
end

# 발행
ActiveSupport::Notifications.instrument("user.signup", user_id: user.id) do
  send_welcome_email(user)
end

Rails 자체 이벤트:

  • sql.active_record (모든 SQL)
  • process_action.action_controller
  • render_template.action_view
  • start_processing.action_controller

APM 도구가 자동 구독.

ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
  event = ActiveSupport::Notifications::Event.new(*args)
  Rails.logger.debug("SQL: #{event.payload[:sql]} took #{event.duration}ms")
end

Numeric 확장

50.bytes        # 50
1.kilobyte      # 1024
2.megabytes     # 2097152
3.gigabytes
4.terabytes

1.day           # ActiveSupport::Duration 60*60*24
1.hour
60.minutes
1.second.in_milliseconds  # 1000

# Numeric 메서드
50.percent_of(100)   # 50.0
50.percentage_of(100)

Callback

class MyClass
  include ActiveSupport::Callbacks
  define_callbacks :process

  set_callback :process, :before, :prepare
  set_callback :process, :after, :cleanup

  def process
    run_callbacks(:process) do
      # 실제 작업
    end
  end

  private

  def prepare; end
  def cleanup; end
end

Rails의 model callbacks, controller filters 모두 이 위에.

Cache (Rails.cache 외)

cache = ActiveSupport::Cache::MemoryStore.new
cache.write("key", value, expires_in: 1.hour)
cache.read("key")
cache.fetch("key") { expensive }

MemoryStore, FileStore, MemCacheStore, RedisCacheStore 등.

Logger

logger = ActiveSupport::Logger.new(STDOUT)
logger = ActiveSupport::TaggedLogging.new(logger)
logger.tagged("request_id=#{id}") do
  logger.info("Processing")
end
# 출력: [request_id=abc] Processing

OrderedOptions

config = ActiveSupport::OrderedOptions.new
config.api_key = "..."
config[:api_key]    # "..." (dot 또는 hash)

옵션 객체.

함정

1. blank?의 의외 동작

0.blank?         # false (0은 truthy)
false.blank?     # true
[nil].blank?     # false (요소 있음)
[nil].compact.blank?    # true

2. try의 silent failure

user.try(:invalid_method)    # nil (예외 안 던짐, 디버깅 어려움)

try 남용은 버그 숨김. 가능하면 &. (NoMethodError는 던짐).

3. require “active_support”

require "active_support"            # 기본만
require "active_support/all"        # 전부 (무거움)
require "active_support/core_ext/object/blank"    # 일부만

Rails 없이 Ruby 프로젝트에서 사용 시.

4. duration 비교

1.day == 86400.seconds    # true (둘 다 Duration)
1.day == 86400             # false (Numeric 비교)
1.day.to_i == 86400        # true

5. tableize / pluralize 오류

"deer".pluralize    # "deers" (실제 영어로 "deer")
"sheep".pluralize   # "sheep"
"person".pluralize  # "people"

영어 불규칙. 커스텀 inflection 등록 가능:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.irregular "person", "people"
  inflect.uncountable %w[equipment]
end

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기