0.01
No commit activity in last 3 years
No release in over 3 years
Database based event counter with throttling per time intervals
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

 Project Readme

Build Status

EventCounter

EventCounter is a database based event counter with throttling per time intervals.

Usage

Let's define counters in model

class Article < ActiveRecord::Base
  has_counter :views, interval: 5.minutes # default interval is :day

  # :year, :month, :week and :day symbols are supported
  has_counter :views_by_week, interval: :week
end

Let's count...

article = Article.create!

article.up!(:views)
# => #<EventCounter id: 1, name: "views", value: 1, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:20:00">
# creates counter (if it doesn't exist) with value 1 and on Time.now() rounded
# to 5 minutes, e.x.:

# at once
article.up!(:views, 3)
# => #<EventCounter id: 1, name: "views", value: 4, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:20:00">
# Updates counter (if the other exists in that interval) with value 3 and
# on Time.now() rounded to 5 minutes

# later
article.up!(:views, 5)
# => #<EventCounter id: 2, name: "views", value: 5, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:25:00">
article.down!(:views, 2)
# => #<EventCounter id: 2, name: "views", value: 3, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:25:00">

# anytime or in a background job
article.up!(:views, 7, on_time: 10.minutes.ago.in_time_zone)
# => #<EventCounter id: 3, name: "views", value: 7, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:15:00">

# we have not got? let's fix it
article.up!(:views, 9, on_time: 10.minutes.ago.in_time_zone, force: true)
# => #<EventCounter id: 3, name: "views", value: 9, countable_id: 1,
# countable_type: "Article", created_at: "2014-10-16 23:15:00">

Let's get some statistics for our charts...

article.data_for(:views)
# => [
# [Thu, 16 Oct 2014 23:15:00 MSK +04:00, 9],
# [Thu, 16 Oct 2014 23:20:00 MSK +04:00, 4],
# [Thu, 16 Oct 2014 23:25:00 MSK +04:00, 3]
# ]

article.data_for(:views, interval: 10.minutes)
# => [
# [Thu, 16 Oct 2014 23:10:00 MSK +04:00, 9],
# [Thu, 16 Oct 2014 23:20:00 MSK +04:00, 7]
# ]

# with range
range_start = Time.zone.local(2014, 10, 16, 23, 0)
range_end   = Time.zone.local(2014, 10, 16, 23, 10)
range = range_start..range_end
article.data_for(:views, interval: 10.minutes, range: range)
#=> [
# [Thu, 16 Oct 2014 23:00:00 MSK +04:00, 0]
# [Thu, 16 Oct 2014 23:10:00 MSK +04:00, 9]
# ]

# for different time zone (although we have no data for that time)
range_start = Time.zone.local(2014, 10, 16, 23, 0).in_time_zone('UTC')
range_end   = Time.zone.local(2014, 10, 16, 23, 10).in_time_zone('UTC')
range = range_start..range_end
article.data_for(:views, interval: 10.minutes, range: range, tz: 'UTC')
#=> [
# [Thu, 16 Oct 2014 23:00:00 UTC +00:00, 0] 
# [Thu, 16 Oct 2014 23:10:00 UTC +00:00, 0]
# ]

article.data_for(:views, interval: :day)
# => [Thu, 16 Oct 2014 00:00:00 MSK +04:00, 16]

article.data_for(:views, interval: :day, raw: true)
#=> [{"created_at" => "2014-10-16 00:00:00", "value" => "16"}]
# The raw result will make difference in performance on a big data.
# The returned time is in the requested time zone. By default, a normalization
# looks as `Time.zone.parse(i['created_at']), i['value'].to_i ]`

# class wide
range_start = Time.zone.local(2014, 10, 15)
range_end   = Time.zone.local(2014, 10, 16)
range = range_start..range_end
Article.data_for(:views, interval: :day, range: range)
# => [
# [Thu, 15 Oct 2014 00:00:00 MSK +04:00, 0]
# [Thu, 16 Oct 2014 00:00:00 MSK +04:00, 16]
# ]

Limitations

  • It works ONLY with PostgreSQL at the moment.
  • Ruby 2+
  • ActiveRecord 3+
  • ActiveSupport 3+
  • It's polymorphic association.
  • It uses ActiveSupport::TimeWithZone to return user friendly statistics. So, you have to operate with dates with time zones.
  • Use it in production with caution because it's early release.

Installation

Add gem to Gemfile

gem 'event-counter'

Create migration rails g migration create_event_counters with the following code:

class CreateEventCounters < ActiveRecord::Migration

  def change
    create_table :event_counters, force: true do |t|
      t.string :name, null: false
      t.integer :value, default: 0, null: false
      t.references :countable, polymorphic: true, null: false

      t.datetime :created_at
    end

    add_index :event_counters, :created_at
    add_index :event_counters,
      [:countable_type, :name, :countable_id], name: 'index_complex'

  end

end

License

MIT