net-imap 0.4.25 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +8 -2
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +47 -236
- data/lib/net/imap/config/attr_type_coercion.rb +22 -23
- data/lib/net/imap/config.rb +38 -162
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -33
- data/lib/net/imap/response_data.rb +62 -118
- data/lib/net/imap/response_parser.rb +30 -65
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +0 -74
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +132 -282
- data/lib/net/imap.rb +118 -301
- data/net-imap.gemspec +1 -1
- metadata +7 -6
- data/lib/net/imap/response_reader.rb +0 -94
- data/lib/net/imap/uidplus_data.rb +0 -326
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2c89d97e842bce303df112c3fbc0f048f20a2ff1dbf3b85bb2314ea0d2e207c5
|
|
4
|
+
data.tar.gz: 11d6ec196e1e768f61a17b0ce7f385a11eb03e3e92af8ab327ba9a90699ee940
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca70e20c3c70f4ca57822f70936546adb3129dd7c0771ef2d054b0356ed4f6a994b7453803cc06bd25ebb37dc0ed10f60b5541bc56749dee38a6126264344599
|
|
7
|
+
data.tar.gz: 2921a79dbdab7bd0476d883c18db50150388d587c711270faac99ad4505574a23e6532fe61a4fb48f51c8bddea6641d96f81d9dcbe245b4d4f4542fcc7418566
|
data/Gemfile
CHANGED
|
@@ -4,7 +4,7 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
gem "digest"
|
|
8
8
|
gem "strscan"
|
|
9
9
|
gem "base64"
|
|
10
10
|
|
|
@@ -13,4 +13,10 @@ gem "rdoc"
|
|
|
13
13
|
gem "test-unit"
|
|
14
14
|
gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
|
|
15
15
|
|
|
16
|
-
gem "benchmark-driver"
|
|
16
|
+
gem "benchmark-driver", require: false
|
|
17
|
+
|
|
18
|
+
group :test do
|
|
19
|
+
gem "simplecov", require: false
|
|
20
|
+
gem "simplecov-html", require: false
|
|
21
|
+
gem "simplecov-json", require: false
|
|
22
|
+
end
|
|
@@ -9,7 +9,7 @@ module Net::IMAP::Authenticators
|
|
|
9
9
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
10
10
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
11
11
|
],
|
|
12
|
-
uplevel: 1
|
|
12
|
+
uplevel: 1, category: :deprecated
|
|
13
13
|
)
|
|
14
14
|
Net::IMAP::SASL.add_authenticator(...)
|
|
15
15
|
end
|
|
@@ -20,7 +20,7 @@ module Net::IMAP::Authenticators
|
|
|
20
20
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
21
21
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
22
22
|
],
|
|
23
|
-
uplevel: 1
|
|
23
|
+
uplevel: 1, category: :deprecated
|
|
24
24
|
)
|
|
25
25
|
Net::IMAP::SASL.authenticator(...)
|
|
26
26
|
end
|
|
@@ -14,19 +14,17 @@ module Net
|
|
|
14
14
|
when nil
|
|
15
15
|
when String
|
|
16
16
|
when Integer
|
|
17
|
-
|
|
18
|
-
if data.negative?
|
|
19
|
-
raise DataFormatError, "Integer argument must be unsigned: #{data}"
|
|
20
|
-
elsif 0xffff_ffff_ffff_ffff < data
|
|
21
|
-
raise DataFormatError, "Integer argument must fit in 64 bits: #{data}"
|
|
22
|
-
end
|
|
17
|
+
NumValidator.ensure_number(data)
|
|
23
18
|
when Array
|
|
24
|
-
data
|
|
25
|
-
|
|
19
|
+
if data[0] == 'CHANGEDSINCE'
|
|
20
|
+
NumValidator.ensure_mod_sequence_value(data[1])
|
|
21
|
+
else
|
|
22
|
+
data.each do |i|
|
|
23
|
+
validate_data(i)
|
|
24
|
+
end
|
|
26
25
|
end
|
|
27
26
|
when Time, Date, DateTime
|
|
28
27
|
when Symbol
|
|
29
|
-
Flag.validate(data)
|
|
30
28
|
else
|
|
31
29
|
data.validate
|
|
32
30
|
end
|
|
@@ -47,7 +45,7 @@ module Net
|
|
|
47
45
|
when Date
|
|
48
46
|
send_date_data(data)
|
|
49
47
|
when Symbol
|
|
50
|
-
|
|
48
|
+
send_symbol_data(data)
|
|
51
49
|
else
|
|
52
50
|
data.send_data(self, tag)
|
|
53
51
|
end
|
|
@@ -79,31 +77,9 @@ module Net
|
|
|
79
77
|
put_string('"' + str.gsub(/["\\]/, "\\\\\\&") + '"')
|
|
80
78
|
end
|
|
81
79
|
|
|
82
|
-
def
|
|
83
|
-
|
|
84
|
-
# `non_sync` is an optional tri-state flag:
|
|
85
|
-
# * `true` -> Force non-synchronizing `LITERAL+`/`LITERAL-` behavior.
|
|
86
|
-
# NOTE: raises DataFormatError when server doesn't support
|
|
87
|
-
# non-synchronizing literal, or literal is too large for LITERAL-.
|
|
88
|
-
# * `false` -> Force normal synchronizing literal behavior.
|
|
89
|
-
# * `nil` -> (default) Currently behaves like `false` (will be dynamic).
|
|
90
|
-
# TODO: Dynamic, based on capabilities and bytesize.
|
|
91
|
-
def send_literal(str, tag = nil, binary: false, non_sync: nil)
|
|
92
|
-
bytesize = str.bytesize
|
|
80
|
+
def send_literal(str, tag = nil)
|
|
93
81
|
synchronize do
|
|
94
|
-
|
|
95
|
-
# TODO: check in Printer, so we don't need to close the connection.
|
|
96
|
-
@sock.close
|
|
97
|
-
raise DataFormatError, "Connection closed: " \
|
|
98
|
-
"Cannot send non-synchronizing literal without known server support"
|
|
99
|
-
end
|
|
100
|
-
prefix = "~" if binary
|
|
101
|
-
plus = "+" if non_sync
|
|
102
|
-
put_string("#{prefix}{#{bytesize}#{plus}}\r\n")
|
|
103
|
-
if non_sync
|
|
104
|
-
put_string(str)
|
|
105
|
-
return
|
|
106
|
-
end
|
|
82
|
+
put_string("{" + str.bytesize.to_s + "}" + CRLF)
|
|
107
83
|
@continued_command_tag = tag
|
|
108
84
|
@continuation_request_exception = nil
|
|
109
85
|
begin
|
|
@@ -118,18 +94,8 @@ module Net
|
|
|
118
94
|
end
|
|
119
95
|
end
|
|
120
96
|
|
|
121
|
-
def non_sync_literal_allowed?(bytesize)
|
|
122
|
-
return unless capabilities_cached?
|
|
123
|
-
return "+" if capable?("LITERAL+")
|
|
124
|
-
return "-" if capable_literal_minus? && bytesize <= 4096
|
|
125
|
-
false
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def capable_literal_minus?; capable?("LITERAL-") || capable?("IMAP4rev2") end
|
|
129
|
-
|
|
130
|
-
# NOTE: +num+ should already be an Integer
|
|
131
97
|
def send_number_data(num)
|
|
132
|
-
put_string(
|
|
98
|
+
put_string(num.to_s)
|
|
133
99
|
end
|
|
134
100
|
|
|
135
101
|
def send_list_data(list, tag = nil)
|
|
@@ -149,236 +115,71 @@ module Net
|
|
|
149
115
|
def send_date_data(date) put_string Net::IMAP.encode_date(date) end
|
|
150
116
|
def send_time_data(time) put_string Net::IMAP.encode_time(time) end
|
|
151
117
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def new(arg = nil, data: arg) super(data: data) end
|
|
156
|
-
alias :[] :new
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
def initialize(data:)
|
|
160
|
-
@data = data
|
|
161
|
-
freeze
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
attr_reader :data
|
|
165
|
-
|
|
166
|
-
def to_h(&block) block ? to_h.to_h(&block) : { data: data } end
|
|
167
|
-
def ==(other) self.class === other && to_h == other.to_h end
|
|
168
|
-
def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
169
|
-
|
|
170
|
-
# following class definition goes beyond the basic Data.define(:data)
|
|
171
|
-
##
|
|
172
|
-
|
|
173
|
-
def self.validate(...)
|
|
174
|
-
data = new(...)
|
|
175
|
-
data.validate
|
|
176
|
-
data
|
|
177
|
-
end
|
|
118
|
+
def send_symbol_data(symbol)
|
|
119
|
+
put_string("\\" + symbol.to_s)
|
|
120
|
+
end
|
|
178
121
|
|
|
122
|
+
class RawData # :nodoc:
|
|
179
123
|
def send_data(imap, tag)
|
|
180
|
-
|
|
124
|
+
imap.__send__(:put_string, @data)
|
|
181
125
|
end
|
|
182
126
|
|
|
183
127
|
def validate
|
|
184
128
|
end
|
|
185
|
-
end
|
|
186
129
|
|
|
187
|
-
|
|
188
|
-
# validations of decoded #data, and differ only in how they are formatted.
|
|
189
|
-
#
|
|
190
|
-
# +data+ may contain any 7-bit ASCII character except +NULL+, +CR+, or +LF+.
|
|
191
|
-
# Any multibyte +UTF-8+ character is also allowed when the connection
|
|
192
|
-
# supports UTF8: either +UTF8=ACCEPT+ or +IMAP4rev2+ have been enabled, or
|
|
193
|
-
# the server supports only +IMAP4rev2+ and not earlier IMAP revisions, or
|
|
194
|
-
# the server advertises +UTF8=ONLY+.
|
|
195
|
-
#
|
|
196
|
-
# NOTE: This does not verify whether the connection supports UTF-8, but that
|
|
197
|
-
# may change in future versions.
|
|
198
|
-
#
|
|
199
|
-
# The string's bytes must be valid ASCII or valid UTF-8. The string's
|
|
200
|
-
# reported encoding is ignored, but the string is _not_ transcoded.
|
|
201
|
-
class ValidNonLiteralData < CommandData
|
|
202
|
-
def initialize(data:)
|
|
203
|
-
data = String(data.to_str)
|
|
204
|
-
unless [Encoding::ASCII, Encoding::UTF_8].include?(data.encoding)
|
|
205
|
-
data = data.dup.force_encoding(data.ascii_only? ? "ASCII" : "UTF-8")
|
|
206
|
-
end
|
|
207
|
-
data = -data
|
|
208
|
-
super
|
|
209
|
-
validate
|
|
210
|
-
end
|
|
130
|
+
private
|
|
211
131
|
|
|
212
|
-
def
|
|
213
|
-
|
|
214
|
-
raise DataFormatError, "must use ASCII or UTF-8 encoding"
|
|
215
|
-
elsif !data.valid_encoding?
|
|
216
|
-
raise DataFormatError, "invalid UTF-8 must be literal encoded"
|
|
217
|
-
elsif data.include?("\0")
|
|
218
|
-
raise DataFormatError, "NULL byte must be binary literal encoded"
|
|
219
|
-
elsif /[\r\n]/.match?(data)
|
|
220
|
-
raise DataFormatError, "CR and LF bytes must be literal encoded"
|
|
221
|
-
end
|
|
132
|
+
def initialize(data)
|
|
133
|
+
@data = data
|
|
222
134
|
end
|
|
223
|
-
|
|
224
|
-
def ascii_only?; data.ascii_only? end
|
|
225
|
-
|
|
226
|
-
def send_data(imap, tag = nil) imap.__send__(:put_string, formatted) end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# Represents IMAP +text+ data, which covers everything in the IMAP grammar,
|
|
230
|
-
# except for +literal+, +literal8+, and the concluding +CRLF+.
|
|
231
|
-
#
|
|
232
|
-
# NOTE: The current implementation does not verify that the connection
|
|
233
|
-
# supports UTF-8. Future versions may validate this.
|
|
234
|
-
class RawText < ValidNonLiteralData # :nodoc:
|
|
235
|
-
# raw: no formatting necessary
|
|
236
|
-
alias formatted data
|
|
237
135
|
end
|
|
238
136
|
|
|
239
|
-
class
|
|
240
|
-
def
|
|
241
|
-
|
|
242
|
-
when String
|
|
243
|
-
data = self.class.split(data)
|
|
244
|
-
when Array
|
|
245
|
-
unless data.all? { |part| RawText === part || Literal === part }
|
|
246
|
-
raise TypeError, "expected String or Array[#{RawText} | #{Literal}]"
|
|
247
|
-
end
|
|
248
|
-
else
|
|
249
|
-
raise TypeError, "expected String or Array[#{RawText} | #{Literal}]"
|
|
250
|
-
end
|
|
251
|
-
super
|
|
252
|
-
validate
|
|
137
|
+
class Atom # :nodoc:
|
|
138
|
+
def send_data(imap, tag)
|
|
139
|
+
imap.__send__(:put_string, @data)
|
|
253
140
|
end
|
|
254
141
|
|
|
255
|
-
def send_data(imap, tag) data.each do _1.send_data(imap, tag) end end
|
|
256
|
-
|
|
257
142
|
def validate
|
|
258
|
-
return unless RawText === data.last
|
|
259
|
-
text = data.last.data
|
|
260
|
-
if text.rindex(/\{\d+\+?\}\z/n)
|
|
261
|
-
raise DataFormatError, "RawData cannot end with literal continuation"
|
|
262
|
-
end
|
|
263
143
|
end
|
|
264
144
|
|
|
265
|
-
|
|
266
|
-
#
|
|
267
|
-
# NOTE: unlike RawData#validate, this does not prevent the final RawText
|
|
268
|
-
# from ending with a literal prefix.
|
|
269
|
-
def self.split(data)
|
|
270
|
-
data = data.b # dups and ensures BINARY encoding
|
|
271
|
-
parts = []
|
|
272
|
-
while data.match(/(~)?\{(0|[1-9]\d*)(\+)?\}\r\n/n)
|
|
273
|
-
text, binary, bytesize, non_sync, data = $`, !!$1, $2, !!$3, $'
|
|
274
|
-
bytesize = Integer bytesize, 10
|
|
275
|
-
parts << RawText[text] unless text.empty?
|
|
276
|
-
parts << extract_literal(data,
|
|
277
|
-
binary: binary,
|
|
278
|
-
bytesize: bytesize,
|
|
279
|
-
non_sync: non_sync)
|
|
280
|
-
data[0, bytesize] = ""
|
|
281
|
-
end
|
|
282
|
-
parts << RawText[data] unless data.empty?
|
|
283
|
-
parts
|
|
284
|
-
end
|
|
145
|
+
private
|
|
285
146
|
|
|
286
|
-
def
|
|
287
|
-
|
|
288
|
-
raise DataFormatError, "Too few bytes in string for literal, " \
|
|
289
|
-
"expected: %s, remaining: %s" % [bytesize, data.bytesize]
|
|
290
|
-
end
|
|
291
|
-
literal = data.byteslice(0, bytesize)
|
|
292
|
-
(binary ? Literal8 : Literal).new(data: literal, non_sync: non_sync)
|
|
147
|
+
def initialize(data)
|
|
148
|
+
@data = data
|
|
293
149
|
end
|
|
294
|
-
private_class_method :extract_literal
|
|
295
150
|
end
|
|
296
151
|
|
|
297
|
-
class
|
|
298
|
-
def
|
|
299
|
-
|
|
300
|
-
validate
|
|
152
|
+
class QuotedString # :nodoc:
|
|
153
|
+
def send_data(imap, tag)
|
|
154
|
+
imap.__send__(:send_quoted_string, @data)
|
|
301
155
|
end
|
|
302
156
|
|
|
303
157
|
def validate
|
|
304
|
-
data.to_s.ascii_only? \
|
|
305
|
-
or raise DataFormatError, "#{self.class} must be ASCII only"
|
|
306
|
-
data.match?(ResponseParser::Patterns::ATOM_SPECIALS) \
|
|
307
|
-
and raise DataFormatError, "#{self.class} must not contain atom-specials"
|
|
308
|
-
data.empty? \
|
|
309
|
-
and raise DataFormatError, "#{self.class} must not be empty"
|
|
310
158
|
end
|
|
311
159
|
|
|
312
|
-
|
|
313
|
-
imap.__send__(:put_string, data.to_s)
|
|
314
|
-
end
|
|
315
|
-
end
|
|
160
|
+
private
|
|
316
161
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
imap.__send__(:put_string, "\\#{data}")
|
|
162
|
+
def initialize(data)
|
|
163
|
+
@data = data
|
|
320
164
|
end
|
|
321
165
|
end
|
|
322
166
|
|
|
323
|
-
# Represents a IMAP +quoted+ string, which can encode any valid ASCII or
|
|
324
|
-
# UTF-8 string, unless it contains any +CR+, +LF+, or +NULL+ bytes.
|
|
325
|
-
#
|
|
326
|
-
# NOTE: The current implementation does not verify that the connection
|
|
327
|
-
# supports UTF-8. Future versions may validate this.
|
|
328
|
-
class QuotedString < ValidNonLiteralData # :nodoc:
|
|
329
|
-
def formatted; %("#{data.gsub(/["\\]/, "\\\\\\&")}") end
|
|
330
|
-
end
|
|
331
|
-
|
|
332
167
|
class Literal # :nodoc:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
super(data: data, non_sync: non_sync)
|
|
336
|
-
end
|
|
337
|
-
alias :[] :new
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
attr_reader :data, :non_sync
|
|
341
|
-
|
|
342
|
-
def to_h(&block) block ? to_h.to_h(&block) : { data: data, non_sync: non_sync } end
|
|
343
|
-
def ==(other) self.class === other && to_h == other.to_h end
|
|
344
|
-
def eql?(other) self.class === other && to_h.eql?(other.to_h) end
|
|
345
|
-
|
|
346
|
-
def initialize(data:, non_sync: nil)
|
|
347
|
-
data = -String(data.to_str).b or
|
|
348
|
-
raise DataFormatError, "#{self.class} expects string input"
|
|
349
|
-
@data, @non_sync = data, non_sync
|
|
350
|
-
validate
|
|
351
|
-
freeze
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def self.validate(...)
|
|
355
|
-
data = new(...)
|
|
356
|
-
data.validate
|
|
357
|
-
data
|
|
168
|
+
def send_data(imap, tag)
|
|
169
|
+
imap.__send__(:send_literal, @data, tag)
|
|
358
170
|
end
|
|
359
171
|
|
|
360
|
-
def bytesize; data.bytesize end
|
|
361
|
-
|
|
362
172
|
def validate
|
|
363
|
-
if data.include?("\0")
|
|
364
|
-
raise DataFormatError, "NULL byte not allowed in #{self.class}. " \
|
|
365
|
-
"Use #{Literal8} or a null-safe encoding."
|
|
366
|
-
end
|
|
367
173
|
end
|
|
368
174
|
|
|
369
|
-
|
|
370
|
-
imap.__send__(:send_literal, data, tag, non_sync: non_sync)
|
|
371
|
-
end
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
class Literal8 < Literal # :nodoc:
|
|
375
|
-
def validate; nil end # all bytes are okay
|
|
175
|
+
private
|
|
376
176
|
|
|
377
|
-
def
|
|
378
|
-
|
|
177
|
+
def initialize(data)
|
|
178
|
+
@data = data
|
|
379
179
|
end
|
|
380
180
|
end
|
|
381
181
|
|
|
182
|
+
# *DEPRECATED*. Replaced by SequenceSet.
|
|
382
183
|
class MessageSet # :nodoc:
|
|
383
184
|
def send_data(imap, tag)
|
|
384
185
|
imap.__send__(:put_string, format_internal(@data))
|
|
@@ -392,6 +193,16 @@ module Net
|
|
|
392
193
|
|
|
393
194
|
def initialize(data)
|
|
394
195
|
@data = data
|
|
196
|
+
warn("DEPRECATED: #{MessageSet} should be replaced with #{SequenceSet}.",
|
|
197
|
+
uplevel: 1, category: :deprecated)
|
|
198
|
+
begin
|
|
199
|
+
# to ensure the input works with SequenceSet, too
|
|
200
|
+
SequenceSet.new(data)
|
|
201
|
+
rescue
|
|
202
|
+
warn "MessageSet input is incompatible with SequenceSet: [%s] %s" % [
|
|
203
|
+
$!.class, $!.message
|
|
204
|
+
]
|
|
205
|
+
end
|
|
395
206
|
end
|
|
396
207
|
|
|
397
208
|
def format_internal(data)
|
|
@@ -18,8 +18,6 @@ module Net
|
|
|
18
18
|
super(attr)
|
|
19
19
|
AttrTypeCoercion.attr_accessor(attr, type: type)
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
module_function def Integer?; NilOrInteger end
|
|
23
21
|
end
|
|
24
22
|
private_constant :Macros
|
|
25
23
|
|
|
@@ -28,33 +26,34 @@ module Net
|
|
|
28
26
|
end
|
|
29
27
|
private_class_method :included
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
def self.attr_accessor(attr, type: nil)
|
|
30
|
+
return unless type
|
|
31
|
+
if :boolean == type then boolean attr
|
|
32
|
+
elsif Integer == type then integer attr
|
|
33
|
+
elsif Array === type then enum attr, type
|
|
34
|
+
else raise ArgumentError, "unknown type coercion %p" % [type]
|
|
35
|
+
end
|
|
38
36
|
end
|
|
39
|
-
Types[:boolean] = Boolean = safe{-> {!!_1}}
|
|
40
|
-
Types[Integer] = safe{->{Integer(_1)}}
|
|
41
37
|
|
|
42
|
-
def self.
|
|
43
|
-
|
|
44
|
-
define_method :"#{attr}
|
|
45
|
-
define_method :"#{attr}?" do send attr end if type == Boolean
|
|
38
|
+
def self.boolean(attr)
|
|
39
|
+
define_method :"#{attr}=" do |val| super !!val end
|
|
40
|
+
define_method :"#{attr}?" do send attr end
|
|
46
41
|
end
|
|
47
42
|
|
|
48
|
-
|
|
43
|
+
def self.integer(attr)
|
|
44
|
+
define_method :"#{attr}=" do |val| super Integer val end
|
|
45
|
+
end
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
enum
|
|
47
|
+
def self.enum(attr, enum)
|
|
48
|
+
enum = enum.dup.freeze
|
|
52
49
|
expected = -"one of #{enum.map(&:inspect).join(", ")}"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
define_method :"#{attr}=" do |val|
|
|
51
|
+
unless enum.include?(val)
|
|
52
|
+
raise ArgumentError, "expected %s, got %p" % [expected, val]
|
|
53
|
+
end
|
|
54
|
+
super val
|
|
55
|
+
end
|
|
56
|
+
end
|
|
58
57
|
|
|
59
58
|
end
|
|
60
59
|
end
|