analytics-logger 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +3 -0
- data/Manifest +21 -0
- data/README.rdoc +0 -0
- data/Rakefile +14 -0
- data/analytics-logger.gemspec +34 -0
- data/generators/analytics/USAGE +1 -0
- data/generators/analytics/analytics_generator.rb +9 -0
- data/generators/analytics/templates/analytics_event.rb +54 -0
- data/generators/analytics/templates/analytics_event_type.rb +19 -0
- data/generators/analytics/templates/init.rb +1 -0
- data/lib/analytics_logger.rb +1 -0
- data/lib/analytics_logger/analytics.rb +18 -0
- data/lib/analytics_logger/analytics/formatters.rb +27 -0
- data/lib/analytics_logger/analytics/loggers.rb +76 -0
- data/lib/analytics_logger/analytics/maintenance.rb +131 -0
- data/lib/analytics_logger/analytics/selectors.rb +24 -0
- data/lib/generators/analytics/USAGE +1 -0
- data/lib/generators/analytics/analytics_generator.rb +9 -0
- data/lib/generators/analytics/templates/analytics_event.rb +54 -0
- data/lib/generators/analytics/templates/analytics_event_type.rb +19 -0
- data/lib/generators/analytics/templates/init.rb +1 -0
- metadata +139 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/CHANGELOG
ADDED
data/Manifest
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
Manifest
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
analytics-logger.gemspec
|
6
|
+
generators/analytics/USAGE
|
7
|
+
generators/analytics/analytics_generator.rb
|
8
|
+
generators/analytics/templates/analytics_event.rb
|
9
|
+
generators/analytics/templates/analytics_event_type.rb
|
10
|
+
generators/analytics/templates/init.rb
|
11
|
+
lib/analytics_logger.rb
|
12
|
+
lib/analytics_logger/analytics.rb
|
13
|
+
lib/analytics_logger/analytics/formatters.rb
|
14
|
+
lib/analytics_logger/analytics/loggers.rb
|
15
|
+
lib/analytics_logger/analytics/maintenance.rb
|
16
|
+
lib/analytics_logger/analytics/selectors.rb
|
17
|
+
lib/generators/analytics/USAGE
|
18
|
+
lib/generators/analytics/analytics_generator.rb
|
19
|
+
lib/generators/analytics/templates/analytics_event.rb
|
20
|
+
lib/generators/analytics/templates/analytics_event_type.rb
|
21
|
+
lib/generators/analytics/templates/init.rb
|
data/README.rdoc
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('analytics-logger', '0.3.7') do |p|
|
6
|
+
p.description = "Monitors, logs and tracks events"
|
7
|
+
p.url = "https://github.com/FutureAdvisor/analytics-logger"
|
8
|
+
p.author = "Jared McFarland of FutureAdvisor"
|
9
|
+
p.email = "jared@futureadvisor.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = ["activesupport"]
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{analytics-logger}
|
5
|
+
s.version = "0.3.7"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Jared McFarland of FutureAdvisor"]
|
9
|
+
s.cert_chain = ["/Users/jared/gems/keys/gem-public_cert.pem"]
|
10
|
+
s.date = %q{2011-04-18}
|
11
|
+
s.description = %q{Monitors, logs and tracks events}
|
12
|
+
s.email = %q{jared@futureadvisor.com}
|
13
|
+
s.extra_rdoc_files = ["CHANGELOG", "README.rdoc", "lib/analytics_logger.rb", "lib/analytics_logger/analytics.rb", "lib/analytics_logger/analytics/formatters.rb", "lib/analytics_logger/analytics/loggers.rb", "lib/analytics_logger/analytics/maintenance.rb", "lib/analytics_logger/analytics/selectors.rb", "lib/generators/analytics/USAGE", "lib/generators/analytics/analytics_generator.rb", "lib/generators/analytics/templates/analytics_event.rb", "lib/generators/analytics/templates/analytics_event_type.rb", "lib/generators/analytics/templates/init.rb"]
|
14
|
+
s.files = ["CHANGELOG", "Manifest", "README.rdoc", "Rakefile", "analytics-logger.gemspec", "generators/analytics/USAGE", "generators/analytics/analytics_generator.rb", "generators/analytics/templates/analytics_event.rb", "generators/analytics/templates/analytics_event_type.rb", "generators/analytics/templates/init.rb", "lib/analytics_logger.rb", "lib/analytics_logger/analytics.rb", "lib/analytics_logger/analytics/formatters.rb", "lib/analytics_logger/analytics/loggers.rb", "lib/analytics_logger/analytics/maintenance.rb", "lib/analytics_logger/analytics/selectors.rb", "lib/generators/analytics/USAGE", "lib/generators/analytics/analytics_generator.rb", "lib/generators/analytics/templates/analytics_event.rb", "lib/generators/analytics/templates/analytics_event_type.rb", "lib/generators/analytics/templates/init.rb"]
|
15
|
+
s.homepage = %q{https://github.com/FutureAdvisor/analytics-logger}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Analytics-logger", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{analytics-logger}
|
19
|
+
s.rubygems_version = %q{1.6.0}
|
20
|
+
s.signing_key = %q{/Users/jared/gems/keys/gem-private_key.pem}
|
21
|
+
s.summary = %q{Monitors, logs and tracks events}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<activesupport>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
./script/generate analytics
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class AnalyticsGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.file 'analytics_event.rb', 'app/models/analytics_event.rb'
|
5
|
+
m.file 'analytics_event_type.rb', 'app/models/analytics_event_type.rb'
|
6
|
+
m.file 'init.rb', 'config/initializers/analytics.rb'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
# This is the actual even that gets logged. Belongs to an AnalyticsEventType for sorting and categorizing
|
3
|
+
#
|
4
|
+
class AnalyticsEvent < ActiveRecord::Base
|
5
|
+
belongs_to :analytics_event_type, :counter_cache => true
|
6
|
+
|
7
|
+
before_save :yamlize_objects
|
8
|
+
|
9
|
+
# Objects assigned to these attributes will be stored in YAML format.
|
10
|
+
YAMLIZED_ATTRIBUTES = [:user, :params, :exception, :backtrace, :additional_objects]
|
11
|
+
|
12
|
+
named_scope :has_user, { :conditions => ["analytics_events.user_id IS NOT NULL"] }
|
13
|
+
named_scope :does_not_have_user, { :conditions => {:user_id => nil} }
|
14
|
+
named_scope :unique_users, { :group => [:user_id] }
|
15
|
+
named_scope :is_not_audit_level, { :conditions => ["analytics_event_types.level != ?", AnalyticsEventType::AUDIT], :joins => :analytics_event_type }
|
16
|
+
named_scope :for_users, lambda { |user_id| { :conditions => { :user_id => user_id } } }
|
17
|
+
named_scope :before_time, lambda { |time| { :conditions => ["analytics_events.created_at < ?", time.utc] } }
|
18
|
+
named_scope :since_time, lambda { |time| { :conditions => ["analytics_events.created_at >= ?", time.utc] } }
|
19
|
+
named_scope :in_level, lambda { |levels| { :conditions => { :analytics_event_types => { :level => levels } }, :joins => :analytics_event_type } }
|
20
|
+
named_scope :in_type, lambda { |types| { :conditions => { :analytics_event_type_id => types.is_a?(Array) ? types.map(&:id) : types.id } } }
|
21
|
+
named_scope :sort_by_most_recent, :order => "analytics_events.id DESC"
|
22
|
+
|
23
|
+
YAMLIZED_ATTRIBUTES.each do |attr|
|
24
|
+
define_method(attr) do
|
25
|
+
value = super()
|
26
|
+
|
27
|
+
# The value may be holding nil in YAML format; if so, return nil.
|
28
|
+
(value == nil.to_yaml) ? nil : value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def name
|
33
|
+
"#{analytics_event_type.name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Designed to be used like
|
38
|
+
# Analytics.find_by_name("User Logged In").unique_by_user
|
39
|
+
# => [array of events unique by user]
|
40
|
+
def self.unique_by_user
|
41
|
+
AnalyticsEvent.has_user.unique_users + AnalyticsEvent.does_not_have_user
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# makes the objects being stored YAML objects
|
47
|
+
# so that we can reference them later
|
48
|
+
#
|
49
|
+
def yamlize_objects
|
50
|
+
YAMLIZED_ATTRIBUTES.each do |attr|
|
51
|
+
__send__("#{attr.to_s}=".to_sym, __send__(attr).to_yaml)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# This is the parent category for a particular event
|
3
|
+
#
|
4
|
+
class AnalyticsEventType < ActiveRecord::Base
|
5
|
+
|
6
|
+
LEVELS = ["Error", "Warning", "Audit", "Info"]
|
7
|
+
|
8
|
+
# Define constants for each level.
|
9
|
+
LEVELS.each do |level|
|
10
|
+
class_eval "#{level.upcase} = #{level.inspect}"
|
11
|
+
end
|
12
|
+
|
13
|
+
has_many :analytics_events, :dependent => :destroy
|
14
|
+
|
15
|
+
validates_presence_of :name
|
16
|
+
|
17
|
+
named_scope :sort_by_count, :order => "analytics_events_count DESC"
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'analytics_logger/analytics'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#
|
2
|
+
# This is the parent class for AnalyticsEvent and AnalyticsEventTypes
|
3
|
+
# You shouldn't access either child class directly, it should all be coordinated through Analytics
|
4
|
+
#
|
5
|
+
module Analytics
|
6
|
+
require 'analytics_logger/analytics/formatters'
|
7
|
+
require 'analytics_logger/analytics/loggers'
|
8
|
+
require 'analytics_logger/analytics/maintenance'
|
9
|
+
require 'analytics_logger/analytics/selectors'
|
10
|
+
|
11
|
+
class << self
|
12
|
+
include Analytics::Loggers
|
13
|
+
include Analytics::Selectors
|
14
|
+
include Analytics::Formatters
|
15
|
+
include Analytics::Maintenance
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
# The formatters for the Analytics class (may eventually split this out to Analytics::Formatters and Analytics::Formatters::HighCharts)
|
3
|
+
#
|
4
|
+
|
5
|
+
module Analytics::Formatters
|
6
|
+
|
7
|
+
def date_array_for_highcharts(events)
|
8
|
+
events.map {|ae| "'#{ae.created_at.strftime("%m/%d/%y")}'" }.uniq.sort
|
9
|
+
end
|
10
|
+
|
11
|
+
def month_array_for_highcharts(events)
|
12
|
+
events.map {|ae| "'#{ae.created_at.strftime("%b %Y")}'" }.uniq
|
13
|
+
end
|
14
|
+
|
15
|
+
def total_data_array_for_highcharts(events)
|
16
|
+
dates = events.map {|ae| "'#{ae.created_at.strftime("%m/%d/%y")}'" }.sort
|
17
|
+
keys = dates.uniq
|
18
|
+
keys.map {|key| dates.count(key) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def total_month_data_array_for_highcharts(events)
|
22
|
+
dates = events.map {|ae| "'#{ae.created_at.strftime("%b %Y")}'" }
|
23
|
+
keys = dates.uniq
|
24
|
+
keys.map {|key| dates.count(key) }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
#
|
2
|
+
# The logging methods associated with the analytics
|
3
|
+
#
|
4
|
+
|
5
|
+
module Analytics::Loggers
|
6
|
+
|
7
|
+
# options
|
8
|
+
# :level -> The log level of the event; e.g., AnalyticsEventType::INFO, AnalyticsEventType::AUDIT, AnalyticsEventType::WARNING, AnalyticsEventType::ERROR
|
9
|
+
# :user -> The user that was logged in when the event was triggered
|
10
|
+
# :params -> The params when the event was triggered
|
11
|
+
# :exception -> The exception that was encountered when the event was triggered
|
12
|
+
# Any has items that are not an explicitly defined option above, will be stuffed into the "additional_objects" column and stored in the DB
|
13
|
+
#
|
14
|
+
def log(name, options = {})
|
15
|
+
analytics_event = AnalyticsEvent.new(setup_log(name, options))
|
16
|
+
analytics_event.save
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# alias methods for auto-assigning log levels
|
21
|
+
AnalyticsEventType::LEVELS.each do |level|
|
22
|
+
define_method(level.downcase.to_sym) do |name, *args|
|
23
|
+
if args[0].nil? || !args[0].is_a?(Hash)
|
24
|
+
options = Hash.new
|
25
|
+
else
|
26
|
+
options = args[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
options[:level] = level
|
30
|
+
self.log(name, options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# same as log, but allows a block, and will only log an event if the block raises an exception
|
35
|
+
# will re-throw the exception unless :throw => false is specified
|
36
|
+
def log_on_error(name, options = {}, &block)
|
37
|
+
options[:level] ||= AnalyticsEventType::ERROR # log as an error by default
|
38
|
+
begin
|
39
|
+
yield
|
40
|
+
rescue
|
41
|
+
self.log(name, options)
|
42
|
+
raise unless options[:throw] == false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
#
|
48
|
+
# helper method that takes the name and options passed into log and returns a hash suitable
|
49
|
+
# for using with Event.create
|
50
|
+
#
|
51
|
+
def setup_log(name, options)
|
52
|
+
options[:level] ||= AnalyticsEventType::INFO
|
53
|
+
|
54
|
+
level = options.delete(:level)
|
55
|
+
user = options.delete(:user)
|
56
|
+
user_id = user.nil? ? nil : user.id
|
57
|
+
params = options.delete(:params)
|
58
|
+
exception = options.delete(:exception)
|
59
|
+
backtrace = exception.nil? ? nil : exception.backtrace
|
60
|
+
|
61
|
+
additional_objects = options
|
62
|
+
|
63
|
+
event_type = AnalyticsEventType.find_or_create_by_name_and_level(name, level)
|
64
|
+
|
65
|
+
{
|
66
|
+
:analytics_event_type_id => event_type.id,
|
67
|
+
:user_id => user_id,
|
68
|
+
:user => user,
|
69
|
+
:params => params,
|
70
|
+
:exception => exception,
|
71
|
+
:backtrace => backtrace,
|
72
|
+
:additional_objects => additional_objects
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#
|
2
|
+
# The maintenance methods associated with the analytics
|
3
|
+
#
|
4
|
+
|
5
|
+
module Analytics::Maintenance
|
6
|
+
class << Analytics::Maintenance
|
7
|
+
private
|
8
|
+
def option_value_must_be_a(obj_type)
|
9
|
+
lambda { |option_value| option_value.is_a?(obj_type) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
CLEAR_EVENTS_OBJECT_OPTIONS = {
|
14
|
+
:all => option_value_must_be_a(TrueClass),
|
15
|
+
:audit => option_value_must_be_a(TrueClass),
|
16
|
+
:no_log => option_value_must_be_a(TrueClass),
|
17
|
+
:since => option_value_must_be_a(Time),
|
18
|
+
:before => option_value_must_be_a(Time)
|
19
|
+
}
|
20
|
+
CLEAR_EVENTS_OBJECT_OR_ARRAY_OPTIONS = {
|
21
|
+
:level => lambda { |option_value| AnalyticsEventType::LEVELS.include?(option_value) },
|
22
|
+
:type => option_value_must_be_a(AnalyticsEventType),
|
23
|
+
:user_id => option_value_must_be_a(Fixnum)
|
24
|
+
}
|
25
|
+
VALID_CLEAR_EVENTS_OPTIONS = CLEAR_EVENTS_OBJECT_OPTIONS.keys + CLEAR_EVENTS_OBJECT_OR_ARRAY_OPTIONS.keys
|
26
|
+
|
27
|
+
CLEAR_EVENTS_HELP = <<-CLEAR_EVENTS_HELP_TEXT
|
28
|
+
At least one of the following options must be specified:
|
29
|
+
:all => true
|
30
|
+
Clear all analytics events; if specified, must be the only option.
|
31
|
+
|
32
|
+
:audit => true
|
33
|
+
Clear audit level analytics events, which by default are not cleared.
|
34
|
+
|
35
|
+
:no_log => true
|
36
|
+
Do not log an analytics event indicating that the log was cleared.
|
37
|
+
|
38
|
+
:since => <time>
|
39
|
+
Clear only analytics events occurring since the specified time.
|
40
|
+
|
41
|
+
:before => <time>
|
42
|
+
Clear only analytics events occurring before the specified time.
|
43
|
+
|
44
|
+
:level => <level or array of levels>
|
45
|
+
Clear only analytics events belonging to the specified level(s).
|
46
|
+
|
47
|
+
:type => <event type or array of event types>
|
48
|
+
Clear only analytics events belonging to the specified type(s).
|
49
|
+
|
50
|
+
:user_id => <user id or array of user ids>
|
51
|
+
Clear only analytics events associated with the specified user(s).
|
52
|
+
|
53
|
+
CLEAR_EVENTS_HELP_TEXT
|
54
|
+
|
55
|
+
def clean_up_events
|
56
|
+
AnalyticsEventType.all.each do |event_type|
|
57
|
+
# Make sure the counter cache is correct in case it became out-of-sync.
|
58
|
+
AnalyticsEventType.reset_counters(event_type.id, :analytics_events)
|
59
|
+
|
60
|
+
# The event type is no longer needed if there are no associated events.
|
61
|
+
if event_type.analytics_events_count == 0
|
62
|
+
event_type.destroy
|
63
|
+
end
|
64
|
+
end
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear_events(options = {})
|
69
|
+
begin
|
70
|
+
validate_clear_events_options(options)
|
71
|
+
|
72
|
+
events_to_clear = AnalyticsEvent
|
73
|
+
events_to_clear = events_to_clear.is_not_audit_level unless options[:audit]
|
74
|
+
events_to_clear = events_to_clear.since_time(options[:since]) if options[:since]
|
75
|
+
events_to_clear = events_to_clear.before_time(options[:before]) if options[:before]
|
76
|
+
events_to_clear = events_to_clear.in_level(options[:level]) if options[:level]
|
77
|
+
events_to_clear = events_to_clear.in_type(options[:type]) if options[:type]
|
78
|
+
events_to_clear = events_to_clear.for_users(options[:user_id]) if options[:user_id]
|
79
|
+
events_to_clear = events_to_clear.all
|
80
|
+
|
81
|
+
puts "Clearing #{events_to_clear.count} events..."
|
82
|
+
events_to_clear.each(&:destroy)
|
83
|
+
|
84
|
+
if !options[:audit] && AnalyticsEvent.in_level(AnalyticsEventType::AUDIT).count > 0
|
85
|
+
puts
|
86
|
+
puts 'Note: Audit level events are not cleared unless :audit => true is specified.'
|
87
|
+
end
|
88
|
+
|
89
|
+
Analytics.audit('Analytics: Cleared analytics events', :count => events_to_clear.count, :options => options) unless options[:no_log] || events_to_clear.count == 0
|
90
|
+
|
91
|
+
# Clean up the state of analytics objects after clearing.
|
92
|
+
clean_up_events
|
93
|
+
rescue ArgumentError => error
|
94
|
+
puts "#{error.class.name}: #{error.message}"
|
95
|
+
puts
|
96
|
+
puts CLEAR_EVENTS_HELP
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def validate_clear_events_options(options)
|
104
|
+
raise ArgumentError.new("invalid options specified: #{options.inspect}") unless options.is_a?(Hash)
|
105
|
+
raise ArgumentError.new("no options specified") if options.empty?
|
106
|
+
|
107
|
+
invalid_options = options.keys - VALID_CLEAR_EVENTS_OPTIONS
|
108
|
+
raise ArgumentError.new("invalid options specified: #{invalid_options.inspect}") unless invalid_options.empty?
|
109
|
+
|
110
|
+
if options[:all]
|
111
|
+
raise ArgumentError.new("if :all is specified, it must be the only option specified") if options.count > 1
|
112
|
+
end
|
113
|
+
|
114
|
+
CLEAR_EVENTS_OBJECT_OPTIONS.each do |option, validate_value|
|
115
|
+
option_value = options[option]
|
116
|
+
if option_value
|
117
|
+
raise ArgumentError.new("invalid value for #{option.inspect} option: #{option_value.inspect}") unless validate_value.call(option_value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
CLEAR_EVENTS_OBJECT_OR_ARRAY_OPTIONS.each do |option, validate_value|
|
122
|
+
option_value = options[option]
|
123
|
+
if option_value
|
124
|
+
validate_array = lambda { option_value.is_a?(Array) && option_value.all? { |value| validate_value.call(value) } }
|
125
|
+
raise ArgumentError.new("invalid value for #{option.inspect} option: #{option_value.inspect}") unless validate_value.call(option_value) || validate_array.call
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
true
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# All the custom selectors for the Analytics class
|
3
|
+
#
|
4
|
+
|
5
|
+
module Analytics::Selectors
|
6
|
+
|
7
|
+
# because AnalyticEvents don't store their own name, we have handy helper to find them
|
8
|
+
def find_by_name(name)
|
9
|
+
AnalyticsEventType.find_by_name(name).analytics_events
|
10
|
+
end
|
11
|
+
|
12
|
+
def latest(limit = 2)
|
13
|
+
AnalyticsEventType.find(:all, :order => "created_at DESC", :limit => limit)
|
14
|
+
end
|
15
|
+
|
16
|
+
def sort_events_by_count
|
17
|
+
AnalyticsEventType.sort_by_count.all
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_event_type(id)
|
21
|
+
AnalyticsEventType.find(id)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
./script/generate analytics
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class AnalyticsGenerator < Rails::Generators::Base
|
2
|
+
source_root File.expand_path('../templates', __FILE__)
|
3
|
+
|
4
|
+
def build
|
5
|
+
copy_file 'analytics_event.rb', 'app/models/analytics_event.rb'
|
6
|
+
copy_file 'analytics_event_type.rb', 'app/models/analytics_event_type.rb'
|
7
|
+
copy_file 'init.rb', 'config/initializers/analytics.rb'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#
|
2
|
+
# This is the actual even that gets logged. Belongs to an AnalyticsEventType for sorting and categorizing
|
3
|
+
#
|
4
|
+
class AnalyticsEvent < ActiveRecord::Base
|
5
|
+
belongs_to :analytics_event_type, :counter_cache => true
|
6
|
+
|
7
|
+
before_save :yamlize_objects
|
8
|
+
|
9
|
+
# Objects assigned to these attributes will be stored in YAML format.
|
10
|
+
YAMLIZED_ATTRIBUTES = [:user, :params, :exception, :backtrace, :additional_objects]
|
11
|
+
|
12
|
+
named_scope :has_user, { :conditions => ["analytics_events.user_id IS NOT NULL"] }
|
13
|
+
named_scope :does_not_have_user, { :conditions => {:user_id => nil} }
|
14
|
+
named_scope :unique_users, { :group => [:user_id] }
|
15
|
+
named_scope :is_not_audit_level, { :conditions => ["analytics_event_types.level != ?", AnalyticsEventType::AUDIT], :joins => :analytics_event_type }
|
16
|
+
named_scope :for_users, lambda { |user_id| { :conditions => { :user_id => user_id } } }
|
17
|
+
named_scope :before_time, lambda { |time| { :conditions => ["analytics_events.created_at < ?", time.utc] } }
|
18
|
+
named_scope :since_time, lambda { |time| { :conditions => ["analytics_events.created_at >= ?", time.utc] } }
|
19
|
+
named_scope :in_level, lambda { |levels| { :conditions => { :analytics_event_types => { :level => levels } }, :joins => :analytics_event_type } }
|
20
|
+
named_scope :in_type, lambda { |types| { :conditions => { :analytics_event_type_id => types.is_a?(Array) ? types.map(&:id) : types.id } } }
|
21
|
+
named_scope :sort_by_most_recent, :order => "analytics_events.id DESC"
|
22
|
+
|
23
|
+
YAMLIZED_ATTRIBUTES.each do |attr|
|
24
|
+
define_method(attr) do
|
25
|
+
value = super()
|
26
|
+
|
27
|
+
# The value may be holding nil in YAML format; if so, return nil.
|
28
|
+
(value == nil.to_yaml) ? nil : value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def name
|
33
|
+
"#{analytics_event_type.name}"
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Designed to be used like
|
38
|
+
# Analytics.find_by_name("User Logged In").unique_by_user
|
39
|
+
# => [array of events unique by user]
|
40
|
+
def self.unique_by_user
|
41
|
+
AnalyticsEvent.has_user.unique_users + AnalyticsEvent.does_not_have_user
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# makes the objects being stored YAML objects
|
47
|
+
# so that we can reference them later
|
48
|
+
#
|
49
|
+
def yamlize_objects
|
50
|
+
YAMLIZED_ATTRIBUTES.each do |attr|
|
51
|
+
__send__("#{attr.to_s}=".to_sym, __send__(attr).to_yaml)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# This is the parent category for a particular event
|
3
|
+
#
|
4
|
+
class AnalyticsEventType < ActiveRecord::Base
|
5
|
+
|
6
|
+
LEVELS = ["Error", "Warning", "Audit", "Info"]
|
7
|
+
|
8
|
+
# Define constants for each level.
|
9
|
+
LEVELS.each do |level|
|
10
|
+
class_eval "#{level.upcase} = #{level.inspect}"
|
11
|
+
end
|
12
|
+
|
13
|
+
has_many :analytics_events, :dependent => :destroy
|
14
|
+
|
15
|
+
validates_presence_of :name
|
16
|
+
|
17
|
+
named_scope :sort_by_count, :order => "analytics_events_count DESC"
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'analytics_logger/analytics'
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: analytics-logger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 7
|
10
|
+
version: 0.3.7
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jared McFarland of FutureAdvisor
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain:
|
17
|
+
- |
|
18
|
+
-----BEGIN CERTIFICATE-----
|
19
|
+
MIIDOjCCAiKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBDMRUwEwYDVQQDDAxqYXJl
|
20
|
+
ZC5vbmxpbmUxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkW
|
21
|
+
A2NvbTAeFw0xMTA0MTQwMDQxMTBaFw0xMjA0MTMwMDQxMTBaMEMxFTATBgNVBAMM
|
22
|
+
DGphcmVkLm9ubGluZTEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPy
|
23
|
+
LGQBGRYDY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwO6egtCl
|
24
|
+
Ia9gAeVfFqmS1pck6b2wseSZ2ZEHTHmUcqL6apfDdx2tbY2WdTqGiSLmeZXcsFJu
|
25
|
+
yqLTSXpDIZHBYXhKyM1tCdT2lWmCVJFDhhU/R52AIlLDS9QuTorMVGbL1My62U86
|
26
|
+
XFHIw4u9QU4C5dINw9hUF83Q9YZbOgPIeM5eL6HCAFAOxLtkD0Yw5/BOZ2cTfgA0
|
27
|
+
f11Yqkyngi5DwwXxNeb52m6Tp+z6rdcVusiYHdEHJFto9pjbtMeEKQqsz4d7TeVe
|
28
|
+
6sBjxofnako0RNZsNh7RpPa6S7JOZU5QKJuAVPPIb8UVrP1DIihK++9FHYq/kz7h
|
29
|
+
ToJ55ft3J/hA8QIDAQABozkwNzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNV
|
30
|
+
HQ4EFgQUslo275mIRkTWVUpZeQxOsXF9bJkwDQYJKoZIhvcNAQEFBQADggEBAA9h
|
31
|
+
SqgjnidOcpoPjE32HkmDHPqFIsU3/m1W7Np8847TCj4X2gsGrpSEL5dy0Po3cw9E
|
32
|
+
0frKfw/jfUUnggYYvO9m+u0+tuas6GpwdY+Ll8ybtBMWQ4G8Msc9fY7EWZ3Jm/hK
|
33
|
+
8n5gejpuKC/MpF6bDllerHWpHI/d2csuHL339sJVR13tZamifgHm748/ig6/bO/K
|
34
|
+
m1verQa8BF6GC+fpZgfGriyoN8ync3F4wi2T/9koGzio1kotNIWhWtz8Ay/+KppJ
|
35
|
+
VFN1Jwb+Xjp/Jea9EZPL03vBfpTp6Z1OuN3MdRK26SaOBfFRdO/7JfKVdGHLWUPO
|
36
|
+
ncO2Ggxc146uqG0Yz4o=
|
37
|
+
-----END CERTIFICATE-----
|
38
|
+
|
39
|
+
date: 2011-04-18 00:00:00 -07:00
|
40
|
+
default_executable:
|
41
|
+
dependencies:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: activesupport
|
44
|
+
prerelease: false
|
45
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
hash: 3
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :development
|
55
|
+
version_requirements: *id001
|
56
|
+
description: Monitors, logs and tracks events
|
57
|
+
email: jared@futureadvisor.com
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files:
|
63
|
+
- CHANGELOG
|
64
|
+
- README.rdoc
|
65
|
+
- lib/analytics_logger.rb
|
66
|
+
- lib/analytics_logger/analytics.rb
|
67
|
+
- lib/analytics_logger/analytics/formatters.rb
|
68
|
+
- lib/analytics_logger/analytics/loggers.rb
|
69
|
+
- lib/analytics_logger/analytics/maintenance.rb
|
70
|
+
- lib/analytics_logger/analytics/selectors.rb
|
71
|
+
- lib/generators/analytics/USAGE
|
72
|
+
- lib/generators/analytics/analytics_generator.rb
|
73
|
+
- lib/generators/analytics/templates/analytics_event.rb
|
74
|
+
- lib/generators/analytics/templates/analytics_event_type.rb
|
75
|
+
- lib/generators/analytics/templates/init.rb
|
76
|
+
files:
|
77
|
+
- CHANGELOG
|
78
|
+
- Manifest
|
79
|
+
- README.rdoc
|
80
|
+
- Rakefile
|
81
|
+
- analytics-logger.gemspec
|
82
|
+
- generators/analytics/USAGE
|
83
|
+
- generators/analytics/analytics_generator.rb
|
84
|
+
- generators/analytics/templates/analytics_event.rb
|
85
|
+
- generators/analytics/templates/analytics_event_type.rb
|
86
|
+
- generators/analytics/templates/init.rb
|
87
|
+
- lib/analytics_logger.rb
|
88
|
+
- lib/analytics_logger/analytics.rb
|
89
|
+
- lib/analytics_logger/analytics/formatters.rb
|
90
|
+
- lib/analytics_logger/analytics/loggers.rb
|
91
|
+
- lib/analytics_logger/analytics/maintenance.rb
|
92
|
+
- lib/analytics_logger/analytics/selectors.rb
|
93
|
+
- lib/generators/analytics/USAGE
|
94
|
+
- lib/generators/analytics/analytics_generator.rb
|
95
|
+
- lib/generators/analytics/templates/analytics_event.rb
|
96
|
+
- lib/generators/analytics/templates/analytics_event_type.rb
|
97
|
+
- lib/generators/analytics/templates/init.rb
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: https://github.com/FutureAdvisor/analytics-logger
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options:
|
104
|
+
- --line-numbers
|
105
|
+
- --inline-source
|
106
|
+
- --title
|
107
|
+
- Analytics-logger
|
108
|
+
- --main
|
109
|
+
- README.rdoc
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 11
|
127
|
+
segments:
|
128
|
+
- 1
|
129
|
+
- 2
|
130
|
+
version: "1.2"
|
131
|
+
requirements: []
|
132
|
+
|
133
|
+
rubyforge_project: analytics-logger
|
134
|
+
rubygems_version: 1.6.0
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Monitors, logs and tracks events
|
138
|
+
test_files: []
|
139
|
+
|
metadata.gz.sig
ADDED
Binary file
|