iodine 0.8.0 → 0.8.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 781c1c2b2b5a70993cb6e87071d66afb80e7e1d5d2750fefc260a1f3350ff34f
4
- data.tar.gz: '07845be2ed2fdeb9857e236738813c1b3555c79d17c84c08ec7502205ddaf0ef'
3
+ metadata.gz: 44f03917758e0311ac3af4953b3882eb6dee6d589ea09d0fe7eb035d3d05dd25
4
+ data.tar.gz: 16268d987f44fcadaef05c851f9bb74975d29607fc074b378f1404726f3661b2
5
5
  SHA512:
6
- metadata.gz: c06c8b755ffa946ecb71c46eede942cf859ea361e2526459875c2a369c820ca2c52f2a43bc5a45bee1bf00618581d32b3162d49d081d2c87acfd5fb78e198ff1
7
- data.tar.gz: c2f45fffdd0728472b07eeb0e1a7cfeb7a5b70cddea589d6572c6da53d578018b5b34bf2e8c046e1a20a5a27c7f5960720886fd5e9d70a262ea5b4bcb011ddce
6
+ metadata.gz: 73151eec8f17b4fa8072cf08af667a8682a9f759fd0868b04132fb7c90456e1a8f31b315178699dc79567230a614188fb0d77ff968b794be650bbc8a807cf061
7
+ data.tar.gz: 24c112ecadfb9a7031230a41c5b8c7c980425b60a1cab14a023a1b58ea0c7ee22861bd631ede2674bee45321f528f143f2c06d6596240cd595f0ba240c37a453
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ Please refer to the current gem version to review the relevant changes for your
7
7
 
8
8
  ## Changes
9
9
 
10
+ #### 0.8.1 - 2026-10-15
11
+
12
+ **Feature**: `Iodine.connect(...)` forwards arguments and keyword options to `Iodine::Connection.new(...)` and returns the new outbound connection.
13
+
14
+ **Fix**: Preserve frozen connection handlers and correct listener handler routing (including callbacks for explicitly mapped handlers).
15
+
10
16
  #### 0.8.0 - 2026-10-14
11
17
 
12
18
  **Update**: updated to facil.io 0.8.0, using the latest version of the [facil.io C STL](https://github.com/facil-io/cstl).
data/ext/iodine/iodine.c CHANGED
@@ -192,6 +192,7 @@ void Init_iodine(void) {
192
192
  IODINE_CONST_ID_STORE(IODINE_ON_OPEN_ID, "on_open");
193
193
  IODINE_CONST_ID_STORE(IODINE_ON_SHUTDOWN_ID, "on_shutdown");
194
194
  IODINE_CONST_ID_STORE(IODINE_ON_TIMEOUT_ID, "on_timeout");
195
+ IODINE_CONST_ID_STORE(IODINE_WRAPPER_VAR_ID, "@_iodine_handler");
195
196
  IODINE_CONST_ID_STORE(IODINE_QUERY_ID, "query");
196
197
 
197
198
  IODINE_CONST_ID_STORE(IODINE_STATE_PRE_START, "pre_start");
data/ext/iodine/iodine.h CHANGED
@@ -114,6 +114,7 @@ static ID IODINE_ON_MESSAGE_ID;
114
114
  static ID IODINE_ON_OPEN_ID;
115
115
  static ID IODINE_ON_SHUTDOWN_ID;
116
116
  static ID IODINE_ON_TIMEOUT_ID;
117
+ static ID IODINE_WRAPPER_VAR_ID;
117
118
  static ID IODINE_QUERY_ID;
118
119
 
119
120
  static ID IODINE_INDEX_ID;
@@ -140,8 +140,9 @@ Ruby Connection Object - Internal Data Structures
140
140
  * These are cached Ruby objects associated with each connection.
141
141
  */
142
142
  typedef enum {
143
- IODINE_CONNECTION_STORE_handler = 0, /**< Handler object for callbacks */
144
- IODINE_CONNECTION_STORE_env, /**< Rack environment hash */
143
+ IODINE_CONNECTION_STORE_handler = 0, /**< Handler object for callbacks */
144
+ IODINE_CONNECTION_STORE_user_handler, /**< User passed Handler */
145
+ IODINE_CONNECTION_STORE_env, /**< Rack environment hash */
145
146
  IODINE_CONNECTION_STORE_rack, /**< Rack response array [status,hdrs,body] */
146
147
  IODINE_CONNECTION_STORE_method, /**< Cached HTTP method string */
147
148
  IODINE_CONNECTION_STORE_path, /**< Cached request path string */
@@ -256,7 +257,8 @@ static VALUE iodine_connection_create_from_io(fio_io_s *io) {
256
257
  VALUE m = rb_obj_alloc(iodine_rb_IODINE_CONNECTION);
257
258
  STORE.hold(m);
258
259
  iodine_connection_s *c = iodine_connection_ptr(m);
259
- c->store[IODINE_CONNECTION_STORE_handler] = (VALUE)fio_io_udata(io);
260
+ c->store[IODINE_CONNECTION_STORE_handler] =
261
+ c->store[IODINE_CONNECTION_STORE_user_handler] = (VALUE)fio_io_udata(io);
260
262
  c->io = io;
261
263
  c->http = NULL;
262
264
  fio_io_udata_set(io, (void *)m);
@@ -270,7 +272,8 @@ static VALUE iodine_connection_create_from_http(fio_http_s *h) {
270
272
  : iodine_connection_alloc(iodine_rb_IODINE_CONNECTION);
271
273
  STORE.hold(m);
272
274
  iodine_connection_s *c = iodine_connection_ptr(m);
273
- c->store[IODINE_CONNECTION_STORE_handler] = (VALUE)fio_http_udata(h);
275
+ c->store[IODINE_CONNECTION_STORE_handler] =
276
+ c->store[IODINE_CONNECTION_STORE_user_handler] = (VALUE)fio_http_udata(h);
274
277
  c->io = fio_http_io(h);
275
278
  if (!(c->flags & IODINE_CONNECTION_CLIENT))
276
279
  c->http = fio_http_dup(h);
@@ -346,23 +349,22 @@ static VALUE iodine_connection_is_tls(VALUE o) {
346
349
  Ruby Store Get/Set
347
350
  ***************************************************************************** */
348
351
 
349
- #define IODINE_DEF_GET_FUNC(val_name) \
352
+ #define IODINE_DEF_GET_FUNC(name, val_name) \
350
353
  /** Returns the client's current val_name object. */ \
351
- static VALUE iodine_connection_##val_name##_get(VALUE self) { \
354
+ static VALUE iodine_connection_##name##_get(VALUE self) { \
352
355
  iodine_connection_s *c = iodine_connection_ptr(self); \
353
356
  return c->store[IODINE_CONNECTION_STORE_##val_name]; \
354
357
  }
355
- #define IODINE_DEF_SET_FUNC(val_name) \
358
+ #define IODINE_DEF_SET_FUNC(name, val_name) \
356
359
  /** Sets the client's val_name object. */ \
357
- static VALUE iodine_connection_##val_name##_set(VALUE self, \
358
- VALUE updated_val) { \
360
+ static VALUE iodine_connection_##name##_set(VALUE self, VALUE updated_val) { \
359
361
  iodine_connection_s *c = iodine_connection_ptr(self); \
360
362
  c->store[IODINE_CONNECTION_STORE_##val_name] = updated_val; \
361
363
  return updated_val; \
362
364
  }
363
365
 
364
- IODINE_DEF_GET_FUNC(handler);
365
- IODINE_DEF_SET_FUNC(env);
366
+ IODINE_DEF_GET_FUNC(handler, user_handler);
367
+ IODINE_DEF_SET_FUNC(env, env);
366
368
 
367
369
  #undef IODINE_DEF_GET_FUNC
368
370
  #undef IODINE_DEF_SET_FUNC
@@ -624,6 +626,8 @@ typedef struct {
624
626
  fio_buf_info_s hint;
625
627
  VALUE headers;
626
628
  VALUE cookies;
629
+ VALUE handler;
630
+ VALUE user_handler;
627
631
  fio_buf_info_s method;
628
632
  fio_buf_info_s body;
629
633
  fio_http_settings_s settings;
@@ -1196,13 +1200,107 @@ static VALUE iodine_handler_on_auth_rack_sse(VALUE handler, VALUE client) {
1196
1200
  IODINE_RACK_UPGRADE_SSE_SYM);
1197
1201
  }
1198
1202
 
1203
+ /* Frozen handlers cannot receive Iodine's default singleton callbacks. Keep
1204
+ * their implemented callbacks on a mutable adapter, with direct C forwarding
1205
+ * (rather than SimpleDelegator's per-request method_missing lookup). */
1206
+ #define IODINE_FORWARD_1(name, id) \
1207
+ static VALUE iodine_handler_forward_##name(VALUE wrapper, VALUE arg) { \
1208
+ VALUE original = rb_ivar_get(wrapper, IODINE_WRAPPER_VAR_ID); \
1209
+ return rb_funcallv(original, id, 1, &arg); \
1210
+ }
1211
+ #define IODINE_FORWARD_2(name, id) \
1212
+ static VALUE iodine_handler_forward_##name(VALUE wrapper, \
1213
+ VALUE a, \
1214
+ VALUE b) { \
1215
+ VALUE original = rb_ivar_get(wrapper, IODINE_WRAPPER_VAR_ID); \
1216
+ VALUE args[] = {a, b}; \
1217
+ return rb_funcallv(original, id, 2, args); \
1218
+ }
1219
+ IODINE_FORWARD_1(call, IODINE_CALL_ID)
1220
+ IODINE_FORWARD_1(on_http, IODINE_ON_HTTP_ID)
1221
+ IODINE_FORWARD_1(on_finish, IODINE_ON_FINISH_ID)
1222
+ IODINE_FORWARD_1(on_close, IODINE_ON_CLOSE_ID)
1223
+ IODINE_FORWARD_2(on_eventsource, IODINE_ON_EVENTSOURCE_ID)
1224
+ IODINE_FORWARD_2(on_eventsource_reconnect, IODINE_ON_EVENTSOURCE_RECONNECT_ID)
1225
+ IODINE_FORWARD_1(on_drained, IODINE_ON_DRAINED_ID)
1226
+ IODINE_FORWARD_1(on_shutdown, IODINE_ON_SHUTDOWN_ID)
1227
+ IODINE_FORWARD_1(on_timeout, IODINE_ON_TIMEOUT_ID)
1228
+ IODINE_FORWARD_1(on_authenticate, IODINE_ON_AUTHENTICATE_ID)
1229
+ IODINE_FORWARD_1(on_authenticate_sse, IODINE_ON_AUTHENTICATE_SSE_ID)
1230
+ IODINE_FORWARD_1(on_authenticate_websocket, IODINE_ON_AUTHENTICATE_WEBSOCKET_ID)
1231
+ IODINE_FORWARD_1(on_open, IODINE_ON_OPEN_ID)
1232
+ IODINE_FORWARD_2(on_message, IODINE_ON_MESSAGE_ID)
1233
+ #undef IODINE_FORWARD_1
1234
+ #undef IODINE_FORWARD_2
1235
+
1236
+ static VALUE iodine_handler_method_injection__inner_frozen(VALUE handler,
1237
+ bool is_middleware) {
1238
+ const struct {
1239
+ ID id;
1240
+ union {
1241
+ VALUE (*one)(VALUE, VALUE);
1242
+ VALUE (*two)(VALUE, VALUE, VALUE);
1243
+ } forward;
1244
+ int argc;
1245
+ bool required;
1246
+ } callbacks[] = {
1247
+ /* clang-format off */
1248
+ {IODINE_CALL_ID, {.one = iodine_handler_forward_call}, 1, false},
1249
+ {IODINE_ON_HTTP_ID, {.one = iodine_handler_forward_on_http}, 1, true},
1250
+ {IODINE_ON_FINISH_ID, {.one = iodine_handler_forward_on_finish}, 1, true},
1251
+ {IODINE_ON_CLOSE_ID, {.one = iodine_handler_forward_on_close}, 1, true},
1252
+ {IODINE_ON_EVENTSOURCE_ID, {.two = iodine_handler_forward_on_eventsource}, 2, true},
1253
+ {IODINE_ON_EVENTSOURCE_RECONNECT_ID,
1254
+ {.two = iodine_handler_forward_on_eventsource_reconnect}, 2, true},
1255
+ {IODINE_ON_DRAINED_ID, {.one = iodine_handler_forward_on_drained}, 1, true},
1256
+ {IODINE_ON_SHUTDOWN_ID, {.one = iodine_handler_forward_on_shutdown}, 1, true},
1257
+ {IODINE_ON_TIMEOUT_ID, {.one = iodine_handler_forward_on_timeout}, 1, true},
1258
+ {IODINE_ON_AUTHENTICATE_ID, {.one = iodine_handler_forward_on_authenticate}, 1, false},
1259
+ {IODINE_ON_AUTHENTICATE_SSE_ID,
1260
+ {.one = iodine_handler_forward_on_authenticate_sse}, 1, true},
1261
+ {IODINE_ON_AUTHENTICATE_WEBSOCKET_ID,
1262
+ {.one = iodine_handler_forward_on_authenticate_websocket}, 1, true},
1263
+ {IODINE_ON_OPEN_ID, {.one = iodine_handler_forward_on_open}, 1, true},
1264
+ {IODINE_ON_MESSAGE_ID, {.two = iodine_handler_forward_on_message}, 2, true},
1265
+ }; /* clang-format on */
1266
+ bool needs_defaults = false;
1267
+ for (size_t i = 0; i < sizeof(callbacks) / sizeof(*callbacks); ++i)
1268
+ needs_defaults |=
1269
+ callbacks[i].required && !rb_respond_to(handler, callbacks[i].id);
1270
+ if (!needs_defaults)
1271
+ return handler;
1272
+
1273
+ VALUE wrapper = rb_obj_alloc(rb_cObject);
1274
+ rb_ivar_set(wrapper, IODINE_WRAPPER_VAR_ID, handler);
1275
+ for (size_t i = 0; i < sizeof(callbacks) / sizeof(*callbacks); ++i) {
1276
+ if (!rb_respond_to(handler, callbacks[i].id))
1277
+ continue;
1278
+ if (callbacks[i].argc == 1)
1279
+ rb_define_singleton_method(wrapper,
1280
+ rb_id2name(callbacks[i].id),
1281
+ callbacks[i].forward.one,
1282
+ 1);
1283
+ else
1284
+ rb_define_singleton_method(wrapper,
1285
+ rb_id2name(callbacks[i].id),
1286
+ callbacks[i].forward.two,
1287
+ 2);
1288
+ }
1289
+ return wrapper;
1290
+ (void)is_middleware;
1291
+ }
1292
+
1199
1293
  static VALUE iodine_handler_method_injection__inner(VALUE self,
1200
1294
  VALUE handler,
1201
1295
  bool is_middleware) {
1202
- static VALUE previous = Qnil;
1203
- if (handler == previous)
1204
- return handler;
1205
- previous = handler;
1296
+ if (rb_obj_frozen_p(handler)) {
1297
+ VALUE original = handler;
1298
+ handler =
1299
+ iodine_handler_method_injection__inner_frozen(handler, is_middleware);
1300
+ if (handler == original)
1301
+ return handler;
1302
+ }
1303
+
1206
1304
  /* test for handler responses and set a callback if missing */
1207
1305
  #define IODINE_DEFINE_MISSING_CALLBACK(id) \
1208
1306
  do { \
@@ -1335,8 +1433,9 @@ static VALUE iodine_connection_handler_set(VALUE client, VALUE handler) {
1335
1433
  return Qnil;
1336
1434
  if (IODINE_STORE_IS_SKIP(handler))
1337
1435
  rb_raise(rb_eArgError, "invalid handler!");
1338
- iodine_handler_method_injection(Qnil, handler);
1339
- c->store[IODINE_CONNECTION_STORE_handler] = handler;
1436
+ c->store[IODINE_CONNECTION_STORE_user_handler] = handler;
1437
+ c->store[IODINE_CONNECTION_STORE_handler] =
1438
+ iodine_handler_method_injection(Qnil, handler);
1340
1439
  return handler;
1341
1440
  }
1342
1441
 
@@ -2248,9 +2347,12 @@ FIO_IFUNC iodine_connection_args_s iodine_connection_parse_args(int argc,
2248
2347
  }
2249
2348
 
2250
2349
  /* No errors, start protecting Ruby data from GC */
2251
- STORE.hold((VALUE)r.settings.udata);
2350
+ r.user_handler = (VALUE)r.settings.udata;
2351
+ STORE.cache(r.user_handler);
2252
2352
  /* make sure all proper callbacks are defined. */
2253
- iodine_handler_method_injection(Qnil, (VALUE)r.settings.udata);
2353
+ r.handler = iodine_handler_method_injection(Qnil, r.user_handler);
2354
+ STORE.cache(r.handler);
2355
+ r.settings.udata = (void *)r.handler;
2254
2356
 
2255
2357
  r.settings.sse_timeout = r.settings.ws_timeout;
2256
2358
 
@@ -2467,7 +2569,8 @@ static VALUE iodine_connection_initialize(int argc, VALUE *argv, VALUE self) {
2467
2569
  "See documentation, use tcp(s)://.../ or http(s)://.../ or "
2468
2570
  "ws(s):/.../ etc'");
2469
2571
  /* Keep the returned client and its handler alive until close/failure. */
2470
- c->store[IODINE_CONNECTION_STORE_handler] = (VALUE)args.settings.udata;
2572
+ c->store[IODINE_CONNECTION_STORE_handler] = (VALUE)args.handler;
2573
+ c->store[IODINE_CONNECTION_STORE_user_handler] = (VALUE)args.user_handler;
2471
2574
  c->flags |= IODINE_CONNECTION_CLIENT;
2472
2575
 
2473
2576
  /* test if HTTP scheme */
@@ -2506,6 +2609,16 @@ static VALUE iodine_connection_initialize(int argc, VALUE *argv, VALUE self) {
2506
2609
  return self;
2507
2610
  }
2508
2611
 
2612
+ /** Creates an outbound connection using Connection.new, forwarding keywords. */
2613
+ static VALUE iodine_connection_connect(int argc, VALUE *argv, VALUE self) {
2614
+ (void)self;
2615
+ return rb_funcallv_kw(iodine_rb_IODINE_CONNECTION,
2616
+ IODINE_NEW_ID,
2617
+ argc,
2618
+ argv,
2619
+ RB_PASS_CALLED_KEYWORDS);
2620
+ }
2621
+
2509
2622
  /** Returns true if the connection appears to be open (no known issues). */
2510
2623
  static VALUE iodine_connection_is_open(VALUE self) {
2511
2624
  iodine_connection_s *c = iodine_connection_ptr(self);
@@ -3212,7 +3325,8 @@ static VALUE iodine_listen_rb(int argc, VALUE *argv, VALUE self) {
3212
3325
  if (!listener) /* resources freed by fio_io_listen if it fails */
3213
3326
  rb_raise(rb_eRuntimeError, "Couldn't open listening socket.");
3214
3327
  r = iodine_listener_new(listener,
3215
- (VALUE)s.settings.udata,
3328
+ (VALUE)s.handler,
3329
+ (VALUE)s.user_handler,
3216
3330
  s.service != IODINE_SERVICE_RAW);
3217
3331
  /* avoid listening to the same address/port combination again */
3218
3332
  fio_cli_set("-p", NULL);
@@ -3416,6 +3530,7 @@ static void Init_Iodine_Connection(void) {
3416
3530
  #undef IODINE_CONST_RACK_STORE
3417
3531
 
3418
3532
  rb_define_method(m, "initialize", iodine_connection_initialize, -1);
3533
+ rb_define_singleton_method(iodine_rb_IODINE, "connect", iodine_connection_connect, -1);
3419
3534
 
3420
3535
  #define IODINE_DEFINE_GETSET_METHOD(name)\
3421
3536
  rb_define_method(m, #name, iodine_connection_##name##_get, 0);\
@@ -39,9 +39,10 @@ static VALUE iodine_rb_IODINE_LISTENER;
39
39
  * (fio_io_listener_s) along with its Ruby handler object.
40
40
  */
41
41
  typedef struct {
42
- void *listener; /**< Pointer to fio_http_listener_s or fio_io_listener_s */
43
- VALUE handler; /**< Ruby handler object for callbacks */
44
- bool is_http; /**< true if HTTP listener, false if raw TCP */
42
+ void *listener; /**< Pointer to fio_http_listener_s or fio_io_listener_s */
43
+ VALUE handler; /**< Ruby handler object for callbacks */
44
+ VALUE user; /**< User supplied handler (handler might wrap it) */
45
+ bool is_http; /**< true if HTTP listener, false if raw TCP */
45
46
  } iodine_listener_s;
46
47
 
47
48
  static void iodine_listener_free(void *p) {
@@ -58,6 +59,8 @@ FIO_IFUNC void iodine_listener_gc_mark(void *p) {
58
59
  iodine_listener_s *m = (iodine_listener_s *)p;
59
60
  if (!IODINE_STORE_IS_SKIP(m->handler))
60
61
  rb_gc_mark(m->handler);
62
+ if (!IODINE_STORE_IS_SKIP(m->user))
63
+ rb_gc_mark(m->user);
61
64
  }
62
65
 
63
66
  static const rb_data_type_t IODINE_LISTNER_DATA_TYPE = {
@@ -100,10 +103,10 @@ Helpers - Internal Handler Management
100
103
  #define IODINE_LISTNER_ONSET(o) \
101
104
  do { \
102
105
  STORE.release(old_value); \
103
- STORE.hold(o->handler); \
104
106
  iodine_handler_method_injection__inner(iodine_rb_IODINE_LISTENER, \
105
107
  o->handler, \
106
108
  0); \
109
+ STORE.hold(o->handler); \
107
110
  } while (0)
108
111
 
109
112
  FIO_DEF_GETSET_FUNC(static,
@@ -127,13 +130,17 @@ FIO_DEF_GETSET_FUNC(static,
127
130
  *
128
131
  * @note Raises NoMemError if allocation fails.
129
132
  */
130
- static VALUE iodine_listener_new(void *listener, VALUE handler, bool is_http) {
133
+ static VALUE iodine_listener_new(void *listener,
134
+ VALUE handler,
135
+ VALUE user_handler,
136
+ bool is_http) {
131
137
  VALUE r = iodine_listener_alloc(iodine_rb_IODINE_LISTENER);
132
138
  if (IODINE_STORE_IS_SKIP(r))
133
139
  rb_raise(rb_eNoMemError, "Listener loocation error!");
134
140
  iodine_listener_s *l = iodine_listener_ptr(r);
135
141
  *l = (iodine_listener_s){.listener = listener,
136
142
  .handler = handler,
143
+ .user = user_handler,
137
144
  .is_http = is_http};
138
145
  return r;
139
146
  }
@@ -192,10 +199,11 @@ static VALUE iodine_listener_map(int argc, VALUE *argv, VALUE o) {
192
199
  ->udata);
193
200
  } else { /* set value for HTTP router */
194
201
  if (!IODINE_STORE_IS_SKIP(handler)) {
202
+ handler =
203
+ iodine_handler_method_injection__inner(iodine_rb_IODINE_LISTENER,
204
+ handler,
205
+ 0);
195
206
  STORE.hold(handler);
196
- iodine_handler_method_injection__inner(iodine_rb_IODINE_LISTENER,
197
- handler,
198
- 0);
199
207
  }
200
208
  settings =
201
209
  *fio_http_listener_settings((fio_http_listener_s *)l->listener);
@@ -304,6 +304,12 @@ module Iodine
304
304
  # Iodine.listen(url: "wss://0.0.0.0:3000", handler: MyWSHandler, tls_io: :iodine)
305
305
  def self.listen(**options); end
306
306
 
307
+ # Opens an outbound connection, forwarding arguments to {Iodine::Connection.new}.
308
+ # The caller receives the new connection; constructor errors propagate unchanged.
309
+ # @return [Iodine::Connection] the new connection
310
+ # @see Iodine::Connection#initialize
311
+ def self.connect(url, **options); end
312
+
307
313
  # Sets a block of code to run when Iodine's core state is updated.
308
314
  #
309
315
  # @param state [Symbol] event the state event for which the block should run (see list).
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Iodine
4
4
  # The Iodine gem version number
5
- VERSION = "0.8.0"
5
+ VERSION = "0.8.1"
6
6
  # NeoRack supported extensions.
7
7
  def self.extensions
8
8
  return (@extensions ||= { iodine: (VERSION.split(?.).map() {|i| i.to_i.to_s == i ? i.to_i : i }),
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
@@ -133,7 +133,7 @@ metadata:
133
133
  homepage_uri: https://github.com/boazsegev/iodine
134
134
  source_code_uri: https://github.com/boazsegev/iodine
135
135
  changelog_uri: https://github.com/boazsegev/iodine/blob/master/CHANGELOG.md
136
- post_install_message: Thank you for installing Iodine 0.8.0.
136
+ post_install_message: Thank you for installing Iodine 0.8.1.
137
137
  rdoc_options: []
138
138
  require_paths:
139
139
  - lib