|
| 1 | +--- |
| 2 | +layout: news_post |
| 3 | +title: "Ruby 3.4.0 릴리스" |
| 4 | +author: "naruse" |
| 5 | +translator: "shia" |
| 6 | +date: 2024-12-25 00:00:00 +0000 |
| 7 | +lang: ko |
| 8 | +--- |
| 9 | + |
| 10 | +{% assign release = site.data.releases | where: "version", "3.4.0" | first %} |
| 11 | +Ruby {{ release.version }} 릴리스를 알리게 되어 기쁩니다. |
| 12 | +Ruby 3.4는 `it` 블록 파라미터 참조, Prism을 기본 파서로 변경, 소켓 라이브러리에 |
| 13 | +Happy Eyeballs Version 2 지원 추가, YJIT 개선, 모듈러 GC 추가 등을 포함하고 있습니다. |
| 14 | + |
| 15 | +## `it` 추가 |
| 16 | + |
| 17 | +`it`은 변수 이름 없이 블록 파라미터를 참조하기 위해 추가되었습니다. [[Feature #18980]] |
| 18 | + |
| 19 | +```ruby |
| 20 | +ary = ["foo", "bar", "baz"] |
| 21 | + |
| 22 | +p ary.map { it.upcase } #=> ["FOO", "BAR", "BAZ"] |
| 23 | +``` |
| 24 | + |
| 25 | +`it`은 `_1`과 거의 동일하게 동작합니다. 블록에서 `_1`만 사용할 때 다른 번호 파라미터인 `_2` 등이 나타날 가능성이 읽는 사람에게 추가적인 인지 부담을 주게 됩니다. 따라서 `it`은 편리한 별칭으로 추가되었습니다. `it`은 `it` 자체로 충분히 명확한 경우에 사용하세요. 예를 들어, 한 줄짜리 블록에서 사용하세요. |
| 26 | + |
| 27 | +## 기본 파서를 Prism으로 변경 |
| 28 | + |
| 29 | +parse.y에서 Prism으로 기본 파서를 변경했습니다. [[Feature #20564]] |
| 30 | + |
| 31 | +이는 내부적인 개선이며 사용자에게는 거의 변화가 없어야 합니다. 호환성 문제가 발생하면 보고해 주세요. |
| 32 | + |
| 33 | +기존 파서를 사용하려면 `--parser=parse.y` 커맨드 라인 인수를 사용하세요. |
| 34 | + |
| 35 | +## 소켓 라이브러리가 Happy Eyeballs Version 2 (RFC 8305) 대응 |
| 36 | + |
| 37 | +소켓 라이브러리는 이제 [Happy Eyeballs Version 2(RFC 8305)](https://datatracker.ietf.org/doc/html/rfc8305)를 지원합니다. 이는 `TCPSocket.new`(`TCPSocket.open`)와 `Socket.tcp`에서 네트워크 연결을 효율적이고 안정적으로 제공하기 위해 많은 언어에서 널리 사용되는 방법의 최신 표준화 버전입니다. |
| 38 | +이 개선 사항은 Ruby가 현대적인 인터넷 환경에 적응된 효율적이고 신뢰할 수 있는 네트워크 연결을 제공할 수 있도록 합니다. |
| 39 | + |
| 40 | +Ruby 3.3까지 이러한 메서드는 이름 해석과 연결 시도를 직렬로 수행했습니다. 해당 알고리즘 도입으로 이제 다음과 같이 작동합니다. |
| 41 | + |
| 42 | +1. IPv6와 IPv4 이름 해석을 동시에 수행 |
| 43 | +2. IPv6를 우선하며 병렬로 연결을 시도하되 250ms 간격으로 떨어뜨려 시도 |
| 44 | +3. 성공한 연결을 반환하고 다른 연결을 취소 |
| 45 | + |
| 46 | +이는 특정 프로토콜이나 IP 주소가 지연되거나 사용할 수 없는 경우에도 연결 지연을 최소화합니다. |
| 47 | +이 기능은 기본적으로 활성화되어 있으므로 추가 구성은 필요하지 않습니다. 전역으로 비활성화하려면 환경 변수 `RUBY_TCP_NO_FAST_FALLBACK=1`을 설정하거나 `Socket.tcp_fast_fallback=false`를 호출하세요. 또는 메서드별로 비활성화하려면 키워드 인수 `fast_fallback: false`를 사용하세요. |
| 48 | + |
| 49 | +## YJIT |
| 50 | + |
| 51 | +### TL;DR |
| 52 | + |
| 53 | +* x86-64와 arm64 플랫폼에서 대부분의 벤치마크에서 성능이 향상되었습니다. |
| 54 | +* 메타 데이터 압축과 통합 메모리 제한을 통해 메모리 사용량이 줄었습니다. |
| 55 | +* 여러 버그가 수정되었습니다. YJIT는 이제 더 견고하고 잘 테스트되었습니다. |
| 56 | + |
| 57 | +### 새 기능 |
| 58 | + |
| 59 | +* 커맨드 라인 옵션 |
| 60 | + * `--yjit-mem-size` 커맨드 라인 옵션(기본값 128MiB)을 통해 YJIT의 통합 메모리 제한을 추가했습니다. |
| 61 | + 이는 이전 `--yjit-exec-mem-size`보다 직관적이며, YJIT의 전체 메모리 사용량을 추적합니다. |
| 62 | + * `--yjit-log`를 통해 무엇이 컴파일되었는지 추적하는 컴파일 로그를 추가했습니다. |
| 63 | +* Ruby API |
| 64 | + * `RubyVM::YJIT.log`로 런타임에서 컴파일 로그의 마지막 부분을 확인할 수 있습니다. |
| 65 | +* YJIT 통계 정보 |
| 66 | + * `RubyVM::YJIT.runtime_stats`는 이제 언제나 무효화, 인라인, 메타 정보 인코딩에 대한 |
| 67 | + 추가 통계 정보를 제공합니다. |
| 68 | + |
| 69 | +### 새 최적화 |
| 70 | + |
| 71 | +* YJIT 메타 데이터를 저장하는 데 필요한 메모리를 줄이는 콘텍스트 압축 |
| 72 | +* 로컬 변수와 Ruby 메서드 인수를 위한 레지스터를 할당합니다. |
| 73 | +* YJIT을 사용할 때 Ruby로 작성된 더 많은 코어 프리미티브를 사용합니다. |
| 74 | + * 성능을 높이기 위해 Ruby로 다시 작성된 `Array#each`, `Array#select`, `Array#map` [[Feature #20182]] |
| 75 | +* 작고 사소한 메서드를 인라인으로 변환하는 능력 |
| 76 | + * 빈 메서드 |
| 77 | + * 상수를 반환하는 메서드 |
| 78 | + * `self`를 반환하는 메서드 |
| 79 | + * 인수를 직접 반환하는 메서드 |
| 80 | +* 더 많은 런타임 메서드에 대한 특별한 코드 생성 |
| 81 | +* `String#getbyte`, `String#setbyte` 및 다른 문자열 메서드를 최적화 |
| 82 | +* 저레벨 비트/바이트 조작을 빠르게 하기 위한 비트 연산 최적화 |
| 83 | +* 멀티 Ractor 모드에서 공유 가능한 상수 지원 |
| 84 | +* 다양한 다른 점진적 최적화 |
| 85 | + |
| 86 | +## 모듈러 GC |
| 87 | + |
| 88 | +* 다른 가비지 컬렉터(GC) 구현을 모듈러 가비지 컬렉터 기능을 통해 동적으로 |
| 89 | + 로드할 수 있습니다. 이 기능을 활성화하려면 Ruby 빌드 시에 |
| 90 | + `--with-modular-gc`를 설정하세요. GC 라이브러리는 환경 변수 `RUBY_GC_LIBRARY`를 |
| 91 | + 사용하여 런타임에 로드할 수 있습니다. |
| 92 | + [[Feature #20351]] |
| 93 | + |
| 94 | +* Ruby의 내장 가비지 컬렉터는 `gc/default/default.c`에 분리되어 있으며, |
| 95 | + `gc/gc_impl.h`에 정의된 API를 사용하여 Ruby와 상호 작용합니다. |
| 96 | + 내장 가비지 컬렉터는 `make modular-gc MODULAR_GC=default`를 사용하여 |
| 97 | + 라이브러리로서 빌드하고 환경 변수 `RUBY_GC_LIBRARY=default`를 |
| 98 | + 사용하여 활성화할 수 있습니다. [[Feature #20470]] |
| 99 | + |
| 100 | +* [MMTk](https://www.mmtk.io/)를 기반으로 한 실험적인 GC 라이브러리가 제공됩니다. |
| 101 | + 이 GC 라이브러리는 `make modular-gc MODULAR_GC=mmtk`를 사용하여 빌드하고 |
| 102 | + 환경 변수 `RUBY_GC_LIBRARY=mmtk`를 사용하여 활성화할 수 있습니다. |
| 103 | + 이는 빌드 머신에 Rust 툴체인이 필요합니다. [[Feature #20860]] |
| 104 | + |
| 105 | +## 언어 변경 |
| 106 | + |
| 107 | +* 파일에 `frozen_string_literal` 주석이 없을 때, 문자열 리터럴이 변경되면 |
| 108 | + 폐기 예정 경고를 출력합니다. |
| 109 | + 이 경고는 `-W:deprecated`나 `Warning[:deprecated] = true` 설정을 통해 활성화할 수 있습니다. |
| 110 | + 이 변경을 무효화하고 싶다면 Ruby를 실행할 때 `--disable-frozen-string-literal` |
| 111 | + 커맨드 라인 인수를 사용하세요. [[Feature #20205]] |
| 112 | + |
| 113 | +* 메서드 호출 시에 `nil`에 키워드 스플랫을 지원합니다. |
| 114 | + `**nil`은 `**{}`와 비슷하게 동작하며, 키워드를 넘기지 않으며, |
| 115 | + 어떤 변환 메서드도 호출하지 않습니다. [[Bug #20064]] |
| 116 | + |
| 117 | +* 블록을 인덱스로 사용할 수 없게 됩니다. [[Bug #19918]] |
| 118 | + |
| 119 | +* 키워드 인수를 인덱스로 사용할 수 없게 됩니다. [[Bug #20218]] |
| 120 | + |
| 121 | +* 최상위 이름 `::Ruby`은 예약되었으며, 이를 정의할 경우 `Warning[:deprecated]`가 발생합니다. [[Feature #20884]] |
| 122 | + |
| 123 | +## 코어 클래스 변경 |
| 124 | + |
| 125 | +주의: 눈에 띄는 코어 클래스 변경만을 포함합니다. |
| 126 | + |
| 127 | +* Exception |
| 128 | + |
| 129 | + * `Exception#set_backtrace`는 이제 `Thread::Backtrace::Location`의 배열을 입력으로 받을 수 있습니다. |
| 130 | + `Kernel#raise`, `Thread#raise`와 `Fiber#raise`도 같은 형식의 입력을 받습니다. [[Feature #13557]] |
| 131 | + |
| 132 | +* GC |
| 133 | + |
| 134 | + * `GC.config`가 추가되어 가비지 컬렉터(GC)의 설정을 변경할 수 있습니다. |
| 135 | + [[Feature #20443]] |
| 136 | + |
| 137 | + * GC 설정 파라미터 `rgengc_allow_full_mark`가 추가되었습니다. `false`일 때 |
| 138 | + GC는 젊은 객체만 마킹합니다. 기본값은 `true`입니다. [[Feature #20443]] |
| 139 | + |
| 140 | +* Ractor |
| 141 | + |
| 142 | + * Ractor 내부에서 `require`가 허용됩니다. 해당 처리는 주 Ractor에서 |
| 143 | + 실행됩니다. 불러오는 처리를 주 Ractor에서 실행하는 |
| 144 | + `Ractor._require(feature)`가 추가되었습니다. |
| 145 | + [[Feature #20627]] |
| 146 | + |
| 147 | + * `Ractor.main?`이 추가되었습니다. [[Feature #20627]] |
| 148 | + |
| 149 | + * 현재 Ractor의 Ractor 로컬 저장소에 접근하는 `Ractor.[]`와 `Ractor.[]=`가 추가되었습니다. |
| 150 | + [[Feature #20715]] |
| 151 | + |
| 152 | + * Ractor 로컬 변수를 스레드 안전하게 초기화하는 `Ractor.store_if_absent(key){ init }`가 |
| 153 | + 추가되었습니다. [[Feature #20875]] |
| 154 | + |
| 155 | +* Range |
| 156 | + |
| 157 | + * `Range#size`는 이제 범위가 순회 가능하지 않다면 `TypeError`를 던집니다. [[Misc #18984]] |
| 158 | + |
| 159 | + |
| 160 | +## 표준 라이브러리 변경 |
| 161 | + |
| 162 | +주의: 눈에 띄는 표준 라이브러리 변경만을 포함합니다. |
| 163 | + |
| 164 | +* RubyGems |
| 165 | + * `--attestation` 옵션을 gem push에 추가했습니다. [sigstore.dev]에 서명을 저장할 수 있습니다. |
| 166 | + |
| 167 | +* Bundler |
| 168 | + * 새 lockfile 생성 시에 체크섬을 포함하는 `lockfile_checksums` 설정을 추가합니다. |
| 169 | + * 기존 lockfile에 체크섬을 추가하는 `--add-checksums`를 추가합니다. |
| 170 | + |
| 171 | +* JSON |
| 172 | + |
| 173 | + * `JSON.parse`의 성능이 json-2.7.x보다 약 1.5배 빨라졌습니다. |
| 174 | + |
| 175 | +* Tempfile |
| 176 | + |
| 177 | + * `Tempfile.create`에 `anonymous: true` 키워드 인수가 구현되었습니다. |
| 178 | + `Tempfile.create(anonymous: true)`는 즉시 생성된 임시 파일을 제거합니다. |
| 179 | + 따라서 애플리케이션에서 파일을 제거할 필요가 없습니다. |
| 180 | + [[Feature #20497]] |
| 181 | + |
| 182 | +* win32/sspi.rb |
| 183 | + |
| 184 | + * 이 라이브러리는 이제 Ruby 저장소에서 [ruby/net-http-sspi]로 추출되었습니다. |
| 185 | + [[Feature #20775]] |
| 186 | + |
| 187 | +## 호환성 문제 |
| 188 | + |
| 189 | +주의: 기능 버그 수정은 포함되어 있지 않습니다. |
| 190 | + |
| 191 | +* 에러 메시지와 백트레이스의 출력 결과가 변경됩니다. |
| 192 | + * 여는 따옴표로 백틱 대신 작은따옴표를 사용합니다. [[Feature #16495]] |
| 193 | + * 메서드 이름 앞에 클래스 이름을 출력합니다(클래스가 불변하는 이름을 가지고 있는 경우만). [[Feature #19117]] |
| 194 | + * `Kernel#caller`, `Thread::Backtrace::Location`의 메서드 등도 마찬가지로 변경됩니다. |
| 195 | + |
| 196 | + ``` |
| 197 | + Old: |
| 198 | + test.rb:1:in `foo': undefined method `time' for an instance of Integer |
| 199 | + from test.rb:2:in `<main>' |
| 200 | +
|
| 201 | + New: |
| 202 | + test.rb:1:in 'Object#foo': undefined method 'time' for an instance of Integer |
| 203 | + from test.rb:2:in '<main>' |
| 204 | + ``` |
| 205 | + |
| 206 | +* `Hash#inspect` 렌더링이 변경되었습니다. [[Bug #20433]] |
| 207 | + |
| 208 | + * 심볼 키는 최신 심볼 키 구문을 사용하여 표시됩니다. 예시: `"{user: 1}"` |
| 209 | + * 다른 키는 `=>` 주변에 공백이 표시됩니다. 예시: `'{"user" => 1}'`. 이전에는 없었습니다. 예시: `'{"user"=>1}'` |
| 210 | + |
| 211 | +* `Kernel#Float()`는 이제 소수 부분이 생략된 소수 문자열을 허용합니다. [[Feature #20705]] |
| 212 | + |
| 213 | + ```rb |
| 214 | + Float("1.") #=> 1.0 (이전에는 ArgumentError가 발생했습니다) |
| 215 | + Float("1.E-1") #=> 0.1 (이전에는 ArgumentError가 발생했습니다) |
| 216 | + ``` |
| 217 | + |
| 218 | +* `String#to_f`는 이제 소수 부분이 생략된 소수 문자열을 허용합니다. 지수가 지정된 경우 결과가 변경됩니다. [[Feature #20705]] |
| 219 | + |
| 220 | + ```rb |
| 221 | + "1.".to_f #=> 1.0 |
| 222 | + "1.E-1".to_f #=> 0.1 (이전에는 1.0이 반환되었습니다) |
| 223 | + ``` |
| 224 | + |
| 225 | +* `Refinement#refined_class`가 삭제되었습니다. [[Feature #19714]] |
| 226 | + |
| 227 | +## 표준 라이브러리 호환성 문제 |
| 228 | + |
| 229 | +* DidYouMean |
| 230 | + |
| 231 | + * `DidYouMean::SPELL_CHECKERS[]=`과 `DidYouMean::SPELL_CHECKERS.merge!`가 삭제됩니다. |
| 232 | + |
| 233 | +* Net::HTTP |
| 234 | + |
| 235 | + * 폐기 예정이었던 상수가 삭제됩니다. |
| 236 | + * `Net::HTTP::ProxyMod` |
| 237 | + * `Net::NetPrivate::HTTPRequest` |
| 238 | + * `Net::HTTPInformationCode` |
| 239 | + * `Net::HTTPSuccessCode` |
| 240 | + * `Net::HTTPRedirectionCode` |
| 241 | + * `Net::HTTPRetriableCode` |
| 242 | + * `Net::HTTPClientErrorCode` |
| 243 | + * `Net::HTTPFatalErrorCode` |
| 244 | + * `Net::HTTPServerErrorCode` |
| 245 | + * `Net::HTTPResponseReceiver` |
| 246 | + * `Net::HTTPResponceReceiver` |
| 247 | + |
| 248 | + 이 상수들은 2012년부터 폐기 예정이었습니다. |
| 249 | + |
| 250 | +* Timeout |
| 251 | + |
| 252 | + * `Timeout.timeout`은 음수 값을 거부합니다. [[Bug #20795]] |
| 253 | + |
| 254 | +* URI |
| 255 | + |
| 256 | + * 기본 파서를 RFC 2396 호환에서 RFC 3986 호환으로 변경했습니다. |
| 257 | + [[Bug #19266]] |
| 258 | + |
| 259 | +## C API 변경 |
| 260 | + |
| 261 | +* `rb_newobj`와 `rb_newobj_of`(그리고 대응하는 매크로인 `RB_NEWOBJ`, `RB_NEWOBJ_OF`, `NEWOBJ`, `NEWOBJ_OF`)가 삭제됩니다. [[Feature #20265]] |
| 262 | +* 폐기 예정이던 `rb_gc_force_recycle` 함수를 삭제했습니다. [[Feature #18290]] |
| 263 | + |
| 264 | +## 그 이외의 변경 |
| 265 | + |
| 266 | +* 상세 모드(`-w`)에서 메서드에 넘긴 블록이 사용되지 않았을 때 |
| 267 | + 경고를 출력합니다. |
| 268 | + [[Feature #15554]] |
| 269 | + |
| 270 | +* `String.freeze`나 `Integer#+`처럼 인터프리터와 JIT이 특별히 최적화하는 |
| 271 | + 몇몇 코어 메서드를 재정의하면 성능 클래스 |
| 272 | + 경고(`-W:performance`나 `Warning[:performance] = true`)를 출력합니다. |
| 273 | + [[Feature #20429]] |
| 274 | + |
| 275 | +더 자세한 내용은 [NEWS](https://docs.ruby-lang.org/en/3.4/NEWS_md.html)나 |
| 276 | +[커밋 로그](https://github.com/ruby/ruby/compare/v3_3_0...{{ release.tag }})를 |
| 277 | +확인해 주세요. |
| 278 | + |
| 279 | +이러한 변경사항에 따라, Ruby 3.3.0 이후로 [파일 {{ release.stats.files_changed }}개 수정, {{ release.stats.insertions }}줄 추가(+), {{ release.stats.deletions }}줄 삭제(-)](https://github.com/ruby/ruby/compare/v3_3_0...{{ release.tag }}#file_bucket)가 |
| 280 | +이루어졌습니다! |
| 281 | + |
| 282 | +메리 크리스마스, 해피 홀리데이, 그리고 Ruby 3.4와 함께 프로그래밍을 즐겨보세요! |
| 283 | + |
| 284 | +## 다운로드 |
| 285 | + |
| 286 | +* <{{ release.url.gz }}> |
| 287 | + |
| 288 | + SIZE: {{ release.size.gz }} |
| 289 | + SHA1: {{ release.sha1.gz }} |
| 290 | + SHA256: {{ release.sha256.gz }} |
| 291 | + SHA512: {{ release.sha512.gz }} |
| 292 | + |
| 293 | +* <{{ release.url.xz }}> |
| 294 | + |
| 295 | + SIZE: {{ release.size.xz }} |
| 296 | + SHA1: {{ release.sha1.xz }} |
| 297 | + SHA256: {{ release.sha256.xz }} |
| 298 | + SHA512: {{ release.sha512.xz }} |
| 299 | + |
| 300 | +* <{{ release.url.zip }}> |
| 301 | + |
| 302 | + SIZE: {{ release.size.zip }} |
| 303 | + SHA1: {{ release.sha1.zip }} |
| 304 | + SHA256: {{ release.sha256.zip }} |
| 305 | + SHA512: {{ release.sha512.zip }} |
| 306 | + |
| 307 | +## Ruby는 |
| 308 | + |
| 309 | +Ruby는 1993년에 Matz(마츠모토 유키히로) 씨가 처음 개발했고, |
| 310 | +현재는 오픈 소스로서 개발되고 있습니다. 여러 플랫폼에서 동작하며, |
| 311 | +특히 웹 개발에서 전 세계적으로 이용되고 있습니다. |
| 312 | + |
| 313 | +[Feature #13557]: https://bugs.ruby-lang.org/issues/13557 |
| 314 | +[Feature #15554]: https://bugs.ruby-lang.org/issues/15554 |
| 315 | +[Feature #16495]: https://bugs.ruby-lang.org/issues/16495 |
| 316 | +[Feature #18290]: https://bugs.ruby-lang.org/issues/18290 |
| 317 | +[Feature #18980]: https://bugs.ruby-lang.org/issues/18980 |
| 318 | +[Misc #18984]: https://bugs.ruby-lang.org/issues/18984 |
| 319 | +[Feature #19117]: https://bugs.ruby-lang.org/issues/19117 |
| 320 | +[Bug #19266]: https://bugs.ruby-lang.org/issues/19266 |
| 321 | +[Feature #19714]: https://bugs.ruby-lang.org/issues/19714 |
| 322 | +[Bug #19918]: https://bugs.ruby-lang.org/issues/19918 |
| 323 | +[Bug #20064]: https://bugs.ruby-lang.org/issues/20064 |
| 324 | +[Feature #20182]: https://bugs.ruby-lang.org/issues/20182 |
| 325 | +[Feature #20205]: https://bugs.ruby-lang.org/issues/20205 |
| 326 | +[Bug #20218]: https://bugs.ruby-lang.org/issues/20218 |
| 327 | +[Feature #20265]: https://bugs.ruby-lang.org/issues/20265 |
| 328 | +[Feature #20351]: https://bugs.ruby-lang.org/issues/20351 |
| 329 | +[Feature #20429]: https://bugs.ruby-lang.org/issues/20429 |
| 330 | +[Feature #20443]: https://bugs.ruby-lang.org/issues/20443 |
| 331 | +[Feature #20470]: https://bugs.ruby-lang.org/issues/20470 |
| 332 | +[Feature #20497]: https://bugs.ruby-lang.org/issues/20497 |
| 333 | +[Feature #20564]: https://bugs.ruby-lang.org/issues/20564 |
| 334 | +[Bug #20620]: https://bugs.ruby-lang.org/issues/20620 |
| 335 | +[Feature #20627]: https://bugs.ruby-lang.org/issues/20627 |
| 336 | +[Feature #20705]: https://bugs.ruby-lang.org/issues/20705 |
| 337 | +[Feature #20715]: https://bugs.ruby-lang.org/issues/20715 |
| 338 | +[Feature #20775]: https://bugs.ruby-lang.org/issues/20775 |
| 339 | +[Bug #20795]: https://bugs.ruby-lang.org/issues/20795 |
| 340 | +[Feature #20860]: https://bugs.ruby-lang.org/issues/20860 |
| 341 | +[Feature #20875]: https://bugs.ruby-lang.org/issues/20875 |
| 342 | +[Feature #20884]: https://bugs.ruby-lang.org/issues/20884 |
| 343 | +[sigstore.dev]: sigstore.dev |
| 344 | +[ruby/net-http-sspi]: https://github.com/ruby/net-http-sspi |
0 commit comments