durable_rules 0.34.01 → 0.34.02

Sign up to get free protection for your applications and to get access to all the features.
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