kpi 0.5.1 → 0.5.2

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.rdoc CHANGED
@@ -22,62 +22,108 @@ Add to your Gemfile:
22
22
  and run in console in project directory:
23
23
 
24
24
  $ bundle
25
+
26
+ === Rails 2.x
25
27
 
26
- == Configuration
27
-
28
- Create config/initializers/kpi.rb:
29
-
30
- KPI.configure do |c|
31
- c.app_name = "Your App Name"
32
- c.mail_from = "Your App KPI Service <kpi@example.com>"
33
- c.mail_to = "your@email.com"
34
- end
28
+ This gem is tested with Rails 2.3.8.
35
29
 
36
30
  == Defining report
37
31
 
38
32
  Example report in app/models/daily_kpi_report.rb:
39
33
 
40
- class DailyKpiReport < KPI::Report::Base
34
+ class DailyKpiReport < KPI::Report
41
35
  def users_count
42
- return ["Users count", User.count, "Total users count"]
36
+ result "Users count", User.count, :description => "Total users count"
43
37
  end
44
38
 
45
39
  def today_registrations_count
46
- return ["Today registrations count", User.where("created_at > ?", @time - 24.hours)]
40
+ result "Today registrations count", User.where("created_at > ?", @time - 24.hours)
41
+ end
42
+
43
+ def total_income
44
+ result "Total income", Order.sum(:total), :unit => 'EUR'
45
+ end
46
+
47
+ def today_income
48
+ result "Total income", Order.where(:created_at => today).sum(:total), :unit => 'EUR'
49
+ end
50
+
51
+ private
52
+
53
+ def today
54
+ ((time - 24.hours)..time)
47
55
  end
48
56
  end
49
57
 
50
- Each defined method should return an array containing 2 or 3 elements:
51
- * KPI title
58
+ Each defined method should return a KPI::Entry object with following accessors:
59
+ * name
52
60
  * value
53
- * optional description
61
+ * description
62
+ * unit
63
+
64
+ A collection of KPI::Entry is available through Enuberable: Report#entries
54
65
 
55
66
  It is important, if your indicator depends on current time (last 24 hours, last week),
56
67
  to rely on @time instance variable instead of Time.now. This lets you to generate report
57
68
  for every moment in history:
58
69
 
59
- report = DailyKpiReport.new(2.days.ago)
60
-
61
- == Sending report
62
-
63
- To collect all statistics and send an email to you just call:
64
-
65
- DailyKpiReport.collect_and_send!
70
+ report = DailyKpiReport.new(:time => 2.days.ago)
71
+
72
+ === Merged reports
73
+
74
+ You can create merged report from two or more reports of the same type. Look at examples:
75
+
76
+ > today = DailyKpiReport.new
77
+ > today.users_count.value
78
+ # => 20
79
+ > today.today_income.value
80
+ # => 350
81
+ > yesterday = DailyKpiReport.new(:time => 1.day.ago)
82
+ > yesterday.users_count.value
83
+ # => 16
84
+ > yesterday.today_income.value
85
+ # => 250
86
+ > diff = KPI::MergedReport.new(today, yesterday) do |today, yesterday|
87
+ > KPI::Entry.new "$$ (change)", today - yesterday
88
+ > end
89
+ > avg = KPI::MergedReport.new(today, yesterday) do |*entries|
90
+ > KPI::Entry.new "$$ (avg)", entries
91
+ > end
92
+ > diff.users_count.value
93
+ # => 4
94
+ > avg.today_income.value
95
+ # => 300
96
+ > avg.today_income.unit
97
+ # => "EUR"
98
+
99
+ More reports can be passed to constructor of Merged report:
100
+ avg = KPI::MergedReport.new(week_now, week_last, week_next_to_last) do |*entries|
101
+ # ...
102
+ end
66
103
 
67
104
  == Other
68
105
  * You can override report title by overriding title method in report:
69
- class DailyKpiReport < KPI::Report::Base
106
+ class DailyKpiReport < KPI::Report
70
107
  def title; "Your daily report"; end
71
108
  # ...
72
109
  end
73
110
  * You can use Whenever gem to generate periodic reports
74
-
111
+ * Private methods are not considered as indicator methods and can be used as helper methods:
112
+ class WeeklyReport < KPI::Report
113
+ # ...
114
+ private
115
+
116
+ def week_range
117
+ t_prev_mon_begin = time - (time.wday-1)*24*60*60 - time.hour*60*60 - time.min*60 - time.sec
118
+ t_next_sun_end = t_prev_mon_begin + 7*24*60*60 - 1
119
+ (t_prev_mon_begin..t_next_sun_end)
120
+ end
121
+ end
75
122
  == Todo
76
123
 
77
124
  * Generators for configuration and reports
78
125
  * Database persistence - storing reports in different databases: PostgreSQL & Mongo prioritized
79
- * Controllers & views allowing to review stored reports
80
- * More tests ;)
126
+ * Support for "result" helper in block passed to constructor of MergedReport
81
127
 
82
128
 
83
129
  == Contributing to KPI
@@ -1,15 +1,16 @@
1
1
  module KPI
2
2
  class Entry
3
- attr_reader :name, :value, :description
3
+ attr_reader :name, :value, :description, :unit
4
4
  def initialize(*args)
5
5
  options = args.extract_options!
6
6
  raise ArgumentError, "Wrong number of arguments (#{args.count} of 2)" unless args.count == 2
7
7
  @name, @value = args
8
8
  @description = options[:description]
9
+ @unit = options[:unit]
9
10
  end
10
11
 
11
12
  def to_a
12
- [@title, @value, @description].compact
13
+ [@title, @value, @description, @unit].compact
13
14
  end
14
15
  end
15
16
  end
@@ -28,9 +28,12 @@ module KPI
28
28
  def method_missing(name, *args)
29
29
  result = @compare.call(*@reports.map(&name.to_sym))
30
30
  orginal = @reports.first.send(name.to_sym)
31
- description = (orginal.description ? result.description.gsub("$$", orginal.description) : nil)
31
+ description = (orginal.description && result.description ? result.description.gsub("$$", orginal.description) : nil)
32
32
 
33
- KPI::Entry.new(result.name.gsub("$$", orginal.name), result.value, :description => description)
33
+ KPI::Entry.new(result.name.gsub("$$", orginal.name),
34
+ result.value,
35
+ :description => description,
36
+ :unit => (result.unit || orginal.unit))
34
37
  end
35
38
  end
36
39
  end
@@ -8,8 +8,8 @@ module KPI
8
8
  blacklist :initialize, :collect!, :entries, :time, :title, :defined_kpis, :result
9
9
 
10
10
  def initialize(*args)
11
- options = args.extract_options!
12
- @time = options[:time] || Time.now
11
+ @options = args.extract_options!
12
+ @time = @options[:time] || Time.now
13
13
  end
14
14
  attr_reader :time
15
15
 
@@ -28,5 +28,13 @@ describe "KPI::Entry" do
28
28
  assert_equal("desc", @entry.description)
29
29
  end
30
30
  end
31
+
32
+ describe "when unit given" do
33
+ before { @entry = KPI::Entry.new "Income", 1294.23, :description => "EUR" }
34
+
35
+ it "returns description" do
36
+ assert_equal("EUR", @entry.unit)
37
+ end
38
+ end
31
39
  end
32
40
  end
@@ -14,7 +14,7 @@ describe "KPI::MergedReport" do
14
14
  end
15
15
 
16
16
  def test_kpi_2
17
- result "title 2 ", @return*2
17
+ result "title 2 ", @return*2, :unit => 'EUR'
18
18
  end
19
19
  end
20
20
  class AnotherReport < KPI::Report
@@ -54,10 +54,10 @@ describe "KPI::MergedReport" do
54
54
 
55
55
  describe "when two reports given for average" do
56
56
  before do
57
- report1 = TestKpi.new(2)
58
- report2 = TestKpi.new(8)
57
+ @report1 = TestKpi.new(2)
58
+ @report2 = TestKpi.new(8)
59
59
 
60
- @average = KPI::MergedReport.new(report1, report2) do |*entries|
60
+ @average = KPI::MergedReport.new(@report1, @report2) do |*entries|
61
61
  average = entries.map(&:value).sum / entries.size
62
62
  KPI::Entry.new "Average $$", average, :description => "$$ (average)"
63
63
  end
@@ -74,6 +74,18 @@ describe "KPI::MergedReport" do
74
74
  it "should change $$ in description to indicator descripiton" do
75
75
  assert_equal "description (average)", @average.test_kpi.description
76
76
  end
77
+
78
+ it "should have unit" do
79
+ assert_equal "EUR", @average.test_kpi_2.unit
80
+ end
81
+
82
+ it "should allow to override unit" do
83
+ @merged = KPI::MergedReport.new(@report1, @report2) do |*entries|
84
+ KPI::Entry.new "merged $$", 1, :unit => "$"
85
+ end
86
+ assert_equal '$', @merged.test_kpi.unit
87
+ assert_equal '$', @merged.test_kpi_2.unit
88
+ end
77
89
 
78
90
  it "should return nil description when no description" do
79
91
  assert_nil @average.test_kpi_2.description
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kpi
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- - 1
10
- version: 0.5.1
9
+ - 2
10
+ version: 0.5.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Artur Roszczyk
@@ -15,12 +15,12 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-13 00:00:00 +02:00
18
+ date: 2011-05-14 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- type: :runtime
23
- requirement: &id001 !ruby/object:Gem::Requirement
22
+ name: actionmailer
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
24
  none: false
25
25
  requirements:
26
26
  - - ">="
@@ -30,12 +30,12 @@ dependencies:
30
30
  - 2
31
31
  - 3
32
32
  version: "2.3"
33
- name: actionmailer
34
- version_requirements: *id001
35
33
  prerelease: false
36
- - !ruby/object:Gem::Dependency
37
34
  type: :runtime
38
- requirement: &id002 !ruby/object:Gem::Requirement
35
+ requirement: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: activesupport
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ">="
@@ -45,12 +45,12 @@ dependencies:
45
45
  - 2
46
46
  - 3
47
47
  version: "2.3"
48
- name: activesupport
49
- version_requirements: *id002
50
48
  prerelease: false
49
+ type: :runtime
50
+ requirement: *id002
51
51
  - !ruby/object:Gem::Dependency
52
- type: :development
53
- requirement: &id003 !ruby/object:Gem::Requirement
52
+ name: minitest
53
+ version_requirements: &id003 !ruby/object:Gem::Requirement
54
54
  none: false
55
55
  requirements:
56
56
  - - ">="
@@ -59,12 +59,12 @@ dependencies:
59
59
  segments:
60
60
  - 0
61
61
  version: "0"
62
- name: minitest
63
- version_requirements: *id003
64
62
  prerelease: false
65
- - !ruby/object:Gem::Dependency
66
63
  type: :development
67
- requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirement: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: bundler
67
+ version_requirements: &id004 !ruby/object:Gem::Requirement
68
68
  none: false
69
69
  requirements:
70
70
  - - ~>
@@ -74,12 +74,12 @@ dependencies:
74
74
  - 1
75
75
  - 0
76
76
  version: "1.0"
77
- name: bundler
78
- version_requirements: *id004
79
77
  prerelease: false
80
- - !ruby/object:Gem::Dependency
81
78
  type: :development
82
- requirement: &id005 !ruby/object:Gem::Requirement
79
+ requirement: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: jeweler
82
+ version_requirements: &id005 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -90,12 +90,12 @@ dependencies:
90
90
  - 5
91
91
  - 2
92
92
  version: 1.5.2
93
- name: jeweler
94
- version_requirements: *id005
95
93
  prerelease: false
96
- - !ruby/object:Gem::Dependency
97
94
  type: :development
98
- requirement: &id006 !ruby/object:Gem::Requirement
95
+ requirement: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: rcov
98
+ version_requirements: &id006 !ruby/object:Gem::Requirement
99
99
  none: false
100
100
  requirements:
101
101
  - - ">="
@@ -104,9 +104,9 @@ dependencies:
104
104
  segments:
105
105
  - 0
106
106
  version: "0"
107
- name: rcov
108
- version_requirements: *id006
109
107
  prerelease: false
108
+ type: :development
109
+ requirement: *id006
110
110
  description: This gem helps you to track key indicators in your Rails app.
111
111
  email: artur.roszczyk@gmail.com
112
112
  executables: []
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
165
  requirements: []
166
166
 
167
167
  rubyforge_project:
168
- rubygems_version: 1.6.2
168
+ rubygems_version: 1.5.2
169
169
  signing_key:
170
170
  specification_version: 3
171
171
  summary: Key Performance Indicators for Rails 3.x