durable_rules 0.34.01 → 0.34.02

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
  SHA1:
3
- metadata.gz: fb52f7512b46132ca90ab9cb4038c7dcb5973dfe
4
- data.tar.gz: 62eaa3495f4deb49bb499e4cd4c876b05857b50f
3
+ metadata.gz: d6180a0278ca667dd708b04c45359d7d205bb5ee
4
+ data.tar.gz: 77f98579d75d5a6ede0cf04de0cb030eb75227cd
5
5
  SHA512:
6
- metadata.gz: 73013f0de7d4fb5eb3624a61a15fa6dd4d6d287a56d48de1877b48eacb5a7b646abc9f7f20ffd207a4ba945632c2a31ab4683759a684512f4fdd40da26436047
7
- data.tar.gz: b5e83a8ce76c35b5e8e6c010b7f04c2bf135c8dd7de9641173db3f9a5e14fc53098a71b9412b30c10a879264d2a53a8d07cfc55b5119fe5284cff88960d3adb4
6
+ metadata.gz: 83a19d52b25b82a7369a679d5223b8e3dc2eb848b0c3a9cc4a75882f327f55434fc7a822bc9f2f91387977f8372daebbb19303cc6223516b67011229aebb172a
7
+ data.tar.gz: 0ec42e6ec72b8e569f632bcdb487787eb9692401c556071393ed2b62cccc46d4064f89d76b72f36c9567c2413eeb796fa03ecfc1cf9506493a5224c20d75bd09
data/librb/durable.rb CHANGED
@@ -5,22 +5,27 @@ module Durable
5
5
  @@rulesets = {}
6
6
  @@start_blocks = []
7
7
 
8
- def self.run(ruleset_definitions = nil, databases = [{:host => 'localhost', :port => 6379, :password => nil}], start = nil)
9
- main_host = Engine::Host.new ruleset_definitions, databases
10
- start.call main_host if start
11
- main_host.start!
12
- Interface::Application.set_host main_host
13
- Interface::Application.run!
8
+ def self.create_queue(ruleset_name, database = {:host => 'localhost', :port => 6379, :password => nil}, state_cache_size = 1024)
9
+ Engine::Queue.new ruleset_name, database, state_cache_size
14
10
  end
15
11
 
16
- def self.run_all(databases = [{:host => 'localhost', :port => 6379, :password => nil}])
17
- main_host = Engine::Host.new @@rulesets, databases
12
+ def self.create_host(databases = [{:host => 'localhost', :port => 6379, :password => nil}], state_cache_size = 1024)
13
+ main_host = Engine::Host.new @@rulesets, databases, state_cache_size
18
14
  for block in @@start_blocks
19
15
  main_host.instance_exec main_host, &block
20
16
  end
21
17
  main_host.start!
18
+ main_host
19
+ end
20
+
21
+ def self.run_all(databases = [{:host => 'localhost', :port => 6379, :password => nil}], host_name = nil, port = nil, run = nil, state_cache_size = 1024)
22
+ main_host = self.create_host databases, state_cache_size
22
23
  Interface::Application.set_host main_host
23
- Interface::Application.run!
24
+ if run
25
+ run(main_host, Interface::Application)
26
+ else
27
+ Interface::Application.run!
28
+ end
24
29
  end
25
30
 
26
31
  def self.ruleset(name, &block)
data/librb/engine.rb CHANGED
@@ -4,8 +4,43 @@ require_relative "../src/rulesrb/rules"
4
4
 
5
5
  module Engine
6
6
  @@timers = nil
7
+
8
+ class Closure_Queue
9
+ attr_reader :_queued_posts, :_queued_asserts, :_queued_retracts
10
+
11
+ def initialize()
12
+ @_queued_posts = []
13
+ @_queued_asserts = []
14
+ @_queued_retracts = []
15
+ end
16
+
17
+ def post(message)
18
+ if message.kind_of? Content
19
+ message = message._d
20
+ end
21
+
22
+ @_queued_posts << message
23
+ end
24
+
25
+ def assert(message)
26
+ if message.kind_of? Content
27
+ message = message._d
28
+ end
29
+
30
+ @_queued_asserts << message
31
+ end
32
+
33
+ def retract(message)
34
+ if message.kind_of? Content
35
+ message = message._d
36
+ end
37
+
38
+ @_queued_retracts << message
39
+ end
40
+ end
41
+
7
42
  class Closure
8
- attr_reader :host, :handle, :ruleset_name, :_timers, :_cancelled_timers, :_branches, :_messages, :_queued_messages, :_facts, :_retract
43
+ attr_reader :host, :handle, :ruleset_name, :_timers, :_cancelled_timers, :_branches, :_messages, :_queues, :_facts, :_retract
9
44
  attr_accessor :s
10
45
 
11
46
  def initialize(host, state, message, handle, ruleset_name)
@@ -16,7 +51,7 @@ module Engine
16
51
  @_timers = {}
17
52
  @_cancelled_timers = {}
18
53
  @_messages = {}
19
- @_queued_messages = {}
54
+ @_queues = {}
20
55
  @_branches = {}
21
56
  @_facts = {}
22
57
  @_retract = {}
@@ -58,22 +93,12 @@ module Engine
58
93
  message_list << message
59
94
  end
60
95
 
61
- def queue(ruleset_name, message)
62
- if message.kind_of? Content
63
- message = message._d
64
- end
65
-
66
- if !(message.key? :sid) && !(message.key? "sid")
67
- message[:sid] = @s.sid
96
+ def get_queue(ruleset_name)
97
+ if !@_queues.key? ruleset_name
98
+ @_queues[ruleset_name] = Closure_Queue.new
68
99
  end
69
100
 
70
- message_list = []
71
- if @_queued_messages.key? ruleset_name
72
- message_list = @_queued_messages[ruleset_name]
73
- else
74
- @_queued_messages[ruleset_name] = message_list
75
- end
76
- message_list << message
101
+ @_queues[ruleset_name]
77
102
  end
78
103
 
79
104
  def start_timer(timer_name, duration, timer_id = nil)
@@ -151,7 +176,7 @@ module Engine
151
176
  def renew_action_lease()
152
177
  if Time.now - @_start_time < 10000
153
178
  @_start_time = Time.now
154
- @host.renew_action_lease(@ruleset_name, @s.sid)
179
+ @host.renew_action_lease @ruleset_name, @s.sid
155
180
  end
156
181
  end
157
182
 
@@ -289,7 +314,7 @@ module Engine
289
314
  c.s.exception = "timeout expired"
290
315
  complete.call nil
291
316
  else
292
- c.renew_action_lease()
317
+ c.renew_action_lease
293
318
  end
294
319
  }
295
320
  Thread.new do
@@ -382,8 +407,8 @@ module Engine
382
407
  Rules.assert_event @handle, JSON.generate(message)
383
408
  end
384
409
 
385
- def queue_event(sid, ruleset_name, message)
386
- Rules.queue_event @handle, sid.to_s, ruleset_name.to_s, JSON.generate(message)
410
+ def queue_assert_event(sid, ruleset_name, message)
411
+ Rules.queue_assert_event @handle, sid.to_s, ruleset_name.to_s, JSON.generate(message)
387
412
  end
388
413
 
389
414
  def start_assert_event(message)
@@ -410,6 +435,10 @@ module Engine
410
435
  Rules.assert_fact @handle, JSON.generate(fact)
411
436
  end
412
437
 
438
+ def queue_assert_fact(sid, ruleset_name, message)
439
+ Rules.queue_assert_fact @handle, sid.to_s, ruleset_name.to_s, JSON.generate(message)
440
+ end
441
+
413
442
  def start_assert_fact(fact)
414
443
  return Rules.start_assert_fact @handle, JSON.generate(fact)
415
444
  end
@@ -426,6 +455,10 @@ module Engine
426
455
  Rules.retract_fact @handle, JSON.generate(fact)
427
456
  end
428
457
 
458
+ def queue_retract_fact(sid, ruleset_name, message)
459
+ Rules.queue_retract_fact @handle, sid.to_s, ruleset_name.to_s, JSON.generate(message)
460
+ end
461
+
429
462
  def start_retract_fact(fact)
430
463
  return Rules.start_retract_fact @handle, JSON.generate(fact)
431
464
  end
@@ -538,9 +571,20 @@ module Engine
538
571
  start_timer c.s.sid, timer_duration[0], timer_duration[1]
539
572
  end
540
573
 
541
- for ruleset_name, messages in c._queued_messages do
542
- for message in messages do
543
- queue_event c.s.sid, ruleset_name, message
574
+ for ruleset_name, q in c._queues do
575
+ for message in q._queued_posts do
576
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
577
+ queue_assert_event sid.to_s, ruleset_name, message
578
+ end
579
+
580
+ for message in q._queued_asserts do
581
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
582
+ queue_assert_fact sid.to_s, ruleset_name, message
583
+ end
584
+
585
+ for message in q._queued_retracts do
586
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
587
+ queue_retract_fact sid.to_s, ruleset_name, message
544
588
  end
545
589
  end
546
590
 
@@ -1077,10 +1121,6 @@ module Engine
1077
1121
  return get_ruleset(ruleset_name).start_retract_facts facts
1078
1122
  end
1079
1123
 
1080
- def start_timer(ruleset_name, sid, timer_name, timer_duration)
1081
- get_ruleset(ruleset_name).start_timer sid, timer_name, timer_duration
1082
- end
1083
-
1084
1124
  def patch_state(ruleset_name, state)
1085
1125
  get_ruleset(ruleset_name).assert_state state
1086
1126
  end
@@ -1145,4 +1185,36 @@ module Engine
1145
1185
 
1146
1186
  end
1147
1187
 
1188
+ class Queue
1189
+
1190
+ def initialize(ruleset_name, database = {:host => 'localhost', :port => 6379, :password => nil}, state_cache_size = 1024)
1191
+ @_ruleset_name = ruleset_name.to_s
1192
+ @handle = Rules.create_client @_ruleset_name, state_cache_size
1193
+ if database.kind_of? String
1194
+ Rules.bind_ruleset @handle, database, 0, nil
1195
+ else
1196
+ Rules.bind_ruleset @handle, database[:host], database[:port], database[:password]
1197
+ end
1198
+ end
1199
+
1200
+ def post(message)
1201
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
1202
+ Rules.queue_assert_event @handle, sid.to_s, @_ruleset_name, JSON.generate(message)
1203
+ end
1204
+
1205
+ def assert(message)
1206
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
1207
+ Rules.queue_assert_fact @handle, sid.to_s, @_ruleset_name, JSON.generate(message)
1208
+ end
1209
+
1210
+ def retract(message)
1211
+ sid = (message.key? :sid) ? message[:sid]: message['sid']
1212
+ Rules.queue_retract_fact @handle, sid.to_s, @_ruleset_name, JSON.generate(message)
1213
+ end
1214
+
1215
+ def close()
1216
+ Rules.delete_client @handle
1217
+ end
1218
+ end
1219
+
1148
1220
  end
data/src/rules/events.c CHANGED
@@ -1436,12 +1436,25 @@ static unsigned int handleTimers(void *handle,
1436
1436
  return result;
1437
1437
  }
1438
1438
 
1439
+ unsigned int action;
1440
+ switch (reply->element[i]->str[0]) {
1441
+ case 'p':
1442
+ action = ACTION_ASSERT_EVENT;
1443
+ break;
1444
+ case 'a':
1445
+ action = ACTION_ASSERT_FACT;
1446
+ break;
1447
+ case 'r':
1448
+ action = ACTION_RETRACT_FACT;
1449
+ break;
1450
+ }
1451
+
1439
1452
  commands[*commandCount] = command;
1440
1453
  ++*commandCount;
1441
1454
  result = handleMessage(handle,
1442
1455
  NULL,
1443
- reply->element[i]->str,
1444
- ACTION_ASSERT_EVENT,
1456
+ reply->element[i]->str + 2,
1457
+ action,
1445
1458
  commands,
1446
1459
  commandCount,
1447
1460
  rulesBinding);
@@ -1847,14 +1860,14 @@ unsigned int abandonAction(void *handle, void *actionHandle) {
1847
1860
  return RULES_OK;
1848
1861
  }
1849
1862
 
1850
- unsigned int queueMessage(void *handle, char *sid, char *destination, char *message) {
1863
+ unsigned int queueMessage(void *handle, unsigned int queueAction, char *sid, char *destination, char *message) {
1851
1864
  void *rulesBinding;
1852
1865
  unsigned int result = resolveBinding(handle, sid, &rulesBinding);
1853
1866
  if (result != RULES_OK) {
1854
1867
  return result;
1855
1868
  }
1856
1869
 
1857
- return registerMessage(rulesBinding, destination, message);
1870
+ return registerMessage(rulesBinding, queueAction, destination, message);
1858
1871
  }
1859
1872
 
1860
1873
  unsigned int startTimer(void *handle, char *sid, unsigned int duration, char *timer) {
data/src/rules/net.c CHANGED
@@ -1,12 +1,17 @@
1
1
  #include <stdio.h>
2
2
  #include <stdlib.h>
3
3
  #include <string.h>
4
- #include <time.h>
5
4
  #include <errno.h>
5
+ #ifndef _WIN32
6
+ #include <time.h> /* for struct timeval */
7
+ #else
8
+ #include <WinSock2.h>
9
+ #endif
6
10
  #include "net.h"
7
11
  #include "rules.h"
8
12
  #include "json.h"
9
13
 
14
+
10
15
  #ifdef _WIN32
11
16
  int asprintf(char** ret, char* format, ...){
12
17
  va_list args;
@@ -299,11 +304,233 @@ static unsigned int createTest(ruleset *tree, expression *expr, char **test, cha
299
304
  return RULES_OK;
300
305
  }
301
306
 
302
- static unsigned int loadCommands(ruleset *tree, binding *rulesBinding) {
307
+ static unsigned int loadPartitionCommand(ruleset *tree, binding *rulesBinding, char *name) {
308
+ redisContext *reContext = rulesBinding->reContext;
309
+ redisReply *reply;
310
+ char *lua = NULL;
311
+ if (asprintf(&lua,
312
+ "local partition_key = \"%s!p\"\n"
313
+ "local res = redis.call(\"hget\", partition_key, ARGV[1])\n"
314
+ "if (not res) then\n"
315
+ " res = redis.call(\"hincrby\", partition_key, \"index\", 1)\n"
316
+ " res = res %% tonumber(ARGV[2])\n"
317
+ " redis.call(\"hset\", partition_key, ARGV[1], res)\n"
318
+ "end\n"
319
+ "return tonumber(res)\n", name) == -1) {
320
+ return ERR_OUT_OF_MEMORY;
321
+ }
322
+
323
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
324
+ redisGetReply(reContext, (void**)&reply);
325
+ if (reply->type == REDIS_REPLY_ERROR) {
326
+ printf("%s\n", reply->str);
327
+ freeReplyObject(reply);
328
+ return ERR_REDIS_ERROR;
329
+ }
330
+
331
+ strncpy(rulesBinding->partitionHash, reply->str, 40);
332
+ rulesBinding->partitionHash[40] = '\0';
333
+ freeReplyObject(reply);
334
+ free(lua);
335
+ return RULES_OK;
336
+ }
337
+
338
+ static unsigned int loadRemoveActionCommand(ruleset *tree, binding *rulesBinding, char *name) {
339
+ redisContext *reContext = rulesBinding->reContext;
340
+ redisReply *reply;
341
+ char *lua = NULL;
342
+ if (asprintf(&lua,
343
+ "local delete_frame = function(key, action_key)\n"
344
+ " local rule_action_key = redis.call(\"lpop\", key)\n"
345
+ " local raw_count = redis.call(\"lpop\", key)\n"
346
+ " local count = 1\n"
347
+ " if raw_count ~= \"single\" then\n"
348
+ " count = tonumber(raw_count)\n"
349
+ " end\n"
350
+ " if count == 0 then\n"
351
+ " local packed_frame = redis.call(\"lpop\", rule_action_key)\n"
352
+ " while packed_frame ~= \"0\" do\n"
353
+ " packed_frame = redis.call(\"lpop\", rule_action_key)\n"
354
+ " end\n"
355
+ " else\n"
356
+ " for i = 0, count - 1, 1 do\n"
357
+ " local packed_frame = redis.call(\"rpop\", rule_action_key)\n"
358
+ " end\n"
359
+ " end\n"
360
+ " return (redis.call(\"llen\", key) > 0)\n"
361
+ "end\n"
362
+ "local sid = ARGV[1]\n"
363
+ "local max_score = tonumber(ARGV[2])\n"
364
+ "local action_key = \"%s!a\"\n"
365
+ "if delete_frame(action_key .. \"!\" .. sid, action_key) then\n"
366
+ " redis.call(\"zadd\", action_key , max_score, sid)\n"
367
+ "else\n"
368
+ " redis.call(\"zrem\", action_key, sid)\n"
369
+ "end\n", name) == -1) {
370
+ return ERR_OUT_OF_MEMORY;
371
+ }
372
+
373
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
374
+ redisGetReply(reContext, (void**)&reply);
375
+ if (reply->type == REDIS_REPLY_ERROR) {
376
+ printf("%s\n", reply->str);
377
+ freeReplyObject(reply);
378
+ free(lua);
379
+ return ERR_REDIS_ERROR;
380
+ }
381
+
382
+ strncpy(rulesBinding->removeActionHash, reply->str, 40);
383
+ rulesBinding->removeActionHash[40] = '\0';
384
+ freeReplyObject(reply);
385
+ free(lua);
386
+ return RULES_OK;
387
+ }
388
+
389
+ static unsigned int loadTimerCommand(ruleset *tree, binding *rulesBinding, char *name) {
303
390
  redisContext *reContext = rulesBinding->reContext;
304
391
  redisReply *reply;
392
+ char *lua = NULL;
393
+ if (asprintf(&lua,
394
+ "local timer_key = \"%s!t\"\n"
395
+ "local timestamp = tonumber(ARGV[1])\n"
396
+ "local res = redis.call(\"zrangebyscore\", timer_key, 0, timestamp, \"limit\", 0, 10)\n"
397
+ "if #res > 0 then\n"
398
+ " for i = 0, #res, 1 do\n"
399
+ " redis.call(\"zincrby\", timer_key, 10, res[i])\n"
400
+ " end\n"
401
+ " return res\n"
402
+ "end\n"
403
+ "return 0\n", name) == -1) {
404
+ return ERR_OUT_OF_MEMORY;
405
+ }
406
+
407
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
408
+ redisGetReply(reContext, (void**)&reply);
409
+ if (reply->type == REDIS_REPLY_ERROR) {
410
+ printf("%s\n", reply->str);
411
+ freeReplyObject(reply);
412
+ return ERR_REDIS_ERROR;
413
+ }
414
+
415
+ strncpy(rulesBinding->timersHash, reply->str, 40);
416
+ rulesBinding->timersHash[40] = '\0';
417
+ freeReplyObject(reply);
418
+ free(lua);
419
+ return RULES_OK;
420
+ }
421
+
422
+ static unsigned int loadUpdateActionCommand(ruleset *tree, binding *rulesBinding, char *name) {
423
+ redisContext *reContext = rulesBinding->reContext;
424
+ redisReply *reply;
425
+ char *lua = NULL;
426
+ if (asprintf(&lua,
427
+ "local actions_key = \"%s!a\"\n"
428
+ "local score = tonumber(ARGV[2])\n"
429
+ "local sid = ARGV[1]\n"
430
+ "if redis.call(\"zscore\", actions_key, sid) then\n"
431
+ " redis.call(\"zadd\", actions_key , score, sid)\n"
432
+ "end\n", name) == -1) {
433
+ return ERR_OUT_OF_MEMORY;
434
+ }
435
+
436
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
437
+ redisGetReply(reContext, (void**)&reply);
438
+ if (reply->type == REDIS_REPLY_ERROR) {
439
+ printf("%s\n", reply->str);
440
+ freeReplyObject(reply);
441
+ return ERR_REDIS_ERROR;
442
+ }
443
+
444
+ strncpy(rulesBinding->updateActionHash, reply->str, 40);
445
+ rulesBinding->updateActionHash[40] = '\0';
446
+ freeReplyObject(reply);
447
+ free(lua);
448
+ return RULES_OK;
449
+ }
450
+
451
+ static unsigned int setNames(ruleset *tree, binding *rulesBinding, char *name) {
452
+ int nameLength = strlen(name);
453
+ char *sessionHashset = malloc((nameLength + 3) * sizeof(char));
454
+ if (!sessionHashset) {
455
+ return ERR_OUT_OF_MEMORY;
456
+ }
457
+
458
+ strncpy(sessionHashset, name, nameLength);
459
+ sessionHashset[nameLength] = '!';
460
+ sessionHashset[nameLength + 1] = 's';
461
+ sessionHashset[nameLength + 2] = '\0';
462
+ rulesBinding->sessionHashset = sessionHashset;
463
+
464
+ char *factsHashset = malloc((nameLength + 3) * sizeof(char));
465
+ if (!factsHashset) {
466
+ return ERR_OUT_OF_MEMORY;
467
+ }
468
+
469
+ strncpy(factsHashset, name, nameLength);
470
+ factsHashset[nameLength] = '!';
471
+ factsHashset[nameLength + 1] = 'f';
472
+ factsHashset[nameLength + 2] = '\0';
473
+ rulesBinding->factsHashset = factsHashset;
474
+
475
+ char *eventsHashset = malloc((nameLength + 3) * sizeof(char));
476
+ if (!eventsHashset) {
477
+ return ERR_OUT_OF_MEMORY;
478
+ }
479
+
480
+ strncpy(eventsHashset, name, nameLength);
481
+ eventsHashset[nameLength] = '!';
482
+ eventsHashset[nameLength + 1] = 'e';
483
+ eventsHashset[nameLength + 2] = '\0';
484
+ rulesBinding->eventsHashset = eventsHashset;
485
+
486
+ char *timersSortedset = malloc((nameLength + 3) * sizeof(char));
487
+ if (!timersSortedset) {
488
+ return ERR_OUT_OF_MEMORY;
489
+ }
490
+
491
+ strncpy(timersSortedset, name, nameLength);
492
+ timersSortedset[nameLength] = '!';
493
+ timersSortedset[nameLength + 1] = 't';
494
+ timersSortedset[nameLength + 2] = '\0';
495
+ rulesBinding->timersSortedset = timersSortedset;
496
+ return RULES_OK;
497
+ }
498
+
499
+ static unsigned int loadCommands(ruleset *tree, binding *rulesBinding) {
305
500
  char *name = &tree->stringPool[tree->nameOffset];
306
501
  int nameLength = strlen(name);
502
+ unsigned int result = loadPartitionCommand(tree, rulesBinding, name);
503
+ if (result != RULES_OK) {
504
+ return result;
505
+ }
506
+
507
+ // client queues have no commands to load,
508
+ if (!tree->stringPool) {
509
+ return RULES_OK;
510
+ }
511
+
512
+ result = loadRemoveActionCommand(tree, rulesBinding, name);
513
+ if (result != RULES_OK) {
514
+ return result;
515
+ }
516
+
517
+ result = loadTimerCommand(tree, rulesBinding, name);
518
+ if (result != RULES_OK) {
519
+ return result;
520
+ }
521
+
522
+ result = loadUpdateActionCommand(tree, rulesBinding, name);
523
+ if (result != RULES_OK) {
524
+ return result;
525
+ }
526
+
527
+ result = setNames(tree, rulesBinding, name);
528
+ if (result != RULES_OK) {
529
+ return result;
530
+ }
531
+
532
+ redisContext *reContext = rulesBinding->reContext;
533
+ redisReply *reply;
307
534
  #ifdef _WIN32
308
535
  char *actionKey = (char *)_alloca(sizeof(char)*(nameLength + 3));
309
536
  sprintf_s(actionKey, nameLength + 3, "%s!a", name);
@@ -1444,7 +1671,7 @@ static unsigned int loadCommands(ruleset *tree, binding *rulesBinding) {
1444
1671
  " for i = 1, #frame, 1 do\n"
1445
1672
  " if type(frame[i]) == \"table\" then\n"
1446
1673
  " redis.call(\"hsetnx\", events_hashset, frame[i][\"id\"], cmsgpack.pack(frame[i]))\n"
1447
- " redis.call(\"zadd\", timers_key, max_score, cjson.encode(frame[i]))\n"
1674
+ " redis.call(\"zadd\", timers_key, max_score, \"p:\" .. cjson.encode(frame[i]))\n"
1448
1675
  " end\n"
1449
1676
  " end\n"
1450
1677
  " full_frame = nil\n"
@@ -1661,170 +1888,6 @@ static unsigned int loadCommands(ruleset *tree, binding *rulesBinding) {
1661
1888
  freeReplyObject(reply);
1662
1889
  free(lua);
1663
1890
 
1664
- if (asprintf(&lua,
1665
- "local delete_frame = function(key, action_key)\n"
1666
- " local rule_action_key = redis.call(\"lpop\", key)\n"
1667
- " local raw_count = redis.call(\"lpop\", key)\n"
1668
- " local count = 1\n"
1669
- " if raw_count ~= \"single\" then\n"
1670
- " count = tonumber(raw_count)\n"
1671
- " end\n"
1672
- " if count == 0 then\n"
1673
- " local packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1674
- " while packed_frame ~= \"0\" do\n"
1675
- " packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1676
- " end\n"
1677
- " else\n"
1678
- " for i = 0, count - 1, 1 do\n"
1679
- " local packed_frame = redis.call(\"rpop\", rule_action_key)\n"
1680
- " end\n"
1681
- " end\n"
1682
- " return (redis.call(\"llen\", key) > 0)\n"
1683
- "end\n"
1684
- "local sid = ARGV[1]\n"
1685
- "local max_score = tonumber(ARGV[2])\n"
1686
- "local action_key = \"%s!a\"\n"
1687
- "if delete_frame(action_key .. \"!\" .. sid, action_key) then\n"
1688
- " redis.call(\"zadd\", action_key , max_score, sid)\n"
1689
- "else\n"
1690
- " redis.call(\"zrem\", action_key, sid)\n"
1691
- "end\n", name) == -1) {
1692
- return ERR_OUT_OF_MEMORY;
1693
- }
1694
-
1695
- redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1696
- redisGetReply(reContext, (void**)&reply);
1697
- if (reply->type == REDIS_REPLY_ERROR) {
1698
- printf("%s\n", reply->str);
1699
- freeReplyObject(reply);
1700
- free(lua);
1701
- return ERR_REDIS_ERROR;
1702
- }
1703
-
1704
- strncpy(rulesBinding->removeActionHash, reply->str, 40);
1705
- rulesBinding->removeActionHash[40] = '\0';
1706
- freeReplyObject(reply);
1707
- free(lua);
1708
-
1709
- if (asprintf(&lua,
1710
- "local partition_key = \"%s!p\"\n"
1711
- "local res = redis.call(\"hget\", partition_key, ARGV[1])\n"
1712
- "if (not res) then\n"
1713
- " res = redis.call(\"hincrby\", partition_key, \"index\", 1)\n"
1714
- " res = res %% tonumber(ARGV[2])\n"
1715
- " redis.call(\"hset\", partition_key, ARGV[1], res)\n"
1716
- "end\n"
1717
- "return tonumber(res)\n", name) == -1) {
1718
- return ERR_OUT_OF_MEMORY;
1719
- }
1720
-
1721
- redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1722
- redisGetReply(reContext, (void**)&reply);
1723
- if (reply->type == REDIS_REPLY_ERROR) {
1724
- printf("%s\n", reply->str);
1725
- freeReplyObject(reply);
1726
- return ERR_REDIS_ERROR;
1727
- }
1728
-
1729
- strncpy(rulesBinding->partitionHash, reply->str, 40);
1730
- rulesBinding->partitionHash[40] = '\0';
1731
- freeReplyObject(reply);
1732
- free(lua);
1733
-
1734
- if (asprintf(&lua,
1735
- "local timer_key = \"%s!t\"\n"
1736
- "local timestamp = tonumber(ARGV[1])\n"
1737
- "local res = redis.call(\"zrangebyscore\", timer_key, 0, timestamp, \"limit\", 0, 10)\n"
1738
- "if #res > 0 then\n"
1739
- " for i = 0, #res, 1 do\n"
1740
- " redis.call(\"zincrby\", timer_key, 10, res[i])\n"
1741
- " end\n"
1742
- " return res\n"
1743
- "end\n"
1744
- "return 0\n", name) == -1) {
1745
- return ERR_OUT_OF_MEMORY;
1746
- }
1747
-
1748
- redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1749
- redisGetReply(reContext, (void**)&reply);
1750
- if (reply->type == REDIS_REPLY_ERROR) {
1751
- printf("%s\n", reply->str);
1752
- freeReplyObject(reply);
1753
- return ERR_REDIS_ERROR;
1754
- }
1755
-
1756
- strncpy(rulesBinding->timersHash, reply->str, 40);
1757
- rulesBinding->timersHash[40] = '\0';
1758
- freeReplyObject(reply);
1759
- free(lua);
1760
-
1761
- if (asprintf(&lua,
1762
- "local actions_key = \"%s!a\"\n"
1763
- "local score = tonumber(ARGV[2])\n"
1764
- "local sid = ARGV[1]\n"
1765
- "if redis.call(\"zscore\", actions_key, sid) then\n"
1766
- " redis.call(\"zadd\", actions_key , score, sid)\n"
1767
- "end\n", name) == -1) {
1768
- return ERR_OUT_OF_MEMORY;
1769
- }
1770
-
1771
- redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1772
- redisGetReply(reContext, (void**)&reply);
1773
- if (reply->type == REDIS_REPLY_ERROR) {
1774
- printf("%s\n", reply->str);
1775
- freeReplyObject(reply);
1776
- return ERR_REDIS_ERROR;
1777
- }
1778
-
1779
- strncpy(rulesBinding->updateActionHash, reply->str, 40);
1780
- rulesBinding->updateActionHash[40] = '\0';
1781
- freeReplyObject(reply);
1782
- free(lua);
1783
-
1784
- char *sessionHashset = malloc((nameLength + 3) * sizeof(char));
1785
- if (!sessionHashset) {
1786
- return ERR_OUT_OF_MEMORY;
1787
- }
1788
-
1789
- strncpy(sessionHashset, name, nameLength);
1790
- sessionHashset[nameLength] = '!';
1791
- sessionHashset[nameLength + 1] = 's';
1792
- sessionHashset[nameLength + 2] = '\0';
1793
- rulesBinding->sessionHashset = sessionHashset;
1794
-
1795
- char *factsHashset = malloc((nameLength + 3) * sizeof(char));
1796
- if (!factsHashset) {
1797
- return ERR_OUT_OF_MEMORY;
1798
- }
1799
-
1800
- strncpy(factsHashset, name, nameLength);
1801
- factsHashset[nameLength] = '!';
1802
- factsHashset[nameLength + 1] = 'f';
1803
- factsHashset[nameLength + 2] = '\0';
1804
- rulesBinding->factsHashset = factsHashset;
1805
-
1806
- char *eventsHashset = malloc((nameLength + 3) * sizeof(char));
1807
- if (!eventsHashset) {
1808
- return ERR_OUT_OF_MEMORY;
1809
- }
1810
-
1811
- strncpy(eventsHashset, name, nameLength);
1812
- eventsHashset[nameLength] = '!';
1813
- eventsHashset[nameLength + 1] = 'e';
1814
- eventsHashset[nameLength + 2] = '\0';
1815
- rulesBinding->eventsHashset = eventsHashset;
1816
-
1817
- char *timersSortedset = malloc((nameLength + 3) * sizeof(char));
1818
- if (!timersSortedset) {
1819
- return ERR_OUT_OF_MEMORY;
1820
- }
1821
-
1822
- strncpy(timersSortedset, name, nameLength);
1823
- timersSortedset[nameLength] = '!';
1824
- timersSortedset[nameLength + 1] = 't';
1825
- timersSortedset[nameLength + 2] = '\0';
1826
- rulesBinding->timersSortedset = timersSortedset;
1827
-
1828
1891
  return RULES_OK;
1829
1892
  }
1830
1893
 
@@ -2302,12 +2365,15 @@ static unsigned int tryGetReply(redisContext *reContext,
2302
2365
 
2303
2366
  if (redisGetReply(reContext, (void**)reply) != REDIS_OK) {
2304
2367
  printf("getReply err %d, %d, %s\n", reContext->err, errno, reContext->errstr);
2368
+ #ifndef _WIN32
2305
2369
  if (redisReconnect(reContext) != REDIS_OK) {
2306
2370
  printf("reconnect err %d, %d, %s\n", reContext->err, errno, reContext->errstr);
2307
2371
  return ERR_REDIS_ERROR;
2308
2372
  }
2309
-
2310
2373
  return ERR_TRY_AGAIN;
2374
+ #else
2375
+ return ERR_REDIS_ERROR;
2376
+ #endif
2311
2377
  }
2312
2378
 
2313
2379
  return RULES_OK;
@@ -2514,7 +2580,7 @@ unsigned int registerTimer(void *rulesBinding, unsigned int duration, char *time
2514
2580
  time_t currentTime = time(NULL);
2515
2581
 
2516
2582
  int result = redisAppendCommand(reContext,
2517
- "zadd %s %ld %s",
2583
+ "zadd %s %ld p:%s",
2518
2584
  currentBinding->timersSortedset,
2519
2585
  currentTime + duration,
2520
2586
  timer);
@@ -2543,7 +2609,7 @@ unsigned int removeTimer(void *rulesBinding, char *timer) {
2543
2609
  redisContext *reContext = currentBinding->reContext;
2544
2610
 
2545
2611
  int result = redisAppendCommand(reContext,
2546
- "zrem %s %s",
2612
+ "zrem %s p:%s",
2547
2613
  currentBinding->timersSortedset,
2548
2614
  timer);
2549
2615
  if (result != REDIS_OK) {
@@ -2566,16 +2632,37 @@ unsigned int removeTimer(void *rulesBinding, char *timer) {
2566
2632
  return RULES_OK;
2567
2633
  }
2568
2634
 
2569
- unsigned int registerMessage(void *rulesBinding, char *destination, char *message) {
2635
+ unsigned int registerMessage(void *rulesBinding, unsigned int queueAction, char *destination, char *message) {
2570
2636
  binding *currentBinding = (binding*)rulesBinding;
2571
2637
  redisContext *reContext = currentBinding->reContext;
2572
2638
  time_t currentTime = time(NULL);
2573
2639
 
2574
- int result = redisAppendCommand(reContext,
2575
- "zadd %s!t %ld %s",
2576
- destination,
2577
- currentTime,
2578
- message);
2640
+ int result = REDIS_OK;
2641
+
2642
+ switch (queueAction) {
2643
+ case QUEUE_ASSERT_FACT:
2644
+ result = redisAppendCommand(reContext,
2645
+ "zadd %s!t %ld a:%s",
2646
+ destination,
2647
+ currentTime,
2648
+ message);
2649
+ break;
2650
+ case QUEUE_ASSERT_EVENT:
2651
+ result = redisAppendCommand(reContext,
2652
+ "zadd %s!t %ld p:%s",
2653
+ destination,
2654
+ currentTime,
2655
+ message);
2656
+ break;
2657
+ case QUEUE_RETRACT_FACT:
2658
+ result = redisAppendCommand(reContext,
2659
+ "zadd %s!t %ld r:%s",
2660
+ destination,
2661
+ currentTime,
2662
+ message);
2663
+ break;
2664
+ }
2665
+
2579
2666
  if (result != REDIS_OK) {
2580
2667
  return ERR_REDIS_ERROR;
2581
2668
  }
data/src/rules/net.h CHANGED
@@ -137,6 +137,7 @@ unsigned int removeTimer(void *rulesBinding,
137
137
  char *timer);
138
138
 
139
139
  unsigned int registerMessage(void *rulesBinding,
140
+ unsigned int queueAction,
140
141
  char *destination,
141
142
  char *message);
142
143
 
data/src/rules/rete.c CHANGED
@@ -1722,5 +1722,62 @@ unsigned int deleteRuleset(void *handle) {
1722
1722
  return RULES_OK;
1723
1723
  }
1724
1724
 
1725
+ unsigned int createClient(void **handle, char *name, unsigned int stateCaheSize) {
1726
+ ruleset *tree = malloc(sizeof(ruleset));
1727
+ if (!tree) {
1728
+ return ERR_OUT_OF_MEMORY;
1729
+ }
1730
+
1731
+ tree->stringPool = NULL;
1732
+ tree->stringPoolLength = 0;
1733
+ tree->nodePool = NULL;
1734
+ tree->nodeOffset = 0;
1735
+ tree->nextPool = NULL;
1736
+ tree->nextOffset = 0;
1737
+ tree->expressionPool = NULL;
1738
+ tree->expressionOffset = 0;
1739
+ tree->idiomPool = NULL;
1740
+ tree->idiomOffset = 0;
1741
+ tree->joinPool = NULL;
1742
+ tree->joinOffset = 0;
1743
+ tree->actionCount = 0;
1744
+ tree->bindingsList = NULL;
1745
+ tree->stateLength = 0;
1746
+ tree->state = calloc(stateCaheSize, sizeof(stateEntry));
1747
+ tree->maxStateLength = stateCaheSize;
1748
+ tree->stateBucketsLength = stateCaheSize / 4;
1749
+ tree->stateBuckets = malloc(tree->stateBucketsLength * sizeof(unsigned int));
1750
+ memset(tree->stateBuckets, 0xFF, tree->stateBucketsLength * sizeof(unsigned int));
1751
+ tree->lruStateOffset = UNDEFINED_HASH_OFFSET;
1752
+ tree->mruStateOffset = UNDEFINED_HASH_OFFSET;
1753
+
1754
+ unsigned int result = storeString(tree, name, &tree->nameOffset, strlen(name));
1755
+ if (result != RULES_OK) {
1756
+ return result;
1757
+ }
1758
+
1759
+ *handle = tree;
1760
+ return RULES_OK;
1761
+ }
1762
+
1763
+ unsigned int deleteClient(void *handle) {
1764
+ ruleset *tree = (ruleset*)(handle);
1765
+ deleteBindingsList(tree);
1766
+ free(tree->stringPool);
1767
+ free(tree->stateBuckets);
1768
+ for (unsigned int i = 0; i < tree->stateLength; ++i) {
1769
+ stateEntry *entry = &tree->state[i];
1770
+ if (entry->state) {
1771
+ free(entry->state);
1772
+ }
1773
+
1774
+ if (entry->sid) {
1775
+ free(entry->sid);
1776
+ }
1777
+ }
1778
+
1779
+ free(tree);
1780
+ return RULES_OK;
1781
+ }
1725
1782
 
1726
1783
 
data/src/rules/rules.h CHANGED
@@ -43,6 +43,10 @@
43
43
  #define BETA_LIST_LENGTH 16
44
44
  #define HASH_MASK 0x1F
45
45
 
46
+ #define QUEUE_ASSERT_FACT 1
47
+ #define QUEUE_ASSERT_EVENT 2
48
+ #define QUEUE_RETRACT_FACT 3
49
+
46
50
  #ifdef __cplusplus
47
51
  extern "C" {
48
52
  #endif
@@ -54,6 +58,12 @@ unsigned int createRuleset(void **handle,
54
58
 
55
59
  unsigned int deleteRuleset(void *handle);
56
60
 
61
+ unsigned int createClient(void **handle,
62
+ char *name,
63
+ unsigned int stateCaheSize);
64
+
65
+ unsigned int deleteClient(void *handle);
66
+
57
67
  unsigned int bindRuleset(void *handle,
58
68
  char *host,
59
69
  unsigned int port,
@@ -161,7 +171,8 @@ unsigned int cancelTimer(void *handle,
161
171
  char *sid,
162
172
  char *timer);
163
173
 
164
- unsigned int queueMessage(void *handle,
174
+ unsigned int queueMessage(void *handle,
175
+ unsigned int queueAction,
165
176
  char *sid,
166
177
  char *destination,
167
178
  char *message);
data/src/rulesrb/rules.c CHANGED
@@ -35,6 +35,38 @@ static VALUE rbDeleteRuleset(VALUE self, VALUE handle) {
35
35
  return Qnil;
36
36
  }
37
37
 
38
+ static VALUE rbCreateClient(VALUE self, VALUE name, VALUE stateCacheSize) {
39
+ Check_Type(name, T_STRING);
40
+ Check_Type(stateCacheSize, T_FIXNUM);
41
+
42
+ void *output = NULL;
43
+ unsigned int result = createClient(&output, RSTRING_PTR(name), FIX2INT(stateCacheSize));
44
+ if (result != RULES_OK) {
45
+ if (result == ERR_OUT_OF_MEMORY) {
46
+ rb_raise(rb_eNoMemError, "Out of memory");
47
+ } else {
48
+ rb_raise(rb_eException, "Could not create client, error code: %d", result);
49
+ }
50
+ }
51
+
52
+ return INT2FIX(output);
53
+ }
54
+
55
+ static VALUE rbDeleteClient(VALUE self, VALUE handle) {
56
+ Check_Type(handle, T_FIXNUM);
57
+
58
+ unsigned int result = deleteClient((void *)FIX2LONG(handle));
59
+ if (result != RULES_OK) {
60
+ if (result == ERR_OUT_OF_MEMORY) {
61
+ rb_raise(rb_eNoMemError, "Out of memory");
62
+ } else {
63
+ rb_raise(rb_eException, "Could not delete client, error code: %d", result);
64
+ }
65
+ }
66
+
67
+ return Qnil;
68
+ }
69
+
38
70
  static VALUE rbBindRuleset(VALUE self, VALUE handle, VALUE host, VALUE port, VALUE password) {
39
71
  Check_Type(handle, T_FIXNUM);
40
72
  Check_Type(host, T_STRING);
@@ -124,18 +156,18 @@ static VALUE rbAssertEvent(VALUE self, VALUE handle, VALUE event) {
124
156
  return Qnil;
125
157
  }
126
158
 
127
- static VALUE rbQueueEvent(VALUE self, VALUE handle, VALUE sid, VALUE destination, VALUE event) {
159
+ static VALUE rbQueueAssertEvent(VALUE self, VALUE handle, VALUE sid, VALUE destination, VALUE event) {
128
160
  Check_Type(handle, T_FIXNUM);
129
161
  Check_Type(sid, T_STRING);
130
162
  Check_Type(destination, T_STRING);
131
163
  Check_Type(event, T_STRING);
132
164
 
133
- unsigned int result = queueMessage((void *)FIX2LONG(handle), RSTRING_PTR(sid), RSTRING_PTR(destination), RSTRING_PTR(event));
165
+ unsigned int result = queueMessage((void *)FIX2LONG(handle), QUEUE_ASSERT_EVENT, RSTRING_PTR(sid), RSTRING_PTR(destination), RSTRING_PTR(event));
134
166
  if (result != RULES_OK) {
135
167
  if (result == ERR_OUT_OF_MEMORY) {
136
168
  rb_raise(rb_eNoMemError, "Out of memory");
137
169
  } else {
138
- rb_raise(rb_eException, "Could not queue event, error code: %d", result);
170
+ rb_raise(rb_eException, "Could not queue assert event, error code: %d", result);
139
171
  }
140
172
  }
141
173
 
@@ -272,6 +304,24 @@ static VALUE rbAssertFact(VALUE self, VALUE handle, VALUE fact) {
272
304
  return Qnil;
273
305
  }
274
306
 
307
+ static VALUE rbQueueAssertFact(VALUE self, VALUE handle, VALUE sid, VALUE destination, VALUE event) {
308
+ Check_Type(handle, T_FIXNUM);
309
+ Check_Type(sid, T_STRING);
310
+ Check_Type(destination, T_STRING);
311
+ Check_Type(event, T_STRING);
312
+
313
+ unsigned int result = queueMessage((void *)FIX2LONG(handle), QUEUE_ASSERT_FACT, RSTRING_PTR(sid), RSTRING_PTR(destination), RSTRING_PTR(event));
314
+ if (result != RULES_OK) {
315
+ if (result == ERR_OUT_OF_MEMORY) {
316
+ rb_raise(rb_eNoMemError, "Out of memory");
317
+ } else {
318
+ rb_raise(rb_eException, "Could not queue assert fact, error code: %d", result);
319
+ }
320
+ }
321
+
322
+ return Qnil;
323
+ }
324
+
275
325
  static VALUE rbStartAssertFacts(VALUE self, VALUE handle, VALUE facts) {
276
326
  Check_Type(handle, T_FIXNUM);
277
327
  Check_Type(facts, T_STRING);
@@ -382,6 +432,24 @@ static VALUE rbRetractFact(VALUE self, VALUE handle, VALUE fact) {
382
432
  return Qnil;
383
433
  }
384
434
 
435
+ static VALUE rbQueueRetractFact(VALUE self, VALUE handle, VALUE sid, VALUE destination, VALUE event) {
436
+ Check_Type(handle, T_FIXNUM);
437
+ Check_Type(sid, T_STRING);
438
+ Check_Type(destination, T_STRING);
439
+ Check_Type(event, T_STRING);
440
+
441
+ unsigned int result = queueMessage((void *)FIX2LONG(handle), QUEUE_RETRACT_FACT, RSTRING_PTR(sid), RSTRING_PTR(destination), RSTRING_PTR(event));
442
+ if (result != RULES_OK) {
443
+ if (result == ERR_OUT_OF_MEMORY) {
444
+ rb_raise(rb_eNoMemError, "Out of memory");
445
+ } else {
446
+ rb_raise(rb_eException, "Could not queue retract fact, error code: %d", result);
447
+ }
448
+ }
449
+
450
+ return Qnil;
451
+ }
452
+
385
453
  static VALUE rbStartRetractFacts(VALUE self, VALUE handle, VALUE facts) {
386
454
  Check_Type(handle, T_FIXNUM);
387
455
  Check_Type(facts, T_STRING);
@@ -667,20 +735,24 @@ void Init_rules() {
667
735
  rulesModule = rb_define_module("Rules");
668
736
  rb_define_singleton_method(rulesModule, "create_ruleset", rbCreateRuleset, 3);
669
737
  rb_define_singleton_method(rulesModule, "delete_ruleset", rbDeleteRuleset, 1);
738
+ rb_define_singleton_method(rulesModule, "create_client", rbCreateClient, 2);
739
+ rb_define_singleton_method(rulesModule, "delete_client", rbDeleteClient, 1);
670
740
  rb_define_singleton_method(rulesModule, "bind_ruleset", rbBindRuleset, 4);
671
741
  rb_define_singleton_method(rulesModule, "complete", rbComplete, 2);
672
742
  rb_define_singleton_method(rulesModule, "assert_event", rbAssertEvent, 2);
673
- rb_define_singleton_method(rulesModule, "queue_event", rbQueueEvent, 4);
743
+ rb_define_singleton_method(rulesModule, "queue_assert_event", rbQueueAssertEvent, 4);
674
744
  rb_define_singleton_method(rulesModule, "start_assert_event", rbStartAssertEvent, 2);
675
745
  rb_define_singleton_method(rulesModule, "assert_events", rbAssertEvents, 2);
676
746
  rb_define_singleton_method(rulesModule, "start_assert_events", rbStartAssertEvents, 2);
677
747
  rb_define_singleton_method(rulesModule, "retract_event", rbRetractEvent, 2);
678
748
  rb_define_singleton_method(rulesModule, "start_assert_fact", rbStartAssertFact, 2);
679
749
  rb_define_singleton_method(rulesModule, "assert_fact", rbAssertFact, 2);
750
+ rb_define_singleton_method(rulesModule, "queue_assert_fact", rbQueueAssertFact, 4);
680
751
  rb_define_singleton_method(rulesModule, "start_assert_facts", rbStartAssertFacts, 2);
681
752
  rb_define_singleton_method(rulesModule, "assert_facts", rbAssertFacts, 2);
682
753
  rb_define_singleton_method(rulesModule, "start_retract_fact", rbStartRetractFact, 2);
683
754
  rb_define_singleton_method(rulesModule, "retract_fact", rbRetractFact, 2);
755
+ rb_define_singleton_method(rulesModule, "queue_retract_fact", rbQueueRetractFact, 4);
684
756
  rb_define_singleton_method(rulesModule, "start_retract_facts", rbStartRetractFacts, 2);
685
757
  rb_define_singleton_method(rulesModule, "retract_facts", rbRetractFacts, 2);
686
758
  rb_define_singleton_method(rulesModule, "assert_state", rbAssertState, 2);
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: durable_rules
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.34.01
4
+ version: 0.34.02
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jesus Ruiz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-04 00:00:00.000000000 Z
11
+ date: 2016-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake