metricky 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24b5d4ea790824df18783742e69969e024b31cf44b4164a191d3ff0215804873
4
- data.tar.gz: 72143e22bb37bd47da23c70d527011e86f35c2d840dd9593d1a845e0619e40a1
3
+ metadata.gz: 658a25239c266d3ecdb01b9a7e189fe872e527b93f2eebf4d18a1ced0c6f44b7
4
+ data.tar.gz: a568dcedbed1b33cd18f5a8fac81acb997557962475f7930399c0d805a0b6ab3
5
5
  SHA512:
6
- metadata.gz: 753fe8c0f2d2e47b36042426d00a4dfe0352745b4bccc7142de4f4e8f621b7cefb3a19276e56894508f19e53911f836a71300b289dd83a337d9ffe0b0fa0ffea
7
- data.tar.gz: cf3896f56e6475af16c5f1d6984d1a177ced083da92f416942ca0eeeeaffe3b70ab1b1caae51c57d8f3cadbcdf77841ff3dd29091dd7cafe86435814aa4afbbe
6
+ metadata.gz: a07937c18a85e031d0dbe2abcda2d9c3234327cccac545841f159f147e9ed542cbec0c6ad31bc7a68d5e5f6e5fb287a0b3187ce5368f268e85ffb3c2010fde17
7
+ data.tar.gz: de9ed1c8abd005edb7dba4a86596feaa833a28093b42f9c108803bbc9700cd218f62f6b5315b245669ab99f23d18fb3b901828bbfdfa1430993de936abb4330f
data/README.md CHANGED
@@ -9,17 +9,22 @@ Make this in Ruby:
9
9
 
10
10
  ## Generate it
11
11
 
12
- `rails g metricky:metric User --scope User --type :count --period :day`
12
+ `rails g metricky:metric TotalUsersMetric --scope User --type :count --period :day`
13
+
14
+ ## Display it
15
+
13
16
  In your view where you want to display the metric:
14
17
 
15
18
  ```erbruby
16
- render_metric :users
19
+ render_metric :total_users
17
20
  ```
18
21
 
19
- In `app/metrics/users_metric.rb`
22
+ ## Customize it
23
+
24
+ In `app/metrics/total_users_metric.rb`
20
25
 
21
26
  ```ruby
22
- class UsersMetric < Metricky::Base
27
+ class TotalUsersMetric < ApplicationMetric
23
28
  def scope
24
29
  User
25
30
  end
@@ -28,10 +33,6 @@ class UsersMetric < Metricky::Base
28
33
  :count
29
34
  end
30
35
 
31
- def columns
32
- :id
33
- end
34
-
35
36
  def period
36
37
  :day
37
38
  end
@@ -75,7 +76,7 @@ def columns
75
76
  end
76
77
  ```
77
78
 
78
- ### Grouping by date
79
+ ### Grouping by period (day, month, year, quarter, etc.)
79
80
 
80
81
  In your metric, define what period:
81
82
 
@@ -105,45 +106,45 @@ def type
105
106
  end
106
107
  ```
107
108
 
108
- This can be any one of `:min, :max, :sum, :count`
109
+ This can be any one of `:min, :max, :sum, :count, :average`
109
110
 
110
111
  ### Ranges
111
112
 
113
+ Ranges are what values are available on the form (used to query the metric, if applicable) via the `range_column`
114
+
115
+ ```ruby
116
+ def range_column
117
+ :created_at
118
+ end
119
+ ```
120
+
121
+ Defaults are `all`, `today`, `24 hours`, `3 days`, `7 days`, `10 days`, `14 days`, `30 days`, `3 months`, `6 months`, `12 months`
122
+
123
+ #### Creating a new range
112
124
  In your metric, define what ranges as a class method:
113
125
 
114
126
  ```ruby
115
- def self.ranges
116
- {
117
- 'all' => 'All',
118
- '30' => '30 Days',
119
- '60' => '60 Days',
120
- '365' => '365 Days',
121
- }
127
+ class TotalUsersMetric < ApplicationMetric
128
+ register_range '15h', label: "15 hours" do
129
+ 15.hours.ago
130
+ end
122
131
  end
123
132
  ```
124
133
 
125
- And then their corresponding values (instance-level):
134
+ #### Removing a range
126
135
 
127
- ```ruby
128
- def range_value
129
- case range
130
- when 'all'
131
- nil
132
- when '30'
133
- 30.days.ago
134
- when '60'
135
- 60.days.ago
136
- when '365'
137
- 365.days.ago
138
- end
136
+ ```ruby
137
+ class TotalUsersMetric < ApplicationMetric
138
+ remove_ranges '24h', '7d' # an array
139
+ remove_range '3d' # individual
139
140
  end
140
141
  ```
141
142
 
142
- This is used with a `where` query against the `range_column`.
143
+ #### Setting the default range
143
144
 
144
- ```ruby
145
- def range_column
146
- :created_at
145
+ ```ruby
146
+ class TotalUsersMetric < ApplicationMetric
147
+ default_range '24h'
147
148
  end
148
149
  ```
149
150
 
@@ -1,3 +1,2 @@
1
1
  class ApplicationMetric < Metricky::Base
2
-
3
2
  end
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
  <% if metric.class.ranges.any? %>
8
8
  <div>
9
- <%= f.select :range, metric.class.ranges.invert, { selected: metric.range }, { class: "form-control-sm", name: "#{metric.form_name}[range]", onchange: "this.form.submit()" } %>
9
+ <%= f.select :range, metric.range_collection, { selected: metric.range }, { class: "form-control-sm", name: "#{metric.form_name}[range]", onchange: "this.form.submit()" } %>
10
10
  </div>
11
11
  <% end %>
12
12
  </div>
@@ -1,5 +1,8 @@
1
1
  <% module_namespacing do -%>
2
2
  class <%= class_name %>Metric < ApplicationMetric
3
+ # ==> Change the default range (default is all)
4
+ # default_range '24h'
5
+
3
6
  def scope
4
7
  <%= options[:scope] %>
5
8
  end
@@ -7,11 +10,31 @@ class <%= class_name %>Metric < ApplicationMetric
7
10
  def type
8
11
  <%= options[:type][0] == ':' ? options[:type] : ":#{options[:type]}" %>
9
12
  end
10
-
11
13
  <% if options[:period] %>
12
14
  def period
13
15
  <%= options[:period][0] == ':' ? options[:period] : ":#{options[:period]}" %>
14
16
  end
17
+ <% else %>
18
+ # ==> Change the period grouping
19
+ # Must be one of Groupdate::PERIODS
20
+ # def period
21
+ # :week
22
+ # end
15
23
  <% end %>
24
+
25
+ # ==> Change the chart
26
+ # def chart
27
+ # :column_chart
28
+ # end
29
+
30
+ # ==> Register a new range
31
+ # Add a range to the select
32
+ # register_range '15w', label: "15 weeks" do
33
+ # 15.weeks.ago
34
+ # end
35
+
36
+ # ==> Remove ranges
37
+ # Remove ranges from the select
38
+ # exclude_ranges '24h', '30d'
16
39
  end
17
40
  <% end -%>
data/lib/metricky/base.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'metricky/period'
2
+ require 'metricky/ranges'
3
+
1
4
  module Metricky
2
5
  class Base
3
6
  VALID_TYPES = [:sum, :max, :min, :average, :count].freeze
@@ -19,32 +22,13 @@ module Metricky
19
22
  end
20
23
 
21
24
  def noun
22
- case type.to_sym
23
- when :count
24
- "total number of"
25
- when :average
26
- "average #{columns}"
27
- when :sum
28
- "total"
29
- when :min
30
- "minimum"
31
- when :max
32
- "maximum"
33
- end
34
- end
35
-
36
- # List of ranges and their values. Used (inverted) on the form.
37
- def self.ranges
38
25
  {
39
- 'all' => 'All',
40
- 'Today' => 'Today',
41
- '30' => '30 Days',
42
- '60' => '60 Days',
43
- '365' => '365 Days',
44
- 'WTD' => 'WTD',
45
- 'MTD' => 'MTD',
46
- 'YTD' => 'YTD',
47
- }
26
+ count: "total number of",
27
+ average: "average #{columns}",
28
+ sum: "total",
29
+ min: "minimum",
30
+ max: "maximum"
31
+ }[type.to_sym]
48
32
  end
49
33
 
50
34
  # Actual result of the metric
@@ -74,32 +58,6 @@ module Metricky
74
58
  self.class.metric_name.tableize
75
59
  end
76
60
 
77
- # Converts range string to a Ruby object
78
- def range_value
79
- case range
80
- when nil
81
- nil
82
- when 'all'
83
- nil
84
- when '30'
85
- 30.days.ago
86
- when '60'
87
- 60.days.ago
88
- when '365'
89
- 365.days.ago
90
- when 'WTD'
91
- DateTime.now.beginning_of_week
92
- when 'MTD'
93
- DateTime.now.beginning_of_month
94
- when 'YTD'
95
- DateTime.now.beginning_of_year
96
- when 'Today'
97
- DateTime.now.beginning_of_day
98
- else
99
- raise TypeError, "unknown range_value for range #{range}. Please define it on range_value"
100
- end
101
- end
102
-
103
61
  # What ActiveRecord class (or scoped class) is being used for the metric
104
62
  def scope
105
63
  raise NotImplementedError, "please add a scope to your metric."
@@ -118,66 +76,26 @@ module Metricky
118
76
  'id'
119
77
  end
120
78
 
121
- # How it's grouped. Leave nil if no grouping
122
- #
123
- # [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week,
124
- # :hour_of_day, :minute_of_hour, :day_of_month, :month_of_year]
125
- def trend
126
- ActiveSupport::Deprecation.warn('Use period instead')
127
- period
128
- end
129
-
130
- def period
131
- nil
132
- end
133
-
134
- # What column to specify for the range calculation. Normally `created_at`
135
- def range_column
136
- 'created_at'
137
- end
138
-
139
- # What column to specify for the trend calculation. Normally `created_at`
140
- def period_column
141
- 'created_at'
142
- end
143
-
144
- def trend_column
145
- ActiveSupport::Deprecation.warn('Use period_column instead')
146
- period_column
147
- end
148
-
149
- def range
150
- params.dig(form_name, :range)
151
- end
152
-
153
79
  private
154
80
 
155
- def period?
156
- period.present?
157
- end
158
-
159
81
  def assets
160
- if range_value != nil
161
- @query = scope.where("#{range_column} > ?", range_value)
162
- else
163
- @query = scope
82
+ @query = scope
83
+ unless range_to_value.nil?
84
+ @query = scope.where("#{range_column} > ?", range_to_value)
164
85
  end
165
86
  if period? && valid_period?
166
- @query = @query.group_by_period(trend, trend_column)
87
+ @query = @query.group_by_period(period, period_column)
88
+ end
89
+ if valid_type?
90
+ @query = @query.send(type, *columns)
91
+ else
92
+ raise TypeError, "#{type} is not a valid type."
167
93
  end
168
- @query = @query.send(type, *columns)
169
94
  @query
170
95
  end
171
96
 
172
- def valid_period?
173
- return true if Groupdate::PERIODS.include?(period.to_sym)
174
- raise NameError, "period must be one of #{Groupdate::PERIODS}. It is #{period}."
175
- end
176
-
177
- def check_type
178
- unless VALID_TYPES.include?(type.to_sym)
179
- raise NameError, "#{type} must be one of :sum, :max, :min, :average, :count"
180
- end
97
+ def valid_type?
98
+ VALID_TYPES.include?(type.to_sym)
181
99
  end
182
100
  end
183
101
  end
@@ -0,0 +1,8 @@
1
+ class Object
2
+ def self.deprecate_and_alias_method(new_method, old_method)
3
+ define_method(old_method) do
4
+ ActiveSupport::Deprecation.warn("#{old_method} is deprecated. Use #{new_method} instead.")
5
+ send(new_method)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,32 @@
1
+ module Metricky
2
+ class Base
3
+ # How it's grouped. Leave nil if no grouping
4
+ #
5
+ # [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week,
6
+ # :hour_of_day, :minute_of_hour, :day_of_month, :month_of_year]
7
+ def period
8
+ nil
9
+ end
10
+ deprecate_and_alias_method :period, :trend
11
+
12
+ # What column to specify for the period calculation. Normally `created_at`
13
+ def period_column
14
+ 'created_at'
15
+ end
16
+ deprecate_and_alias_method :period_column, :trend_column
17
+
18
+ private
19
+
20
+ def period?
21
+ period.present?
22
+ end
23
+ deprecate_and_alias_method :period?, :trend?
24
+
25
+ def valid_period?
26
+ return true if Groupdate::PERIODS.include?(period.to_sym)
27
+ raise NameError, "period must be one of #{Groupdate::PERIODS}. It is #{period}."
28
+ end
29
+ deprecate_and_alias_method :valid_period?, :valid_trend?
30
+
31
+ end
32
+ end
@@ -0,0 +1,10 @@
1
+ module Metricky
2
+ class RangeThing
3
+ attr_reader :label, :priority, :value
4
+ def initialize(label, priority, value)
5
+ @label = label
6
+ @priority = priority
7
+ @value = value
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,104 @@
1
+ module Metricky
2
+ class Base
3
+ class_attribute :ranges, default: {}
4
+ class_attribute :excluded_ranges, default: []
5
+ class_attribute :default_range_key, default: 'all'
6
+
7
+ def self.default_range(val = nil)
8
+ self.default_range_key = val
9
+ end
10
+
11
+ # Rewrite the priority of a range thing.
12
+ #
13
+ # class UserMetric
14
+ # range_priority '24h', 99
15
+ # end
16
+ def self.range_priority(key, priority)
17
+ self.ranges[key.to_s].priority = priority
18
+ end
19
+
20
+ # Register a range. Priority is used for the select order.
21
+ #
22
+ # @param [String] key The value for the option in the select for the form
23
+ # @param [String] label The text shown on the option in the select for the form
24
+ # @param [Integer] priority What order the resulting collection
25
+ # @param [Block/Proc] block The Ruby-converted value. Usually a DateTime/Date/Time object.
26
+ #
27
+ # class UserMetric
28
+ # register_range '15w', label: '15 weeks', priority: 99 do
29
+ # 15.weeks.ago
30
+ # end
31
+ # end
32
+ #
33
+ # @return RangeThing
34
+ def self.register_range(key, label: nil, priority: nil, &block)
35
+ label ||= key
36
+ priority ||= self.ranges.size
37
+ self.ranges[key.to_s] = RangeThing.new(label, priority, block)
38
+ end
39
+
40
+ # Removes the listed ranges from the select
41
+ def self.remove_ranges(*keys)
42
+ keys.each { self.remove_range(key) }
43
+ end
44
+
45
+ def self.remove_range(key)
46
+ self.excluded_ranges << key.to_s
47
+ end
48
+
49
+ register_range 'all', label: 'All' do
50
+ nil
51
+ end
52
+
53
+ register_range 'today', label: 'Today' do
54
+ DateTime.now.beginning_of_day
55
+ end
56
+
57
+ register_range '24h', label: '24 hours' do
58
+ 24.hours.ago
59
+ end
60
+
61
+ [3, 7, 10, 14, 21, 30].each do |day|
62
+ register_range "#{day}d", label: "#{day} Days" do
63
+ day.days.ago
64
+ end
65
+ end
66
+
67
+ register_range "MTD", label: "MTD" do
68
+ DateTime.now.beginning_of_month
69
+ end
70
+
71
+ register_range "QTD", label: "QTD" do
72
+ DateTime.now.beginning_of_quarter
73
+ end
74
+
75
+ register_range "YTD", label: "YTD" do
76
+ DateTime.now.beginning_of_year
77
+ end
78
+
79
+ # Lookup the passed range and convert it to a value for our ActiveRecord query
80
+ def range_to_value
81
+ return nil if range.nil?
82
+ if val = self.ranges[range]
83
+ val.value.call
84
+ else
85
+ raise TypeError, "invalid range #{range}. Please define it."
86
+ end
87
+ end
88
+ deprecate_and_alias_method :range_to_value, :range_value
89
+
90
+ # What column to specify for the range calculation. Normally `created_at`
91
+ def range_column
92
+ 'created_at'
93
+ end
94
+
95
+ def range
96
+ params.dig(form_name, :range) || default_range_key
97
+ end
98
+
99
+ # Used in the HTML form for the metric as the select options
100
+ def range_collection
101
+ ranges.except(*excluded_ranges).sort_by { |_, range| range.priority }.collect { |key, range_thing| [range_thing.label, key] }
102
+ end
103
+ end
104
+ end
@@ -1,3 +1,3 @@
1
1
  module Metricky
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
data/lib/metricky.rb CHANGED
@@ -1,11 +1,15 @@
1
1
  require "groupdate"
2
2
  require "chartkick"
3
+ require 'metricky/core_ext'
4
+ require 'active_support/core_ext'
5
+ require 'active_support/concern'
6
+ require "metricky/range_thing"
7
+ require "metricky/ranges"
3
8
  require "metricky/base"
4
9
  require "metricky/helper"
5
10
  require "metricky/engine"
6
11
 
7
- module Metricky
8
- end
12
+ module Metricky; end
9
13
 
10
14
  ActiveSupport.on_load(:action_view) do
11
15
  include Metricky::Helper
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metricky
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Brody
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-09 00:00:00.000000000 Z
11
+ date: 2019-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -126,8 +126,12 @@ files:
126
126
  - lib/generators/metricky/templates/metric.rb.tt
127
127
  - lib/metricky.rb
128
128
  - lib/metricky/base.rb
129
+ - lib/metricky/core_ext.rb
129
130
  - lib/metricky/engine.rb
130
131
  - lib/metricky/helper.rb
132
+ - lib/metricky/period.rb
133
+ - lib/metricky/range_thing.rb
134
+ - lib/metricky/ranges.rb
131
135
  - lib/metricky/version.rb
132
136
  homepage: https://github.com/joshmn/metricky
133
137
  licenses: