액티브 서포트 코어 익스텐션

액티브 서포트는 루비 언어 익스텐션과 유틸리티를 제공하는 Ruby on Rails 구성 요소입니다.

이는 Rails 애플리케이션 개발과 Ruby on Rails 자체 개발을 위해 언어 수준에서 더 풍부한 기능을 제공합니다.

이 가이드를 읽고 나면 다음을 알 수 있습니다:

  • 코어 익스텐션이 무엇인지.
  • 모든 익스텐션을 로드하는 방법.
  • 원하는 익스텐션만 선택적으로 로드하는 방법.
  • 액티브 서포트가 제공하는 익스텐션.

코어 익스텐션 로드하기

독립형 액티브 서포트

기본 footprint를 최소화하기 위해 액티브 서포트는 기본적으로 최소한의 종속성만 로드합니다. 원하는 익스텐션만 로드할 수 있도록 작은 조각으로 나뉘어 있습니다. 또한 관련 익스텐션을 한 번에 모두 로드할 수 있는 편의 진입점도 있습니다.

따라서 다음과 같은 간단한 require 문으로:

require "active_support"

액티브 서포트 프레임워크에 필요한 익스텐션만 로드됩니다.

정의 선택적으로 로드하기

이 예제는 Hash#with_indifferent_access를 로드하는 방법을 보여줍니다. 이 익스텐션은 키를 문자열이나 기호로 접근할 수 있는 ActiveSupport::HashWithIndifferentAccess로 해시를 변환합니다.

{ a: 1 }.with_indifferent_access["a"] # => 1

이 가이드에서 각 코어 익스텐션 메서드에 대해 정의된 위치를 알려주는 주석이 있습니다. with_indifferent_access의 경우 다음과 같은 주석이 있습니다:

주석: active_support/core_ext/hash/indifferent_access.rb에 정의되어 있습니다.

이는 다음과 같이 로드할 수 있다는 뜻입니다:

require "active_support"
require "active_support/core_ext/hash/indifferent_access"

액티브 서포트는 엄격하게 필요한 종속성만 로드하도록 주의 깊게 수정되었습니다.

그룹화된 코어 익스텐션 로드하기

다음 단계는 Hash에 대한 모든 익스텐션을 한 번에 로드하는 것입니다. 일반적으로 SomeClass에 대한 익스텐션은 active_support/core_ext/some_class를 로드하면 한 번에 사용할 수 있습니다.

따라서 Hash에 대한 모든 익스텐션(including with_indifferent_access)을 로드하려면:

require "active_support"
require "active_support/core_ext/hash"

모든 코어 익스텐션 로드하기

모든 코어 익스텐션을 로드하고 싶다면 다음 파일을 로드하면 됩니다:

require "active_support"
require "active_support/core_ext"

모든 액티브 서포트 로드하기

마지막으로 모든 액티브 서포트를 사용하고 싶다면 다음과 같이 하면 됩니다:

require "active_support/all"

이렇게 하면 메모리에 전체 액티브 서포트가 올라가지 않습니다. 일부 기능은 autoload를 통해 구현되어 있어 사용될 때만 로드됩니다.

Ruby on Rails 애플리케이션 내에서 액티브 서포트

Ruby on Rails 애플리케이션은 config.active_support.bare가 true가 아닌 한 모든 액티브 서포트를 로드합니다. 이 경우 프레임워크 자체가 선택한 것만 로드되며, 이전 섹션에서 설명한 것처럼 원하는 수준의 세부사항까지 선택적으로 로드할 수 있습니다.

모든 객체에 대한 익스텐션

blank?present?

다음과 같은 값들은 Rails 애플리케이션에서 비어 있는 것으로 간주됩니다:

  • nilfalse,

  • 공백 문자로만 구성된 문자열(아래 주석 참고),

  • 빈 배열과 해시, 그리고

  • empty?에 응답하고 비어 있는 기타 객체.

정보: 문자열에 대한 술어는 유니코드 인식 문자 클래스 [:space:]를 사용하므로, 예를 들어 U+2029(문단 구분자)도 공백으로 간주됩니다.

경고: 숫자는 언급되지 않았다는 점에 유의하세요. 특히 0과 0.0은 비어 있지 않습니다.

예를 들어, ActionController::HttpAuthentication::Token::ControllerMethods의 이 메서드는 토큰이 있는지 확인하기 위해 blank?를 사용합니다:

def authenticate(controller, &login_procedure)
  token, options = token_and_options(controller.request)
  unless token.blank?
    login_procedure.call(token, options)
  end
end

present? 메서드는 !blank?와 동등합니다. 이 예제는 ActionDispatch::Http::Cache::Response에서 가져온 것입니다:

def set_conditional_cache_control!
  unless self["Cache-Control"].present?
    # ...
  end
end

주석: active_support/core_ext/object/blank.rb에 정의되어 있습니다.

presence

presence 메서드는 present?이면 자신을 반환하고, 그렇지 않으면 nil을 반환합니다. 다음과 같은 관용구에 유용합니다:

host = config[:host].presence || "localhost"

주석: active_support/core_ext/object/blank.rb에 정의되어 있습니다.

duplicable?

Ruby 2.5부터 대부분의 객체는 dup 또는 clone을 통해 복제할 수 있습니다:

"foo".dup           # => "foo"
"".dup              # => ""
Rational(1).dup     # => (1/1)
Complex(0).dup      # => (0+0i)
1.method(:+).dup    # => TypeError (allocator undefined for Method)

액티브 서포트는 duplicable?를 제공하여 객체의 복제 가능 여부를 확인할 수 있습니다:

"foo".duplicable?           # => true
"".duplicable?              # => true
Rational(1).duplicable?     # => true
Complex(1).duplicable?      # => true
1.method(:+).duplicable?    # => false

경고: 어떤 클래스든 dupclone을 제거하거나 예외를 발생시켜 복제를 금지할 수 있습니다. 따라서 임의의 객체가 복제 가능한지 확인하려면 rescue만이 답변을 줄 수 있습니다. duplicable?는 위의 하드 코딩된 목록에 의존하지만, rescue보다 훨씬 빠릅니다. 이 하드 코딩된 목록이 사용 사례에 충분한 경우에만 사용하세요.

주석: active_support/core_ext/object/duplicable.rb에 정의되어 있습니다.

deep_dup

deep_dup 메서드는 주어진 객체의 깊은 복사본을 반환합니다. 일반적으로 객체에 다른 객체가 포함되어 있을 때 dup를 하면 Ruby는 그것들을 복사하지 않고 얕은 복사를 만듭니다. 예를 들어 문자열이 포함된 배열이 있다면 다음과 같습니다:

array     = ["string"]
duplicate = array.dup

duplicate.push "another-string"

# 객체가 복제되었으므로 요소는 복제된 배열에만 추가되었습니다.
array     # => ["string"]
duplicate # => ["string", "another-string"]

duplicate.first.gsub!("string", "foo")

# 첫 번째 요소가 복제되지 않았으므로 두 배열 모두 변경되었습니다.
array     # => ["foo"]
duplicate # => ["foo", "another-string"]

객체의 깊은 복사본이 필요한 경우 deep_dup를 사용해야 합니다. 다음은 예시입니다:

array     = ["string"]
duplicate = array.deep_dup

duplicate.first.gsub!("string", "foo")

array     # => ["string"]
duplicate # => ["foo"]

객체가 복제 불가능한 경우 deep_dup는 그냥 객체 자체를 반환합니다:

number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id   # => true

주석: active_support/core_ext/object/deep_dup.rb에 정의되어 있습니다.

try

객체가 nil이 아닌 경우에만 메서드를 호출하고 싶을 때 가장 간단한 방법은 조건문을 사용하는 것이지만, 불필요한 복잡성을 야기합니다. 대신 try를 사용할 수 있습니다. tryObject#public_send와 유사하지만, 대상이 nil이면 nil을 반환합니다.

다음은 예시입니다:

try 없이

unless @number.nil? @number.next end

try 사용

@number.try(:next) “`

다른 예로, @loggernil일 수 있는 ActiveRecord::ConnectionAdapters::AbstractAdapter의 코드를 보면 try를 사용하여 불필요한 검사를 피하고 있습니다.

def log_info(sql, name, ms)
  if @logger.try(:debug?)
    name = "%s (%.1fms)" % [name || "SQL", ms]
    @logger.debug(format_log_entry(name, sql.squeeze(" ")))
  end
end

try는 인수 없이 블록으로 호출할 수도 있습니다. 이 경우 객체가 nil이 아닌 경우에만 블록이 실행됩니다:

@person.try { |p| "#{p.first_name} #{p.last_name}" }

try는 메서드 없음 오류를 삼켜 nil을 반환합니다. 타이핑 오류를 방지하려면 try!를 대신 사용하세요:

@number.try(:nest)  # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer

주석: active_support/core_ext/object/try.rb에 정의되어 있습니다.

class_eval(*args, &block)

[class_eval][Kernel#class_eval]을 사용하면 임의의 객체의 싱글톤 클래스에서 코드를 평가할 수 있습니다:

class Proc
  def bind(object)
    block, time = self, Time.current
    object.class_eval do
      method_name = "__bind_#{time.to_i}_#{time.usec}"
      define_method(method_name, &block)
      metho, 번역을 계속하겠습니다.

### `acts_like?(duck)`

[`acts_like?`][Object#acts_like?] 메서드는 간단한 규약에 따라 어떤 클래스가 다른 클래스처럼 동작하는지 확인할 수 있습니다: `String`과 같은 인터페이스를 제공하는 클래스는

```ruby
def acts_like_string?
end

와 같이 정의합니다. 이는 단순한 표식일 뿐, 메서드 본문이나 반환 값은 중요하지 않습니다. 그리고 클라이언트 코드는 다음과 같이 오리 타입 안전성을 쿼리할 수 있습니다:

some_klass.acts_like?(:string)

Rails에는 DateTime처럼 행동하는 클래스가 있으며, 이 계약을 따릅니다.

주석: active_support/core_ext/object/acts_like.rb에 정의되어 있습니다.

to_param

모든 객체는 Rails에서 to_param 메서드를 구현하며, 이는 쿼리 문자열이나 URL 조각에서 해당 객체를 나타내는 값을 반환하는 것이 목적입니다.

기본적으로 to_paramto_s를 호출합니다:

7.to_param # => "7"

to_param의 반환 값은 이스케이프되지 않아야 합니다:

"Tom & Jerry".to_param # => "Tom & Jerry"

Rails의 여러 클래스에서 이 메서드를 재정의합니다.

예를 들어 nil, true, false는 자신을 반환합니다. Array#to_param은 요소의 to_param을 호출하고 결과를 ”/“로 연결합니다:

[0, true, String].to_param # => "0/true/String"

특히 Rails 라우팅 시스템은 모델의 to_param을 호출하여 :id 자리 표시자의 값을 얻습니다. ActiveRecord::Base#to_param은 모델의 id를 반환하지만, 모델에서 이 메서드를 재정의할 수 있습니다. 예를 들어 다음과 같이 정의하면:

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

다음과 같이 사용할 수 있습니다:

user_path(@user) # => "/users/357-john-smith"

경고. 컨트롤러는 to_param의 재정의를 인식해야 합니다. 왜냐하면 그런 요청이 들어오면 "357-john-smith"가 params[:id]의 값이 되기 때문입니다.

주석: active_support/core_ext/object/to_param.rb에 정의되어 있습니다.

to_query

to_query 메서드는 주어진 keyto_param의 반환 값을 연결하여 쿼리 문자열을 생성합니다. 예를 들어 다음과 같은 to_param 정의가 있다면:

class User
  def to_param
    "#{id}-#{name.parameterize}"
  end
end

다음과 같이 사용할 수 있습니다:

current_user.to_query("user") # => "user=357-john-smith"

이 메서드는 키와 값 모두 필요한 부분을 이스케이프합니다:

account.to_query("company[name]")
# => "company%5Bname%5D=Johnson+%26+Johnson"

따라서 쿼리 문자열에 바로 사용할 수 있습니다.

배열은 각 요소에 대해 to_query를 호출하고 key[]를 키로 사용한 뒤 결과를 ”&“로 연결합니다:

[3.4, -45.6].to_query("sample")
# => "sample%5B%5D=3.4&sample%5B%5D=-45.6"

해시도 to_query에 응답하지만 시그니처가 다릅니다. 인수를 전달하지 않으면 키/값 할당을 호출하고 결과를 ”&“로 연결합니다:

{ c: 3, b: 2, a: 1 }.to_query # => "a=1&b=2&c=3"

Hash#to_query 메서드는 선택적으로 네임스페이스를 받습니다:

{ id: 89, name: "John Smith" }.to_query("user")
# => "user%5Bid%5D=89&user%5Bname%5D=John+Smith"

주석: active_support/core_ext/object/to_query.rb에 정의되어 있습니다.

with_options

with_options 메서드는 일련의 메서드 호출에서 공통 옵션을 추출할 수 있는 방법을 제공합니다.

기본 옵션 해시가 주어지면, with_options는 블록에 프록시 객체를 전달합니다. 블록 내에서 프록시에 대한 메서드 호출은 옵션이 병합된 상태로 수신기에 전달됩니다. 예를 들어, 다음과 같은 중복을 제거할 수 있습니다:

class Account < ApplicationRecord
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end

다음과 같이 작성할 수 있습니다:

class Account < ApplicationRecord
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

이 관용구는 그룹화를 독자에게 전달할 수도 있습니다. 예를 들어 사용자별 언어로 뉴스레터를 보내고 싶다고 가정해 보겠습니다. 메일러 내부에서 다음과 같이 그룹화할 수 있습니다:

I18n.with_options locale: user.locale, scope: "newsletter" do |i18n|
  subject i18n.t :subject
  body    i18n.t :body, user_name: user.name
end

팁: with_options는 수신기에 호출을 전달하므로 중첩할 수 있습니다. 각 중첩 수준은 자신의 기본값 외에 상속된 기본값도 병합합니다.

주석: active_support/core_ext/object/with_options.rb에 정의되어 있습니다.

JSON 지원

액티브 서포트는 json 젬이 일반적으로 제공하는 것보다 Ruby 객체에 대한 to_json의 구현이 더 나은 버전을 제공합니다. 이는 HashProcess::Status와 같은 일부 클래스가 적절한 JSON 표현을 제공하기 위해 특별한 처리가 필요하기 때문입니다.

주석: active_support/core_ext/object/json.rb에 정의되어 있습니다.

인스턴스 변수

액티브 서포트는 인스턴스 변수에 대한 액세스를 쉽게 하는 여러 메서드를 제공합니다.

instance_values

instance_values 메서드는 인스턴스 변수 이름(@ 제외)을 키로, 해당 값을 값으로 하는 해시를 반환합니다:

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}

주석: active_support/core_ext/object/instance_variables.rb에 정의되어 있습니다.

instance_variable_names

instance_variable_names 메서드는 각 이름에 ”@“가 포함된 배열을 반환합니다.

class C
  def initialize(x, y)
    @x, @y = x, y
  end
end

C.new(0, 1).instance_variable_names # => ["@x", "@y"]

주석: active_support/core_ext/object/instance_variables.rb에 정의되어 있습니다.

경고 및 예외 억제

silence_warningsenable_warnings 메서드는 블록의 지속 기간 동안 $VERBOSE의 값을 변경한 후 다시 원래대로 설정합니다:

silence_warnings { Object.const_set "RAILS_DEFAULT_LOGGER", logger }

suppress 메서드를 사용하면 예외도 억제할 수 있습니다. 이 메서드는 임의의 수의 예외 클래스를 받습니다. 블록 실행 중 예외가 발생하고 인수 중 하나와 kind_of?되면 suppress가 예외를 캡처하고 조용히 반환합니다. 그렇지 않으면 예외가 캡처되지 않습니다:

# 사용자가 잠겨 있으면 증가가 손실되지만 큰 문제는 아닙니다.
suppress(ActiveRecord::StaleObjectError) do
  current_user.increment! :visits
end

주석: active_support/core_ext/kernel/reporting.rb에 정의되어 있습니다.

in?

in? 술어는 객체가 다른 객체에 포함되어 있는지 테스트합니다. 인수가 include?에 응답하지 않으면 ArgumentError 예외가 발생합니다.

in?의 예:

1.in?([1, 2])        # => true
"lo".in?("hello")   # => true
25.in?(30..50)      # => false
1.in?(1)            # => ArgumentError

주석: active_support/core_ext/object/inclusion.rb에 정의되어 있습니다.

모듈 익스텐션

속성

alias_attribute

모델 속성에는 리더, 라이터, 술어가 있습니다. alias_attribute를 사용하면 이 세 가지 메서드가 모두 정의된 별칭을 만들 수 있습니다. 다른 별칭 메서드와 마찬가지로, 새 이름이 첫 번째 인수이고 이전 이름이 두 번째 인수입니다(할당과 같은 순서로 기억하면 좋습니다):

class User < ApplicationRecord
  # 이메일 열을 "login"으로 참조할 수 있습니다.
  # 인증 코드에 의미 있을 수 있습니다.
  alias_attribute :login, :email
end

주석: active_support/core_ext/module/aliasing.rb에 정의되어 있습니다.

내부 속성

하위 클래스에서 속성을 정의할 때 이름 충돌의 위험이 있습니다. 이는 라이브러리에 매우 중요합니다.

액티브네, 번역을 계속하겠습니다.

액티브 서포트는 attr_internal_reader, attr_internal_writer, attr_internal_accessor 매크로를 정의합니다. 이들은 해당 Ruby 내장 attr_* 대응물과 동일하게 작동하지만, 충돌 가능성이 낮은 방식으로 기본 인스턴스 변수 이름을 지정합니다.

attr_internal 매크로는 attr_internal_accessor의 별칭입니다:

# 라이브러리
class ThirdPartyLibrary::Crawler
  attr_internal :log_level
end

# 클라이언트 코드
class MyCrawler < ThirdPartyLibrary::Crawler
  attr_accessor :log_level
end

이전 예에서 :log_level은 라이브러리의 공개 인터페이스에 속하지 않고 개발 용도로만 사용될 수 있습니다. 클라이언트 코드는 이 잠재적 충돌을 인식하지 못하고 자신의 :log_level을 정의합니다. attr_internal을 사용하면 충돌이 방지됩니다.

기본적으로 내부 인스턴스 변수의 이름은 선행 밑줄 @_log_level과 같이 지정됩니다. 그러나 Module.attr_internal_naming_format을 통해 구성할 수 있으며, 선행 @%s가 포함된 sprintf와 유사한 형식 문자열을 전달할 수 있습니다.

Rails는 몇 군데에서 내부 속성을 사용합니다. 예를 들어 뷰에서:

module ActionView
  class Base
    attr_internal :captures
    attr_internal :request, :layout
    attr_internal :controller, :template
  end
end

주석: active_support/core_ext/module/attr_internal.rb에 정의되어 있습니다.

모듈 속성

mattr_reader, mattr_writer, mattr_accessor 매크로는 클래스에 대해 정의된 cattr_* 매크로와 동일합니다. 사실 cattr_* 매크로는 mattr_* 매크로의 별칭에 불과합니다. 클래스 속성을 참조하세요.

예를 들어 액티브 스토리지의 로거 API는 mattr_accessor로 생성됩니다:

module ActiveStorage
  mattr_accessor :logger
end

주석: active_support/core_ext/module/attribute_accessors.rb에 정의되어 있습니다.

부모

module_parent

중첩된 명명된 모듈의 경우 module_parent 메서드는 해당 상수가 포함된 모듈을 반환합니다:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parent # => X::Y
M.module_parent       # => X::Y

모듈이 익명이거나 최상위 수준인 경우 module_parentObject를 반환합니다.

경고: 이 경우 module_parent_namenil을 반환합니다.

주석: active_support/core_ext/module/introspection.rb에 정의되어 있습니다.

module_parent_name

중첩된 명명된 모듈의 경우 module_parent_name 메서드는 해당 상수가 포함된 모듈의 정규화된 이름을 반환합니다:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parent_name # => "X::Y"
M.module_parent_name       # => "X::Y"

최상위 수준 또는 익명 모듈의 경우 module_parent_namenil을 반환합니다.

경고: 이 경우 module_parentObject를 반환합니다.

주석: active_support/core_ext/module/introspection.rb에 정의되어 있습니다.

module_parents

module_parents 메서드는 module_parent를 수신기에 대해 호출하고 Object에 도달할 때까지 계속합니다. 체인은 아래에서 위로 배열로 반환됩니다:

module X
  module Y
    module Z
    end
  end
end
M = X::Y::Z

X::Y::Z.module_parents # => [X::Y, X, Object]
M.module_parents       # => [X::Y, X, Object]

주석: active_support/core_ext/module/introspection.rb에 정의되어 있습니다.

익명

모듈은 이름이 있을 수도, 없을 수도 있습니다:

module M
end
M.name # => "M"

N = Module.new
N.name # => "N"

Module.new.name # => nil

anonymous? 술어를 사용하여 모듈이 이름이 있는지 확인할 수 있습니다:

module M
end
M.anonymous? # => false

Module.new.anonymous? # => true

접근할 수 없다고 해서 익명이 아닌 것은 아닙니다:

module M
end

m = Object.send(:remove_const, :M)

m.anonymous? # => false

그러나 익명 모듈은 정의상 접근할 수 없습니다.

주석: active_support/core_ext/module/anonymous.rb에 정의되어 있습니다.

메서드 위임

delegate

delegate 매크로는 메서드를 전달하는 쉬운 방법을 제공합니다.

사용자의 로그인 정보가 User 모델에 있지만 이름과 다른 데이터는 별도의 Profile 모델에 있다고 가정해 보겠습니다:

class User < ApplicationRecord
  has_one :profile
end

이 구성에서 사용자의 이름을 얻으려면 user.profile.name을 사용해야 합니다. 그러나 이러한 속성에 직접 액세스할 수 있다면 편리할 것입니다:

class User < ApplicationRecord
  has_one :profile

  def name
    profile.name
  end
end

이것이 delegate가 해주는 일입니다:

class User < ApplicationRecord
  has_one :profile

  delegate :name, to: :profile
end

더 짧고 의도가 더 명확합니다.

메서드는 대상에서 public이어야 합니다.

delegate 매크로는 여러 메서드를 허용합니다:

delegate :name, :age, :address, :twitter, to: :profile

문자열로 보간될 때 :to 옵션은 대상 객체를 평가하는 표현식이 되어야 합니다. 일반적으로 문자열이나 기호입니다. 이 표현식은 수신기의 컨텍스트에서 평가됩니다:

# Rails 상수에 위임
delegate :logger, to: :Rails

# 수신기의 클래스에 위임
delegate :table_name, to: :class

경고: :prefix 옵션이 true이면 이것은 더 일반적이지 않습니다. 아래를 참조하세요.

기본적으로 위임으로 인해 NoMethodError가 발생하고 대상이 nil인 경우 예외가 전파됩니다. :allow_nil 옵션을 사용하면 대신 nil을 반환하도록 할 수 있습니다:

delegate :name, to: :profile, allow_nil: true

allow_nil을 사용하면 사용자에게 프로필이 없는 경우 user.namenil을 반환합니다.

:prefix 옵션은 생성된 메서드의 이름에 접두사를 추가합니다. 이는 더 나은 이름을 얻기 위해 유용할 수 있습니다:

delegate :street, to: :address, prefix: true

이전 예에서는 address_street가 생성되었습니다.

경고: 이 경우 생성된 메서드의 이름은 대상 객체와 대상 메서드 이름으로 구성되므로 :to 옵션은 메서드 이름이어야 합니다.

사용자 정의 접두사도 구성할 수 있습니다:

delegate :size, to: :attachment, prefix: :avatar

이전 예에서는 avatar_size가 생성되었습니다.

:private 옵션은 메서드 범위를 변경합니다:

delegate :date_of_birth, to: :profile, private: true

기본적으로 위임된 메서드는 public입니다. private: true를 전달하면 이를 변경할 수 있습니다.

주석: active_support/core_ext/module/delegation.rb에 정의되어 있습니다.

delegate_missing_to

User 객체에서 누락된 모든 것을 Profile에 위임하고 싶다고 가정해 보겠습니다. delegate_missing_to 매크로를 사용하면 이를 간단하게 구현할 수 있습니다:

class User < ApplicationRecord
  has_one :profile

  delegate_missing_to :profile
end

대상은 객체 내에서 호출 가능한 것이면 무엇이든 될 수 있습니다. 예를 들어 인스턴스 변수, 메서드, 상수 등입니다. 대상의 public 메서드만 위임됩니다.

주석: active_support/core_ext/module/delegation.rb에 정의되어 있습니다.

메서드 재정의

때로는 define_method를 사용하여 메서드를 정의해야 하지만 해당 이름의 메서드가 이미 존재하는지 알 수 없습니다. 존재하는 경우 경고가 발행됩니다. 큰 문제는 아니지만 깨끗하지 않습니다.

redefine_method 메서드는 이러한 잠재적 경고를 방지하고, 필요한 경우 기존 메서드를 제거합니다.

silence_redefinition_of_method를 사용하면 자신이 대체 메서드를 정의해야 하는 경우(예: delegate를 사용하는 경우)에도 유용합니다.

주석: active_support/core_ext/module/redefine_method.rb에 정의되어 있습니다.

클래스 익스텐션

클래스 속성

class_attribute

class_attribute 메서드는 계층 구조 아래에서 재정의될 수 있는 상속 네, 번역을 계속하겠습니다.

class_attribute 메서드는 계층 구조 아래에서 재정의될 수 있는 상속 가능한 클래스 속성을 선언합니다.

class A
  class_attribute :x
end

class B < A; end

class C < B; end

A.x = :a
B.x # => :a
C.x # => :a

B.x = :b
A.x # => :a
C.x # => :b

C.x = :c
A.x # => :a
B.x # => :b

예를 들어 ActionMailer::Base는 다음과 같이 정의합니다:

class_attribute :default_params
self.default_params = {
  mime_version: "1.0",
  charset: "UTF-8",
  content_type: "text/plain",
  parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze

인스턴스 수준에서도 액세스하고 재정의할 수 있습니다.

A.x = 1

a1 = A.new
a2 = A.new
a2.x = 2

a1.x # => 1, A에서 온 것
a2.x # => 2, a2에서 재정의된 것

:instance_writer 옵션을 false로 설정하면 라이터 인스턴스 메서드 생성을 방지할 수 있습니다.

module ActiveRecord
  class Base
    class_attribute :table_name_prefix, instance_writer: false, default: "my"
  end
end

모델에서 이 옵션을 사용하면 대량 할당으로부터 속성을 보호할 수 있습니다.

:instance_reader 옵션을 false로 설정하면 리더 인스턴스 메서드 생성을 방지할 수 있습니다.

class A
  class_attribute :x, instance_reader: false
end

A.new.x = 1
A.new.x # NoMethodError

편의를 위해 class_attribute는 인스턴스 리더의 이중 부정인 인스턴스 술어도 정의합니다. 위의 예에서는 x?라고 합니다.

:instance_readerfalse이면 인스턴스 술어도 NoMethodError를 반환합니다.

인스턴스 술어를 원하지 않으면 instance_predicate: false를 전달하면 정의되지 않습니다.

주석: active_support/core_ext/class/attribute.rb에 정의되어 있습니다.

cattr_reader, cattr_writer, 및 cattr_accessor

cattr_reader, cattr_writer, cattr_accessor 매크로는 각각의 attr_* 대응물이지만 클래스에 대한 것입니다. 클래스 변수를 nil로 초기화하고 해당 클래스 메서드를 생성합니다:

class MysqlAdapter < AbstractAdapter
  # @@emulate_booleans에 대한 클래스 메서드를 생성합니다.
  cattr_accessor :emulate_booleans
end

또한 기본값을 설정하는 블록을 cattr_*에 전달할 수 있습니다:

class MysqlAdapter < AbstractAdapter
  # @@emulate_booleans에 대한 클래스 메서드를 생성하고 기본값을 true로 설정합니다.
  cattr_accessor :emulate_booleans, default: true
end

인스턴스 메서드도 생성되지만 클래스 속성에 대한 프록시에 불과합니다. 따라서 인스턴스에서 클래스 속성을 변경할 수 있지만 재정의할 수는 없습니다(class_attribute와 다름, 위 참조).

:instance_reader 옵션을 false로 설정하면 리더 인스턴스 메서드 생성을 방지할 수 있고, :instance_writer 옵션을 false로 설정하면 라이터 인스턴스 메서드 생성을 방지할 수 있습니다. :instance_accessor 옵션을 false로 설정하면 둘 다 방지할 수 있습니다. 모든 경우에 값은 정확히 false여야 하며 그 외의 false 값은 허용되지 않습니다.

module A
  class B
    # first_name 인스턴스 리더가 생성되지 않습니다.
    cattr_accessor :first_name, instance_reader: false
    # last_name= 인스턴스 라이터가 생성되지 않습니다.
    cattr_accessor :last_name, instance_writer: false
    # surname 인스턴스 리더 또는 surname= 라이터가 생성되지 않습니다.
    cattr_accessor :surname, instance_accessor: false
  end
end

모델에서 :instance_accessorfalse로 설정하면 대량 할당으로부터 속성을 보호하는 데 유용할 수 있습니다.

주석: active_support/core_ext/module/attribute_accessors.rb에 정의되어 있습니다.

하위 클래스 및 자손

subclasses

subclasses 메서드는 수신기의 하위 클래스를 반환합니다:

class C; end
C.subclasses # => []

class B < C; end
C.subclasses # => [B]

class A < B; end
C.subclasses # => [B]

class D < C; end
C.subclasses # => [B, D]

이 클래스들이 반환되는 순서는 지정되어 있지 않습니다.

주석: active_support/core_ext/class/subclasses.rb에 정의되어 있습니다.

descendants

descendants 메서드는 수신기보다 작은 모든 클래스를 반환합니다:

class C; end
C.descendants # => []

class B < C; end
C.descendants # => [B]

class A < B; end
C.descendants # => [B, A]

class D < C; end
C.descendants # => [B, A, D]

이 클래스들이 반환되는 순서는 지정되어 있지 않습니다.

주석: active_support/core_ext/class/subclasses.rb에 정의되어 있습니다.

문자열 익스텐션

출력 안전성

동기화

데이터를 HTML 템플릿에 삽입하려면 특별한 주의가 필요합니다. 예를 들어 @review.title을 그대로 삽입할 수는 없습니다. 우선 리뷰 제목이 "Flanagan & Matz rules!"인 경우 출력이 잘못된 HTML이 됩니다. 왜냐하면 앰퍼샌드를 ”&amp;“로 이스케이프해야 하기 때문입니다. 더 중요한 것은 애플리케이션에 따라 이것이 큰 보안 취약점이 될 수 있다는 점입니다. 보안 가이드의 크로스 사이트 스크립팅 섹션을 참조하세요.

안전한 문자열

액티브 서포트에는 (html) 안전 문자열 개념이 있습니다. 안전한 문자열은 HTML에 그대로 삽입할 수 있다고 표시된 문자열입니다. 이는 이스케이프되었는지 여부와 관계없이 신뢰됩니다.

문자열은 기본적으로 안전하지 않습니다:

"".html_safe? # => false

html_safe 메서드를 사용하여 주어진 문자열에서 안전한 문자열을 얻을 수 있습니다:

s = "".html_safe
s.html_safe? # => true

html_safe가 실제로 이스케이프를 수행하지 않는다는 점을 이해하는 것이 중요합니다. 단순히 어설션일 뿐입니다:

s = "<script>...</script>".html_safe
s.html_safe? # => true
s            # => "<script>...</script>"

특정 문자열에 html_safe를 호출하는 것이 안전한지 확인하는 것은 사용자의 책임입니다.

안전한 문자열에 추가하는 경우, 제자리에서 concat/<<로 또는 +로 추가하든 결과는 안전한 문자열입니다. 안전하지 않은 인수는 이스케이프됩니다:

"".html_safe + "<" # => "&lt;"

안전한 인수는 직접 추가됩니다:

"".html_safe + "<".html_safe # => "<"

이러한 메서드는 일반적인 뷰에서 사용해서는 안 됩니다. 안전하지 않은 값은 자동으로 이스케이프됩니다:

<%= @review.title %> <%# 필요한 경우 이스케이프됩니다 %>

문자열을 그대로 삽입하려면 raw 헬퍼를 사용하세요. html_safe를 직접 호출하지 마세요:

<%= raw @cms.current_template %> <%# @cms.current_template를 그대로 삽입합니다 %>

또는 <%==를 사용할 수 있습니다:

<%== @cms.current_template %> <%# @cms.current_template를 그대로 삽입합니다 %>

raw 헬퍼는 내부적으로 html_safe를 호출합니다:

def raw(stringish)
  stringish.to_s.html_safe
end

주석: active_support/core_ext/string/output_safety.rb에 정의되어 있습니다.

변환

일반적으로 연결을 제외하고는(위에서 설명한 대로) 문자열을 변경할 수 있는 메서드는 모두 안전하지 않은 문자열을 반환합니다. 이는 downcase, gsub, strip, chomp, underscore 등입니다.

제자리 변환의 경우 gsub!와 같이 수신기 자체가 안전하지 않게 됩니다.

정보: 변환이 실제로 무언가를 변경했는지 여부와 관계없이 안전성 비트는 항상 손실됩니다.

변환 및 강제 변환

to_s를 호출하면 안전한 문자열이 반환되지만, to_str로 강제 변환하면 안전하지 않은 문자열이 반환됩니다.

복사

dup 또는 clone을 호출하면 안전한 문자열이 생성됩니다.

remove

remove 메서드는 패턴의 모든 발생을 제거합니다:

"Hello World".remove(/Hello /) # => "World"

파괴적인 버전 String#remove!도 있습니다.

주석: active_support/core_ext/string/filters.rb에 정의되어 있습니다.

squish

[squish][String#squish] 메서드는 선행 및 후행 공백을 제거하고 연속된 공백을 단일 공백으로 대체합니다:

" \n  foo\n\r \t bar \n".squish # => "foo bar"

파괴적인 버전 String#squish!도 있습니다.

ASCII와 유니코드 공백 모두 처리한다는 점에 유의하세요.

주석: active_support/core_ext/string/filters.rb에 정의되어 있습니다.

[String#squish]: https://api.rubyonrails.org/classes/String.html#method-i네, 번역을 계속하겠습니다.

truncate

truncate 메서드는 지정된 length 이후 문자열의 복사본을 반환합니다:

"Oh dear! Oh dear! I shall be late!".truncate(20)
# => "Oh dear! Oh dear!..."

생략 부호는 :omission 옵션으로 사용자 정의할 수 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate(20, omission: "&hellip;")
# => "Oh dear! Oh &hellip;"

특히 생략 문자열의 길이를 고려한다는 점에 유의하세요.

:separator 옵션을 전달하면 자연스러운 구분점에서 문자열을 자를 수 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate(18)
# => "Oh dear! Oh dea..."
"Oh dear! Oh dear! I shall be late!".truncate(18, separator: " ")
# => "Oh dear! Oh..."

:separator 옵션은 정규식일 수도 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate(18, separator: /\s/)
# => "Oh dear! Oh..."

위의 예에서 "dear"가 먼저 잘리지만 :separator로 인해 방지됩니다.

주석: active_support/core_ext/string/filters.rb에 정의되어 있습니다.

truncate_bytes

truncate_bytes 메서드는 최대 bytesize 바이트로 문자열의 복사본을 반환합니다:

"👍👍👍👍".truncate_bytes(15)
# => "👍👍👍…"

생략 부호는 :omission 옵션으로 사용자 정의할 수 있습니다:

"👍👍👍👍".truncate_bytes(15, omission: "🖖")
# => "👍👍🖖"

주석: active_support/core_ext/string/filters.rb에 정의되어 있습니다.

truncate_words

truncate_words 메서드는 지정된 단어 수 이후 문자열의 복사본을 반환합니다:

"Oh dear! Oh dear! I shall be late!".truncate_words(4)
# => "Oh dear! Oh dear!..."

생략 부호는 :omission 옵션으로 사용자 정의할 수 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate_words(4, omission: "&hellip;")
# => "Oh dear! Oh dear!&hellip;"

:separator 옵션을 전달하면 자연스러운 구분점에서 문자열을 자를 수 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate_words(3, separator: "!")
# => "Oh dear! Oh dear! I shall be late..."

:separator 옵션은 정규식일 수도 있습니다:

"Oh dear! Oh dear! I shall be late!".truncate_words(4, separator: /\s/)
# => "Oh dear! Oh dear!..."

주석: active_support/core_ext/string/filters.rb에 정의되어 있습니다.

inquiry

inquiry 메서드는 문자열을 StringInquirer 객체로 변환하여 등호 검사를 더 쉽게 만듭니다.

"production".inquiry.production? # => true
"active".inquiry.inactive?       # => false

주석: active_support/core_ext/string/inquiry.rb에 정의되어 있습니다.

starts_with?ends_with?

액티브 서포트는 String#start_with?String#end_with?의 3인칭 별칭을 정의합니다:

"foo".starts_with?("f") # => true
"foo".ends_with?("o")   # => true

주석: active_support/core_ext/string/starts_ends_with.rb에 정의되어 있습니다.

strip_heredoc

strip_heredoc 메서드는 heredoc의 들여쓰기를 제거합니다.

예를 들어 다음과 같은 경우

if options[:usage]
  puts <<-USAGE.strip_heredoc
    This command does such and such.

    Supported options are:
      -h         This message
      ...
  USAGE
end

사용자는 왼쪽 여백에 맞춰진 사용법 메시지를 볼 수 있습니다.

기술적으로는 전체 문자열에서 가장 적게 들여쓰인 줄을 찾아 그 양만큼의 선행 공백을 제거합니다.

주석: active_support/core_ext/string/strip.rb에 정의되어 있습니다.

indent

indent 메서드는 수신기의 줄을 들여씁니다:

<<EOS.indent(2)
def some_method
  some_code
end
EOS
# =>
  def some_method
    some_code
  end

두 번째 인수 indent_string은 사용할 들여쓰기 문자열을 지정합니다. 기본값은 nil로, 첫 번째 들여쓰인 줄을 보고 추측하고 그렇지 않으면 공백으로 대체합니다.

"  foo".indent(2)        # => "    foo"
"foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
"foo".indent(2, "\t")    # => "\t\tfoo"

indent_string은 일반적으로 공백이나 탭이지만 어떤 문자열이든 될 수 있습니다.

세 번째 인수 indent_empty_lines는 빈 줄도 들여쓰일지 여부를 나타내는 플래그입니다. 기본값은 false입니다.

"foo\n\nbar".indent(2)            # => "  foo\n\n  bar"
"foo\n\nbar".indent(2, nil, true) # => "  foo\n  \n  bar"

indent! 메서드는 제자리에서 들여쓰기를 수행합니다.

주석: active_support/core_ext/string/indent.rb에 정의되어 있습니다.

액세스

at(position)

at 메서드는 문자열의 position 위치에 있는 문자를 반환합니다:

"hello".at(0)  # => "h"
"hello".at(4)  # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil

주석: active_support/core_ext/string/access.rb에 정의되어 있습니다.

from(position)

from 메서드는 position 위치부터 시작하는 문자열의 부분 문자열을 반환합니다:

"hello".from(0)  # => "hello"
"hello".from(2)  # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil

주석: active_support/core_ext/string/access.rb에 정의되어 있습니다.

to(position)

to 메서드는 문자열의 position 위치까지의 부분 문자열을 반환합니다:

"hello".to(0)  # => "h"
"hello".to(2)  # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"

주석: active_support/core_ext/string/access.rb에 정의되어 있습니다.

first(limit = 1)

first 메서드는 문자열의 처음 limit 문자로 이루어진 부분 문자열을 반환합니다.

str.first(n) 호출은 n > 0이면 str.to(n-1)과 동등하고, n == 0이면 빈 문자열을 반환합니다.

주석: active_support/core_ext/string/access.rb에 정의되어 있습니다.

last(limit = 1)

last 메서드는 문자열의 마지막 limit 문자로 이루어진 부분 문자열을 반환합니다.

str.last(n) 호출은 n > 0이면 str.from(-n)과 동등하고, n == 0이면 빈 문자열을 반환합니다.

주석: active_support/core_ext/string/access.rb에 정의되어 있습니다.

변형

pluralize

pluralize 메서드는 수신기의 복수형을 반환합니다:

"table".pluralize     # => "tables"
"ruby".pluralize      # => "rubies"
"equipment".pluralize # => "equipment"

이전 예에서 보듯이 액티브 서포트는 일부 불규칙 복수형과 불가산 명사를 알고 있습니다. 내장 규칙은 config/initializers/inflections.rb에서 확장할 수 있습니다. 이 파일은 기본적으로 rails new 명령으로 생성되며 주석에 지침이 있습니다.

pluralize는 선택적으로 count 매개변수를 받을 수 있습니다. count == 1이면 단수형이 반환되고, 그 외의 값이면 복수형이 반환됩니다:

"dude".pluralize(0) # => "dudes"
"dude".pluralize(1) # => "dude"
"dude".pluralize(2) # => "dudes"

액티브 레코드는 이 메서드를 사용하여 모델에 해당하는 기본 테이블 이름을 계산합니다:

# active_record/model_schema.rb
def undecorated_table_name(model_name)
  table_name = model_name.to_s.demodulize.underscore
  pluralize_table_names ? table_name.pluralize : table_name
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

singularize

singularize 메서드는 pluralize의 역함수입니다:

"tables".singularize    # => "table"
"rubies".singularize    # => "ruby"
"equipment".singularize # => "equipment"

연관관계는 기본 연관 클래스의 이름을 계산할 때 이 메서드를 사용합니다:

# active_record/reflection.rb
def derive_class_name
  class_name = name.to_s.camelize
  class_name = class_name.singularize if collection?
  class_name
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

camelize

camelize 메서드는 수신기를 카멜 케이스로 반환합니다:

"product".camelize    # => "Product"
"admin_user".camelize # => "AdminUser"

일반적으로 이 메서드는 경로를 Ruby 클래스 또는 모듈 이름으로 변환하는 데 사용되며, 슬래시는 네임스페이스를 구분합니다:

"backoffice/session".camelize # => "Backoffice::Session"

예를 들어 액션 팩은 이 메서드를 사용하여 특정 세션 저장소 네, 번역을 계속하겠습니다.

예를 들어 액션 팩은 이 메서드를 사용하여 특정 세션 저장소 클래스를 로드합니다:

# action_controller/metal/session_management.rb
def session_store=(store)
  @@session_store = store.is_a?(Symbol) ?
    ActionDispatch::Session.const_get(store.to_s.camelize) :
    store
end

camelize는 선택적으로 :upper(기본값) 또는 :lower 인수를 받습니다. 후자를 사용하면 첫 번째 문자가 소문자가 됩니다:

"visual_effect".camelize(:lower) # => "visualEffect"

다른 언어의 메서드 이름 규칙을 따르는 경우 유용할 수 있습니다. 예를 들어 JavaScript.

정보: 일반적으로 camelizeunderscore의 역함수로 생각할 수 있지만, 그렇지 않은 경우도 있습니다: "SSLError".underscore.camelize"SslError"를 반환합니다. 이러한 경우를 지원하기 위해 액티브 서포트는 config/initializers/inflections.rb에서 약어를 지정할 수 있습니다:

ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym 'SSL'
end

"SSLError".underscore.camelize # => "SSLError"

camelizecamelcase의 별칭입니다.

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

underscore

underscore 메서드는 반대 방향으로, 카멜 케이스에서 경로로 변환합니다:

"Product".underscore   # => "product"
"AdminUser".underscore # => "admin_user"

또한 ”::“를 ”/“로 변환합니다:

"Backoffice::Session".underscore # => "backoffice/session"

그리고 소문자로 시작하는 문자열도 이해합니다:

"visualEffect".underscore # => "visual_effect"

underscore는 인수를 받지 않습니다.

Rails는 underscore를 사용하여 컨트롤러 클래스의 소문자 이름을 얻습니다:

# actionpack/lib/abstract_controller/base.rb
def controller_path
  @controller_path ||= name.delete_suffix("Controller").underscore
end

예를 들어 이 값은 params[:controller]에서 얻을 수 있습니다.

정보: 일반적으로 underscorecamelize의 역함수로 생각할 수 있지만, 그렇지 않은 경우도 있습니다. 예를 들어 "SSLError".underscore.camelize"SslError"를 반환합니다.

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

titleize

titleize 메서드는 수신기의 단어를 대문자로 만듭니다:

"alice in wonderland".titleize # => "Alice In Wonderland"
"fermat's enigma".titleize     # => "Fermat's Enigma"

titleizetitlecase의 별칭입니다.

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

dasherize

dasherize 메서드는 수신기의 밑줄을 대시로 바꿉니다:

"name".dasherize         # => "name"
"contact_data".dasherize # => "contact-data"

모델의 XML 직렬화기는 이 메서드를 사용하여 노드 이름에 대시를 사용합니다:

# active_model/serializers/xml.rb
def reformat_name(name)
  name = name.camelize if camelize?
  dasherize? ? name.dasherize : name
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

demodulize

정규화된 상수 이름이 있는 문자열에서 demodulize 메서드는 가장 오른쪽 부분, 즉 상수 이름 자체를 반환합니다:

"Product".demodulize                        # => "Product"
"Backoffice::UsersController".demodulize    # => "UsersController"
"Admin::Hotel::ReservationUtils".demodulize # => "ReservationUtils"
"::Inflections".demodulize                  # => "Inflections"
"".demodulize                               # => ""

예를 들어 액티브 레코드는 이 메서드를 사용하여 카운터 캐시 열의 이름을 계산합니다:

# active_record/reflection.rb
def counter_cache_column
  if options[:counter_cache] == true
    "#{active_record.name.demodulize.underscore.pluralize}_count"
  elsif options[:counter_cache]
    options[:counter_cache]
  end
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

deconstantize

정규화된 상수 참조 표현식이 있는 문자열에서 deconstantize 메서드는 가장 오른쪽 부분을 제거하여 일반적으로 상수의 컨테이너 이름을 남깁니다:

"Product".deconstantize                        # => ""
"Backoffice::UsersController".deconstantize    # => "Backoffice"
"Admin::Hotel::ReservationUtils".deconstantize # => "Admin::Hotel"

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

parameterize

parameterize 메서드는 수신기를 예쁜 URL에 사용할 수 있도록 정규화합니다.

"John Smith".parameterize # => "john-smith"
"Kurt Gödel".parameterize # => "kurt-godel"

대소문자를 유지하려면 preserve_case 인수를 true로 설정하세요. 기본적으로 preserve_case는 false입니다.

"John Smith".parameterize(preserve_case: true) # => "John-Smith"
"Kurt Gödel".parameterize(preserve_case: true) # => "Kurt-Godel"

사용자 정의 구분자를 사용하려면 separator 인수를 재정의하세요.

"John Smith".parameterize(separator: "_") # => "john_smith"
"Kurt Gödel".parameterize(separator: "_") # => "kurt_godel"

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

tableize

tableize 메서드는 underscorepluralize한 것입니다.

"Person".tableize      # => "people"
"Invoice".tableize     # => "invoices"
"InvoiceLine".tableize # => "invoice_lines"

일반적으로 tableize는 간단한 경우 모델에 해당하는 테이블 이름을 반환합니다. 실제 액티브 레코드 구현은 단순한 tableize가 아니며, 클래스 이름을 demodulize하고 몇 가지 옵션을 확인합니다.

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

classify

classify 메서드는 tableize의 역함수입니다. 테이블 이름에 해당하는 클래스 이름을 반환합니다:

"people".classify        # => "Person"
"invoices".classify      # => "Invoice"
"invoice_lines".classify # => "InvoiceLine"

이 메서드는 정규화된 테이블 이름도 이해합니다:

"highrise_production.companies".classify # => "Company"

classify는 문자열 형태의 클래스 이름을 반환한다는 점에 유의하세요. constantize를 호출하면 실제 클래스 객체를 얻을 수 있습니다.

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

constantize

constantize 메서드는 수신기의 상수 참조 표현식을 해결합니다:

"Integer".constantize # => Integer

module M
  X = 1
end
"M::X".constantize # => 1

문자열이 알려진 상수로 평가되지 않거나 내용이 유효한 상수 이름이 아닌 경우 constantizeNameError를 발생시킵니다.

constantize에 의한 상수 이름 확인은 항상 최상위 Object에서 시작합니다. 심지어 선행 ”::“이 없어도 마찬가지입니다.

X = :in_Object
module M
  X = :in_M

  X                 # => :in_M
  "::X".constantize # => :in_Object
  "X".constantize   # => :in_Object (!)
end

따라서 일반적으로 동일한 위치에서 실제 상수가 평가되는 것과 동일하지 않습니다.

메일러 테스트 케이스는 테스트 클래스의 이름을 사용하여 테스트 중인 메일러를 얻습니다:

# action_mailer/test_case.rb
def determine_default_mailer(name)
  name.delete_suffix("Test").constantize
rescue NameError => e
  raise NonInferrableMailerError.new(name)
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

humanize

humanize 메서드는 사용자에게 표시할 수 있도록 속성 이름을 조정합니다.

구체적으로는 다음과 같은 변환을 수행합니다:

  • 인수에 인간 변형 규칙을 적용합니다.
  • 선행 밑줄(있는 경우)을 삭제합니다.
  • ”_id" 접미사를 제거합니다(있는 경우).
  • 밑줄을 공백으로 바꿉니다(있는 경우).
  • 두문자어를 제외한 모든 단어를 소문자로 변환합니다.
  • 첫 번째 단어를 대문자로 변환합니다.

첫 번째 단어의 대문자화는 :capitalize 옵션을 false로 설정하여 끌 수 있습니다(기본값은 true).

"name".humanize                         # => "Name"
"author_id".humanize                    # => "Author"
"author_id".humanize(capitalize: false) # => "author"
"comments_count".humanize               # => "Comments count"
"_id".humanize                          # => "Id"

“SSL"이 약어로 정의된 경우:

"ssl_error".humanize # => "SSL error"

헬퍼 메서드 full_messages는 속성 이름을 포함하기 위해 humanize를 사용합니다:

def full_messages
  map { |attribute, message| full_message(attribute, message) }
end

def full_message
  # ...
  attr_name = attribute.to_s.tr(".", "_").humanize
  attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
  # ...
end

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

String#humanize: https://api.rubyonrails.org/classes/String.html#네, 번역을 계속하겠습니다.

foreign_key

foreign_key 메서드는 클래스 이름에서 외래 키 열 이름을 생성합니다. 이를 위해 demodulize, underscore, ”_id" 접미사를 추가합니다:

"User".foreign_key           # => "user_id"
"InvoiceLine".foreign_key    # => "invoice_line_id"
"Admin::Session".foreign_key # => "session_id"

밑줄 없이 “_id"를 원하면 false 인수를 전달하세요:

"User".foreign_key(false) # => "userid"

연관관계는 이 메서드를 사용하여 외래 키를 추론합니다. 예를 들어 has_onehas_many가 이렇게 합니다:

# active_record/associations.rb
foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

upcase_first

upcase_first 메서드는 수신기의 첫 번째 문자를 대문자로 만듭니다:

"employee salary".upcase_first # => "Employee salary"
"".upcase_first                # => ""

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

downcase_first

downcase_first 메서드는 수신기의 첫 번째 문자를 소문자로 변환합니다:

"If I had read Alice in Wonderland".downcase_first # => "if I had read Alice in Wonderland"
"".downcase_first                                  # => ""

주석: active_support/core_ext/string/inflections.rb에 정의되어 있습니다.

변환

to_date, to_time, to_datetime

to_date, to_time, to_datetime 메서드는 기본적으로 Date._parse의 편의 래퍼입니다:

"2010-07-27".to_date              # => Tue, 27 Jul 2010
"2010-07-27 23:37:00".to_time     # => 2010-07-27 23:37:00 +0200
"2010-07-27 23:37:00".to_datetime # => Tue, 27 Jul 2010 23:37:00 +0000

to_time은 선택적으로 :utc 또는 :local 인수를 받아 원하는 시간대의 시간을 반환합니다:

"2010-07-27 23:42:00".to_time(:utc)   # => 2010-07-27 23:42:00 UTC
"2010-07-27 23:42:00".to_time(:local) # => 2010-07-27 23:42:00 +0200

기본값은 :local입니다.

자세한 내용은 Date._parse 문서를 참조하세요.

정보: 이 세 메서드 모두 빈 수신기에 대해 nil을 반환합니다.

주석: active_support/core_ext/string/conversions.rb에 정의되어 있습니다.

기호 익스텐션

starts_with?ends_with?

액티브 서포트는 Symbol#start_with?Symbol#end_with?의 3인칭 별칭을 정의합니다:

:foo.starts_with?("f") # => true
:foo.ends_with?("o")   # => true

주석: active_support/core_ext/symbol/starts_ends_with.rb에 정의되어 있습니다.

숫자 익스텐션

바이트

모든 숫자는 다음 메서드에 응답합니다:

이들은 변환 계수 1024를 사용하여 해당 바이트 수를 반환합니다:

2.kilobytes   # => 2048
3.megabytes   # => 3145728
3.5.gigabytes # => 3758096384.0
-4.exabytes   # => -4611686018427387904

단수형 별칭도 있어 다음과 같이 사용할 수 있습니다:

1.megabyte # => 1048576

주석: active_support/core_ext/numeric/bytes.rb에 정의되어 있습니다.

시간

다음 메서드:

은 시간 선언과 계산을 가능하게 합니다. 예를 들어 45.minutes + 2.hours + 4.weeks와 같이 사용할 수 있습니다. 반환 값은 Time 객체에 추가하거나 빼는 데 사용할 수 있습니다.

이 메서드는 from_now, ago 등과 결합하여 정확한 날짜 계산을 할 수 있습니다. 예:

# Time.current.advance(days: 1)와 동등
1.day.from_now

# Time.current.advance(weeks: 2)와 동등
2.weeks.from_now

# Time.current.advance(days: 4, weeks: 5)와 동등
(4.days + 5.weeks).from_now

경고. 다른 지속 기간은 Integer 확장을 참조하세요.

주석: active_support/core_ext/numeric/time.rb에 정의되어 있습니다.

형식화

숫자를 다양한 방식으로 문자열로 표현할 수 있습니다.

전화번호 형식의 숫자 문자열을 생성합니다:

5551234.to_fs(:phone)
# => 555-1234
1235551234.to_fs(:phone)
# => 123-555-1234
1235551234.to_fs(:phone, area_code: true)
# => (123) 555-1234
1235551234.to_fs(:phone, delimiter: " ")
# => 123 555 1234
1235551234.to_fs(:phone, area_code: true, extension: 555)
# => (123) 555-1234 x 555
1235551234.to_fs(:phone, country_code: 1)
# => +1-123-555-1234

통화 형식의 숫자 문자열을 생성합니다:

1234567890.50.to_fs(:currency)                 # => $1,234,567,890.50
1234567890.506.to_fs(:currency)                # => $1,234,567,890.51
1234567890.506.to_fs(:currency, precision: 3)  # => $1,234,567,890.506

백분율 형식의 숫자 문자열을 생성합니다:

100.to_fs(:percentage)
# => 100.000%
100.to_fs(:percentage, precision: 0)
# => 100%
1000.to_fs(:percentage, delimiter: ".", separator: ",")
# => 1.000,000%
302.24398923423.to_fs(:percentage, precision: 5)
# => 302.24399%

구분 기호가 있는 숫자 문자열을 생성합니다:

12345678.to_fs(:delimited)                     # => 12,345,678
12345678.05.to_fs(:delimited)                  # => 12,345,678.05
12345678.to_fs(:delimited, delimiter: ".")     # => 12.345.678
12345678.to_fs(:delimited, delimiter: ",")     # => 12,345,678
12345678.05.to_fs(:delimited, separator: " ")  # => 12,345,678 05

지정된 정밀도로 반올림된 숫자 문자열을 생성합니다:

111.2345.to_fs(:rounded)                     # => 111.235
111.2345.to_fs(:rounded, precision: 2)       # => 111.23
13.to_fs(:rounded, precision: 5)             # => 13.00000
389.32314.to_fs(:rounded, precision: 0)      # => 389
111.2345.to_fs(:rounded, significant: true)  # => 111

사람이 읽을 수 있는 바이트 수 형식의 숫자 문자열을 생성합니다:

123.to_fs(:human_size)                  # => 123 Bytes
1234.to_fs(:human_size)                 # => 1.21 KB
12345.to_fs(:human_size)                # => 12.1 KB
1234567.to_fs(:human_size)              # => 1.18 MB
1234567890.to_fs(:human_size)           # => 1.15 GB
1234567890123.to_fs(:human_size)        # => 1.12 TB
1234567890123456.to_fs(:human_size)     # => 1.1 PB
1234567890123456789.to_fs(:human_size)  # => 1.07 EB

사람이 읽을 수 있는 단어 형식의 숫자 문자열을 생성합니다:

123.to_fs(:human)               # => "123"
1234.to_fs(:human)              # => "1.23 Thousand"
12345.to_fs(:human)             # => "12.3 Thousand"
1234567.to_fs(:human)           # => "1.23 Million"
1234567890.to_fs(:human)        # => "1.23 Billion"
1234567890123.to_fs(:human)     # => "1.23 Trillion"
1234567890123456.to_fs(:human)  # => "1.23 Quadrillion"

주석: active_support/core_ext/numeric/conversions.rb에 정의되어 있습니다.

정수 익스텐션

multiple_of?

multiple_of? 메서드는 정수가 인수의 배수인지 테스트합니다:

2.multiple_of?(1) # => true
1.multiple_of?(2) # => false

주석: active_support/core_ext/integer/multiple.rb에 정의되어 있습니다.

ordinal

ordinal 메서드는 수신기 네, 번역을 계속하겠습니다.

ordinal 메서드는 수신기 정수에 해당하는 서수 접미사 문자열을 반환합니다:

1.ordinal    # => "st"
2.ordinal    # => "nd"
53.ordinal   # => "rd"
2009.ordinal # => "th"
-21.ordinal  # => "st"
-134.ordinal # => "th"

주석: active_support/core_ext/integer/inflections.rb에 정의되어 있습니다.

ordinalize

ordinalize 메서드는 수신기 정수에 해당하는 서수 문자열을 반환합니다. ordinal 메서드와 달리 접미사만 반환하지 않습니다.

1.ordinalize    # => "1st"
2.ordinalize    # => "2nd"
53.ordinalize   # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize  # => "-21st"
-134.ordinalize # => "-134th"

주석: active_support/core_ext/integer/inflections.rb에 정의되어 있습니다.

시간

다음 메서드:

는 시간 선언과 계산을 가능하게 합니다. 예를 들어 4.months + 5.years와 같이 사용할 수 있습니다. 반환 값은 Time 객체에 추가하거나 빼는 데 사용할 수 있습니다.

이 메서드는 from_now, ago 등과 결합하여 정확한 날짜 계산을 할 수 있습니다. 예:

# Time.current.advance(months: 1)와 동등
1.month.from_now

# Time.current.advance(years: 2)와 동등
2.years.from_now

# Time.current.advance(months: 4, years: 5)와 동등
(4.months + 5.years).from_now

경고. 다른 지속 기간은 Numeric 확장을 참조하세요.

주석: active_support/core_ext/integer/time.rb에 정의되어 있습니다.

BigDecimal 익스텐션

to_s

to_s 메서드는 기본 지정자로 "F"를 제공합니다. 즉, to_s를 단순히 호출하면 부동 소수점 표현이 반환되며 공학 표기법은 사용되지 않습니다:

BigDecimal(5.00, 6).to_s       # => "5.0"

공학 표기법은 여전히 지원됩니다:

BigDecimal(5.00, 6).to_s("e")  # => "0.5E1"

Enumerable 익스텐션

index_by

index_by 메서드는 열거형의 요소를 키로 하는 해시를 생성합니다.

블록에 각 요소를 전달하고 반환된 값으로 요소를 인덱싱합니다:

invoices.index_by(&:number)
# => {"2009-032" => <Invoice ...>, "2009-008" => <Invoice ...>, ...}

경고. 키는 일반적으로 고유해야 합니다. 블록이 다른 요소에 대해 동일한 값을 반환하면 해당 키에 대한 컬렉션이 구축되지 않습니다. 마지막 항목이 승리합니다.

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

index_with

index_with 메서드는 열거형의 요소를 키로 하는 해시를 생성합니다. 값은 전달된 기본값이거나 블록에서 반환됩니다.

post = Post.new(title: "hey there", body: "what's up?")

%i( title body ).index_with { |attr_name| post.public_send(attr_name) }
# => { title: "hey there", body: "what's up?" }

WEEKDAYS.index_with(Interval.all_day)
# => { monday: [ 0, 1440 ], … }

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

many?

many? 메서드는 collection.size > 1의 단축 표현입니다:

<% if pages.many? %>
  <%= pagination_links %>
<% end %>

선택적 블록이 주어지면 many?는 해당 요소만 고려합니다:

@see_more = videos.many? { |video| video.category == params[:category] }

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

exclude?

exclude? 술어는 주어진 객체가 컬렉션에 속하지 않는지 테스트합니다. 내장 include?의 부정입니다:

to_visit << node if visited.exclude?(node)

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

including

including 메서드는 전달된 요소를 포함하는 새 열거형을 반환합니다:

[ 1, 2, 3 ].including(4, 5)                    # => [ 1, 2, 3, 4, 5 ]
["David", "Rafael"].including %w[ Aaron Todd ] # => ["David", "Rafael", "Aaron", "Todd"]

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

excluding

excluding 메서드는 지정된 요소를 제외한 열거형의 복사본을 반환합니다. 이는 Enumerable#excluding의 최적화 버전으로, 성능 향상을 위해 Array#-를 사용합니다.

["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ])                  # => [ [ 0, 1 ] ]

excludingwithout의 별칭입니다.

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

pluck

pluck 메서드는 각 요소에서 지정된 키를 추출합니다:

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name) # => ["David", "Rafael", "Aaron"]
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name) # => [[1, "David"], [2, "Rafael"]]

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

pick

pick 메서드는 첫 번째 요소에서 지정된 키를 추출합니다:

[{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name) # => "David"
[{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name) # => [1, "David"]

주석: active_support/core_ext/enumerable.rb에 정의되어 있습니다.

배열 익스텐션

액세스

액티브 서포트는 배열에 대한 API를 강화하여 특정 방식으로 액세스할 수 있게 합니다. 예를 들어 to 메서드는 전달된 인덱스까지의 하위 배열을 반환합니다:

%w(a b c d).to(2) # => ["a", "b", "c"]
[].to(7)          # => []

마찬가지로 from 메서드는 전달된 인덱스부터 끝까지의 꼬리 부분을 반환합니다. 인덱스가 배열 길이보다 크면 빈 배열을 반환합니다.

%w(a b c d).from(2)  # => ["c", "d"]
%w(a b c d).from(10) # => []
[].from(0)           # => []

including 메서드는 전달된 요소를 포함하는 새 배열을 반환합니다:

[ 1, 2, 3 ].including(4, 5)          # => [ 1, 2, 3, 4, 5 ]
[ [ 0, 1 ] ].including([ [ 1, 0 ] ]) # => [ [ 0, 1 ], [ 1, 0 ] ]

excluding 메서드는 지정된 요소를 제외한 배열의 복사본을 반환합니다. 이는 성능 향상을 위해 Array#-를 사용하는 Enumerable#excluding의 최적화 버전입니다.

["David", "Rafael", "Aaron", "Todd"].excluding("Aaron", "Todd") # => ["David", "Rafael"]
[ [ 0, 1 ], [ 1, 0 ] ].excluding([ [ 1, 0 ] ])                  # => [ [ 0, 1 ] ]

second, third, fourth, fifth 메서드는 해당 요소를 반환하며, second_to_lastthird_to_last도 마찬가지입니다(firstlast는 내장). 사회적 지혜와 긍정적인 건설성 덕분에 forty_two도 사용할 수 있습니다.

%w(a b c d).third # => "c"
%w(a b c d).fifth # => nil

주석: active_support/core_ext/array/access.rb에 정의되어 있습니다.

추출

extract! 메서드는 블록이 true를 반환하는 요소를 제거하고 반환합니다. 블록이 주어지지 않으면 대신 Enumerator가 반환됩니다.

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5,네, 번역을 계속하겠습니다.

```ruby
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = numbers.extract! { |number| number.odd? } # => [1, 3, 5, 7, 9]
numbers # => [0, 2, 4, 6, 8]

주석: active_support/core_ext/array/extract.rb에 정의되어 있습니다.

옵션 추출

메서드 호출에서 마지막 인수가 해시인 경우(&block 인수는 제외), Ruby는 괄호를 생략할 수 있습니다:

User.exists?(email: params[:email])

이러한 구문적 설탕은 위치 인수가 너무 많은 경우 피하기 위해 Rails에서 많이 사용됩니다. 특히 옵션 해시를 사용하는 것이 관용적입니다.

그러나 가변 인수를 받고 선언에 *를 사용하는 메서드의 경우 이러한 옵션 해시가 인수 배열의 항목이 되어 그 역할을 잃게 됩니다.

이러한 경우 extract_options!를 사용하여 옵션 해시를 구분할 수 있습니다. 이 메서드는 배열의 마지막 항목이 해시인지 확인하고, 그렇다면 해당 해시를 팝업하여 반환합니다. 그렇지 않으면 빈 해시를 반환합니다.

예를 들어 caches_action 컨트롤러 매크로의 정의를 보겠습니다:

def caches_action(*actions)
  return unless cache_configured?
  options = actions.extract_options!
  # ...
end

이 메서드는 임의의 수의 작업 이름과 선택적 옵션 해시를 마지막 인수로 받습니다. extract_options! 호출을 통해 옵션 해시를 얻고 actions에서 제거할 수 있습니다.

주석: active_support/core_ext/array/extract_options.rb에 정의되어 있습니다.

변환

to_sentence

to_sentence 메서드는 배열을 항목을 열거하는 문장 형태의 문자열로 변환합니다:

%w().to_sentence                # => ""
%w(Earth).to_sentence           # => "Earth"
%w(Earth Wind).to_sentence      # => "Earth and Wind"
%w(Earth Wind Fire).to_sentence # => "Earth, Wind, and Fire"

이 메서드는 세 가지 옵션을 받습니다:

  • :two_words_connector: 길이가 2인 배열에 사용됩니다. 기본값은 ” and “입니다.
  • :words_connector: 3개 이상의 요소가 있는 배열에서 마지막 두 개를 제외한 요소를 연결하는 데 사용됩니다. 기본값은 ”, “입니다.
  • :last_word_connector: 3개 이상의 요소가 있는 배열에서 마지막 두 개의 요소를 연결하는 데 사용됩니다. 기본값은 ”, and “입니다.

이 옵션의 기본값은 지역화될 수 있으며, 해당 키는 다음과 같습니다:

옵션 I18n 키
:two_words_connector support.array.two_words_connector
:words_connector support.array.words_connector
:last_word_connector support.array.last_word_connector

주석: active_support/core_ext/array/conversions.rb에 정의되어 있습니다.

to_fs

to_fs 메서드는 기본적으로 to_s와 동일하게 작동합니다.

그러나 배열에 id에 응답하는 항목이 포함된 경우 :db 기호를 인수로 전달할 수 있습니다. 이는 일반적으로 Active Record 객체의 컬렉션에 사용됩니다. 반환되는 문자열은 다음과 같습니다:

[].to_fs(:db)            # => "null"
[user].to_fs(:db)        # => "8456"
invoice.lines.to_fs(:db) # => "23,567,556,12"

예제의 정수는 각각 id 호출에서 온 것으로 가정됩니다.

주석: active_support/core_ext/array/conversions.rb에 정의되어 있습니다.

to_xml

to_xml 메서드는 수신기의 XML 표현을 포함하는 문자열을 반환합니다:

Contributor.limit(2).order(:rank).to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors type="array">
#   <contributor>
#     <id type="integer">4356</id>
#     <name>Jeremy Kemper</name>
#     <rank type="integer">1</rank>
#     <url-id>jeremy-kemper</url-id>
#   </contributor>
#   <contributor>
#     <id type="integer">4404</id>
#     <name>David Heinemeier Hansson</name>
#     <rank type="integer">2</rank>
#     <url-id>david-heinemeier-hansson</url-id>
#   </contributor>
# </contributors>

이를 위해 각 항목에 to_xml를 보내고 결과를 루트 노드 아래에 수집합니다. 모든 항목은 to_xml에 응답해야 하며, 그렇지 않으면 예외가 발생합니다.

기본적으로 루트 요소의 이름은 첫 번째 항목의 클래스 이름을 언더스코어와 대시로 변환한 복수형입니다. 단, 나머지 요소가 해당 유형(is_a?로 확인)에 속하고 해시가 아닌 경우에만 그렇습니다. 위의 예에서는 "contributors"입니다.

요소 중 하나라도 첫 번째 것과 다른 유형이면 루트 노드가 "objects"가 됩니다:

[Contributor.first, Commit.first].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
#   <object>
#     <id type="integer">4583</id>
#     <name>Aaron Batalion</name>
#     <rank type="integer">53</rank>
#     <url-id>aaron-batalion</url-id>
#   </object>
#   <object>
#     <author>Joshua Peek</author>
#     <authored-timestamp type="datetime">2009-09-02T16:44:36Z</authored-timestamp>
#     <branch>origin/master</branch>
#     <committed-timestamp type="datetime">2009-09-02T16:44:36Z</committed-timestamp>
#     <committer>Joshua Peek</committer>
#     <git-show nil="true"></git-show>
#     <id type="integer">190316</id>
#     <imported-from-svn type="boolean">false</imported-from-svn>
#     <message>Kill AMo observing wrap_with_notifications since ARes was only using it</message>
#     <sha1>723a47bfb3708f968821bc969a9a3fc873a3ed58</sha1>
#   </object>
# </objects>

수신기가 해시 배열인 경우 기본적으로 루트 요소도 "objects"입니다:

[{ a: 1, b: 2 }, { c: 3 }].to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <objects type="array">
#   <object>
#     <b type="integer">2</b>
#     <a type="integer">1</a>
#   </object>
#   <object>
#     <c type="integer">3</c>
#   </object>
# </objects>

경고. 컬렉션이 비어 있으면 기본적으로 루트 요소는 "nil-classes"가 됩니다. 이는 함정이 될 수 있습니다. 예를 들어 위의 기여자 목록의 루트 요소는 컬렉션이 비어 있으면 "contributors"가 아니라 "nil-classes"가 됩니다. :root 옵션을 사용하여 일관된 루트 요소를 보장할 수 있습니다.

자식 노드의 이름은 기본적으로 루트 노드의 단수형입니다. 위의 예에서는 "contributor"와 "object"를 보았습니다. :children 옵션을 사용하여 이러한 노드 이름을 설정할 수 있습니다.

기본 XML 빌더는 Builder::XmlMarkup의 새 인스턴스입니다. :builder 옵션을 사용하여 사용자 정의 빌더를 구성할 수 있습니다. 이 메서드는 또한 :dasherize 등의 옵션을 받으며, 이는 빌더에 전달됩니다:

Contributor.limit(2).order(:rank).to_xml(skip_types: true)
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <contributors>
#   <contributor>
#     <id>4356</id>
#     <name>Jeremy Kemper</name>
#     <rank>1</rank>
#     <url-id>jeremy-kemper</url-id>
#   </contributor>
#   <contributor>
#     <id>4404</id>
#     <name>David Heinemeier Hansson</name>
#     <rank>2</rank>
#     <url-id>david-heinemeier-hansson</url-id>
#   </contributor>
# </contributors>

주석: active_support/core_ext/array/conversions.rb에 정의되어 있습니다.

래핑

Array.wrap 메서드는 인수가 이미 배열(또는 배열 유사)이 아닌 경우 인수를 배열로 래핑합니다.

구체적으로:

  • 인수가 nil이면 빈 배열이 반환됩니다.
  • 그렇지 않고 인수가 to_ary에 응답하면 해당 값이 호출되고, 반환된 값이 nil이 아니면 해당 값이 반환됩니다.
  • 그렇지 않으면 인수를 단일 요소로 하는 배열이 반환됩니다.
Array.wrap(nil)       # => []
Array.wrap([1, 2, 3]) # => [1, 2, 3]
Array.wrap(0)         # => [0]

이 메서드는 Kernel#Array와 유사한 목적이지만 몇 가지 차이점이 있습니다:

  • 인수가 to_ary에 응답하면 해당 메서드가 호출됩니다. Kernel#Array는 반환된 값이 nil이면 to_a를 시도하지만, Array.wrap은 즉시 인수를 단일 요소로 하는 배열을 반환합니다.
  • to_ary에서 반환된 값이 nil도 배열도 아닌 경우 Kernel#Array는 예외를 발생시키지만 Array.wrap은 그렇지 않고 그냥 값을 반환합니다.
  • to_a를 호출하지 않습니다. 인수가 to_ary에 응답하지 않으면 인수를 단일 요소로 하는 배열을 반환합니다.

마지막 포인트는 일부 열거형에 대해 특히 비교할 만한 가치가 있습니다:

Array.wrap(foo: :bar) # => [{:foo=>:bar}]
Array(foo: :bar)      # => [[:foo, :bar]]

또한 스플랫 연산자를 사용하는 관련 관용구가 있습니다:

[*object]

주석: active_support/core_ext/array/wrap.rb에 정의되어 있습니다.

복제

Array#deep_dup 메서드는 Active Support의 Object#deep_dup 메서드를 사용하여 자신과 내부의 모든 객체를 재귀적으로 복제합니다. Array#map과 같이 작동하지만 네, 번역을 계속하겠습니다.

Array#deep_dup 메서드는 Active Support의 Object#deep_dup 메서드를 사용하여 자신과 내부의 모든 객체를 재귀적으로 복제합니다. Array#map과 같이 작동하지만 각 객체에 deep_dup 메서드를 보냅니다.

array = [1, [2, 3]]
dup = array.deep_dup
dup[1][2] = 4
array[1][2] == nil   # => true

주석: active_support/core_ext/object/deep_dup.rb에 정의되어 있습니다.

그룹화

in_groups_of(number, fill_with = nil)

in_groups_of 메서드는 배열을 지정된 크기의 연속 그룹으로 분할합니다. 그룹을 포함하는 배열을 반환합니다:

[1, 2, 3].in_groups_of(2) # => [[1, 2], [3, nil]]

또는 블록을 전달하면 차례로 그룹을 생성합니다:

<% sample.in_groups_of(3) do |a, b, c| %>
  <tr>
    <td><%= a %></td>
    <td><%= b %></td>
    <td><%= c %></td>
  </tr>
<% end %>

첫 번째 예에서는 in_groups_of가 마지막 그룹을 요청된 크기만큼 nil로 채웁니다. 두 번째 인수를 사용하여 채우는 값을 변경할 수 있습니다:

[1, 2, 3].in_groups_of(2, 0) # => [[1, 2], [3, 0]]

마지막 그룹을 채우지 않도록 false를 전달할 수도 있습니다:

[1, 2, 3].in_groups_of(2, false) # => [[1, 2], [3]]

따라서 false는 채우는 값으로 사용할 수 없습니다.

주석: active_support/core_ext/array/grouping.rb에 정의되어 있습니다.

in_groups(number, fill_with = nil)

in_groups 메서드는 배열을 지정된 수의 그룹으로 분할합니다. 메서드는 그룹을 포함하는 배열을 반환합니다:

%w(1 2 3 4 5 6 7).in_groups(3)
# => [["1", "2", "3"], ["4", "5", nil], ["6", "7", nil]]

또는 블록을 전달하면 차례로 그룹을 생성합니다:

%w(1 2 3 4 5 6 7).in_groups(3) { |group| p group }
["1", "2", "3"]
["4", "5", nil]
["6", "7", nil]

위의 예에서 in_groups는 필요한 경우 마지막 그룹에 nil 요소를 추가합니다. 그룹은 최대 하나의 이러한 추가 요소를 가질 수 있으며, 항상 마지막 그룹에 있습니다.

두 번째 선택적 인수를 사용하여 채우는 값을 변경할 수 있습니다:

%w(1 2 3 4 5 6 7).in_groups(3, "0")
# => [["1", "2", "3"], ["4", "5", "0"], ["6", "7", "0"]]

그리고 false를 전달하여 더 작은 그룹을 채우지 않도록 할 수 있습니다:

%w(1 2 3 4 5 6 7).in_groups(3, false)
# => [["1", "2", "3"], ["4", "5"], ["6", "7"]]

따라서 false는 채우는 값으로 사용할 수 없습니다.

주석: active_support/core_ext/array/grouping.rb에 정의되어 있습니다.

split(value = nil)

split 메서드는 구분자로 배열을 나누고 결과 청크를 반환합니다.

블록이 전달되면 블록이 true를 반환하는 요소가 구분자가 됩니다:

(-5..5).to_a.split { |i| i.multiple_of?(4) }
# => [[-5], [-3, -2, -1], [1, 2, 3], [5]]

그렇지 않으면 기본값인 nil을 받은 값이 구분자가 됩니다:

[0, 1, -5, 1, 1, "foo", "bar"].split(1)
# => [[0], [-5], [], ["foo", "bar"]]

팁: 이전 예에서 연속된 구분자로 인해 빈 배열이 생성된다는 점에 유의하세요.

주석: active_support/core_ext/array/grouping.rb에 정의되어 있습니다.

해시 익스텐션

변환

to_xml

to_xml 메서드는 수신기의 XML 표현을 포함하는 문자열을 반환합니다:

{ foo: 1, bar: 2 }.to_xml
# =>
# <?xml version="1.0" encoding="UTF-8"?>
# <hash>
#   <foo type="integer">1</foo>
#   <bar type="integer">2</bar>
# </hash>

이를 위해 메서드는 쌍을 반복하고 에 따라 노드를 빌드합니다. 키, 값 쌍이 주어지면:

  • 값이 해시인 경우 :root를 키로 하여 재귀적으로 호출합니다.

  • 값이 배열인 경우 :root를 키로, 키의 단수형을 :children으로 하여 재귀적으로 호출합니다.

  • 값이 호출 가능한 객체인 경우 1개 또는 2개의 인수를 받아야 합니다. 인수 수에 따라 호출 가능 객체는 :root를 키로 하는 options 해시와 키의 단수형을 두 번째 인수로 받습니다. 반환 값이 새 노드가 됩니다.

  • 값이 to_xml에 응답하는 경우 메서드가 :root를 키로 호출됩니다.

  • 그 외의 경우 key 태그의 노드가 생성되고 value의 문자열 표현이 텍스트 노드가 됩니다. valuenil이면 "nil” 속성이 “true"로 추가됩니다. :skip_types 옵션이 존재하고 true가 아닌 경우, 다음 매핑에 따라 "type” 속성도 추가됩니다:

XML_TYPE_NAMES = {
  "Symbol"     => "symbol",
  "Integer"    => "integer",
  "BigDecimal" => "decimal",
  "Float"      => "float",
  "TrueClass"  => "boolean",
  "FalseClass" => "boolean",
  "Date"       => "date",
  "DateTime"   => "datetime",
  "Time"       => "datetime"
}

기본적으로 루트 노드는 “hash"지만 :root 옵션으로 구성할 수 있습니다.

기본 XML 빌더는 Builder::XmlMarkup의 새 인스턴스입니다. :builder 옵션을 사용하여 사용자 정의 빌더를 구성할 수 있습니다. 이 메서드는 또한 :dasherize 등의 옵션을 받으며, 이는 빌더에 전달됩니다.

주석: active_support/core_ext/hash/conversions.rb에 정의되어 있습니다.

병합

Ruby에는 두 해시를 병합하는 내장 메서드 Hash#merge가 있습니다:

{ a: 1, b: 1 }.merge(a: 0, c: 2)
# => {:a=>0, :b=>1, :c=>2}

액티브 서포트는 충돌 시 인수의 해시 키가 승리하는 merge와 다른 몇 가지 편리한 해시 병합 방법을 정의합니다.

reverse_mergereverse_merge!

충돌 시 인수의 해시 키가 승리하는 merge와 달리 reverse_merge를 사용하면 옵션 해시의 기본값을 간단하게 설정할 수 있습니다:

options = options.reverse_merge(length: 30, omission: "...")

그리고 제자리에서 수행하는 reverse_merge! 버전도 있습니다:

options.reverse_merge!(length: 30, omission: "...")

경고. reverse_merge!는 호출자의 해시를 변경할 수 있으므로 좋은 생각이 아닐 수 있습니다.

주석: active_support/core_ext/hash/reverse_merge.rb에 정의되어 있습니다.

reverse_update

reverse_update 메서드는 reverse_merge!의 별칭입니다.

경고. reverse_update에는 뱅 접미사가 없다는 점에 유의하세요.

주석: active_support/core_ext/hash/reverse_merge.rb에 정의되어 있습니다.

deep_mergedeep_merge!

이전 예에서 볼 수 있듯이 키가 두 해시에 모두 있는 경우 인수의 값이 승리합니다.

액티브 서포트는 Hash#deep_merge를 정의합니다. 깊은 병합에서는 키가 두 해시에 모두 있고 해당 값도 해시인 경우 해당 병합이 결과 해시의 값이 됩니다:

{ a: { b: 1 } }.deep_merge(a: { c: 2 })
# => {:a=>{:b=>1, :c=>2}}

deep_merge! 메서드는 제자리에서 깊은 병합을 수행합니다.

주석: active_support/core_ext/hash/deep_merge.rb에 정의되어 있습니다.

깊은 복제

Hash#deep_dup 메서드는 Active Support의 Object#deep_dup 메서드를 사용하여 자신과 모든 키와 값을 재귀적으로 복제합니다. Enumerator#each_with_object와 같이 작동하지만 내부의 각 쌍에 deep_dup 메서드를 보냅니다.

hash = { a: 1, b: { c: 2, d: [3, 4] } }

dup = hash.deep_dup
dup[:b][:e] = 5
dup[:b][:d] << 5

hash[:b][:e] == nil      # => true
hash[:b][:d] == [3, 4]   # => true

주석: active_support/core_ext/object/deep_dup.rb에 정의되어 있습니다.

키 작업

exceptexcept!

except 메서드는 인수 목록에 있는 키를 제거한 해시를 반환합니다(있는 경우):

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

수신기가 convert_key에 응답하는 경우 각 인수에 대해 해당 메서드가 호출네, 번역을 계속하겠습니다.

except 메서드는 인수 목록에 있는 키를 제거한 해시를 반환합니다(있는 경우):

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

수신기가 convert_key에 응답하는 경우 각 인수에 대해 해당 메서드가 호출됩니다. 이를 통해 예를 들어 인디퍼런트 액세스 해시와 함께 사용할 수 있습니다:

{ a: 1 }.with_indifferent_access.except(:a)  # => {}
{ a: 1 }.with_indifferent_access.except("a") # => {}

제자리에서 키를 제거하는 뱅 버전 except!도 있습니다.

주석: active_support/core_ext/hash/except.rb에 정의되어 있습니다.

stringify_keysstringify_keys!

stringify_keys 메서드는 수신기의 키를 문자열로 변환한 새 해시를 반환합니다. 이를 위해 각 키에 to_s를 보냅니다:

{ nil => nil, 1 => 1, a: :a }.stringify_keys
# => {"" => nil, "1" => 1, "a" => :a}

키 충돌의 경우 가장 최근에 삽입된 값이 유지됩니다:

{ "a" => 1, a: 2 }.stringify_keys
# 결과는
# => {"a"=>2}

이 메서드는 예를 들어 기호와 문자열을 모두 옵션으로 허용하는 데 유용할 수 있습니다. 예를 들어 ActionView::Helpers::FormHelper는 다음과 같이 정의합니다:

def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
  options = options.stringify_keys
  options["type"] = "checkbox"
  # ...
end

두 번째 줄에서 "type” 키에 안전하게 액세스할 수 있으며, 사용자가 :type 또는 “type"을 전달할 수 있습니다.

제자리에서 키를 문자열로 변환하는 뱅 버전 stringify_keys!도 있습니다.

또한 deep_stringify_keysdeep_stringify_keys!를 사용하여 주어진 해시와 그 안의 모든 해시의 모든 키를 문자열로 변환할 수 있습니다. 결과의 예는 다음과 같습니다:

{ nil => nil, 1 => 1, nested: { a: 3, 5 => 5 } }.deep_stringify_keys
# => {""=>nil, "1"=>1, "nested"=>{"a"=>3, "5"=>5}}

주석: active_support/core_ext/hash/keys.rb에 정의되어 있습니다.

symbolize_keyssymbolize_keys!

symbolize_keys 메서드는 가능한 경우 수신기의 키를 기호로 변환한 새 해시를 반환합니다. 이를 위해 각 키에 to_sym을 보냅니다:

{ nil => nil, 1 => 1, "a" => "a" }.symbolize_keys
# => {nil=>nil, 1=>1, :a=>"a"}

경고. 이전 예에서 하나의 키만 기호화되었다는 점에 유의하세요.

키 충돌의 경우 가장 최근에 삽입된 값이 유지됩니다:

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

이 메서드는 예를 들어 기호와 문자열을 모두 옵션으로 허용하는 데 유용할 수 있습니다. 예를 들어 ActionText::TagHelper는 다음과 같이 정의합니다:

def rich_text_area_tag(name, value = nil, options = {})
  options = options.symbolize_keys

  options[:input] ||= "trix_input_#{ActionText::TagHelper.id += 1}"
  # ...
end

세 번째 줄에서 :input 키에 안전하게 액세스할 수 있으며, 사용자가 :input 또는 "input"을 전달할 수 있습니다.

제자리에서 키를 기호화하는 뱅 버전 symbolize_keys!도 있습니다.

또한 deep_symbolize_keysdeep_symbolize_keys!를 사용하여 주어진 해시와 그 안의 모든 해시의 모든 키를 기호화할 수 있습니다. 결과의 예는 다음과 같습니다:

{ nil => nil, 1 => 1, "nested" => { "a" => 3, 5 => 5 } }.deep_symbolize_keys
# => {nil=>nil, 1=>1, nested:{a:3, 5=>5}}

주석: active_support/core_ext/hash/keys.rb에 정의되어 있습니다.

to_optionsto_options!

to_optionsto_options! 메서드는 각각 symbolize_keyssymbolize_keys!의 별칭입니다.

주석: active_support/core_ext/hash/keys.rb에 정의되어 있습니다.

assert_valid_keys

assert_valid_keys 메서드는 임의의 수의 인수를 받고, 수신기에 해당 목록 외의 키가 있는지 확인합니다. 있는 경우 ArgumentError가 발생합니다.

{ a: 1 }.assert_valid_keys(:a)  # 통과
{ a: 1 }.assert_valid_keys("a") # ArgumentError

액티브 레코드는 연관관계를 빌드할 때 알 수 없는 옵션을 허용하지 않습니다. 이 제어는 assert_valid_keys를 통해 구현됩니다.

주석: active_support/core_ext/hash/keys.rb에 정의되어 있습니다.

값 작업

deep_transform_valuesdeep_transform_values!

deep_transform_values 메서드는 블록 연산을 통해 모든 값을 변환한 새 해시를 반환합니다. 이는 루트 해시와 중첩된 해시 및 배열의 값을 모두 포함합니다.

hash = { person: { name: "Rob", age: "28" } }

hash.deep_transform_values { |value| value.to_s.upcase }
# => {person: {name: "ROB", age: "28"}}

제자리에서 블록 연산을 사용하여 모든 값을 변환하는 뱅 버전 deep_transform_values!도 있습니다.

주석: active_support/core_ext/hash/deep_transform_values.rb에 정의되어 있습니다.

슬라이싱

slice! 메서드는 지정된 키만 남기고 나머지 키/값 쌍을 제거한 후 제거된 쌍을 포함하는 해시를 반환합니다.

hash = { a: 1, b: 2 }
rest = hash.slice!(:a) # => {:b=>2}
hash                   # => {:a=>1}

주석: active_support/core_ext/hash/slice.rb에 정의되어 있습니다.

추출

extract! 메서드는 지정된 키에 해당하는 키/값 쌍을 제거하고 반환합니다.

hash = { a: 1, b: 2 }
rest = hash.extract!(:a) # => {:a=>1}
hash                     # => {:b=>2}

extract! 메서드는 수신기와 동일한 해시 하위 클래스를 반환합니다.

hash = { a: 1, b: 2 }.with_indifferent_access
rest = hash.extract!(:a).class
# => ActiveSupport::HashWithIndifferentAccess

주석: active_support/core_ext/hash/slice.rb에 정의되어 있습니다.

인디퍼런트 액세스

with_indifferent_access 메서드는 수신기에서 ActiveSupport::HashWithIndifferentAccess를 반환합니다:

{ a: 1 }.with_indifferent_access["a"] # => 1

주석: active_support/core_ext/hash/indifferent_access.rb에 정의되어 있습니다.

정규식 익스텐션

multiline?

multiline? 메서드는 정규식에 /m 플래그가 설정되어 있는지, 즉 점이 줄바꿈 문자를 일치시키는지 말합니다.

%r{.}.multiline?  # => false
%r{.}m.multiline? # => true

Regexp.new(".").multiline?                    # => false
Regexp.new(".", Regexp::MULTILINE).multiline? # => true

Rails는 이 메서드를 한 곳에서만 사용합니다. 라우팅 코드에서도 마찬가지입니다. 다중 행 정규식은 라우팅 요구 사항에 허용되지 않으며, 이 플래그를 사용하여 이 제약 조건을 적용합니다.

def verify_regexp_requirements(requirements)
  # ...
  if requirement.multiline?
    raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
  end
  # ...
end

주석: active_support/core_ext/regexp.rb에 정의되어 있습니다.

범위 익스텐션

to_fs

액티브 서포트는 Range#to_fsto_s의 대안으로 정의하며, 선택적 형식 인수를 이해합니다. 현재로서는 :db만 지원됩니다:

(Date.today..Date.tomorrow).to_fs
# => "2009-10-25..2009-10-26"

(Date.today..Date.tomorrow).to_fs(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26네, 번역을 계속하겠습니다.

(Date.today..Date.tomorrow).to_fs(:db)
# => "BETWEEN '2009-10-25' AND '2009-10-26'"

예에서 보듯이 :db 형식은 BETWEEN SQL 절을 생성합니다. 이는 범위 값 조건에 대한 Active Record의 지원에 사용됩니다.

주석: active_support/core_ext/range/conversions.rb에 정의되어 있습니다.

===include?

Range#===Range#include? 메서드는 주어진 인스턴스의 끝점 사이에 어떤 값이 속하는지 말합니다:

(2..3).include?(Math::E) # => true

액티브 서포트는 이 메서드를 확장하여 인수가 다른 범위일 수 있도록 합니다. 이 경우 인수 범위의 끝점이 수신기 자체에 속하는지 테스트합니다:

(1..10) === (3..7)  # => true
(1..10) === (0..7)  # => false
(1..10) === (3..11) # => false
(1...9) === (3..9)  # => false

(1..10).include?(3..7)  # => true
(1..10).include?(0..7)  # => false
(1..10).include?(3..11) # => false
(1...9).include?(3..9)  # => false

주석: active_support/core_ext/range/compare_range.rb에 정의되어 있습니다.

overlap?

Range#overlap? 메서드는 두 범위가 겹치는지 말합니다:

(1..10).overlap?(7..11)  # => true
(1..10).overlap?(0..7)   # => true
(1..10).overlap?(11..27) # => false

주석: active_support/core_ext/range/overlap.rb에 정의되어 있습니다.

날짜 익스텐션

계산

정보: 다음 계산 메서드에는 1582년 10월의 엣지 케이스가 있습니다. 5일부터 14일까지의 날짜가 존재하지 않기 때문입니다. 이 가이드에서는 간단히 설명하기 위해 이 동작을 문서화하지 않습니다. 즉, Date.new(1582, 10, 4).tomorrowDate.new(1582, 10, 15)를 반환합니다. Active Support 테스트 스위트의 test/core_ext/date_ext_test.rb를 확인하여 예상 동작을 확인하세요.

Date.current

액티브 서포트는 Date.current를 현재 시간대의 오늘 날짜로 정의합니다. 이는 Date.today와 같지만 사용자 시간대를 고려합니다. 또한 Date.yesterdayDate.tomorrow를 정의하고, past?, today?, tomorrow?, next_day?, yesterday?, prev_day?, future?, on_weekday?on_weekend? 인스턴스 술어를 정의하며, 이 모두는 Date.current를 기준으로 합니다.

사용자 시간대를 고려하는 날짜 비교 메서드를 사용할 때는 Date.today가 아닌 Date.current를 사용해야 합니다. 사용자 시간대가 시스템 시간대보다 미래일 수 있기 때문에 Date.todayDate.yesterday와 같을 수 있습니다.

주석: active_support/core_ext/date/calculations.rb에 정의되어 있습니다.

명명된 날짜

beginning_of_week, end_of_week

beginning_of_weekend_of_week 메서드는 각각 주의 시작과 끝 날짜를 반환합니다. 주는 월요일부터 시작하는 것으로 가정되지만, 인수를 전달하거나 스레드 로컬 Date.beginning_of_week 또는 config.beginning_of_week을 설정하여 변경할 수 있습니다.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.beginning_of_week          # => Mon, 03 May 2010
d.beginning_of_week(:sunday) # => Sun, 02 May 2010
d.end_of_week                # => Sun, 09 May 2010
d.end_of_week(:sunday)       # => Sat, 08 May 2010

beginning_of_weekat_beginning_of_week의 별칭이고, end_of_weekat_end_of_week의 별칭입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

monday, sunday

mondaysunday 메서드는 각각 이전 월요일과 다음 일요일의 날짜를 반환합니다.

d = Date.new(2010, 5, 8)     # => Sat, 08 May 2010
d.monday                     # => Mon, 03 May 2010
d.sunday                     # => Sun, 09 May 2010

d = Date.new(2012, 9, 10)    # => Mon, 10 Sep 2012
d.monday                     # => Mon, 10 Sep 2012

d = Date.new(2012, 9, 16)    # => Sun, 16 Sep 2012
d.sunday                     # => Sun, 16 Sep 2012

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

prev_week, next_week

next_week 메서드는 영어 요일 이름(기본값은 스레드 로컬 Date.beginning_of_week 또는 config.beginning_of_week, 또는 :monday)을 받고 해당 요일의 날짜를 반환합니다.

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.next_week              # => Mon, 10 May 2010
d.next_week(:saturday)   # => Sat, 15 May 2010

prev_week 메서드는 이와 유사합니다:

d.prev_week              # => Mon, 26 Apr 2010
d.prev_week(:saturday)   # => Sat, 01 May 2010
d.prev_week(:friday)     # => Fri, 30 Apr 2010

prev_weeklast_week의 별칭입니다.

next_weekprev_weekDate.beginning_of_week 또는 config.beginning_of_week가 설정된 경우에도 예상대로 작동합니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

beginning_of_month, end_of_month

beginning_of_monthend_of_month 메서드는 각각 월의 시작과 끝 날짜를 반환합니다:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_month     # => Sat, 01 May 2010
d.end_of_month           # => Mon, 31 May 2010

beginning_of_monthat_beginning_of_month의 별칭이고, end_of_monthat_end_of_month의 별칭입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

quarter, beginning_of_quarter, end_of_quarter

quarter 메서드는 수신기의 회계 연도 분기를 반환합니다:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.quarter                # => 2

beginning_of_quarterend_of_quarter 메서드는 수신기의 회계 연도 분기의 시작과 끝 날짜를 반환합니다:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_quarter   # => Thu, 01 Apr 2010
d.end_of_quarter         # => Wed, 30 Jun 2010

beginning_of_quarterat_beginning_of_quarter의 별칭이고, end_of_quarterat_end_of_quarter의 별칭입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

[DateAndTime::Calculations#at_beginning네, 번역을 계속하겠습니다.

beginning_of_year, end_of_year

beginning_of_yearend_of_year 메서드는 각각 연도의 시작과 끝 날짜를 반환합니다:

d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
d.beginning_of_year      # => Fri, 01 Jan 2010
d.end_of_year            # => Fri, 31 Dec 2010

beginning_of_yearat_beginning_of_year의 별칭이고, end_of_yearat_end_of_year의 별칭입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

기타 날짜 계산

years_ago, years_since

years_ago 메서드는 지정된 년 수만큼 이전의 날짜를 반환합니다:

date = Date.new(2010, 6, 7)
date.years_ago(10) # => Wed, 07 Jun 2000

years_since는 미래로 이동합니다:

date = Date.new(2010, 6, 7)
date.years_since(10) # => Sun, 07 Jun 2020

그러한 날짜가 존재하지 않는 경우 해당 월의 마지막 날짜가 반환됩니다:

Date.new(2012, 2, 29).years_ago(3)     # => Sat, 28 Feb 2009
Date.new(2012, 2, 29).years_since(3)   # => Sat, 28 Feb 2015

last_year#years_ago(1)의 단축 표현입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

months_ago, months_since

months_agomonths_since 메서드는 월에 대해 유사하게 작동합니다:

Date.new(2010, 4, 30).months_ago(2)   # => Sun, 28 Feb 2010
Date.new(2010, 4, 30).months_since(2) # => Wed, 30 Jun 2010

그러한 날짜가 존재하지 않는 경우 해당 월의 마지막 날짜가 반환됩니다:

Date.new(2010, 4, 30).months_ago(2)    # => Sun, 28 Feb 2010
Date.new(2009, 12, 31).months_since(2) # => Sun, 28 Feb 2010

last_month#months_ago(1)의 단축 표현입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

weeks_ago, weeks_since

weeks_ago 및 [weeks_since][DateAndTime::Calculations#week_since] 메서드는 주에 대해 유사하게 작동합니다:

Date.new(2010, 5, 24).weeks_ago(1)   # => Mon, 17 May 2010
Date.new(2010, 5, 24).weeks_since(2) # => Mon, 07 Jun 2010

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

advance

날짜를 다른 날짜로 이동하는 가장 일반적인 방법은 advance입니다. 이 메서드는 :years, :months, :weeks, :days 키가 있는 해시를 받고 해당 키에 지정된 만큼 날짜를 이동한 결과를 반환합니다:

date = Date.new(2010, 6, 6)
date.advance(years: 1, weeks: 2)  # => Mon, 20 Jun 2011
date.advance(months: 2, days: -2) # => Wed, 04 Aug 2010

이전 예에서 증가량이 음수일 수 있다는 점에 유의하세요.

주석: active_support/core_ext/date/calculations.rb에 정의되어 있습니다.

구성 요소 변경

change 메서드를 사용하면 수신기와 동일하지만 지정된 연도, 월 또는 일만 다른 새 날짜를 얻을 수 있습니다:

Date.new(2010, 12, 23).change(year: 2011, month: 11)
# => Wed, 23 Nov 2011

이 메서드는 존재하지 않는 날짜에 대해 관용적이지 않습니다. 변경이 잘못된 경우 ArgumentError가 발생합니다:

Date.new(2010, 1, 31).change(month: 2)
# => ArgumentError: invalid date

주석: active_support/core_ext/date/calculations.rb에 정의되어 있습니다.

지속 기간

Duration 객체를 날짜에 추가하거나 빼는 것이 가능합니다:

d = Date.current
# => Mon, 09 Aug 2010
d + 1.year
# => Tue, 09 Aug 2011
d - 3.hours
# => Sun, 08 Aug 2010 21:00:00 UTC +00:00

이는 since 또는 advance 호출로 변환됩니다. 예를 들어 여기서는 달력 개혁에 맞는 점프를 얻습니다:

Date.new(1582, 10, 4) + 1.day
# => Fri, 15 Oct 1582

타임스탬프

정보: 다음 메서드는 가능하면 Time 객체를 반환하고, 그렇지 않으면 DateTime을 반환합니다. 설정된 경우 사용자 시간대를 고려합니다.

beginning_of_day, end_of_day

beginning_of_day 메서드는 하루의 시작(00:00:00)에 해당하는 타임스탬프를 반환합니다:

date = Date.new(2010, 6, 7)
date.beginning_of_day # => Mon Jun 07 00:00:00 +0200 2010

end_of_day 메서드는 하루의 끝(23:59:59)에 해당하는 타임스탬프를 반환합니다:

date = Date.new(2010, 6, 7)
date.end_of_day # => Mon Jun 07 23:59:59 +0200 2010

beginning_of_dayat_beginning_of_day, midnight, at_midnight의 별칭입니다.

주석: active_support/core_ext/date/calculations.rb에 정의되어 있습니다.

beginning_of_hour, end_of_hour

beginning_of_hour 메서드는 시간의 시작(hh:00:00)에 해당하는 타임스탬프를 반환합니다:

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_hour # => Mon Jun 07 19:00:00 +0200 2010

end_of_hour 메서드는 시간의 끝(hh:59:59)에 해당하는 타임스탬프를 반환합니다:

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010

beginning_of_hourat_beginning_of_hour의 별칭입니다.

정보: beginning_of_hour, end_of_hour, beginning_of_minute, end_of_minuteTimeDateTime에 구현되어 있지만 Date에는 없습니다. 시간이나 분에 대한 시작이나 끝은 Date 인스턴스에 의미가 없기 때문입니다.

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

beginning_of_minute, end_of_minute

beginning_of_minute 메서드는 분의 시작(hh:mm:00)에 해당하는 타임스탬프를 반환합니다:

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010

end_of_minute 메서드는 분의 끝(hh:mm:59)에 해당하는 타임스탬프를 반환합니다:

date = DateTime.new(2010, 6, 7, 19, 55, 25)
date.end_of_minute # => Mon Jun 07 19:55:59네, 번역을 계속하겠습니다.

[DateTime#end_of_minute] # => Mon Jun 07 19:55:59 +0200 2010

beginning_of_minuteat_beginning_of_minute의 별칭입니다.

정보: beginning_of_hour, end_of_hour, beginning_of_minute, end_of_minuteTimeDateTime에 구현되어 있지만 Date에는 없습니다. 시간이나 분에 대한 시작이나 끝은 Date 인스턴스에 의미가 없기 때문입니다.

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

ago, since

ago 메서드는 초 단위의 인수를 받고 그만큼 이전의 자정 시간을 반환합니다:

date = Date.current # => Fri, 11 Jun 2010
date.ago(1)         # => Thu, 10 Jun 2010 23:59:59 EDT -04:00

마찬가지로 since는 미래로 이동합니다:

date = Date.current # => Fri, 11 Jun 2010
date.since(1)       # => Fri, 11 Jun 2010 00:00:01 EDT -04:00

주석: active_support/core_ext/date/calculations.rb에 정의되어 있습니다.

DateTime 익스텐션

경고: DateTime은 DST 규칙을 인식하지 않으므로 이러한 메서드 중 일부에는 DST 변경이 진행 중일 때 엣지 케이스가 있습니다. 예를 들어 seconds_since_midnight은 그런 날에 실제 양을 반환하지 않을 수 있습니다.

계산

DateTime 클래스는 Date의 하위 클래스이므로 active_support/core_ext/date/calculations.rb를 로드하면 이러한 메서드와 별칭을 상속받지만, 항상 DateTime을 반환합니다.

다음 메서드는 재구현되므로 active_support/core_ext/date/calculations.rb를 로드할 필요가 없습니다:

한편 advancechange도 정의되어 있으며 더 많은 옵션을 지원합니다. 이들은 아래에 문서화되어 있습니다.

다음 메서드는 active_support/core_ext/date_time/calculations.rb에만 구현되어 있으며, DateTime 인스턴스에서만 의미가 있습니다:

명명된 DateTime

DateTime.current

액티브 서포트는 DateTime.currentTime.now.to_datetime과 같지만, 사용자 시간대를 고려한다는 점에서 다릅니다. 인스턴스 술어 past?future?DateTime.current를 기준으로 합니다.

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

기타 익스텐션

seconds_since_midnight

seconds_since_midnight 메서드는 자정 이후 경과된 초 수를 반환합니다:

now = DateTime.current     # => Mon, 07 Jun 2010 20:26:36 +0000
now.seconds_since_midnight # => 73596

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

utc

utc 메서드는 수신기와 동일한 DateTime을 UTC로 반환합니다.

now = DateTime.current # => Mon, 07 Jun 2010 19:27:52 -0400
now.utc                # => Mon, 07 Jun 2010 23:27:52 +0000

이 메서드는 getutc의 별칭으로도 사용됩니다.

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

utc?

utc? 술어는 수신기의 시간대가 UTC인지 말합니다:

now = DateTime.now # => Mon, 07 Jun 2010 19:30:47 -0400
now.utc?           # => false
now.utc.utc?       # => true

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

advance

DateTime을 다른 DateTime으로 이동하는 가장 일반적인 방법은 advance입니다. 이 메서드는 :years, :months, :weeks, :days, :hours, :minutes, :seconds 키가 있는 해시를 받고 해당 키에 지정된 만큼 DateTime을 이동한 결과를 반환합니다.

d = DateTime.current
# => Thu, 05 Aug 2010 11:33:31 +0000
d.advance(years: 1, months: 1, days: 1, hours: 1, minutes: 1, seconds: 1)
# => Tue, 06 Sep 2011 12:34:32 +0000

이 메서드는 먼저 :years, :months, :weeks, :days를 사용하여 Date#advance에 정의된 대로 날짜 부분을 계산합니다. 그 다음 since를 호출하여 초 단위로 시간을 조정합니다. 이 순서는 중요합니다. 순서가 다르면 일부 엣지 케이스에서 다른 DateTime이 계산됩니다. 위의 Date#advance 예제가 적용되며, 시간 부분과 관련하여 확장할 수 있습니다.

먼저 날짜 부분(처리 순서도 상대적)을 이동하고 나서 시간 부분을 이동하면 다음과 같은 계산이 이루어집니다:

d = DateTime.new(2010, 2, 28, 23, 59, 59)
# => Sun, 28 Feb 2010 23:59:59 +0000
d.advance(months: 1, seconds: 1)
# => Mon, 29 Mar 2010 00:00:00 +0000

그러나 반대로 계산하면 결과가 다릅니다:

d.advance(seconds: 1).advance(months: 1)
# => Thu, 01 Apr 2010 00:00:00 +0000

경고: DateTime은 DST에 대해 인식하지 않으므로 존재하지 않는 시점으로 이동할 수 있으며, 경고나 오류 없이 그렇게 됩니다.

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

구성 요소 변경

change 메서드를 사용하면 수신기와 동일하지만 :year, :month, :day, :hour, :min, :sec, :offset, :start 옵션으로 지정된 값이 다른 새 DateTime을 얻을 수 있습니다:

now = DateTime.current
# => Tue, 08 Jun 2010 01:56:22 +0000
now.change(year: 2011, offset: Rational(-6, 24))
# => Wed, 08 Jun 2011 01:56:22 -0600

시간이 0으로 설정되면 분과 초도 0이 됩니다(값이 주어진 경우는 제외):

now.change(hour: 0)
# => Tue, 08 Jun 2010 00:00:00 +0000

마찬가지로 분이 0으로 설정되면 초도 0이 됩니다(값이 주어진 경우는 제외):

now.change(min: 0)
# => Tue, 08 Jun 2010 01:00:00 +0000

이 메서드는 존재하지 않는 날짜에 대해 관용적이지 않습니다. 변경이 잘못된 경우 ArgumentError가 발생합니다:

DateTime.current.change(month: 2, day: 30)
# => ArgumentError: invalid date

주석: active_support/core_ext/date_time/calculations.rb에 정의되어 있습니다.

지속 기간

Duration 객체를 DateTime에 추가하거나 빼는 것이 가능합니다:

now = DateTime.current
# => Mon, 09 Aug 2010 23:15:17 +0000
now + 1.year
# => Tue, 09 Aug 2011 23:15:17 +0000
now - 1.week
# => Mon, 02 Aug 2010 23:15:17 +0000

이는 since 또는 advance 호출로 변환됩니다. 예를 들어 여기서는 달력 개혁에 맞는 점프를 얻습니다:

DateTime.new(1582, 10, 4, 23) + 1.hour
# => Fri, 15 Oct 1582 00:00:00 +0000

Time 익스텐션

계산

이들은 유사합니다. 위의 문서를 참조하고 다음과 같은 차이점을 고려하세요:

  • change는 추가적으로 :usec 옵션을 받습니다.
  • Time은 DST를 이해하므로 다음과 같이 올바른 DST 계산을 수행합니다.
Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>

# 바르셀로나에서 2010/03/28 02:00 +0100은 DST로 인해 2010/03/28 03:00 +0200가 됩니다네, 번역을 계속하겠습니다.

# 바르셀로나에서 2010/03/28 02:00 +0100은 DST로 인해 2010/03/28 03:00 +0200가 됩니다.
t = Time.local(2010, 3, 28, 1, 59, 59)
# => Sun Mar 28 01:59:59 +0100 2010
t.advance(seconds: 1)
# => Sun Mar 28 03:00:00 +0200 2010
  • since 또는 agoTime으로 표현할 수 없는 시간으로 점프하면 DateTime 객체가 반환됩니다.

Time.current

액티브 서포트는 Time.current를 현재 시간대의 오늘 날짜로 정의합니다. 이는 Time.now와 같지만, 사용자 시간대를 고려합니다. 또한 인스턴스 술어 past?, today?, tomorrow?, next_day?, yesterday?, prev_day?future?를 정의하며, 이 모두는 Time.current를 기준으로 합니다.

사용자 시간대를 고려하는 Time 비교 메서드를 사용할 때는 Time.now가 아닌 Time.current를 사용해야 합니다. 사용자 시간대가 시스템 시간대보다 미래일 수 있기 때문에 Time.now.to_dateDate.yesterday와 같을 수 있습니다.

주석: active_support/core_ext/time/calculations.rb에 정의되어 있습니다.

all_day, all_week, all_month, all_quarter, 및 all_year

all_day 메서드는 현재 시간의 하루 전체를 나타내는 범위를 반환합니다.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_day
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Mon, 09 Aug 2010 23:59:59 UTC +00:00

마찬가지로 all_week, all_month, all_quarterall_year는 시간 범위를 생성하는 목적을 serve합니다.

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
now.all_week(:sunday)
# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
# => Thu, 01 Jul 2010 00:00:00 UTC +00:00..Thu, 30 Sep 2010 23:59:59 UTC +00:00
now.all_year
# => Fri, 01 Jan 2010 00:00:00 UTC +00:00..Fri, 31 Dec 2010 23:59:59 UTC +00:00

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

prev_day, next_day

prev_daynext_day 메서드는 각각 이전 날짜와 다음 날짜의 시간을 반환합니다:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_day               # => 2010-05-07 00:00:00 +0900
t.next_day               # => 2010-05-09 00:00:00 +0900

주석: active_support/core_ext/time/calculations.rb에 정의되어 있습니다.

prev_month, next_month

prev_monthnext_month 메서드는 동일한 날짜를 가진 이전 또는 다음 달의 시간을 반환합니다:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_month             # => 2010-04-08 00:00:00 +0900
t.next_month             # => 2010-06-08 00:00:00 +0900

그러한 날짜가 존재하지 않는 경우 해당 월의 마지막 날짜가 반환됩니다:

Time.new(2000, 5, 31).prev_month # => 2000-04-30 00:00:00 +0900
Time.new(2000, 3, 31).prev_month # => 2000-02-29 00:00:00 +0900
Time.new(2000, 5, 31).next_month # => 2000-06-30 00:00:00 +0900
Time.new(2000, 1, 31).next_month # => 2000-02-29 00:00:00 +0900

주석: active_support/core_ext/time/calculations.rb에 정의되어 있습니다.

prev_year, next_year

prev_yearnext_year 메서드는 동일한 날짜/월을 가진 이전 또는 다음 연도의 시간을 반환합니다:

t = Time.new(2010, 5, 8) # => 2010-05-08 00:00:00 +0900
t.prev_year              # => 2009-05-08 00:00:00 +0900
t.next_year              # => 2011-05-08 00:00:00 +0900

2월 29일이 있는 윤년의 경우 28일이 반환됩니다:

t = Time.new(2000, 2, 29) # => 2000-02-29 00:00:00 +0900
t.prev_year               # => 1999-02-28 00:00:00 +0900
t.next_year               # => 2001-02-28 00:00:00 +0900

주석: active_support/core_ext/time/calculations.rb에 정의되어 있습니다.

prev_quarter, next_quarter

prev_quarternext_quarter 메서드는 동일한 날짜를 가진 이전 또는 다음 분기의 시간을 반환합니다:

t = Time.local(2010, 5, 8) # => 2010-05-08 00:00:00 +0300
t.prev_quarter             # => 2010-02-08 00:00:00 +0200
t.next_quarter             # => 2010-08-08 00:00:00 +0300

그러한 날짜가 존재하지 않는 경우 해당 월의 마지막 날짜가 반환됩니다:

Time.local(2000, 7, 31).prev_quarter  # => 2000-04-30 00:00:00 +0300
Time.local(2000, 5, 31).prev_quarter  # => 2000-02-29 00:00:00 +0200
Time.local(2000, 10, 31).prev_quarter # => 2000-07-31 00:00:00 +0300
Time.local(2000, 11, 31).next_quarter # => 2001-03-01 00:00:00 +0200

prev_quarterlast_quarter의 별칭입니다.

주석: active_support/core_ext/date_and_time/calculations.rb에 정의되어 있습니다.

Time 생성자

액티브 서포트는 사용자 시간대가 정의된 경우 Time.zone.now를, 그렇지 않으면 Time.now를 반환하는 Time.current를 정의합니다:

Time.zone_default
# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...>
Time.current
# => Fri, 06 Aug 2010 17:11:58 CEST +02:00

DateTime과 마찬가지로 past?future? 술어는 Time.current를 기준으로 합니다.

플랫폼에서 지원하는 범위를 벗어나는 시간을 생성하려면 usec가 버려지고 DateTime 객체가 반환됩니다.

지속 기간

Duration 객체를 Time 객체에 추가하거나 빼는 것이 가능합니다:

now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now + 1.year
# => Tue, 09 Aug 2011 23:21:11 UTC +00:00
now - 1.week
# => Mon, 02 Aug 2010 23:21:11 UTC +00:00

이는 since 또는 advance 호출로 변환됩니다. 예를 들어 여기서는 달력 개혁에 맞는 점프를 얻습니다:

Time.utc(1582, 10, 3) + 5.days
# => Mon Oct 18 00:00:00 UTC 1582

파일 익스텐션

atomic_write

File.atomic_write 클래스 메서드를 사용하면 읽는 사람이 중간 내용을 볼 수 없도록 파일에 쓸 수 있습니네, 번역을 계속하겠습니다.

File.atomic_write 클래스 메서드를 사용하면 읽는 사람이 중간 내용을 볼 수 없도록 파일에 쓸 수 있습니다.

파일 이름이 인수로 전달되며, 메서드는 쓰기 위해 열린 파일 핸들을 블록에 전달합니다. 블록이 완료되면 atomic_write가 파일 핸들을 닫고 작업을 완료합니다.

예를 들어 Action Pack은 이 메서드를 사용하여 all.css와 같은 자산 캐시 파일을 작성합니다:

File.atomic_write(joined_asset_path) do |cache|
  cache.write(join_asset_file_contents(asset_paths))
end

이를 위해 atomic_write는 임시 파일을 생성합니다. 블록 내부 코드가 실제로 작성하는 파일이 바로 이 임시 파일입니다. 완료되면 임시 파일이 원래 파일로 이름이 변경되는데, 이는 POSIX 시스템에서 원자적 작업입니다. 대상 파일이 이미 존재하는 경우 atomic_write는 소유권과 권한을 유지하면서 덮어씁니다. 그러나 파일 소유권이나 권한을 변경할 수 없는 경우가 있으며, 이 오류는 캐치되고 무시됩니다. 파일이 필요한 프로세스에 액세스할 수 있도록 하는 것은 사용자/파일 시스템의 책임입니다.

주의. atomic_write가 수행하는 chmod 작업으로 인해 대상 파일에 ACL이 설정된 경우 ACL이 다시 계산/수정될 수 있습니다.

경고. atomic_write로는 추가할 수 없습니다.

보조 파일은 표준 임시 파일 디렉토리에 작성되지만, 두 번째 인수로 원하는 디렉토리를 전달할 수 있습니다.

주석: active_support/core_ext/file/atomic.rb에 정의되어 있습니다.

NameError 익스텐션

액티브 서포트는 NameErrormissing_name?을 추가합니다. 이 메서드는 예외가 인수로 전달된 이름 때문에 발생했는지 테스트합니다.

이름은 기호 또는 문자열로 제공될 수 있습니다. 기호는 bare 상수 이름에 대해, 문자열은 정규화된 상수 이름에 대해 테스트됩니다.

팁: 기호는 :"ActiveRecord::Base"와 같이 정규화된 상수 이름을 나타낼 수 있으므로, 기호에 대한 동작은 편의를 위해 정의된 것이지 기술적으로 그래야 하는 것은 아닙니다.

예를 들어 ArticlesController의 작업이 호출될 때 Rails는 ArticlesHelper를 최적으로 사용하려고 시도합니다. 헬퍼 모듈이 존재하지 않는 것은 괜찮지만, articles_helper.rb가 실제 알 수 없는 상수 때문에 예외를 발생시키는 경우에는 다시 발생시켜야 합니다. missing_name? 메서드는 이 두 경우를 구분하는 방법을 제공합니다:

def default_helper_module!
  module_name = name.delete_suffix("Controller")
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

주석: active_support/core_ext/name_error.rb에 정의되어 있습니다.

LoadError 익스텐션

액티브 서포트는 LoadErroris_missing?을 추가합니다.

경로 이름을 받아 is_missing?는 예외가 해당 파일 때문에 발생했는지(확장자 ”.rb"는 제외) 테스트합니다.

예를 들어 ArticlesController의 작업이 호출될 때 Rails는 articles_helper.rb를 로드하려고 시도하지만, 해당 파일이 존재하지 않을 수 있습니다. 이는 문제가 되지 않으며, 헬퍼 모듈은 필수가 아니므로 Rails는 이 로드 오류를 무시합니다. 그러나 헬퍼 모듈이 존재하고 다른 라이브러리를 요구하는 동안 해당 라이브러리가 누락된 경우에는 예외를 다시 발생시켜야 합니다. is_missing? 메서드는 이 두 경우를 구분하는 방법을 제공합니다:

def default_helper_module!
  module_name = name.delete_suffix("Controller")
  module_path = module_name.underscore
  helper module_path
rescue LoadError => e
  raise e unless e.is_missing? "helpers/#{module_path}_helper"
rescue NameError => e
  raise e unless e.missing_name? "#{module_name}Helper"
end

주석: active_support/core_ext/load_error.rb에 정의되어 있습니다.

Pathname 익스텐션

existence

existence 메서드는 지정된 파일이 존재하면 수신기를 반환하고, 그렇지 않으면 nil을 반환합니다. 다음과 같은 관용구에 유용합니다:

content = Pathname.new("file").existence&.read

주석: active_support/core_ext/pathname/existence.rb에 정의되어 있습니다.

한국어 번역이 완료되었습니다.