fnordmetric 0.5.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -277,6 +277,23 @@ FnordMetric.namespace :myapp do
277
277
 
278
278
  end
279
279
 
280
+ FnordMetric.server_configuration = {
281
+ :redis_url => "redis://localhost:6379",
282
+ :redis_prefix => "fnordmetric",
283
+ :inbound_stream => ["0.0.0.0", "1339"],
284
+ :start_worker => true,
285
+ :print_stats => 3,
286
+
287
+ # events that aren't processed after 2 min get dropped
288
+ :event_queue_ttl => 120,
289
+
290
+ # event data is kept for one month
291
+ :event_data_ttl => 3600*24*30,
292
+
293
+ # session data is kept for one month
294
+ :session_data_ttl => 3600*24*30
295
+ }
296
+
280
297
  FnordMetric.standalone
281
298
  ```
282
299
 
@@ -288,6 +305,8 @@ Contributors
288
305
  + Marco Borromeo (http://github.com/mborromeo)
289
306
  + Leo Lou (http://github.com/l4u)
290
307
  + Andy Lindeman (http://github.com/alindeman)
308
+ + Jurriaan Pruis (http://github.com/jurriaan)
309
+ + Kacper Bielecki (http://github.com/kazjote)
291
310
 
292
311
  To contribute, please fork this repository, make your changes and run the specs, commit them to your github repository and send me a pull request.
293
312
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.9
1
+ 0.6.0
data/doc/ulm_stats.rb CHANGED
@@ -615,6 +615,24 @@ FnordMetric.namespace :ulm do
615
615
 
616
616
  end
617
617
 
618
+ FnordMetric.server_configuration = {
619
+ :redis_url => "redis://localhost:6379",
620
+ :redis_prefix => "fnordmetric",
621
+ :inbound_stream => ["0.0.0.0", "1337"],
622
+ :web_interface => ["0.0.0.0", "4242"],
623
+ :start_worker => true,
624
+ :print_stats => 3,
625
+
626
+ # events that aren't processed after 2 min get dropped
627
+ :event_queue_ttl => 120,
628
+
629
+ # event data is kept for one month
630
+ :event_data_ttl => 3600*24*30,
631
+
632
+ # session data is kept for one month
633
+ :session_data_ttl => 3600*24*30
634
+ }
635
+
618
636
  #task :setup do
619
637
  # @fm_opts = {:web_interface => ["0.0.0.0", "2323"]} if ENV['DEV']
620
638
  #end
data/fnordmetric.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "fnordmetric"
8
- s.version = "0.5.9"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Paul Asmuth"]
12
- s.date = "2012-01-04"
12
+ s.date = "2012-01-11"
13
13
  s.description = "FnordMetric is a Ruby Event-Tracking gem on steroids"
14
14
  s.email = "paul@paulasmuth.com"
15
15
  s.extra_rdoc_files = [
data/lib/fnordmetric.rb CHANGED
@@ -15,6 +15,10 @@ module FnordMetric
15
15
  def self.namespace(key=nil, &block)
16
16
  @@namespaces[key] = block
17
17
  end
18
+
19
+ def self.server_configuration=(configuration)
20
+ @@server_configuration = configuration
21
+ end
18
22
 
19
23
  def self.default_options(opts)
20
24
 
@@ -90,7 +94,8 @@ module FnordMetric
90
94
  puts(msg); exit!
91
95
  end
92
96
 
93
- def self.run(opts={})
97
+ def self.run
98
+ opts = (defined?(@@server_configuration) && @@server_configuration) || {}
94
99
  start_em(opts)
95
100
  rescue Exception => e
96
101
  log "!!! eventmachine died, restarting... #{e.message}"
@@ -97,6 +97,8 @@ class FnordMetric::App < Sinatra::Base
97
97
 
98
98
  events = if params[:type]
99
99
  current_namespace.events(:by_type, :type => params[:type])
100
+ elsif params[:session_key]
101
+ current_namespace.events(:by_session_key, :session_key => params[:session_key])
100
102
  else
101
103
  find_opts = { :limit => 100 }
102
104
  find_opts.merge!(:since => params[:since].to_i+1) if params[:since]
@@ -1,78 +1,86 @@
1
- class FnordMetric::Event
1
+ module FnordMetric
2
+ class Event
2
3
 
3
- attr_accessor :time, :type, :event_id
4
+ attr_accessor :time, :type, :event_id
4
5
 
5
- #def self.track!(event_type, event_data)
6
- #end
6
+ #def self.track!(event_type, event_data)
7
+ #end
7
8
 
8
- def self.all(opts)
9
- range_opts = { :withscores => true }
10
- range_opts.merge!(:limit => [0,opts[:limit]]) if opts[:limit]
11
- opts[:redis].zrevrangebyscore(
12
- "#{opts[:namespace_prefix]}-timeline",
13
- '+inf', opts[:since]||'0',
14
- range_opts
15
- ).in_groups_of(2).map do |event_id, ts|
16
- next if event_id.blank?
17
- find(event_id, opts).tap{ |e| e.time = ts }
9
+ def self.all(opts)
10
+ range_opts = { :withscores => true }
11
+ range_opts.merge!(:limit => [0,opts[:limit]]) if opts[:limit]
12
+ opts[:redis].zrevrangebyscore(
13
+ "#{opts[:namespace_prefix]}-timeline",
14
+ '+inf', opts[:since]||'0',
15
+ range_opts
16
+ ).in_groups_of(2).map do |event_id, ts|
17
+ next if event_id.blank?
18
+ find(event_id, opts).tap{ |e| e.time = ts }
19
+ end
18
20
  end
19
- end
20
21
 
21
- def self.by_type(_type, opts)
22
- opts[:redis].lrange(
23
- "#{opts[:namespace_prefix]}-type-#{_type}",
24
- 0, 200).map do |event_id|
25
- next if event_id.blank?
26
- find(event_id, opts).tap{ |e| }
22
+ def self.by_type(_type, opts)
23
+ opts[:redis].lrange(
24
+ "#{opts[:namespace_prefix]}-type-#{_type}",
25
+ 0, 200).map do |event_id|
26
+ next if event_id.blank?
27
+ find(event_id, opts).tap{ |e| }
28
+ end
27
29
  end
28
- end
29
30
 
30
- def self.find(event_id, opts)
31
- self.new(event_id, opts).tap do |event|
32
- event.fetch!
31
+ def self.by_session_key(_session_key, opts)
32
+ session = Session.find(_session_key, opts)
33
+ session.fetch_event_ids!(200).reject(&:blank?).map do |event_id|
34
+ find(event_id, opts)
35
+ end
33
36
  end
34
- end
35
37
 
36
- def initialize(event_id, opts)
37
- @opts = opts
38
- @event_id = event_id
39
- end
38
+ def self.find(event_id, opts)
39
+ self.new(event_id, opts).tap do |event|
40
+ event.fetch!
41
+ end
42
+ end
40
43
 
41
- def fetch!
42
- @data = JSON.parse(fetch_json).tap do |event|
43
- @type = event.delete("_type")
44
+ def initialize(event_id, opts)
45
+ @opts = opts
46
+ @event_id = event_id
44
47
  end
45
- end
46
48
 
47
- def fetch_json
48
- @opts[:redis].get(redis_key) || "{}"
49
- end
49
+ def fetch!
50
+ @data = JSON.parse(fetch_json).tap do |event|
51
+ @type = event.delete("_type")
52
+ end
53
+ end
50
54
 
51
- def redis_key
52
- [@opts[:redis_prefix], :event, @event_id].join("-")
53
- end
55
+ def fetch_json
56
+ @opts[:redis].get(redis_key) || "{}"
57
+ end
54
58
 
55
- def session_key
56
- @data["_session"] ? Digest::MD5.hexdigest(@data["_session"]) : nil
57
- end
59
+ def redis_key
60
+ [@opts[:redis_prefix], :event, @event_id].join("-")
61
+ end
58
62
 
59
- def id
60
- @event_id
61
- end
62
-
63
- def data(key=nil)
64
- key ? @data[key.to_s] : @data
65
- end
63
+ def session_key
64
+ @data["_session"] ? Digest::MD5.hexdigest(@data["_session"]) : nil
65
+ end
66
66
 
67
- alias :[] :data
67
+ def id
68
+ @event_id
69
+ end
70
+
71
+ def data(key=nil)
72
+ key ? @data[key.to_s] : @data
73
+ end
68
74
 
69
- def to_json
70
- @data.merge!(
71
- :_type => @type,
72
- :_session_key => session_key,
73
- :_eid => @event_id,
74
- :_time => @time
75
- )
76
- end
75
+ alias :[] :data
77
76
 
78
- end
77
+ def to_json
78
+ @data.merge!(
79
+ :_type => @type,
80
+ :_session_key => session_key,
81
+ :_eid => @event_id,
82
+ :_time => @time
83
+ )
84
+ end
85
+ end
86
+ end
@@ -1,3 +1,4 @@
1
+ require 'securerandom'
1
2
  class FnordMetric::InboundStream < EventMachine::Connection
2
3
 
3
4
  @@opts = nil
@@ -43,7 +44,7 @@ class FnordMetric::InboundStream < EventMachine::Connection
43
44
  end
44
45
 
45
46
  def get_next_uuid
46
- rand(9999999999999999999).to_s # FIXME
47
+ SecureRandom.uuid
47
48
  end
48
49
 
49
50
  def close_connection?
@@ -77,6 +77,7 @@ class FnordMetric::Namespace
77
77
  def events(_ids, opts={})
78
78
  return FnordMetric::Event.all(extend_opts(opts)) if _ids == :all
79
79
  return FnordMetric::Event.by_type(opts.delete(:type), extend_opts(opts)) if _ids == :by_type
80
+ return FnordMetric::Event.by_session_key(opts.delete(:session_key), extend_opts(opts)) if _ids == :by_session_key
80
81
  end
81
82
 
82
83
  def method_missing(m, *args, &block)
@@ -89,6 +89,7 @@ class FnordMetric::Session
89
89
 
90
90
  def add_event(event)
91
91
  @redis.zadd(redis_key(:events), event[:_time], event[:_eid])
92
+
92
93
  add_data(:_picture, event[:url]) if event[:_type] == "_set_picture"
93
94
  add_data(:_name, event[:name]) if event[:_type] == "_set_name"
94
95
  add_event_data(event) if event[:_type] == "_set_data"
@@ -125,7 +126,7 @@ class FnordMetric::Session
125
126
 
126
127
  def fetch_event_ids!(since=-1)
127
128
  # FIXME: use WITHSCORE to get the timestamps and return event objects
128
- @event_ids = @redis.zrange(redis_key(:events), 0, since)
129
+ @event_ids = @redis.zrevrange(redis_key(:events), 0, since)
129
130
  end
130
131
 
131
- end
132
+ end
data/pub/fnordmetric.css CHANGED
@@ -107,11 +107,14 @@ text-shadow: 1px 0px 2px rgba(255, 255, 255, 1);
107
107
 
108
108
 
109
109
  ul.session_list{ list-style-type:none; margin:0; padding:9px 16px 0 11px; }
110
- ul.session_list li{ color:#0A0A0A; margin-bottom:10px; height:18px; overflow:hidden; line-height:18px; padding:4px; }
110
+ ul.session_list li{ color:#0A0A0A; height:32px; overflow:hidden; line-height:18px; padding:4px; }
111
111
  ul.session_list li:hover{ background:#eee; cursor:pointer; }
112
+ ul.session_list li input{ float: left; margin-right: 7px; }
112
113
  ul.session_list li .picture{ height:18px; width:18px; float:left; background:#333; overflow:hidden; }
113
114
  ul.session_list li .name{ float:left; width:120px; overflow:hidden; margin-left:10px; font-size:12px; }
114
115
  ul.session_list li .time{ float:right; width:40px; overflow:hidden; text-align:right; font-size:10px; }
116
+ ul.session_list li .history{ float:right; color:#999; font-size:10px; line-height: 12px;}
117
+ ul.session_list li .history:hover{ color:#333; text-decoration:underline; }
115
118
 
116
119
  .sessions_feed{ min-width:300px; min-height:100px; float:left; }
117
120
  .sessions_feed ul.feed_inner{ margin:5px 15px; min-height:100px; padding:0px; }
@@ -139,4 +142,4 @@ ul.event_type_list li .history:hover{ color:#333; text-decoration:underline; }
139
142
  height: 0;
140
143
  }
141
144
 
142
- .highcharts-series circle{ stroke-width:1px; }
145
+ .highcharts-series circle{ stroke-width:1px; }
data/pub/fnordmetric.js CHANGED
@@ -78,9 +78,15 @@ var FnordMetric = (function(){
78
78
  }
79
79
  }
80
80
 
81
+ function formatPercentValue(value){
82
+ return value + '%';
83
+ }
84
+
81
85
  function formatGaugeValue(gauge_key, value){
82
86
  if(gauge_key.slice(0,8) === '__time__'){
83
87
  return formatTimeValue(value);
88
+ } else if(gauge_key.slice(0,11) === '__percent__'){
89
+ return formatPercentValue(value);
84
90
  } else {
85
91
  return formatValue(value);
86
92
  }
@@ -733,7 +739,7 @@ var FnordMetric = (function(){
733
739
  ).append(listElem);
734
740
 
735
741
  var eventsPolledUntil = false;
736
- var eventsFilter = [];
742
+ var eventsFilter = {uncheckedTypes: [], checkedSessions: []};
737
743
  var sessionData = {};
738
744
  var pollRunning = true;
739
745
 
@@ -771,10 +777,11 @@ var FnordMetric = (function(){
771
777
  });
772
778
  };
773
779
 
774
- function loadEventHistory(event_type){
780
+ function loadEventHistory(params){
775
781
  feedInnerElem.html('');
776
782
  $.ajax({
777
- url: FnordMetric.p + '/' + currentNamespace+'/events?type='+event_type,
783
+ url: FnordMetric.p + '/' + currentNamespace+'/events',
784
+ data: params,
778
785
  success: function(_data, _status){
779
786
  var data = JSON.parse(_data).events;
780
787
  for(var n=data.length; n >= 0; n--){
@@ -807,14 +814,19 @@ var FnordMetric = (function(){
807
814
  });
808
815
  };
809
816
 
817
+ function setCheckboxesCheckedState(types_state, sessions_state) {
818
+ $('.event_type_list .event_type input').attr('checked', types_state);
819
+ $('.session_list .session input').attr('checked', sessions_state);
820
+ }
821
+
810
822
  function addEventType(type, display){
811
823
  typeListElem.append(
812
824
  $('<li class="event_type"></li>').append(
813
825
  $('<span class="history"></span>').html('history')
814
826
  .click(function(){
815
- $('.event_type_list .event_type input').attr('checked', false);
827
+ setCheckboxesCheckedState(true, true);
816
828
  $('input', $(this).parent()).attr('checked', true);
817
- updateEventFilter(); loadEventHistory(type);
829
+ updateEventFilter(); loadEventHistory({type: type});
818
830
  })
819
831
  ).append(
820
832
  $('<input type="checkbox" />').attr('checked', true)
@@ -832,7 +844,15 @@ var FnordMetric = (function(){
832
844
  _unchecked_types.push($(v).attr('rel'));
833
845
  }
834
846
  });
835
- eventsFilter = _unchecked_types;
847
+ eventsFilter.uncheckedTypes = _unchecked_types;
848
+
849
+ var _checked_sessions = [];
850
+ $('ul.session_list li.session').each(function(i,v){
851
+ if($('input', v).attr('checked')){
852
+ _checked_sessions.push($(v).data().session);
853
+ }
854
+ });
855
+ eventsFilter.checkedSessions = _checked_sessions;
836
856
  }
837
857
 
838
858
  function doEventsPoll(){
@@ -848,18 +868,40 @@ var FnordMetric = (function(){
848
868
  return (function(_data, _status){
849
869
  var data = JSON.parse(_data)
850
870
  var events = data.events;
851
- var timout = 1000;
871
+ var timeout = 1000;
852
872
  var maxevents = 200;
873
+ var passesFiltering = function(event_data) {
874
+ var passes_type_filtering = false;
875
+ var passes_session_filtering = false;
876
+ if(eventsFilter.uncheckedTypes.indexOf(event_data._type) == -1) {
877
+ if(parseInt(v._time)<=eventsPolledUntil) {
878
+ passes_type_filtering = true;
879
+ }
880
+ }
881
+ if(!passes_type_filtering) return false;
882
+
883
+ if(eventsFilter.checkedSessions.length == 0){
884
+ return true; // No filter set - show all events
885
+ } else {
886
+ if(event_data._session_key){
887
+ if(eventsFilter.checkedSessions.indexOf(event_data._session_key) >= 0){
888
+ return true; // Filter set and match
889
+ } else {
890
+ return false; // Filter set but no match
891
+ }
892
+ } else {
893
+ return false; // Filter set but event is not associated with session
894
+ }
895
+ }
896
+ }
897
+
853
898
  if(events.length > 0){
854
- timeout = 200;
855
899
  eventsPolledUntil = parseInt(events[0]._time)-1;
856
900
  }
857
901
  for(var n=events.length-1; n >= 0; n--){
858
902
  var v = events[n];
859
- if(eventsFilter.indexOf(v._type) == -1){
860
- if(parseInt(v._time)<=eventsPolledUntil){
861
- renderEvent(v);
862
- }
903
+ if(passesFiltering(v)) {
904
+ renderEvent(v);
863
905
  }
864
906
  };
865
907
  var elems = $("p", feedInnerElem);
@@ -867,13 +909,17 @@ var FnordMetric = (function(){
867
909
  $(elems[n]).remove();
868
910
  }
869
911
  if(pollRunning){
870
- window.setTimeout(doEventsPoll(), timout);
912
+ window.setTimeout(doEventsPoll(), timeout);
871
913
  }
872
914
  });
873
915
  };
874
916
 
875
917
  function updateSession(session_data){
876
- sessionData[session_data.session_key] = session_data;
918
+ var session_key = session_data.session_key;
919
+ if(!sessionData[session_key]){
920
+ updateEventFilter()
921
+ }
922
+ sessionData[session_key] = session_data;
877
923
  renderSession(session_data);
878
924
  }
879
925
 
@@ -913,11 +959,20 @@ var FnordMetric = (function(){
913
959
 
914
960
  listElem.append(
915
961
  $('<li class="session"></li>').append(
962
+ $('<input type="checkbox" />').click(function(){ updateEventFilter(); })
963
+ ).append(
916
964
  $('<div class="picture"></div>').html(session_picture)
917
965
  ).append(
918
966
  $('<span class="name"></span>').html(session_name)
919
967
  ).append(
920
968
  $('<span class="time"></span>').html(session_time)
969
+ ).append(
970
+ $('<span class="history"></span>').html('history')
971
+ .click(function(){
972
+ setCheckboxesCheckedState(true, false);
973
+ $('input', $(this).parent()).attr('checked', true);
974
+ updateEventFilter(); loadEventHistory({session_key: session_data["session_key"]});
975
+ })
921
976
  ).attr('data-session', session_data["session_key"])
922
977
  );
923
978
 
data/spec/app_spec.rb CHANGED
@@ -334,6 +334,38 @@ describe "app" do
334
334
  JSON.parse(last_response.body)["events"].length.should == 1
335
335
  end
336
336
 
337
+ it "should render all events for a single session key" do
338
+ @namespace.ready!(@redis_wrap).announce(
339
+ :_time => @now,
340
+ :_session => "max_session",
341
+ :_eid => "124234"
342
+ )
343
+ @namespace.ready!(@redis_wrap).announce(
344
+ :_time => @now,
345
+ :_session => "kate_session",
346
+ :_eid => "12235234"
347
+ )
348
+ @namespace.ready!(@redis_wrap).announce(
349
+ :_time => @now,
350
+ :_session => "kate_session",
351
+ :_eid => "124234234"
352
+ )
353
+ @namespace.ready!(@redis_wrap).announce(
354
+ :_time => @now,
355
+ :_eid => "124234234"
356
+ )
357
+ max_session_key = Digest::MD5.hexdigest "max_session"
358
+ kate_session_key = Digest::MD5.hexdigest "kate_session"
359
+
360
+ get "/foospace/events?session_key=#{max_session_key}"
361
+ events = JSON.parse(last_response.body)["events"]
362
+ events.length.should == 1
363
+
364
+ get "/foospace/events?session_key=#{kate_session_key}"
365
+ events = JSON.parse(last_response.body)["events"]
366
+ events.length.should == 2
367
+ end
368
+
337
369
  it "should render a list of event types" do
338
370
  @namespace.ready!(@redis_wrap).announce(
339
371
  :_type => "fn0rd",
@@ -449,4 +481,4 @@ describe "app" do
449
481
 
450
482
  end
451
483
 
452
- end
484
+ end
data/spec/event_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require ::File.expand_path('../spec_helper.rb', __FILE__)
2
+ require 'irb'
2
3
 
3
4
  describe FnordMetric::Event do
4
5
 
@@ -116,6 +117,48 @@ describe FnordMetric::Event do
116
117
  Event.by_type('f00bar', @opts).length.should == 1
117
118
  end
118
119
 
120
+ describe "looking by session_key" do
121
+ let(:namespace) do
122
+ options = {:redis_prefix => "fnordmetric-test", :session_data_ttl => 1}
123
+ Namespace.new(:ns123, options)
124
+ end
125
+
126
+ let(:events_data) do
127
+ [[1, "max_session"], [2, "kate_session"], [3, "kate_session"], [12345678, nil]]
128
+ end
129
+
130
+ let(:kate_session_key) { Digest::MD5.hexdigest "kate_session" }
131
+ let(:events) { Event.by_session_key kate_session_key, @opts }
132
+
133
+ before do
134
+ created_events_data.each do |(event_id, session)|
135
+ event_data = { :_time => @now + event_id, :_eid => event_id }
136
+ event_data[:_session] = session if session
137
+ namespace.ready!(@redis_wrap).announce event_data
138
+ end
139
+ end
140
+
141
+ context "when events are added in ascending time order" do
142
+ let(:created_events_data) { events_data }
143
+
144
+ it "should find correct events" do
145
+ ["2","3"].each {|id| events.map(&:id).should include(id) }
146
+ end
147
+
148
+ it "should have correct order" do
149
+ events.map(&:id).should == ["3","2"]
150
+ end
151
+ end
152
+
153
+ context "when events are added in descending time order" do
154
+ let(:created_events_data) { events_data.reverse }
155
+
156
+ it "should have correct order" do
157
+ events.map(&:id).should == ["3","2"]
158
+ end
159
+ end
160
+ end
161
+
119
162
  def create_event(event_id, event_data)
120
163
  @redis_wrap.zadd(@timeline, event_data.delete(:_time), event_id)
121
164
  @redis.set("fnordmetric-test-event-#{event_id}", event_data.to_json)
@@ -124,4 +167,4 @@ describe FnordMetric::Event do
124
167
  end
125
168
 
126
169
 
127
- end
170
+ end
data/spec/session_spec.rb CHANGED
@@ -212,8 +212,8 @@ describe FnordMetric::Session do
212
212
  @redis_wrap.zadd("#{@namespace}-session-#{sesshash}-events", @now, "fnord")
213
213
  sess = Session.find(sesshash, @opts)
214
214
  sess.fetch_event_ids!
215
- sess.event_ids[0].should == "fnord"
216
- sess.event_ids[1].should == "shmoo"
215
+ sess.event_ids.should include("shmoo")
216
+ sess.event_ids.should include("fnord")
217
217
  end
218
218
 
219
219
  def create_session(sesskey, sesstime, sessdata)
@@ -228,4 +228,4 @@ describe FnordMetric::Session do
228
228
  end
229
229
 
230
230
 
231
- end
231
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fnordmetric
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.9
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-04 00:00:00.000000000 Z
12
+ date: 2012-01-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bson_ext
16
- requirement: &70308910325440 !ruby/object:Gem::Requirement
16
+ requirement: &70186955498840 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.4.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70308910325440
24
+ version_requirements: *70186955498840
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sinatra
27
- requirement: &70308910324960 !ruby/object:Gem::Requirement
27
+ requirement: &70186955498360 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.2.6
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70308910324960
35
+ version_requirements: *70186955498360
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: redis
38
- requirement: &70308910324480 !ruby/object:Gem::Requirement
38
+ requirement: &70186955497820 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.2.2
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70308910324480
46
+ version_requirements: *70186955497820
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: eventmachine
49
- requirement: &70308910324000 !ruby/object:Gem::Requirement
49
+ requirement: &70186955497320 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70308910324000
57
+ version_requirements: *70186955497320
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: em-hiredis
60
- requirement: &70308910323480 !ruby/object:Gem::Requirement
60
+ requirement: &70186955496720 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70308910323480
68
+ version_requirements: *70186955496720
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: json
71
- requirement: &70308910322980 !ruby/object:Gem::Requirement
71
+ requirement: &70186955496080 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :runtime
78
78
  prerelease: false
79
- version_requirements: *70308910322980
79
+ version_requirements: *70186955496080
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: i18n
82
- requirement: &70308910322360 !ruby/object:Gem::Requirement
82
+ requirement: &70186955495200 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :runtime
89
89
  prerelease: false
90
- version_requirements: *70308910322360
90
+ version_requirements: *70186955495200
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: haml
93
- requirement: &70308910321760 !ruby/object:Gem::Requirement
93
+ requirement: &70186955494700 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :runtime
100
100
  prerelease: false
101
- version_requirements: *70308910321760
101
+ version_requirements: *70186955494700
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: rack
104
- requirement: &70308910321100 !ruby/object:Gem::Requirement
104
+ requirement: &70186955494200 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :runtime
111
111
  prerelease: false
112
- version_requirements: *70308910321100
112
+ version_requirements: *70186955494200
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: rack-test
115
- requirement: &70308910320340 !ruby/object:Gem::Requirement
115
+ requirement: &70186955490620 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: '0'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *70308910320340
123
+ version_requirements: *70186955490620
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: yajl-ruby
126
- requirement: &70308910319860 !ruby/object:Gem::Requirement
126
+ requirement: &70186955490140 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ! '>='
@@ -131,10 +131,10 @@ dependencies:
131
131
  version: '0'
132
132
  type: :runtime
133
133
  prerelease: false
134
- version_requirements: *70308910319860
134
+ version_requirements: *70186955490140
135
135
  - !ruby/object:Gem::Dependency
136
136
  name: thin
137
- requirement: &70308910319200 !ruby/object:Gem::Requirement
137
+ requirement: &70186955489620 !ruby/object:Gem::Requirement
138
138
  none: false
139
139
  requirements:
140
140
  - - ! '>='
@@ -142,10 +142,10 @@ dependencies:
142
142
  version: '0'
143
143
  type: :runtime
144
144
  prerelease: false
145
- version_requirements: *70308910319200
145
+ version_requirements: *70186955489620
146
146
  - !ruby/object:Gem::Dependency
147
147
  name: activesupport
148
- requirement: &70308910312620 !ruby/object:Gem::Requirement
148
+ requirement: &70186955489120 !ruby/object:Gem::Requirement
149
149
  none: false
150
150
  requirements:
151
151
  - - ! '>='
@@ -153,10 +153,10 @@ dependencies:
153
153
  version: '0'
154
154
  type: :runtime
155
155
  prerelease: false
156
- version_requirements: *70308910312620
156
+ version_requirements: *70186955489120
157
157
  - !ruby/object:Gem::Dependency
158
158
  name: delorean
159
- requirement: &70308910312100 !ruby/object:Gem::Requirement
159
+ requirement: &70186955488640 !ruby/object:Gem::Requirement
160
160
  none: false
161
161
  requirements:
162
162
  - - ! '>='
@@ -164,10 +164,10 @@ dependencies:
164
164
  version: '0'
165
165
  type: :development
166
166
  prerelease: false
167
- version_requirements: *70308910312100
167
+ version_requirements: *70186955488640
168
168
  - !ruby/object:Gem::Dependency
169
169
  name: rspec
170
- requirement: &70308910311600 !ruby/object:Gem::Requirement
170
+ requirement: &70186955488160 !ruby/object:Gem::Requirement
171
171
  none: false
172
172
  requirements:
173
173
  - - ~>
@@ -175,10 +175,10 @@ dependencies:
175
175
  version: 2.6.0
176
176
  type: :development
177
177
  prerelease: false
178
- version_requirements: *70308910311600
178
+ version_requirements: *70186955488160
179
179
  - !ruby/object:Gem::Dependency
180
180
  name: shoulda
181
- requirement: &70308910311120 !ruby/object:Gem::Requirement
181
+ requirement: &70186955487680 !ruby/object:Gem::Requirement
182
182
  none: false
183
183
  requirements:
184
184
  - - ! '>='
@@ -186,10 +186,10 @@ dependencies:
186
186
  version: '0'
187
187
  type: :development
188
188
  prerelease: false
189
- version_requirements: *70308910311120
189
+ version_requirements: *70186955487680
190
190
  - !ruby/object:Gem::Dependency
191
191
  name: bundler
192
- requirement: &70308910310640 !ruby/object:Gem::Requirement
192
+ requirement: &70186955487140 !ruby/object:Gem::Requirement
193
193
  none: false
194
194
  requirements:
195
195
  - - ~>
@@ -197,10 +197,10 @@ dependencies:
197
197
  version: 1.0.0
198
198
  type: :development
199
199
  prerelease: false
200
- version_requirements: *70308910310640
200
+ version_requirements: *70186955487140
201
201
  - !ruby/object:Gem::Dependency
202
202
  name: jeweler
203
- requirement: &70308910310160 !ruby/object:Gem::Requirement
203
+ requirement: &70186955486600 !ruby/object:Gem::Requirement
204
204
  none: false
205
205
  requirements:
206
206
  - - ~>
@@ -208,7 +208,7 @@ dependencies:
208
208
  version: 1.5.2
209
209
  type: :development
210
210
  prerelease: false
211
- version_requirements: *70308910310160
211
+ version_requirements: *70186955486600
212
212
  description: FnordMetric is a Ruby Event-Tracking gem on steroids
213
213
  email: paul@paulasmuth.com
214
214
  executables: []