Searching 85 files for "redisrank" /Users/felipeclopes/projects/redisrank/coverage/.resultset.json: 2 "RSpec": { 3 "coverage": { 4: "/Users/felipeclopes/projects/redisrank/lib/redisrank.rb": [ 5 null, 6 1, . 110 null 111 ], 112: "/Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/options.rb": [ 113 1, 114 1, ... 153 null 154 ], 155: "/Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/synchronize.rb": [ 156 1, 157 null, ... 207 null 208 ], 209: "/Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/database.rb": [ 210 1, 211 1, ... 220 null 221 ], 222: "/Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/date_helper.rb": [ 223 1, 224 1, ... 230 null 231 ], 232: "/Users/felipeclopes/projects/redisrank/lib/redisrank/connection.rb": [ 233 1, 234 null, ... 321 null 322 ], 323: "/Users/felipeclopes/projects/redisrank/lib/redisrank/buffer.rb": [ 324 1, 325 null, ... 433 null 434 ], 435: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/hash.rb": [ 436 1, 437 null, ... 455 null 456 ], 457: "/Users/felipeclopes/projects/redisrank/lib/redisrank/collection.rb": [ 458 1, 459 1, ... 477 null 478 ], 479: "/Users/felipeclopes/projects/redisrank/lib/redisrank/date.rb": [ 480 1, 481 1, ... 567 null 568 ], 569: "/Users/felipeclopes/projects/redisrank/lib/redisrank/event.rb": [ 570 1, 571 1, ... 667 null 668 ], 669: "/Users/felipeclopes/projects/redisrank/lib/redisrank/finder.rb": [ 670 1, 671 null, ... 914 null 915 ], 916: "/Users/felipeclopes/projects/redisrank/lib/redisrank/finder/date_set.rb": [ 917 1, 918 1, ... 1015 null 1016 ], 1017: "/Users/felipeclopes/projects/redisrank/lib/redisrank/key.rb": [ 1018 1, 1019 1, .... 1101 null 1102 ], 1103: "/Users/felipeclopes/projects/redisrank/lib/redisrank/label.rb": [ 1104 1, 1105 1, .... 1172 null 1173 ], 1174: "/Users/felipeclopes/projects/redisrank/lib/redisrank/model.rb": [ 1175 1, 1176 1, .... 1251 null 1252 ], 1253: "/Users/felipeclopes/projects/redisrank/lib/redisrank/result.rb": [ 1254 1, 1255 null, .... 1271 null 1272 ], 1273: "/Users/felipeclopes/projects/redisrank/lib/redisrank/scope.rb": [ 1274 1, 1275 1, .... 1291 null 1292 ], 1293: "/Users/felipeclopes/projects/redisrank/lib/redisrank/summary.rb": [ 1294 1, 1295 1, .... 1383 null 1384 ], 1385: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext.rb": [ 1386 1, 1387 1, .... 1390 1 1391 ], 1392: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/bignum.rb": [ 1393 1, 1394 1, .... 1400 null 1401 ], 1402: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/date.rb": [ 1403 1, 1404 1, .... 1410 null 1411 ], 1412: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/fixnum.rb": [ 1413 1, 1414 1, .... 1420 null 1421 ], 1422: "/Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/time.rb": [ 1423 1, 1424 1, /Users/felipeclopes/projects/redisrank/coverage/index.html: 52 53 54: lib/redisrank.rb 55 90.91 % 56 106 .. 62 63 64: lib/redisrank/buffer.rb 65 100.0 % 66 110 .. 72 73 74: lib/redisrank/collection.rb 75 100.0 % 76 20 .. 82 83 84: lib/redisrank/connection.rb 85 86.67 % 86 89 .. 92 93 94: lib/redisrank/core_ext.rb 95 100.0 % 96 5 .. 102 103 104: lib/redisrank/core_ext/bignum.rb 105 75.0 % 106 8 ... 112 113 114: lib/redisrank/core_ext/date.rb 115 100.0 % 116 8 ... 122 123 124: lib/redisrank/core_ext/fixnum.rb 125 100.0 % 126 8 ... 132 133 134: lib/redisrank/core_ext/hash.rb 135 100.0 % 136 20 ... 142 143 144: lib/redisrank/core_ext/time.rb 145 100.0 % 146 3 ... 152 153 154: lib/redisrank/date.rb 155 96.49 % 156 88 ... 162 163 164: lib/redisrank/event.rb 165 96.08 % 166 98 ... 172 173 174: lib/redisrank/finder.rb 175 98.68 % 176 245 ... 182 183 184: lib/redisrank/finder/date_set.rb 185 100.0 % 186 99 ... 192 193 194: lib/redisrank/key.rb 195 100.0 % 196 84 ... 202 203 204: lib/redisrank/label.rb 205 100.0 % 206 69 ... 212 213 214: lib/redisrank/mixins/database.rb 215 100.0 % 216 11 ... 222 223 224: lib/redisrank/mixins/date_helper.rb 225 100.0 % 226 8 ... 232 233 234: lib/redisrank/mixins/options.rb 235 95.24 % 236 41 ... 242 243 244: lib/redisrank/mixins/synchronize.rb 245 100.0 % 246 52 ... 252 253 254: lib/redisrank/model.rb 255 100.0 % 256 77 ... 262 263 264: lib/redisrank/result.rb 265 100.0 % 266 18 ... 272 273 274: lib/redisrank/scope.rb 275 100.0 % 276 18 ... 282 283 284: lib/redisrank/summary.rb 285 100.0 % 286 90 ... 309
310
311:

lib/redisrank.rb

312

90.91 % covered

313
... 432 1 433 434: require 'redisrank/mixins/options' 435 436 ... 438 1 439 440: require 'redisrank/mixins/synchronize' 441 442 ... 444 1 445 446: require 'redisrank/mixins/database' 447 448 ... 450 1 451 452: require 'redisrank/mixins/date_helper' 453 454 ... 462 1 463 464: require 'redisrank/connection' 465 466 ... 468 1 469 470: require 'redisrank/buffer' 471 472 ... 474 1 475 476: require 'redisrank/collection' 477 478 ... 480 1 481 482: require 'redisrank/date' 483 484 ... 486 1 487 488: require 'redisrank/event' 489 490 ... 492 1 493 494: require 'redisrank/finder' 495 496 ... 498 1 499 500: require 'redisrank/key' 501 502 ... 504 1 505 506: require 'redisrank/label' 507 508 ... 510 1 511 512: require 'redisrank/model' 513 514 ... 516 1 517 518: require 'redisrank/result' 519 520 ... 522 1 523 524: require 'redisrank/scope' 525 526 ... 528 1 529 530: require 'redisrank/summary' 531 532 ... 534 1 535 536: require 'redisrank/version' 537 538 ... 546 1 547 548: require 'redisrank/core_ext' 549 550 ... 564 1 565 566: module Redistat 567 568 ... 588 1 589 590: KEY_LABELS = "Redistat.labels:" # used for reverse label hash lookup 591 592 ... 858 859 860: puts "WARNING: Redistat.flush is deprecated. Use Redistat.redis.flushdb instead." 861 862 ... 948 1 949 950: Redistat.buffer.flush(true) 951 952 ... 963
964
965:

lib/redisrank/buffer.rb

966

100.0 % covered

967
... 978 1 979 980: require 'redisrank/core_ext/hash' 981 982 ... 990 1 991 992: module Redistat 993 994 ... 1641
1642
1643:

lib/redisrank/collection.rb

1644

100.0 % covered

1645
.... 1656 1 1657 1658: module Redistat 1659 1660 .... 1779
1780
1781:

lib/redisrank/connection.rb

1782

86.67 % covered

1783
.... 1806 1 1807 1808: module Redistat 1809 1810 .... 2331
2332
2333:

lib/redisrank/core_ext.rb

2334

100.0 % covered

2335
.... 2346 1 2347 2348: require 'redisrank/core_ext/bignum' 2349 2350 .... 2352 1 2353 2354: require 'redisrank/core_ext/date' 2355 2356 .... 2358 1 2359 2360: require 'redisrank/core_ext/fixnum' 2361 2362 .... 2364 1 2365 2366: require 'redisrank/core_ext/hash' 2367 2368 .... 2370 1 2371 2372: require 'redisrank/core_ext/time' 2373 2374 .... 2379
2380
2381:

lib/redisrank/core_ext/bignum.rb

2382

75.0 % covered

2383
.... 2400 1 2401 2402: include Redistat::DateHelper 2403 2404 .... 2445
2446
2447:

lib/redisrank/core_ext/date.rb

2448

100.0 % covered

2449
.... 2466 1 2467 2468: include Redistat::DateHelper 2469 2470 .... 2511
2512
2513:

lib/redisrank/core_ext/fixnum.rb

2514

100.0 % covered

2515
.... 2532 1 2533 2534: include Redistat::DateHelper 2535 2536 .... 2577
2578
2579:

lib/redisrank/core_ext/hash.rb

2580

100.0 % covered

2581
.... 2715
2716
2717:

lib/redisrank/core_ext/time.rb

2718

100.0 % covered

2719
.... 2736 1 2737 2738: include Redistat::DateHelper 2739 2740 .... 2751
2752
2753:

lib/redisrank/date.rb

2754

96.49 % covered

2755
.... 2766 1 2767 2768: module Redistat 2769 2770 .... 3297
3298
3299:

lib/redisrank/event.rb

3300

96.08 % covered

3301
.... 3312 1 3313 3314: module Redistat 3315 3316 .... 3903
3904
3905:

lib/redisrank/finder.rb

3906

98.68 % covered

3907
.... 3918 1 3919 3920: require 'redisrank/finder/date_set' 3921 3922 .... 3930 1 3931 3932: module Redistat 3933 3934 .... 5391
5392
5393:

lib/redisrank/finder/date_set.rb

5394

100.0 % covered

5395
.... 5406 1 5407 5408: module Redistat 5409 5410 .... 6003
6004
6005:

lib/redisrank/key.rb

6006

100.0 % covered

6007
.... 6018 1 6019 6020: module Redistat 6021 6022 .... 6162 313 6163 6164: @date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date 6165 6166 .... 6240 312 6241 6242: @scope = (input.instance_of?(Redistat::Scope)) ? input : Scope.new(input) 6243 6244 .... 6270 310 6271 6272: @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options) 6273 6274 .... 6366 19 6367 6368: self.class.new(self.scope, child_label.join(Redistat.group_separator), self.date, @options) 6369 6370 .... 6525
6526
6527:

lib/redisrank/label.rb

6528

100.0 % covered

6529
.... 6540 1 6541 6542: module Redistat 6543 6544 .... 6630 28 6631 6632: self.new(args.reject {|i| i.blank? }.join(Redistat.group_separator)) 6633 6634 .... 6840 109 6841 6842: self.to_s.split(Redistat.group_separator).last 6843 6844 .... 6882 266 6883 6884: self.to_s.split(Redistat.group_separator).each do |part| 6885 6886 .... 6894 324 6895 6896: group = ((parent.blank?) ? "" : "#{parent}#{Redistat.group_separator}") + part 6897 6898 .... 6957
6958
6959:

lib/redisrank/mixins/database.rb

6960

100.0 % covered

6961
.... 6972 1 6973 6974: module Redistat 6975 6976 .... 7014 1667 7015 7016: Redistat.connection(ref) 7017 7018 .... 7041
7042
7043:

lib/redisrank/mixins/date_helper.rb

7044

100.0 % covered

7045
.... 7056 1 7057 7058: module Redistat 7059 7060 .... 7068 1 7069 7070: def to_redisrank(depth = nil) 7071 7072 .... 7074 544 7075 7076: Redistat::Date.new(self, depth) 7077 7078 .... 7086 1 7087 7088: alias :to_rs :to_redisrank 7089 7090 .... 7107
7108
7109:

lib/redisrank/mixins/options.rb

7110

95.24 % covered

7111
.... 7122 1 7123 7124: module Redistat 7125 7126 .... 7371
7372
7373:

lib/redisrank/mixins/synchronize.rb

7374

100.0 % covered

7375
.... 7398 1 7399 7400: module Redistat 7401 7402 .... 7701
7702
7703:

lib/redisrank/model.rb

7704

100.0 % covered

7705
.... 7716 1 7717 7718: module Redistat 7719 7720 .... 8181
8182
8183:

lib/redisrank/result.rb

8184

100.0 % covered

8185
.... 8208 1 8209 8210: module Redistat 8211 8212 .... 8307
8308
8309:

lib/redisrank/scope.rb

8310

100.0 % covered

8311
.... 8322 1 8323 8324: module Redistat 8325 8326 .... 8433
8434
8435:

lib/redisrank/summary.rb

8436

100.0 % covered

8437
.... 8448 1 8449 8450: module Redistat 8451 8452 .... 8544 105 8545 8546: Redistat.buffer 8547 8548 .... 8862 151 8863 8864: parts = key.to_s.split(Redistat.group_separator) 8865 8866 .... 8898 12 8899 8900: sum_key = sum_parts.join(Redistat.group_separator) 8901 8902 /Users/felipeclopes/projects/redisrank/Gemfile: 1 source 'http://rubygems.org/' 2 3: # Specify your gem's dependencies in redisrank.gemspec 4 gemspec 5 /Users/felipeclopes/projects/redisrank/Gemfile.lock: 2 remote: . 3 specs: 4: redisrank (0.5.0) 5 activesupport (>= 2.3.6) 6 json (>= 1.4.0) . 55 DEPENDENCIES 56 rake (>= 0.8.7) 57: redisrank! 58 rspec (>= 2.1.0) 59 simplecov (>= 0.6.1) /Users/felipeclopes/projects/redisrank/lib/redisrank.rb: 17 require 'json' 18 19: require 'redisrank/mixins/options' 20: require 'redisrank/mixins/synchronize' 21: require 'redisrank/mixins/database' 22: require 'redisrank/mixins/date_helper' 23 24: require 'redisrank/connection' 25: require 'redisrank/buffer' 26: require 'redisrank/collection' 27: require 'redisrank/date' 28: require 'redisrank/event' 29: require 'redisrank/finder' 30: require 'redisrank/key' 31: require 'redisrank/label' 32: require 'redisrank/model' 33: require 'redisrank/result' 34: require 'redisrank/scope' 35: require 'redisrank/summary' 36: require 'redisrank/version' 37 38: require 'redisrank/core_ext' 39 40 41: module Redistat 42 43 KEY_NEXT_ID = ".next_id" 44 KEY_EVENT = ".event:" 45: KEY_LABELS = "Redistat.labels:" # used for reverse label hash lookup 46 KEY_EVENT_IDS = ".event_ids" 47 LABEL_INDEX = ".label_index:" .. 88 89 def flush 90: puts "WARNING: Redistat.flush is deprecated. Use Redistat.redis.flushdb instead." 91 connection.flushdb 92 end .. 103 # ensure buffer is flushed on program exit 104 Kernel.at_exit do 105: Redistat.buffer.flush(true) 106 end 107 /Users/felipeclopes/projects/redisrank/lib/redisrank/buffer.rb: 1: require 'redisrank/core_ext/hash' 2 3: module Redistat 4 class Buffer 5 include Synchronize /Users/felipeclopes/projects/redisrank/lib/redisrank/collection.rb: 1: module Redistat 2 class Collection < ::Array 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/connection.rb: 1 require 'monitor' 2 3: module Redistat 4 module Connection 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext.rb: 1: require 'redisrank/core_ext/bignum' 2: require 'redisrank/core_ext/date' 3: require 'redisrank/core_ext/fixnum' 4: require 'redisrank/core_ext/hash' 5: require 'redisrank/core_ext/time' 6 /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/bignum.rb: 1 class Bignum 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/date.rb: 1 class Date 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/fixnum.rb: 1 class Fixnum 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/time.rb: 1 class Time 2: include Redistat::DateHelper 3 end 4 /Users/felipeclopes/projects/redisrank/lib/redisrank/date.rb: 1: module Redistat 2 class Date 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/event.rb: 1: module Redistat 2 class Event 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/finder.rb: 1: require 'redisrank/finder/date_set' 2 3: module Redistat 4 class Finder 5 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/finder/date_set.rb: 1: module Redistat 2 class Finder 3 class DateSet < Array /Users/felipeclopes/projects/redisrank/lib/redisrank/key.rb: 1: module Redistat 2 class Key 3 include Database . 23 24 def date=(input) 25: @date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date 26 end 27 attr_reader :date .. 36 37 def scope=(input) 38: @scope = (input.instance_of?(Redistat::Scope)) ? input : Scope.new(input) 39 end 40 attr_reader :scope 41 42 def label=(input) 43: @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options) 44 end 45 attr_reader :label .. 57 members.map { |member| 58 child_label = [@label, member].reject { |i| i.nil? } 59: self.class.new(self.scope, child_label.join(Redistat.group_separator), self.date, @options) 60 } 61 end /Users/felipeclopes/projects/redisrank/lib/redisrank/label.rb: 1: module Redistat 2 class Label 3 include Database . 14 def self.join(*args) 15 args = args.map {|i| i.to_s} 16: self.new(args.reject {|i| i.blank? }.join(Redistat.group_separator)) 17 end 18 .. 49 50 def me 51: self.to_s.split(Redistat.group_separator).last 52 end 53 .. 56 @groups = [] 57 parent = "" 58: self.to_s.split(Redistat.group_separator).each do |part| 59 if !part.blank? 60: group = ((parent.blank?) ? "" : "#{parent}#{Redistat.group_separator}") + part 61 @groups << Label.new(group) 62 parent = group /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/database.rb: 1: module Redistat 2 module Database 3 def self.included(base) . 6 def db(ref = nil) 7 ref ||= @options[:connection_ref] if !@options.nil? 8: Redistat.connection(ref) 9 end 10 end /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/date_helper.rb: 1: module Redistat 2 module DateHelper 3: def to_redisrank(depth = nil) 4: Redistat::Date.new(self, depth) 5 end 6: alias :to_rs :to_redisrank 7 end 8 end /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/options.rb: 1: module Redistat 2 module Options 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/synchronize.rb: 1 require 'monitor' 2 3: module Redistat 4 module Synchronize 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/model.rb: 1: module Redistat 2 module Model 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/result.rb: 1 require 'active_support/core_ext/hash/indifferent_access' 2 3: module Redistat 4 class Result < HashWithIndifferentAccess 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/scope.rb: 1: module Redistat 2 class Scope 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/summary.rb: 1: module Redistat 2 class Summary 3 include Database . 15 16 def buffer 17: Redistat.buffer 18 end 19 .. 68 summaries = {} 69 stats.each do |key, value| 70: parts = key.to_s.split(Redistat.group_separator) 71 parts.pop 72 if parts.size > 0 .. 74 parts.each do |part| 75 sum_parts << part 76: sum_key = sum_parts.join(Redistat.group_separator) 77 (summaries.has_key?(sum_key)) ? summaries[sum_key] += value : summaries[sum_key] = value 78 end /Users/felipeclopes/projects/redisrank/lib/redisrank/version.rb: 1: module Redistat 2 VERSION = "0.5.0" 3 end /Users/felipeclopes/projects/redisrank/Rakefile: 63 # 64 65: desc "Start an irb console with Redistat pre-loaded." 66 task :console do 67 exec "irb -r spec/spec_helper" /Users/felipeclopes/projects/redisrank/README.md: 1: # Redistat [![Build Status](https://secure.travis-ci.org/jimeh/redisrank.png)](http://travis-ci.org/jimeh/redisrank) 2 3 A Redis-backed statistics storage and querying library written in Ruby. 4 5: Redistat was originally created to replace a small hacked together statistics 6 collection solution which was MySQL-based. When I started I had a short list 7 of requirements: . 18 ## Installation 19 20: gem install redisrank 21 22 If you are using Ruby 1.8.x, it's recommended you also install the .. 28 29 ```ruby 30: require 'redisrank' 31 32 class ViewStats 33: include Redistat::Model 34 end 35 36: # if using Redistat in multiple threads set this 37 # somewhere in the beginning of the execution stack 38: Redistat.thread_safe = true 39 ``` 40 .. 118 ``` 119 120: Fetch list of products known to Redistat: 121 122 ```ruby ... 136 ### Scope 137 138: A type of global-namespace for storing data. When using the `Redistat::Model` 139 wrapper, the scope is automatically set to the class name. In the examples 140 above, the scope is `ViewStats`. Can be overridden by calling the `#scope` ... 151 label called `views/product/44`, the data is stored for the label you specify, 152 and also for `views/product` and `views`. You may also configure a different 153: group separator using the `Redistat.group_separator=` method. For example: 154 155 ```ruby 156: Redistat.group_separator = '|' 157 ``` 158 ... 164 ### Input Statistics Data 165 166: You provide Redistat with the data you want to store using a Ruby Hash. This 167 data is then stored in a corresponding Redis hash with identical key/field 168 names. ... 175 176 Define how accurately data should be stored, and how accurately it's looked up 177: when fetching it again. By default Redistat uses a depth value of `:hour`, 178 which means it's impossible to separate two events which were stored at 10:18 179 and 10:23. In Redis they are both stored within a date key of `2011031610`. ... 186 When you fetch data, you need to specify a start and an end time. The 187 selection behavior can seem a bit weird at first when, but makes sense when 188: you understand how Redistat works internally. 189 190 For example, if we are using a Depth value of `:hour`, and we trigger a fetch ... 197 ### The Finder Object 198 199: Calling the `#find` method on a Redistat model class returns a 200: `Redistat::Finder` object. The finder is a lazy-loaded gateway to your 201 data. Meaning you can create a new finder, and modify instantiated finder's 202 label, scope, dates, and more. It does not call Redis and fetch the data until ... 222 ```ruby 223 class ViewStats 224: include Redistat::Model 225 226 depth :sec ... 243 ### Storing / Writing 244 245: Redistat stores all data into a Redis hash keys. The Redis key name the used 246 consists of three parts. The scope, label, and datetime: 247 ... 296 ### Fetching / Reading 297 298: By default when fetching statistics, Redistat will figure out how to do the 299 least number of reads from Redis. First it checks how long range you're 300 fetching. If whole days, months or years for example fit within the start and ... 310 311 The buffer is a new, still semi-beta, feature aimed to reduce the number of 312: Redis `hincrby` that Redistat sends. This should only really be useful when 313 you're hitting north of 30,000 Redis requests per second, if your Redis server 314 has limited resources, or against my recommendation you've opted to use 10, ... 318 possible by merging the statistics hashes from all calls and groups them based 319 on scope, label, date depth, and more. You configure the the buffer by setting 320: `Redistat.buffer_size` to an integer higher than 1. This basically tells 321: Redistat how many `store` calls to buffer in memory before writing all data to 322 Redis. 323 ... 333 334 [Global Personals](http://globalpersonals.co.uk/) deserves a thank 335: you. Currently the primary user of Redistat, they've allowed me to spend some 336 company time to further develop the project. 337 /Users/felipeclopes/projects/redisrank/redisrank.gemspec: 1 # -*- encoding: utf-8 -*- 2 $:.push File.expand_path("../lib", __FILE__) 3: require "redisrank/version" 4 5 Gem::Specification.new do |s| 6: s.name = "redisrank" 7: s.version = Redistat::VERSION 8 s.platform = Gem::Platform::RUBY 9 s.authors = ["Jim Myhrberg"] 10 s.email = ["contact@jimeh.me"] 11: s.homepage = "http://github.com/jimeh/redisrank" 12 s.summary = %q{A Redis-backed statistics storage and querying library written in Ruby.} 13 s.description = %q{A Redis-backed statistics storage and querying library written in Ruby.} 14 15: s.rubyforge_project = "redisrank" 16 17 s.files = `git ls-files`.split("\n") /Users/felipeclopes/projects/redisrank/spec/buffer_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Buffer do 4 5 before(:each) do 6: @class = Redistat::Buffer 7: @buffer = Redistat::Buffer.instance 8 @key = double("Key", :to_s => "Scope/label:2011") 9 @stats = {:count => 1, :views => 3} .. 73 }} 74 item = data.first[1] 75: Redistat::Summary.should_receive(:update).with(@key, @stats, @depth_limit, @opts) 76 @buffer.send(:flush_data, data) 77 end /Users/felipeclopes/projects/redisrank/spec/collection_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Collection do 4 5 it "should initialize properly" do 6 options = {:from => "from", :till => "till", :depth => "depth"} 7: result = Redistat::Collection.new(options) 8 result.from.should == options[:from] 9 result.till.should == options[:till] .. 12 13 it "should have a rank property" do 14: col = Redistat::Collection.new() 15 col.rank.should == {} 16 col.rank = {:foo => "bar"} /Users/felipeclopes/projects/redisrank/spec/connection_spec.rb: 1 require "spec_helper" 2: include Redistat 3 4: describe Redistat::Connection do 5 6 before(:each) do 7: @redis = Redistat.redis 8 end 9 10 it "should have a valid Redis client instance" do 11: Redistat.redis.should_not be_nil 12 end 13 .. 34 end 35 36: it "should be accessible from Redistat module" do 37: Redistat.redis.should == Connection.get 38: Redistat.redis.should == Redistat.connection 39 end 40 41 it "should handle multiple connections with refs" do 42: Redistat.redis.client.db.should == 15 43: Redistat.connect(:port => 8379, :db => 14, :ref => "Custom") 44: Redistat.redis.client.db.should == 15 45: Redistat.redis("Custom").client.db.should == 14 46 end 47 48 it "should be able to overwrite default and custom refs" do 49: Redistat.redis.client.db.should == 15 50: Redistat.connect(:port => 8379, :db => 14) 51: Redistat.redis.client.db.should == 14 52 53: Redistat.redis("Custom").client.db.should == 14 54: Redistat.connect(:port => 8379, :db => 15, :ref => "Custom") 55: Redistat.redis("Custom").client.db.should == 15 56 57 # Reset the default connection to the testing server or all hell 58 # might brake loose from the rest of the specs 59: Redistat.connect(:port => 8379, :db => 15) 60 end 61 /Users/felipeclopes/projects/redisrank/spec/database_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Database do 4: include Redistat::Database 5 6 it "should make #db method available when included" do 7: db.should == Redistat.redis 8 end 9 /Users/felipeclopes/projects/redisrank/spec/date_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Date do 4 5 it "should initialize from Time object" do 6 now = Time.now 7: [Redistat::Date.new(now), now.to_rs].each do |rdate| 8: Redistat::Date::DEPTHS.each { |k| rdate.send(k).should == now.send(k) } 9 end 10 end .. 12 it "should initialize from Date object" do 13 today = Date.today 14: [Redistat::Date.new(today), today.to_rs].each do |rdate| 15 [:year, :month, :day].each { |k| rdate.send(k).should == today.send(k) } 16 [:hour, :min, :sec, :usec].each { |k| rdate.send(k).should == 0 } .. 21 now = Time.now.to_i 22 time = Time.at(now) 23: [Redistat::Date.new(now), now.to_rs].each do |rdate| 24 [:year, :month, :day, :hour, :min, :sec].each { |k| rdate.send(k).should == time.send(k) } 25 end .. 28 it "should initialize from String object" do 29 now = Time.now 30: rdate = Redistat::Date.new(now.to_s) 31 [:year, :month, :day, :hour, :min, :sec].each { |k| rdate.send(k).should == now.send(k) } 32 end 33 34: it "should initialize from Redistat date String" do 35 now = Time.now 36: rdate = Redistat::Date.new(now.to_s) 37 [:year, :month, :day, :hour, :min, :sec].each { |k| 38: rdate.to_s(k).should == Redistat::Date.new(rdate.to_s(k)).to_s(k) 39 } 40 end .. 42 it "should convert to Time object" do 43 now = Time.now 44: rdate = Redistat::Date.new(now) 45 rdate.to_time.to_s.should == now.to_s 46 end .. 48 it "should convert to Date object" do 49 today = Date.today 50: rdate = Redistat::Date.new(today) 51 rdate.to_date.to_s.should == today.to_s 52 end .. 54 it "should convert to Fixnum object (UNIX Timestamp)" do 55 now = Time.now 56: rdate = Redistat::Date.new(now) 57 rdate.to_i.should == now.to_i 58 end .. 61 today = Date.today 62 now = Time.now 63: [[now, Redistat::Date.new(now)], [today, Redistat::Date.new(today)]].each do |current, rdate| 64 props = [:year, :month, :day, :hour, :min, :sec, nil] 65 if rdate.usec > 0 .. 82 now = Time.now 83 84: date = Redistat::Date.new(now) 85 date.depth.should be_nil 86 date.to_s.should == now.to_rs(:sec).to_s 87 date.to_s.should == now.to_rs.to_s(:sec) 88 89: date = Redistat::Date.new(now, :hour) 90 date.depth.should == :hour 91 date.to_s.should == now.to_rs(:hour).to_s /Users/felipeclopes/projects/redisrank/spec/event_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Event do 4: include Redistat::Database 5 6 before(:each) do . 13 @options = {:depth => :hour} 14 @date = Time.now 15: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options, @meta) 16 end 17 .. 44 45 it "should increment next_id" do 46: event = Redistat::Event.new("VisitorCount", @label, @date, @stats, @options, @meta) 47 @event.next_id.should == 1 48 event.next_id.should == 1 .. 52 53 it "should store event properly" do 54: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options.merge({:store_event => true}), @meta) 55 expect(@event.new?).to be true 56 @event.save 57 expect(@event.new?).to be false 58 keys = db.keys "*" 59: keys.should include("#{@event.scope}#{Redistat::KEY_EVENT}#{@event.id}") 60: keys.should include("#{@event.scope}#{Redistat::KEY_EVENT_IDS}") 61 end 62 63 it "should find event by id" do 64: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options.merge({:store_event => true}), @meta).save 65: fetched = Redistat::Event.find(@scope, @event.id) 66 @event.scope.to_s.should == fetched.scope.to_s 67 @event.label.to_s.should == fetched.label.to_s .. 73 it "should store summarized statistics" do 74 2.times do |i| 75: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options, @meta).save 76: Redistat::Date::DEPTHS.each do |depth| 77 summary = db.zrevrange @event.key.to_s(depth), 0, -1, :with_scores => true 78 expect(summary.count).to be > 0 /Users/felipeclopes/projects/redisrank/spec/finder/date_set_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Finder::DateSet do 4 5 before(:all) do 6: @finder = Redistat::Finder::DateSet.new 7 end 8 . 10 t_start = Time.utc(2010, 8, 28, 22, 54, 57) 11 t_end = Time.utc(2013, 12, 4, 22, 52, 3) 12: result = Redistat::Finder::DateSet.new(t_start, t_end) 13 result.should == [ 14 { :add => ["2010082822", "2010082823"], :rem => [] }, .. 26 27 t_end = t_start + 4.hours 28: result = Redistat::Finder::DateSet.new.find_date_sets(t_start, t_end, :hour, true) 29 result[0][:add].should == ["2010082818", "2010082819", "2010082820", "2010082821", "2010082822"] 30 result[0][:rem].should == [] 31: result.should == Redistat::Finder::DateSet.new(t_start, t_end, nil, :hour) 32 33 t_end = t_start + 4.days 34: result = Redistat::Finder::DateSet.new.find_date_sets(t_start, t_end, :day, true) 35 result[0][:add].should == ["20100828", "20100829", "20100830", "20100831", "20100901"] 36 result[0][:rem].should == [] 37: result.should == Redistat::Finder::DateSet.new(t_start, t_end, nil, :day) 38 end 39 /Users/felipeclopes/projects/redisrank/spec/finder_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Finder do 4: include Redistat::Database 5 6 before(:each) do . 9 @label = "about_us" 10 @date = Time.now 11: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :day}) 12 @stats = {"user_3" => 3, "user_2" => 2} 13 @stats2 = {"user_3" => 6, "user_2" => 2} .. 19 options = {:scope => "PageViews", :label => "Label", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour} 20 21: finder = Redistat::Finder.new 22 finder.send(:set_options, options) 23: finder.options[:scope].should be_a(Redistat::Scope) 24 finder.options[:scope].to_s.should == options[:scope] 25: finder.options[:label].should be_a(Redistat::Label) 26 finder.options[:label].to_s.should == options[:label] 27 finder.options.should == options.merge(:scope => finder.options[:scope], :label => finder.options[:label]) 28 29: finder = Redistat::Finder.scope("hello") 30 finder.options[:scope].to_s.should == "hello" 31 finder.scope.to_s.should == "hello" 32 33: finder = Redistat::Finder.label("hello") 34 finder.options[:label].to_s.should == "hello" 35 finder.label.to_s.should == "hello" 36 37: finder = Redistat::Finder.dates(@two_hours_ago, @one_hour_ago) 38 finder.options[:from].should == @two_hours_ago 39 finder.options[:till].should == @one_hour_ago 40 41: finder = Redistat::Finder.from(@two_hours_ago) 42 finder.options[:from].should == @two_hours_ago 43 finder.from.should == @two_hours_ago 44 45: finder = Redistat::Finder.till(@one_hour_ago) 46 finder.options[:till].should == @one_hour_ago 47 finder.till.should == @one_hour_ago 48 49: finder = Redistat::Finder.depth(:hour) 50 finder.options[:depth].should == :hour 51 finder.depth.should == :hour 52 53: finder = Redistat::Finder.interval(true) 54 expect(finder.options[:interval]).to be true 55 expect(finder.interval).to be true 56: finder = Redistat::Finder.interval(false) 57 expect(finder.options[:interval]).to be false 58 expect(finder.interval).to be false .. 62 first_stat, last_stat = create_example_stats 63 64: stats = Redistat::Finder.find({:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour}) 65 stats.from.should == first_stat 66 stats.till.should == last_stat .. 73 first_stat, last_stat = create_example_stats 74 75: stats = Redistat::Finder.find(:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour, :interval => :hour) 76 stats.from.should == first_stat 77 stats.till.should == last_stat .. 90 91 it "should return empty hash when attempting to fetch non-existent results" do 92: stats = Redistat::Finder.find({:from => 3.hours.ago, :till => 2.hours.from_now, :scope => @scope, :label => @label, :depth => :hour}) 93 stats.rank.should == {} 94 end 95 96 it "should throw error on invalid options" do 97: lambda { Redistat::Finder.find(:from => 3.hours.ago) }.should raise_error(Redistat::InvalidOptions) 98 end 99 ... 101 before(:each) do 102 @options = {:scope => "PageViews", :label => "message/public", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour} 103: @finder = Redistat::Finder.new(@options) 104 end 105 106 it "should return parent finder" do 107 @finder.instance_variable_get("@parent").should be_nil 108: @finder.parent.should be_a(Redistat::Finder) 109 @finder.instance_variable_get("@parent").should_not be_nil 110 @finder.parent.options[:label].to_s.should == 'message' ... 117 118 it "should find children" do 119: Redistat::Key.new("PageViews", "message/public/die").update_index 120: Redistat::Key.new("PageViews", "message/public/live").update_index 121: Redistat::Key.new("PageViews", "message/public/fester").update_index 122: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}message/public") # checking 'message/public' 123: @finder.children.first.should be_a(Redistat::Finder) 124 subs = @finder.children.map { |f| f.options[:label].me } 125 expect(subs.count).to eq(3) ... 135 @first_stat, @last_stat = create_example_stats 136 137: @finder = Redistat::Finder.new 138 @finder.from(@first_stat).till(@last_stat).scope(@scope).label(@label).depth(:hour) 139 ... 192 193 def create_example_stats 194: key = Redistat::Key.new(@scope, @label, (first = Time.parse("2010-05-14 13:43"))) 195: Redistat::Summary.send(:update_fields, key, @stats, :hour) 196: key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 13:53")) 197: Redistat::Summary.send(:update_fields, key, @stats, :hour) 198: key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 14:52")) 199: Redistat::Summary.send(:update_fields, key, @stats, :hour) 200: key = Redistat::Key.new(@scope, @label, (last = Time.parse("2010-05-14 15:02"))) 201: Redistat::Summary.send(:update_fields, key, @stats2, :hour) 202 [first - 1.hour, last + 1.hour] 203 end /Users/felipeclopes/projects/redisrank/spec/key_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Key do 4: include Redistat::Database 5 6 before(:each) do . 10 @label_hash = Digest::SHA1.hexdigest(@label) 11 @date = Time.now 12: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour}) 13 end 14 .. 18 @key.label_hash.should == @label_hash 19 @key.groups.map { |k| k.instance_variable_get("@label") }.should == @key.instance_variable_get("@label").groups 20: @key.date.should be_instance_of(Redistat::Date) 21 @key.date.to_time.to_s.should == @date.to_s 22 end .. 29 props.pop 30 end 31: key = Redistat::Key.new(@scope, nil, @date, {:depth => :hour}) 32 key.to_s.should == "#{@scope}:#{key.date.to_s(:hour)}" 33 end 34 35 it "should abide to hashed_label option" do 36: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => true}) 37 @key.to_s.should == "#{@scope}/#{@label_hash}:#{@key.date.to_s(:hour)}" 38: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => false}) 39 @key.to_s.should == "#{@scope}/#{@label}:#{@key.date.to_s(:hour)}" 40 end 41 42 it "should have default depth option" do 43: @key = Redistat::Key.new(@scope, @label, @date) 44 @key.depth.should == :hour 45 end .. 69 before(:each) do 70 @label = "message/public/offensive" 71: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour}) 72 end 73 .. 78 "message" ] 79 80: key = Redistat::Key.new(@scope, label, @date, {:depth => :hour}) 81 82 key.groups.map { |k| k.label.to_s }.should == result .. 84 85 it "should know it's parent" do 86: @key.parent.should be_a(Redistat::Key) 87 @key.parent.label.to_s.should == 'message/public' 88: Redistat::Key.new(@scope, 'hello', @date).parent.should be_nil 89 end 90 91 it "should update label index and return children" do 92: db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}").should == [] 93 @key.children.count.should be(0) 94 95 @key.update_index # indexing 'message/publish/offensive' 96: Redistat::Key.new("PageViews", "message/public/die").update_index # indexing 'message/publish/die' 97: Redistat::Key.new("PageViews", "message/public/live").update_index # indexing 'message/publish/live' 98 99: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}") # checking 'message/public' 100 members.count.should be(3) 101 members.should include('offensive') ... 104 105 key = @key.parent 106: key.children.first.should be_a(Redistat::Key) 107 key.children.count.should be(3) 108 key.children.map { |k| k.label.me }.should == members 109 110: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{key.label.parent}") # checking 'message' 111 members.count.should be(1) 112 members.should include('public') ... 116 key.children.map { |k| k.label.me }.should == members 117 118: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}") # checking '' 119 members.count.should be(1) 120 members.should include('message') 121 122 key.parent.should be_nil 123: key = Redistat::Key.new("PageViews") 124 key.children.count.should be(1) 125 key.children.map { |k| k.label.me }.should include('message') /Users/felipeclopes/projects/redisrank/spec/label_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Label do 4: include Redistat::Database 5 6 before(:each) do 7 db.flushdb 8 @name = "about_us" 9: @label = Redistat::Label.new(@name) 10 end 11 .. 16 17 it "should store a label hash lookup key" do 18: label = Redistat::Label.new(@name, {:hashed_label => true}).save 19 label.saved?.should be(true) 20: db.hget(Redistat::KEY_LABELS, label.hash).should == @name 21 22 name = "contact_us" 23: label = Redistat::Label.create(name, {:hashed_label => true}) 24 label.saved?.should be(true) 25: db.hget(Redistat::KEY_LABELS, label.hash).should == name 26 end 27 28 it "should join labels" do 29: include Redistat 30: label = Redistat::Label.join('email', 'message', 'public') 31: label.should be_a(Redistat::Label) 32 label.to_s.should == 'email/message/public' 33: label = Redistat::Label.join(Redistat::Label.new('email'), Redistat::Label.new('message'), Redistat::Label.new('public')) 34: label.should be_a(Redistat::Label) 35 label.to_s.should == 'email/message/public' 36: label = Redistat::Label.join('email', '', 'message', nil, 'public') 37: label.should be_a(Redistat::Label) 38 label.to_s.should == 'email/message/public' 39 end 40 41 it "should allow you to use a different group separator" do 42: include Redistat 43: Redistat.group_separator = '|' 44: label = Redistat::Label.join('email', 'message', 'public') 45: label.should be_a(Redistat::Label) 46 label.to_s.should == 'email|message|public' 47: label = Redistat::Label.join(Redistat::Label.new('email'), Redistat::Label.new('message'), Redistat::Label.new('public')) 48: label.should be_a(Redistat::Label) 49 label.to_s.should == 'email|message|public' 50: label = Redistat::Label.join('email', '', 'message', nil, 'public') 51: label.should be_a(Redistat::Label) 52 label.to_s.should == 'email|message|public' 53: Redistat.group_separator = Redistat::GROUP_SEPARATOR 54 end 55 .. 57 before(:each) do 58 @name = "message/public/offensive" 59: @label = Redistat::Label.new(@name) 60 end 61 62 it "should know it's parent label group" do 63 @label.parent.to_s.should == 'message/public' 64: Redistat::Label.new('hello').parent.should be_nil 65 end 66 .. 72 73 @name = "/message/public/" 74: @label = Redistat::Label.new(@name) 75 @label.name.should == @name 76 @label.groups.map { |l| l.to_s }.should == [ "message/public", .. 78 79 @name = "message" 80: @label = Redistat::Label.new(@name) 81 @label.name.should == @name 82 @label.groups.map { |l| l.to_s }.should == [ "message" ] /Users/felipeclopes/projects/redisrank/spec/model_helper.rb: 1: require "redisrank" 2 3 class ModelHelper1 4: include Redistat::Model 5 6 . 8 9 class ModelHelper2 10: include Redistat::Model 11 12 depth :day .. 17 18 class ModelHelper3 19: include Redistat::Model 20 21 connect_to :port => 8379, :db => 14 .. 24 25 class ModelHelper4 26: include Redistat::Model 27 28 scope "FancyHelper" /Users/felipeclopes/projects/redisrank/spec/model_spec.rb: 2 require "model_helper" 3 4: describe Redistat::Model do 5: include Redistat::Database 6 7 before(:each) do . 22 one_hour_ago = 1.hour.ago 23 finder = ModelHelper1.find('label', two_hours_ago, one_hour_ago) 24: finder.should be_a(Redistat::Finder) 25 finder.options[:scope].to_s.should == 'ModelHelper1' 26 finder.options[:label].to_s.should == 'label' .. 30 31 it "should #find_event" do 32: Redistat::Event.should_receive(:find).with('ModelHelper1', 1) 33 ModelHelper1.find_event(1) 34 end .. 151 describe "Write Buffer" do 152 before(:each) do 153: Redistat.buffer_size = 20 154 end 155 156 after(:each) do 157: Redistat.buffer_size = 0 158 end 159 ... 181 end 182 ModelHelper1.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1)).rank.should == {} 183: Redistat.buffer.flush(true) 184 185 stats = ModelHelper1.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1)) /Users/felipeclopes/projects/redisrank/spec/options_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Options do 4 5 before(:each) do . 24 25 class OptionsHelper 26: include Redistat::Options 27 28 option_accessor :hello /Users/felipeclopes/projects/redisrank/spec/result_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Result do 4 5 it "should should initialize properly" do 6 options = {:from => "from", :till => "till"} 7: result = Redistat::Result.new(options) 8 result.from.should == "from" 9 result.till.should == "till" .. 11 12 it "should have merge_to_max method" do 13: result = Redistat::Result.new 14 result[:world].should be_nil 15 result.merge_to_max(:world, 3) /Users/felipeclopes/projects/redisrank/spec/scope_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Scope do 4: include Redistat::Database 5 6 before(:all) do . 10 before(:each) do 11 @name = "PageViews" 12: @scope = Redistat::Scope.new(@name) 13 end 14 .. 18 19 it "should increment next_id" do 20: scope = Redistat::Scope.new("Visitors") 21 @scope.next_id.should == 1 22 scope.next_id.should == 1 /Users/felipeclopes/projects/redisrank/spec/spec_helper.rb: 10 11 # require stuff 12: require 'redisrank' 13 require 'rspec' 14 require 'rspec/autorun' 15 16: # use the test Redistat instance 17: Redistat.connect(:port => 8379, :db => 15, :thread_safe => true) 18: Redistat.redis.flushdb 19 /Users/felipeclopes/projects/redisrank/spec/summary_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Summary do 4: include Redistat::Database 5 6 before(:each) do . 9 @label = "about_us" 10 @date = Time.now 11: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :day}) 12 @stats = {"views" => 3, "visitors" => 2} 13 @expire = {:hour => 24*3600} .. 15 16 it "should update a single summary properly" do 17: Redistat::Summary.send(:update_fields, @key, @stats, :hour) 18 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 19 expect(summary.count).to be 2 .. 25 expect(visitors.last).to be 2.0 26 27: Redistat::Summary.send(:update_fields, @key, @stats, :hour) 28 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 29 expect(summary.count).to be 2 .. 37 38 it "should set key expiry properly" do 39: Redistat::Summary.update_all(@key, @stats, :hour,{:expire => @expire}) 40 ((24*3600)-1..(24*3600)+1).should include(db.ttl(@key.to_s(:hour))) 41 [:day, :month, :year].each do |depth| .. 44 45 db.flushdb 46: Redistat::Summary.update_all(@key, @stats, :hour, {:expire => {}}) 47 [:hour, :day, :month, :year].each do |depth| 48 db.ttl(@key.to_s(depth)).should == -1 .. 51 52 it "should update all summaries properly" do 53: Redistat::Summary.update_all(@key, @stats, :sec) 54 [:year, :month, :day, :hour, :min, :sec, :usec].each do |depth| 55 summary = db.zrevrange(@key.to_s(depth), 0, -1, :with_scores => true) .. 69 70 it "should update summaries even if no label is set" do 71: key = Redistat::Key.new(@scope, nil, @date, {:depth => :day}) 72: Redistat::Summary.send(:update_fields, key, @stats, :hour) 73 summary = db.zrevrange(key.to_s(:hour), 0, -1, :with_scores => true) 74 views = summary.first .. 84 "death/bomb" => 4, "death/unicorn" => 3, 85 :"od/sugar" => 7, :"od/meth" => 8 } 86: res = Redistat::Summary.send(:inject_group_summaries, hash) 87 res.should == { "count" => 10, "count/hello" => 3, "count/world" => 7, 88 "death" => 7, "death/bomb" => 4, "death/unicorn" => 3, .. 92 it "should properly store key group summaries" do 93 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 94: Redistat::Summary.update_all(@key, stats, :hour) 95 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 96 summary.count.should eq(4) .. 109 it "should not store key group summaries when option is disabled" do 110 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 111: Redistat::Summary.update_all(@key, stats, :hour, {:enable_grouping => false}) 112 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 113 summary.count.should eq(3) ... 125 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 126 label = "views/about_us" 127: key = Redistat::Key.new(@scope, label, @date) 128: Redistat::Summary.update_all(key, stats, :hour) 129 130 key.groups[0].label.to_s.should == "views/about_us" ... 134 135 label = "views/contact" 136: key = Redistat::Key.new(@scope, label, @date) 137: Redistat::Summary.update_all(key, stats, :hour) 138 139 key.groups[0].label.to_s.should == "views/contact" /Users/felipeclopes/projects/redisrank/spec/synchronize_spec.rb: 1 require "spec_helper" 2 3: module Redistat 4 describe Synchronize do 5 . 62 63 describe '.monitor' do 64: it 'defers to Redistat::Synchronize' do 65 klass.should_receive(:monitor).once 66 subject.monitor .. 69 70 describe '.thread_safe' do 71: it ' defers to Redistat::Synchronize' do 72 klass.should_receive(:thread_safe).once 73 subject.thread_safe .. 76 77 describe '.thread_safe=' do 78: it 'defers to Redistat::Synchronize' do 79 klass.should_receive(:thread_safe=).once.with(true) 80 subject.thread_safe = true .. 119 120 end # Synchronize 121: end # Redistat 122 123 class SynchronizeSpecHelper 124: include Redistat::Synchronize 125 end 126 /Users/felipeclopes/projects/redisrank/spec/thread_safety_spec.rb: 2 3 describe "Thread-Safety" do 4: include Redistat::Database 5 6 before(:each) do . 23 it "should store event in multiple threads" do 24 class ThreadSafetySpec 25: include Redistat::Model 26 end 27 threads = [] 467 matches across 50 files Searching 85 files for "Redistat" (case sensitive) /Users/felipeclopes/projects/redisrank/coverage/index.html: 564 1 565 566: module Redistat 567 568 ... 588 1 589 590: KEY_LABELS = "Redistat.labels:" # used for reverse label hash lookup 591 592 ... 858 859 860: puts "WARNING: Redistat.flush is deprecated. Use Redistat.redis.flushdb instead." 861 862 ... 948 1 949 950: Redistat.buffer.flush(true) 951 952 ... 990 1 991 992: module Redistat 993 994 ... 1656 1 1657 1658: module Redistat 1659 1660 .... 1806 1 1807 1808: module Redistat 1809 1810 .... 2400 1 2401 2402: include Redistat::DateHelper 2403 2404 .... 2466 1 2467 2468: include Redistat::DateHelper 2469 2470 .... 2532 1 2533 2534: include Redistat::DateHelper 2535 2536 .... 2736 1 2737 2738: include Redistat::DateHelper 2739 2740 .... 2766 1 2767 2768: module Redistat 2769 2770 .... 3312 1 3313 3314: module Redistat 3315 3316 .... 3930 1 3931 3932: module Redistat 3933 3934 .... 5406 1 5407 5408: module Redistat 5409 5410 .... 6018 1 6019 6020: module Redistat 6021 6022 .... 6162 313 6163 6164: @date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date 6165 6166 .... 6240 312 6241 6242: @scope = (input.instance_of?(Redistat::Scope)) ? input : Scope.new(input) 6243 6244 .... 6270 310 6271 6272: @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options) 6273 6274 .... 6366 19 6367 6368: self.class.new(self.scope, child_label.join(Redistat.group_separator), self.date, @options) 6369 6370 .... 6540 1 6541 6542: module Redistat 6543 6544 .... 6630 28 6631 6632: self.new(args.reject {|i| i.blank? }.join(Redistat.group_separator)) 6633 6634 .... 6840 109 6841 6842: self.to_s.split(Redistat.group_separator).last 6843 6844 .... 6882 266 6883 6884: self.to_s.split(Redistat.group_separator).each do |part| 6885 6886 .... 6894 324 6895 6896: group = ((parent.blank?) ? "" : "#{parent}#{Redistat.group_separator}") + part 6897 6898 .... 6972 1 6973 6974: module Redistat 6975 6976 .... 7014 1667 7015 7016: Redistat.connection(ref) 7017 7018 .... 7056 1 7057 7058: module Redistat 7059 7060 .... 7074 544 7075 7076: Redistat::Date.new(self, depth) 7077 7078 .... 7122 1 7123 7124: module Redistat 7125 7126 .... 7398 1 7399 7400: module Redistat 7401 7402 .... 7716 1 7717 7718: module Redistat 7719 7720 .... 8208 1 8209 8210: module Redistat 8211 8212 .... 8322 1 8323 8324: module Redistat 8325 8326 .... 8448 1 8449 8450: module Redistat 8451 8452 .... 8544 105 8545 8546: Redistat.buffer 8547 8548 .... 8862 151 8863 8864: parts = key.to_s.split(Redistat.group_separator) 8865 8866 .... 8898 12 8899 8900: sum_key = sum_parts.join(Redistat.group_separator) 8901 8902 /Users/felipeclopes/projects/redisrank/lib/redisrank.rb: 39 40 41: module Redistat 42 43 KEY_NEXT_ID = ".next_id" 44 KEY_EVENT = ".event:" 45: KEY_LABELS = "Redistat.labels:" # used for reverse label hash lookup 46 KEY_EVENT_IDS = ".event_ids" 47 LABEL_INDEX = ".label_index:" .. 88 89 def flush 90: puts "WARNING: Redistat.flush is deprecated. Use Redistat.redis.flushdb instead." 91 connection.flushdb 92 end .. 103 # ensure buffer is flushed on program exit 104 Kernel.at_exit do 105: Redistat.buffer.flush(true) 106 end 107 /Users/felipeclopes/projects/redisrank/lib/redisrank/buffer.rb: 1 require 'redisrank/core_ext/hash' 2 3: module Redistat 4 class Buffer 5 include Synchronize /Users/felipeclopes/projects/redisrank/lib/redisrank/collection.rb: 1: module Redistat 2 class Collection < ::Array 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/connection.rb: 1 require 'monitor' 2 3: module Redistat 4 module Connection 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/bignum.rb: 1 class Bignum 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/date.rb: 1 class Date 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/fixnum.rb: 1 class Fixnum 2: include Redistat::DateHelper 3 4 def to_time /Users/felipeclopes/projects/redisrank/lib/redisrank/core_ext/time.rb: 1 class Time 2: include Redistat::DateHelper 3 end 4 /Users/felipeclopes/projects/redisrank/lib/redisrank/date.rb: 1: module Redistat 2 class Date 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/event.rb: 1: module Redistat 2 class Event 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/finder.rb: 1 require 'redisrank/finder/date_set' 2 3: module Redistat 4 class Finder 5 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/finder/date_set.rb: 1: module Redistat 2 class Finder 3 class DateSet < Array /Users/felipeclopes/projects/redisrank/lib/redisrank/key.rb: 1: module Redistat 2 class Key 3 include Database . 23 24 def date=(input) 25: @date = (input.instance_of?(Redistat::Date)) ? input : Date.new(input) # Redistat::Date, not ::Date 26 end 27 attr_reader :date .. 36 37 def scope=(input) 38: @scope = (input.instance_of?(Redistat::Scope)) ? input : Scope.new(input) 39 end 40 attr_reader :scope 41 42 def label=(input) 43: @label = (input.instance_of?(Redistat::Label)) ? input : Label.create(input, @options) 44 end 45 attr_reader :label .. 57 members.map { |member| 58 child_label = [@label, member].reject { |i| i.nil? } 59: self.class.new(self.scope, child_label.join(Redistat.group_separator), self.date, @options) 60 } 61 end /Users/felipeclopes/projects/redisrank/lib/redisrank/label.rb: 1: module Redistat 2 class Label 3 include Database . 14 def self.join(*args) 15 args = args.map {|i| i.to_s} 16: self.new(args.reject {|i| i.blank? }.join(Redistat.group_separator)) 17 end 18 .. 49 50 def me 51: self.to_s.split(Redistat.group_separator).last 52 end 53 .. 56 @groups = [] 57 parent = "" 58: self.to_s.split(Redistat.group_separator).each do |part| 59 if !part.blank? 60: group = ((parent.blank?) ? "" : "#{parent}#{Redistat.group_separator}") + part 61 @groups << Label.new(group) 62 parent = group /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/database.rb: 1: module Redistat 2 module Database 3 def self.included(base) . 6 def db(ref = nil) 7 ref ||= @options[:connection_ref] if !@options.nil? 8: Redistat.connection(ref) 9 end 10 end /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/date_helper.rb: 1: module Redistat 2 module DateHelper 3 def to_redisrank(depth = nil) 4: Redistat::Date.new(self, depth) 5 end 6 alias :to_rs :to_redisrank /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/options.rb: 1: module Redistat 2 module Options 3 /Users/felipeclopes/projects/redisrank/lib/redisrank/mixins/synchronize.rb: 1 require 'monitor' 2 3: module Redistat 4 module Synchronize 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/model.rb: 1: module Redistat 2 module Model 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/result.rb: 1 require 'active_support/core_ext/hash/indifferent_access' 2 3: module Redistat 4 class Result < HashWithIndifferentAccess 5 /Users/felipeclopes/projects/redisrank/lib/redisrank/scope.rb: 1: module Redistat 2 class Scope 3 include Database /Users/felipeclopes/projects/redisrank/lib/redisrank/summary.rb: 1: module Redistat 2 class Summary 3 include Database . 15 16 def buffer 17: Redistat.buffer 18 end 19 .. 68 summaries = {} 69 stats.each do |key, value| 70: parts = key.to_s.split(Redistat.group_separator) 71 parts.pop 72 if parts.size > 0 .. 74 parts.each do |part| 75 sum_parts << part 76: sum_key = sum_parts.join(Redistat.group_separator) 77 (summaries.has_key?(sum_key)) ? summaries[sum_key] += value : summaries[sum_key] = value 78 end /Users/felipeclopes/projects/redisrank/lib/redisrank/version.rb: 1: module Redistat 2 VERSION = "0.5.0" 3 end /Users/felipeclopes/projects/redisrank/Rakefile: 63 # 64 65: desc "Start an irb console with Redistat pre-loaded." 66 task :console do 67 exec "irb -r spec/spec_helper" /Users/felipeclopes/projects/redisrank/README.md: 1: # Redistat [![Build Status](https://secure.travis-ci.org/jimeh/redisrank.png)](http://travis-ci.org/jimeh/redisrank) 2 3 A Redis-backed statistics storage and querying library written in Ruby. 4 5: Redistat was originally created to replace a small hacked together statistics 6 collection solution which was MySQL-based. When I started I had a short list 7 of requirements: . 31 32 class ViewStats 33: include Redistat::Model 34 end 35 36: # if using Redistat in multiple threads set this 37 # somewhere in the beginning of the execution stack 38: Redistat.thread_safe = true 39 ``` 40 .. 118 ``` 119 120: Fetch list of products known to Redistat: 121 122 ```ruby ... 136 ### Scope 137 138: A type of global-namespace for storing data. When using the `Redistat::Model` 139 wrapper, the scope is automatically set to the class name. In the examples 140 above, the scope is `ViewStats`. Can be overridden by calling the `#scope` ... 151 label called `views/product/44`, the data is stored for the label you specify, 152 and also for `views/product` and `views`. You may also configure a different 153: group separator using the `Redistat.group_separator=` method. For example: 154 155 ```ruby 156: Redistat.group_separator = '|' 157 ``` 158 ... 164 ### Input Statistics Data 165 166: You provide Redistat with the data you want to store using a Ruby Hash. This 167 data is then stored in a corresponding Redis hash with identical key/field 168 names. ... 175 176 Define how accurately data should be stored, and how accurately it's looked up 177: when fetching it again. By default Redistat uses a depth value of `:hour`, 178 which means it's impossible to separate two events which were stored at 10:18 179 and 10:23. In Redis they are both stored within a date key of `2011031610`. ... 186 When you fetch data, you need to specify a start and an end time. The 187 selection behavior can seem a bit weird at first when, but makes sense when 188: you understand how Redistat works internally. 189 190 For example, if we are using a Depth value of `:hour`, and we trigger a fetch ... 197 ### The Finder Object 198 199: Calling the `#find` method on a Redistat model class returns a 200: `Redistat::Finder` object. The finder is a lazy-loaded gateway to your 201 data. Meaning you can create a new finder, and modify instantiated finder's 202 label, scope, dates, and more. It does not call Redis and fetch the data until ... 222 ```ruby 223 class ViewStats 224: include Redistat::Model 225 226 depth :sec ... 243 ### Storing / Writing 244 245: Redistat stores all data into a Redis hash keys. The Redis key name the used 246 consists of three parts. The scope, label, and datetime: 247 ... 296 ### Fetching / Reading 297 298: By default when fetching statistics, Redistat will figure out how to do the 299 least number of reads from Redis. First it checks how long range you're 300 fetching. If whole days, months or years for example fit within the start and ... 310 311 The buffer is a new, still semi-beta, feature aimed to reduce the number of 312: Redis `hincrby` that Redistat sends. This should only really be useful when 313 you're hitting north of 30,000 Redis requests per second, if your Redis server 314 has limited resources, or against my recommendation you've opted to use 10, ... 318 possible by merging the statistics hashes from all calls and groups them based 319 on scope, label, date depth, and more. You configure the the buffer by setting 320: `Redistat.buffer_size` to an integer higher than 1. This basically tells 321: Redistat how many `store` calls to buffer in memory before writing all data to 322 Redis. 323 ... 333 334 [Global Personals](http://globalpersonals.co.uk/) deserves a thank 335: you. Currently the primary user of Redistat, they've allowed me to spend some 336 company time to further develop the project. 337 /Users/felipeclopes/projects/redisrank/redisrank.gemspec: 5 Gem::Specification.new do |s| 6 s.name = "redisrank" 7: s.version = Redistat::VERSION 8 s.platform = Gem::Platform::RUBY 9 s.authors = ["Jim Myhrberg"] /Users/felipeclopes/projects/redisrank/spec/buffer_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Buffer do 4 5 before(:each) do 6: @class = Redistat::Buffer 7: @buffer = Redistat::Buffer.instance 8 @key = double("Key", :to_s => "Scope/label:2011") 9 @stats = {:count => 1, :views => 3} .. 73 }} 74 item = data.first[1] 75: Redistat::Summary.should_receive(:update).with(@key, @stats, @depth_limit, @opts) 76 @buffer.send(:flush_data, data) 77 end /Users/felipeclopes/projects/redisrank/spec/collection_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Collection do 4 5 it "should initialize properly" do 6 options = {:from => "from", :till => "till", :depth => "depth"} 7: result = Redistat::Collection.new(options) 8 result.from.should == options[:from] 9 result.till.should == options[:till] .. 12 13 it "should have a rank property" do 14: col = Redistat::Collection.new() 15 col.rank.should == {} 16 col.rank = {:foo => "bar"} /Users/felipeclopes/projects/redisrank/spec/connection_spec.rb: 1 require "spec_helper" 2: include Redistat 3 4: describe Redistat::Connection do 5 6 before(:each) do 7: @redis = Redistat.redis 8 end 9 10 it "should have a valid Redis client instance" do 11: Redistat.redis.should_not be_nil 12 end 13 .. 34 end 35 36: it "should be accessible from Redistat module" do 37: Redistat.redis.should == Connection.get 38: Redistat.redis.should == Redistat.connection 39 end 40 41 it "should handle multiple connections with refs" do 42: Redistat.redis.client.db.should == 15 43: Redistat.connect(:port => 8379, :db => 14, :ref => "Custom") 44: Redistat.redis.client.db.should == 15 45: Redistat.redis("Custom").client.db.should == 14 46 end 47 48 it "should be able to overwrite default and custom refs" do 49: Redistat.redis.client.db.should == 15 50: Redistat.connect(:port => 8379, :db => 14) 51: Redistat.redis.client.db.should == 14 52 53: Redistat.redis("Custom").client.db.should == 14 54: Redistat.connect(:port => 8379, :db => 15, :ref => "Custom") 55: Redistat.redis("Custom").client.db.should == 15 56 57 # Reset the default connection to the testing server or all hell 58 # might brake loose from the rest of the specs 59: Redistat.connect(:port => 8379, :db => 15) 60 end 61 /Users/felipeclopes/projects/redisrank/spec/database_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Database do 4: include Redistat::Database 5 6 it "should make #db method available when included" do 7: db.should == Redistat.redis 8 end 9 /Users/felipeclopes/projects/redisrank/spec/date_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Date do 4 5 it "should initialize from Time object" do 6 now = Time.now 7: [Redistat::Date.new(now), now.to_rs].each do |rdate| 8: Redistat::Date::DEPTHS.each { |k| rdate.send(k).should == now.send(k) } 9 end 10 end .. 12 it "should initialize from Date object" do 13 today = Date.today 14: [Redistat::Date.new(today), today.to_rs].each do |rdate| 15 [:year, :month, :day].each { |k| rdate.send(k).should == today.send(k) } 16 [:hour, :min, :sec, :usec].each { |k| rdate.send(k).should == 0 } .. 21 now = Time.now.to_i 22 time = Time.at(now) 23: [Redistat::Date.new(now), now.to_rs].each do |rdate| 24 [:year, :month, :day, :hour, :min, :sec].each { |k| rdate.send(k).should == time.send(k) } 25 end .. 28 it "should initialize from String object" do 29 now = Time.now 30: rdate = Redistat::Date.new(now.to_s) 31 [:year, :month, :day, :hour, :min, :sec].each { |k| rdate.send(k).should == now.send(k) } 32 end 33 34: it "should initialize from Redistat date String" do 35 now = Time.now 36: rdate = Redistat::Date.new(now.to_s) 37 [:year, :month, :day, :hour, :min, :sec].each { |k| 38: rdate.to_s(k).should == Redistat::Date.new(rdate.to_s(k)).to_s(k) 39 } 40 end .. 42 it "should convert to Time object" do 43 now = Time.now 44: rdate = Redistat::Date.new(now) 45 rdate.to_time.to_s.should == now.to_s 46 end .. 48 it "should convert to Date object" do 49 today = Date.today 50: rdate = Redistat::Date.new(today) 51 rdate.to_date.to_s.should == today.to_s 52 end .. 54 it "should convert to Fixnum object (UNIX Timestamp)" do 55 now = Time.now 56: rdate = Redistat::Date.new(now) 57 rdate.to_i.should == now.to_i 58 end .. 61 today = Date.today 62 now = Time.now 63: [[now, Redistat::Date.new(now)], [today, Redistat::Date.new(today)]].each do |current, rdate| 64 props = [:year, :month, :day, :hour, :min, :sec, nil] 65 if rdate.usec > 0 .. 82 now = Time.now 83 84: date = Redistat::Date.new(now) 85 date.depth.should be_nil 86 date.to_s.should == now.to_rs(:sec).to_s 87 date.to_s.should == now.to_rs.to_s(:sec) 88 89: date = Redistat::Date.new(now, :hour) 90 date.depth.should == :hour 91 date.to_s.should == now.to_rs(:hour).to_s /Users/felipeclopes/projects/redisrank/spec/event_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Event do 4: include Redistat::Database 5 6 before(:each) do . 13 @options = {:depth => :hour} 14 @date = Time.now 15: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options, @meta) 16 end 17 .. 44 45 it "should increment next_id" do 46: event = Redistat::Event.new("VisitorCount", @label, @date, @stats, @options, @meta) 47 @event.next_id.should == 1 48 event.next_id.should == 1 .. 52 53 it "should store event properly" do 54: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options.merge({:store_event => true}), @meta) 55 expect(@event.new?).to be true 56 @event.save 57 expect(@event.new?).to be false 58 keys = db.keys "*" 59: keys.should include("#{@event.scope}#{Redistat::KEY_EVENT}#{@event.id}") 60: keys.should include("#{@event.scope}#{Redistat::KEY_EVENT_IDS}") 61 end 62 63 it "should find event by id" do 64: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options.merge({:store_event => true}), @meta).save 65: fetched = Redistat::Event.find(@scope, @event.id) 66 @event.scope.to_s.should == fetched.scope.to_s 67 @event.label.to_s.should == fetched.label.to_s .. 73 it "should store summarized statistics" do 74 2.times do |i| 75: @event = Redistat::Event.new(@scope, @label, @date, @stats, @options, @meta).save 76: Redistat::Date::DEPTHS.each do |depth| 77 summary = db.zrevrange @event.key.to_s(depth), 0, -1, :with_scores => true 78 expect(summary.count).to be > 0 /Users/felipeclopes/projects/redisrank/spec/finder/date_set_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Finder::DateSet do 4 5 before(:all) do 6: @finder = Redistat::Finder::DateSet.new 7 end 8 . 10 t_start = Time.utc(2010, 8, 28, 22, 54, 57) 11 t_end = Time.utc(2013, 12, 4, 22, 52, 3) 12: result = Redistat::Finder::DateSet.new(t_start, t_end) 13 result.should == [ 14 { :add => ["2010082822", "2010082823"], :rem => [] }, .. 26 27 t_end = t_start + 4.hours 28: result = Redistat::Finder::DateSet.new.find_date_sets(t_start, t_end, :hour, true) 29 result[0][:add].should == ["2010082818", "2010082819", "2010082820", "2010082821", "2010082822"] 30 result[0][:rem].should == [] 31: result.should == Redistat::Finder::DateSet.new(t_start, t_end, nil, :hour) 32 33 t_end = t_start + 4.days 34: result = Redistat::Finder::DateSet.new.find_date_sets(t_start, t_end, :day, true) 35 result[0][:add].should == ["20100828", "20100829", "20100830", "20100831", "20100901"] 36 result[0][:rem].should == [] 37: result.should == Redistat::Finder::DateSet.new(t_start, t_end, nil, :day) 38 end 39 /Users/felipeclopes/projects/redisrank/spec/finder_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Finder do 4: include Redistat::Database 5 6 before(:each) do . 9 @label = "about_us" 10 @date = Time.now 11: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :day}) 12 @stats = {"user_3" => 3, "user_2" => 2} 13 @stats2 = {"user_3" => 6, "user_2" => 2} .. 19 options = {:scope => "PageViews", :label => "Label", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour} 20 21: finder = Redistat::Finder.new 22 finder.send(:set_options, options) 23: finder.options[:scope].should be_a(Redistat::Scope) 24 finder.options[:scope].to_s.should == options[:scope] 25: finder.options[:label].should be_a(Redistat::Label) 26 finder.options[:label].to_s.should == options[:label] 27 finder.options.should == options.merge(:scope => finder.options[:scope], :label => finder.options[:label]) 28 29: finder = Redistat::Finder.scope("hello") 30 finder.options[:scope].to_s.should == "hello" 31 finder.scope.to_s.should == "hello" 32 33: finder = Redistat::Finder.label("hello") 34 finder.options[:label].to_s.should == "hello" 35 finder.label.to_s.should == "hello" 36 37: finder = Redistat::Finder.dates(@two_hours_ago, @one_hour_ago) 38 finder.options[:from].should == @two_hours_ago 39 finder.options[:till].should == @one_hour_ago 40 41: finder = Redistat::Finder.from(@two_hours_ago) 42 finder.options[:from].should == @two_hours_ago 43 finder.from.should == @two_hours_ago 44 45: finder = Redistat::Finder.till(@one_hour_ago) 46 finder.options[:till].should == @one_hour_ago 47 finder.till.should == @one_hour_ago 48 49: finder = Redistat::Finder.depth(:hour) 50 finder.options[:depth].should == :hour 51 finder.depth.should == :hour 52 53: finder = Redistat::Finder.interval(true) 54 expect(finder.options[:interval]).to be true 55 expect(finder.interval).to be true 56: finder = Redistat::Finder.interval(false) 57 expect(finder.options[:interval]).to be false 58 expect(finder.interval).to be false .. 62 first_stat, last_stat = create_example_stats 63 64: stats = Redistat::Finder.find({:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour}) 65 stats.from.should == first_stat 66 stats.till.should == last_stat .. 73 first_stat, last_stat = create_example_stats 74 75: stats = Redistat::Finder.find(:from => first_stat, :till => last_stat, :scope => @scope, :label => @label, :depth => :hour, :interval => :hour) 76 stats.from.should == first_stat 77 stats.till.should == last_stat .. 90 91 it "should return empty hash when attempting to fetch non-existent results" do 92: stats = Redistat::Finder.find({:from => 3.hours.ago, :till => 2.hours.from_now, :scope => @scope, :label => @label, :depth => :hour}) 93 stats.rank.should == {} 94 end 95 96 it "should throw error on invalid options" do 97: lambda { Redistat::Finder.find(:from => 3.hours.ago) }.should raise_error(Redistat::InvalidOptions) 98 end 99 ... 101 before(:each) do 102 @options = {:scope => "PageViews", :label => "message/public", :from => @two_hours_ago, :till => @one_hour_ago, :depth => :hour, :interval => :hour} 103: @finder = Redistat::Finder.new(@options) 104 end 105 106 it "should return parent finder" do 107 @finder.instance_variable_get("@parent").should be_nil 108: @finder.parent.should be_a(Redistat::Finder) 109 @finder.instance_variable_get("@parent").should_not be_nil 110 @finder.parent.options[:label].to_s.should == 'message' ... 117 118 it "should find children" do 119: Redistat::Key.new("PageViews", "message/public/die").update_index 120: Redistat::Key.new("PageViews", "message/public/live").update_index 121: Redistat::Key.new("PageViews", "message/public/fester").update_index 122: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}message/public") # checking 'message/public' 123: @finder.children.first.should be_a(Redistat::Finder) 124 subs = @finder.children.map { |f| f.options[:label].me } 125 expect(subs.count).to eq(3) ... 135 @first_stat, @last_stat = create_example_stats 136 137: @finder = Redistat::Finder.new 138 @finder.from(@first_stat).till(@last_stat).scope(@scope).label(@label).depth(:hour) 139 ... 192 193 def create_example_stats 194: key = Redistat::Key.new(@scope, @label, (first = Time.parse("2010-05-14 13:43"))) 195: Redistat::Summary.send(:update_fields, key, @stats, :hour) 196: key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 13:53")) 197: Redistat::Summary.send(:update_fields, key, @stats, :hour) 198: key = Redistat::Key.new(@scope, @label, Time.parse("2010-05-14 14:52")) 199: Redistat::Summary.send(:update_fields, key, @stats, :hour) 200: key = Redistat::Key.new(@scope, @label, (last = Time.parse("2010-05-14 15:02"))) 201: Redistat::Summary.send(:update_fields, key, @stats2, :hour) 202 [first - 1.hour, last + 1.hour] 203 end /Users/felipeclopes/projects/redisrank/spec/key_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Key do 4: include Redistat::Database 5 6 before(:each) do . 10 @label_hash = Digest::SHA1.hexdigest(@label) 11 @date = Time.now 12: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour}) 13 end 14 .. 18 @key.label_hash.should == @label_hash 19 @key.groups.map { |k| k.instance_variable_get("@label") }.should == @key.instance_variable_get("@label").groups 20: @key.date.should be_instance_of(Redistat::Date) 21 @key.date.to_time.to_s.should == @date.to_s 22 end .. 29 props.pop 30 end 31: key = Redistat::Key.new(@scope, nil, @date, {:depth => :hour}) 32 key.to_s.should == "#{@scope}:#{key.date.to_s(:hour)}" 33 end 34 35 it "should abide to hashed_label option" do 36: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => true}) 37 @key.to_s.should == "#{@scope}/#{@label_hash}:#{@key.date.to_s(:hour)}" 38: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour, :hashed_label => false}) 39 @key.to_s.should == "#{@scope}/#{@label}:#{@key.date.to_s(:hour)}" 40 end 41 42 it "should have default depth option" do 43: @key = Redistat::Key.new(@scope, @label, @date) 44 @key.depth.should == :hour 45 end .. 69 before(:each) do 70 @label = "message/public/offensive" 71: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :hour}) 72 end 73 .. 78 "message" ] 79 80: key = Redistat::Key.new(@scope, label, @date, {:depth => :hour}) 81 82 key.groups.map { |k| k.label.to_s }.should == result .. 84 85 it "should know it's parent" do 86: @key.parent.should be_a(Redistat::Key) 87 @key.parent.label.to_s.should == 'message/public' 88: Redistat::Key.new(@scope, 'hello', @date).parent.should be_nil 89 end 90 91 it "should update label index and return children" do 92: db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}").should == [] 93 @key.children.count.should be(0) 94 95 @key.update_index # indexing 'message/publish/offensive' 96: Redistat::Key.new("PageViews", "message/public/die").update_index # indexing 'message/publish/die' 97: Redistat::Key.new("PageViews", "message/public/live").update_index # indexing 'message/publish/live' 98 99: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{@key.label.parent}") # checking 'message/public' 100 members.count.should be(3) 101 members.should include('offensive') ... 104 105 key = @key.parent 106: key.children.first.should be_a(Redistat::Key) 107 key.children.count.should be(3) 108 key.children.map { |k| k.label.me }.should == members 109 110: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}#{key.label.parent}") # checking 'message' 111 members.count.should be(1) 112 members.should include('public') ... 116 key.children.map { |k| k.label.me }.should == members 117 118: members = db.smembers("#{@scope}#{Redistat::LABEL_INDEX}") # checking '' 119 members.count.should be(1) 120 members.should include('message') 121 122 key.parent.should be_nil 123: key = Redistat::Key.new("PageViews") 124 key.children.count.should be(1) 125 key.children.map { |k| k.label.me }.should include('message') /Users/felipeclopes/projects/redisrank/spec/label_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Label do 4: include Redistat::Database 5 6 before(:each) do 7 db.flushdb 8 @name = "about_us" 9: @label = Redistat::Label.new(@name) 10 end 11 .. 16 17 it "should store a label hash lookup key" do 18: label = Redistat::Label.new(@name, {:hashed_label => true}).save 19 label.saved?.should be(true) 20: db.hget(Redistat::KEY_LABELS, label.hash).should == @name 21 22 name = "contact_us" 23: label = Redistat::Label.create(name, {:hashed_label => true}) 24 label.saved?.should be(true) 25: db.hget(Redistat::KEY_LABELS, label.hash).should == name 26 end 27 28 it "should join labels" do 29: include Redistat 30: label = Redistat::Label.join('email', 'message', 'public') 31: label.should be_a(Redistat::Label) 32 label.to_s.should == 'email/message/public' 33: label = Redistat::Label.join(Redistat::Label.new('email'), Redistat::Label.new('message'), Redistat::Label.new('public')) 34: label.should be_a(Redistat::Label) 35 label.to_s.should == 'email/message/public' 36: label = Redistat::Label.join('email', '', 'message', nil, 'public') 37: label.should be_a(Redistat::Label) 38 label.to_s.should == 'email/message/public' 39 end 40 41 it "should allow you to use a different group separator" do 42: include Redistat 43: Redistat.group_separator = '|' 44: label = Redistat::Label.join('email', 'message', 'public') 45: label.should be_a(Redistat::Label) 46 label.to_s.should == 'email|message|public' 47: label = Redistat::Label.join(Redistat::Label.new('email'), Redistat::Label.new('message'), Redistat::Label.new('public')) 48: label.should be_a(Redistat::Label) 49 label.to_s.should == 'email|message|public' 50: label = Redistat::Label.join('email', '', 'message', nil, 'public') 51: label.should be_a(Redistat::Label) 52 label.to_s.should == 'email|message|public' 53: Redistat.group_separator = Redistat::GROUP_SEPARATOR 54 end 55 .. 57 before(:each) do 58 @name = "message/public/offensive" 59: @label = Redistat::Label.new(@name) 60 end 61 62 it "should know it's parent label group" do 63 @label.parent.to_s.should == 'message/public' 64: Redistat::Label.new('hello').parent.should be_nil 65 end 66 .. 72 73 @name = "/message/public/" 74: @label = Redistat::Label.new(@name) 75 @label.name.should == @name 76 @label.groups.map { |l| l.to_s }.should == [ "message/public", .. 78 79 @name = "message" 80: @label = Redistat::Label.new(@name) 81 @label.name.should == @name 82 @label.groups.map { |l| l.to_s }.should == [ "message" ] /Users/felipeclopes/projects/redisrank/spec/model_helper.rb: 2 3 class ModelHelper1 4: include Redistat::Model 5 6 . 8 9 class ModelHelper2 10: include Redistat::Model 11 12 depth :day .. 17 18 class ModelHelper3 19: include Redistat::Model 20 21 connect_to :port => 8379, :db => 14 .. 24 25 class ModelHelper4 26: include Redistat::Model 27 28 scope "FancyHelper" /Users/felipeclopes/projects/redisrank/spec/model_spec.rb: 2 require "model_helper" 3 4: describe Redistat::Model do 5: include Redistat::Database 6 7 before(:each) do . 22 one_hour_ago = 1.hour.ago 23 finder = ModelHelper1.find('label', two_hours_ago, one_hour_ago) 24: finder.should be_a(Redistat::Finder) 25 finder.options[:scope].to_s.should == 'ModelHelper1' 26 finder.options[:label].to_s.should == 'label' .. 30 31 it "should #find_event" do 32: Redistat::Event.should_receive(:find).with('ModelHelper1', 1) 33 ModelHelper1.find_event(1) 34 end .. 151 describe "Write Buffer" do 152 before(:each) do 153: Redistat.buffer_size = 20 154 end 155 156 after(:each) do 157: Redistat.buffer_size = 0 158 end 159 ... 181 end 182 ModelHelper1.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1)).rank.should == {} 183: Redistat.buffer.flush(true) 184 185 stats = ModelHelper1.fetch("sheep.black", @time.hours_ago(5), @time.hours_since(1)) /Users/felipeclopes/projects/redisrank/spec/options_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Options do 4 5 before(:each) do . 24 25 class OptionsHelper 26: include Redistat::Options 27 28 option_accessor :hello /Users/felipeclopes/projects/redisrank/spec/result_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Result do 4 5 it "should should initialize properly" do 6 options = {:from => "from", :till => "till"} 7: result = Redistat::Result.new(options) 8 result.from.should == "from" 9 result.till.should == "till" .. 11 12 it "should have merge_to_max method" do 13: result = Redistat::Result.new 14 result[:world].should be_nil 15 result.merge_to_max(:world, 3) /Users/felipeclopes/projects/redisrank/spec/scope_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Scope do 4: include Redistat::Database 5 6 before(:all) do . 10 before(:each) do 11 @name = "PageViews" 12: @scope = Redistat::Scope.new(@name) 13 end 14 .. 18 19 it "should increment next_id" do 20: scope = Redistat::Scope.new("Visitors") 21 @scope.next_id.should == 1 22 scope.next_id.should == 1 /Users/felipeclopes/projects/redisrank/spec/spec_helper.rb: 14 require 'rspec/autorun' 15 16: # use the test Redistat instance 17: Redistat.connect(:port => 8379, :db => 15, :thread_safe => true) 18: Redistat.redis.flushdb 19 /Users/felipeclopes/projects/redisrank/spec/summary_spec.rb: 1 require "spec_helper" 2 3: describe Redistat::Summary do 4: include Redistat::Database 5 6 before(:each) do . 9 @label = "about_us" 10 @date = Time.now 11: @key = Redistat::Key.new(@scope, @label, @date, {:depth => :day}) 12 @stats = {"views" => 3, "visitors" => 2} 13 @expire = {:hour => 24*3600} .. 15 16 it "should update a single summary properly" do 17: Redistat::Summary.send(:update_fields, @key, @stats, :hour) 18 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 19 expect(summary.count).to be 2 .. 25 expect(visitors.last).to be 2.0 26 27: Redistat::Summary.send(:update_fields, @key, @stats, :hour) 28 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 29 expect(summary.count).to be 2 .. 37 38 it "should set key expiry properly" do 39: Redistat::Summary.update_all(@key, @stats, :hour,{:expire => @expire}) 40 ((24*3600)-1..(24*3600)+1).should include(db.ttl(@key.to_s(:hour))) 41 [:day, :month, :year].each do |depth| .. 44 45 db.flushdb 46: Redistat::Summary.update_all(@key, @stats, :hour, {:expire => {}}) 47 [:hour, :day, :month, :year].each do |depth| 48 db.ttl(@key.to_s(depth)).should == -1 .. 51 52 it "should update all summaries properly" do 53: Redistat::Summary.update_all(@key, @stats, :sec) 54 [:year, :month, :day, :hour, :min, :sec, :usec].each do |depth| 55 summary = db.zrevrange(@key.to_s(depth), 0, -1, :with_scores => true) .. 69 70 it "should update summaries even if no label is set" do 71: key = Redistat::Key.new(@scope, nil, @date, {:depth => :day}) 72: Redistat::Summary.send(:update_fields, key, @stats, :hour) 73 summary = db.zrevrange(key.to_s(:hour), 0, -1, :with_scores => true) 74 views = summary.first .. 84 "death/bomb" => 4, "death/unicorn" => 3, 85 :"od/sugar" => 7, :"od/meth" => 8 } 86: res = Redistat::Summary.send(:inject_group_summaries, hash) 87 res.should == { "count" => 10, "count/hello" => 3, "count/world" => 7, 88 "death" => 7, "death/bomb" => 4, "death/unicorn" => 3, .. 92 it "should properly store key group summaries" do 93 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 94: Redistat::Summary.update_all(@key, stats, :hour) 95 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 96 summary.count.should eq(4) .. 109 it "should not store key group summaries when option is disabled" do 110 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 111: Redistat::Summary.update_all(@key, stats, :hour, {:enable_grouping => false}) 112 summary = db.zrevrange(@key.to_s(:hour), 0, -1, :with_scores => true) 113 summary.count.should eq(3) ... 125 stats = {"views" => 3, "visitors/eu" => 2, "visitors/us" => 4} 126 label = "views/about_us" 127: key = Redistat::Key.new(@scope, label, @date) 128: Redistat::Summary.update_all(key, stats, :hour) 129 130 key.groups[0].label.to_s.should == "views/about_us" ... 134 135 label = "views/contact" 136: key = Redistat::Key.new(@scope, label, @date) 137: Redistat::Summary.update_all(key, stats, :hour) 138 139 key.groups[0].label.to_s.should == "views/contact" /Users/felipeclopes/projects/redisrank/spec/synchronize_spec.rb: 1 require "spec_helper" 2 3: module Redistat 4 describe Synchronize do 5 . 62 63 describe '.monitor' do 64: it 'defers to Redistat::Synchronize' do 65 klass.should_receive(:monitor).once 66 subject.monitor .. 69 70 describe '.thread_safe' do 71: it ' defers to Redistat::Synchronize' do 72 klass.should_receive(:thread_safe).once 73 subject.thread_safe .. 76 77 describe '.thread_safe=' do 78: it 'defers to Redistat::Synchronize' do 79 klass.should_receive(:thread_safe=).once.with(true) 80 subject.thread_safe = true .. 119 120 end # Synchronize 121: end # Redistat 122 123 class SynchronizeSpecHelper 124: include Redistat::Synchronize 125 end 126 /Users/felipeclopes/projects/redisrank/spec/thread_safety_spec.rb: 2 3 describe "Thread-Safety" do 4: include Redistat::Database 5 6 before(:each) do . 23 it "should store event in multiple threads" do 24 class ThreadSafetySpec 25: include Redistat::Model 26 end 27 threads = [] 304 matches across 46 files