analytics-logger 0.3.7
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.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
|