active_reporting 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +15 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +25 -0
- data/.travis.yml +13 -2
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +301 -5
- data/Rakefile +5 -5
- data/active_reporting.gemspec +20 -13
- data/bin/console +3 -3
- data/gemfiles/4.0.gemfile +5 -0
- data/gemfiles/5.0.gemfile +5 -0
- data/lib/active_reporting.rb +39 -2
- data/lib/active_reporting/active_record_adaptor.rb +21 -0
- data/lib/active_reporting/configuration.rb +80 -0
- data/lib/active_reporting/dimension.rb +59 -0
- data/lib/active_reporting/dimension_filter.rb +30 -0
- data/lib/active_reporting/fact_model.rb +170 -0
- data/lib/active_reporting/metric.rb +49 -0
- data/lib/active_reporting/report.rb +149 -0
- data/lib/active_reporting/reporting_dimension.rb +102 -0
- data/lib/active_reporting/version.rb +1 -1
- metadata +78 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e776c2130450c3db98c5518529079744a8e3f7e
|
4
|
+
data.tar.gz: 8284510c02504a70d78e3db21ba9697ac95bece6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab8d651c7b980707ad6d4af9736c444c1c021f3a3f6d967e7f9eb8398270c17579c0b3dd35049e426225a0448924fa49edb77d4db9d10e8784786ae710e3b1e6
|
7
|
+
data.tar.gz: 9b488e5c85ce172cf06103a244faaf6150c34039750c670d235792539c970642110f15692cf24fc3e714ec4a394f4d8fc922560f5b6b9c27d789779c63a125b5
|
data/.codeclimate.yml
ADDED
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- test/**/*
|
4
|
+
TargetRubyVersion: 2.2
|
5
|
+
|
6
|
+
Metrics/LineLength:
|
7
|
+
Max: 120
|
8
|
+
|
9
|
+
Metrics/ParameterLists:
|
10
|
+
Max: 8
|
11
|
+
|
12
|
+
MethodLength:
|
13
|
+
Max: 20
|
14
|
+
|
15
|
+
Style/ClassAndModuleChildren:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/Documentation:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/PredicateName:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/RaiseArgs:
|
25
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
- 2.2.
|
5
|
-
|
4
|
+
- 2.2.7
|
5
|
+
- 2.3.4
|
6
|
+
- 2.4.1
|
7
|
+
gemfiles:
|
8
|
+
- gemfiles/4.2.gemfile
|
9
|
+
- gemfiles/5.0.gemfile
|
10
|
+
matrix:
|
11
|
+
exclude:
|
12
|
+
- rvm: 2.2
|
13
|
+
gemfile: gemfiles/5.0.gemfile
|
14
|
+
script:
|
15
|
+
- bundle exec rake
|
16
|
+
|
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at t27duck@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/t27duck/active_reporting.svg?branch=master)](https://travis-ci.org/t27duck/active_reporting)
|
2
|
+
|
3
|
+
[![Code Climate](https://codeclimate.com/github/t27duck/active_reporting/badges/gpa.svg)](https://codeclimate.com/github/t27duck/active_reporting)
|
4
|
+
|
1
5
|
# ActiveReporting
|
2
6
|
|
3
|
-
|
7
|
+
ActiveReporting implements various terminology used in Relational Online Analytical Processing (Commonly referred to as ROLAP) with ActiveRecord. It provides a DSL to describe reports and analytics on your data.
|
4
8
|
|
5
|
-
|
9
|
+
ActiveReporting officially supports MySQL, PostgreSQL, and SQLite.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
@@ -20,9 +24,301 @@ Or install it yourself as:
|
|
20
24
|
|
21
25
|
$ gem install active_reporting
|
22
26
|
|
23
|
-
##
|
27
|
+
## What is "Reporting"?
|
28
|
+
|
29
|
+
Reporting is the collection and presentation data so that it can be analyzed. Our databases only store one thing: data. Data is great for computers but mostly worthless to humans. What ActiveReoprting does is turn that *data* into *information* to help humans make decisions.
|
30
|
+
|
31
|
+
## Terminology
|
32
|
+
|
33
|
+
ROLAP uses a set of terms to describe how a report is generated. ActiveReporting implements them in the closest way possible in Ruby-land.
|
34
|
+
|
35
|
+
### Fact table (can be sometimes called fact model)
|
36
|
+
|
37
|
+
A fact table is the primary table where information is derived from in a report. It commonly contains fact columns (usually numeric values) and dimension columns (foreign keys to other tables or values that can be grouped together).
|
38
|
+
|
39
|
+
SQL Equivalent: FROM
|
40
|
+
Rails: ActiveRecord model
|
41
|
+
|
42
|
+
### Dimension
|
43
|
+
|
44
|
+
A dimension is a point of data used to "slice and dice" data from a fact model. It's either a column that lives on the fact table or a foreign key to another table.
|
45
|
+
|
46
|
+
Examples:
|
47
|
+
* A sales rep on a fact table of orders
|
48
|
+
* A state of an order on a state machine
|
49
|
+
* The manufacture on a fact table of widgets
|
50
|
+
|
51
|
+
SQL Equivalent: JOIN, GROUP BY
|
52
|
+
Rails: ActiveRecord relation or attribute
|
53
|
+
|
54
|
+
### Dimension Hierarchy
|
55
|
+
|
56
|
+
A hierarchy for a dimension is related attributes that live on a dimension table used to drill down and drill up through a dimension.
|
57
|
+
|
58
|
+
Examples:
|
59
|
+
* Dates: Date, Month, Year, Quarter
|
60
|
+
* Mobile Phone: Model, Manufacture, OS, Wireless Technology
|
61
|
+
|
62
|
+
### Dimension Member (also known as dimension labels)
|
63
|
+
|
64
|
+
This is information related to a dimension. When the dimension lives on the fact table, the label is the column used. When the dimension is a related table, the label is a column representing the hierarchy level.
|
65
|
+
|
66
|
+
Examples:
|
67
|
+
* When dimensioning blog posts by category, the dimension is the category_id which leads to the categories table. The label would be the category name.
|
68
|
+
|
69
|
+
### Dimension Filter (or just "filter")
|
70
|
+
|
71
|
+
This isn't really an official term, but I like using it to describe further filtering of dimensionable data.
|
72
|
+
|
73
|
+
SQL Equivalent: WHERE
|
74
|
+
Rails: `where()`, scopes, etc.
|
75
|
+
|
76
|
+
### Measure
|
77
|
+
|
78
|
+
A measure is a column in a fact table (usually a numeric value) used in aggregations such as sum, maximum, average, etc.
|
79
|
+
|
80
|
+
Examples:
|
81
|
+
* Total amount in a sale
|
82
|
+
* Number of units used in a transaction
|
83
|
+
|
84
|
+
SQL Equivalent: Column in the fact table used in an aggregation function
|
85
|
+
Rails: ActiveRecord attribute
|
86
|
+
|
87
|
+
### Metric
|
88
|
+
|
89
|
+
A metric is a measured value and the subject of the report. It is the result of *the* question you want answered.
|
90
|
+
|
91
|
+
SQL Equivalent: A query result
|
92
|
+
Rails: The result of an ActiveRecord query
|
93
|
+
|
94
|
+
### Star Schema
|
95
|
+
|
96
|
+
Star schema is a way of structuring your relational data. It is one of the most common forms of organization for relational data warehousing. The layout of a star schema consists of a fact table referencing one or more dimension tables. When laid out in an entity relationship diagram, it resembles a star.
|
97
|
+
|
98
|
+
[TODO: ADD PICTURE HERE]
|
99
|
+
|
100
|
+
More information: https://en.wikipedia.org/wiki/Star_schema
|
101
|
+
|
102
|
+
### Snowflake Schema
|
103
|
+
|
104
|
+
Snowflake schema is a super class of star schema. A fact table still resides in the middle of the diagram, but dimension tables are normalized out into multiple tables resulting in the resemblance of a snowflake.
|
105
|
+
|
106
|
+
[TODO: ADD PICTURE HERE]
|
107
|
+
|
108
|
+
More information: https://en.wikipedia.org/wiki/Snowflake_schema
|
109
|
+
|
110
|
+
ActiveReporting is built with star schema in mind, but will work with snowflake.
|
111
|
+
|
112
|
+
## Configuration
|
113
|
+
|
114
|
+
Configure ActiveReporting via block configuration or by setting individual settings:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
ActiveReporting::Configuration.config do |c|
|
118
|
+
c.setting = value
|
119
|
+
end
|
120
|
+
```
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
ActiveReporting::Configuration.setting = value
|
124
|
+
```
|
125
|
+
|
126
|
+
### Configuration Options
|
127
|
+
|
128
|
+
`default_dimension_label` - If a fact model does not have a default label set for when it's used as a dimension, this value will be used. (Default: `:name`)
|
129
|
+
|
130
|
+
`default_measure` - If a fact model does not specify a measure to use for aggregates, this value will be used. (Default: `:value`)
|
131
|
+
|
132
|
+
`ransack_fallback` - If the ransack gem is loaded, allow all unknown dimension filters to be delegated to ransack. (Default: `false`)
|
133
|
+
|
134
|
+
`metric_lookup_class` - The name of a constant used to lookup prebuilt `Reporting::Metric` objects by name. The constant should define a class method called `#lookup` which can take a string or symbol of the metric name. (Default: `::Metric`)
|
135
|
+
|
136
|
+
|
137
|
+
## ActiveReporting::FactModel
|
138
|
+
|
139
|
+
In ActiveReporting, a fact model stores configuration information on how it can be used in reports. We use the term fact model instead of fact table because this class "models how the fact table interacts with dimensions and other reporting features".
|
140
|
+
|
141
|
+
You can put these classes anywhere you want in your app, though I recommend putting them in `app/fact_models`
|
142
|
+
|
143
|
+
### Linking a fact model to an ActiveRecord model
|
144
|
+
|
145
|
+
Every fact model links to an ActiveRecord model. This is done either by naming convention or by explicitly declaring the model.
|
146
|
+
|
147
|
+
This naming convention is `[ModelName]FactModel`. Meaning if you have an ActiveRecord model named `Ticket`, you'll then have a `TicketFactModel` to link them together.
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
class TicketFactModel < ActiveRecord::FactModel
|
151
|
+
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
Alternatively, you may manually specify the model manually with `use_model`
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
class TicketFactModel < ActiveRecord::FactModel
|
159
|
+
use_model SomeOtherModel
|
160
|
+
# OR you may pass in a string or symbol
|
161
|
+
# use_model :some_other_model
|
162
|
+
# use_model 'some_other_model'
|
163
|
+
# use_model 'SomeOtherModel'
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
### Configuring a fact model's measure
|
168
|
+
|
169
|
+
ActiveReporting assumes the column of a fact model used for summing, averaging, etc. is called `value`. This may be changed on a fact model using `measure=`. You may pass in a string or symbol of the column you wish to use for aggregations.
|
24
170
|
|
25
|
-
|
171
|
+
```ruby
|
172
|
+
class OrderFactModel < ActiveReporting::FactModel
|
173
|
+
measure = :total
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
## Configuring Dimensions
|
178
|
+
|
179
|
+
### Declaring dimensions on a fact model
|
180
|
+
|
181
|
+
You must declare what a fact model is dimensional by. A valid dimension is a column on the fact model's ActiveRecord model or a `belongs_to`/`has_one through` relationship. `has_many` relationships do not work (well) at all.
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
class TicketFactModel < ActiveReporting::FactModel
|
185
|
+
dimension :creator # belongs_to relationship
|
186
|
+
dimension :assignee # belongs_to relationship
|
187
|
+
dimension :category # Column on the tickets table
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
### When a fact model is used as a dimension
|
192
|
+
|
193
|
+
When another fact model uses a relationship as a dimension, that ActiveRecord model's fact model class can hold configuration information for how to act when used as a dimension.
|
194
|
+
|
195
|
+
By default, it is assumed a dimension's label is a column called `name`. This can be changed on the fact model.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
class UserFactModel < ActiveReporting::FactModel
|
199
|
+
default_dimension_label :username
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
### Dimension Hierarchies
|
204
|
+
|
205
|
+
For dimensions that can have a hierarchy (such as a mobile phone), you can declare the what columns make it up. This will allow reports to dimension against a fact model and be able to use different labels to group by.
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class PhoneFactModel < ActiveReporting::FactModel
|
209
|
+
dimension_hierarchy [:model_name, :manufacturer, :os, :wireless_technology]
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
## Configuring Dimension Filters
|
214
|
+
|
215
|
+
A dimension filter provides filtering for a report. In SQL-land, this is the `WHERE` clause.
|
216
|
+
|
217
|
+
Available dimension filters are defined on a `FactModel`. They can be implemented via a similar syntax to a Rails scope, link to the fact model's ActiveRecord model's scope, or delegate to ransack.
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
class TicketFactModel < ActiveReporting::FactModel
|
221
|
+
dimension_filter :open
|
222
|
+
dimension_filter :for_category_name, ->(x) { joins(:category).where(categories: {name: x}) }
|
223
|
+
dimension_filter :subject_cont, :ransack
|
224
|
+
end
|
225
|
+
```
|
226
|
+
|
227
|
+
The first example exposes the `Ticket.open` scope to the fact model allowing it to be used as a dimension filter.
|
228
|
+
|
229
|
+
The second example defines a lambda to be invoked like a Rails scope. It joins against the `category` relationship on `Ticket` and filters by the category's name.
|
230
|
+
|
231
|
+
The third example defines a filter called "subject_cont" and will delegate it to ransack when called.
|
232
|
+
|
233
|
+
Only dimension filters defined in the fact model may be used. Whitelisting available filters allows for more control over what the user may filter by. Giving the user full control to call any scope or method from the ActiveRecord model could lead to unexpected results, poor performing queries, or possible security concerns.
|
234
|
+
|
235
|
+
If ransack is available, you may flag a fact model to delegate all unknown dimension filters to ransack.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
class TicketFactModel < ActiveReporting::FactModel
|
239
|
+
use_ransack_for_unknown_dimension_filters
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
## ActiveReporting::Metric
|
244
|
+
|
245
|
+
A `Metric` is the basic building block used to describe a question you want to answer. At minimum, a metric needs a name, a fact table and an aggregate. You can expand a metric further by including dimensions and dimension filters.
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
my_metric = ActiveReporting::Metric.new(
|
249
|
+
:order_total,
|
250
|
+
fact_model: OrderFactModel,
|
251
|
+
aggregate: :sum
|
252
|
+
)
|
253
|
+
```
|
254
|
+
|
255
|
+
`name` - This is the identifying name of the metric.
|
256
|
+
|
257
|
+
`fact_model` - An `ActiveReporting::FactModel` class
|
258
|
+
|
259
|
+
`aggregate` - The SQL aggregate used to calculate the metric. Supported aggregates include count, max, min, avg, and sum. (Default: `:count`)
|
260
|
+
|
261
|
+
`dimensions - An array of dimensions used for the metric. When given just a symbol, the default dimension label will be used for the dimension. You may specify a hierarchy level by using a hash. (Examples: `[:sales_rep, {order_date: :month}]`)
|
262
|
+
|
263
|
+
`dimension_filter` - A hash were the keys are dimension filter names and the values are the values passed into the filter.
|
264
|
+
|
265
|
+
`metric_filter` - An additional HAVING clause to be tacked on to the end of the query. This allows for the further filtering of the end results based on the value of the aggregate. (Examples: `{gt: 3}`, `{eq: 5}`, `{lte: 7}`)
|
266
|
+
|
267
|
+
`order_by_dimension` - Allows you to set the ordering of the results based on a dimension label. (Examples: `{author: :desc}`, `{sales_ref: :asc}`)
|
268
|
+
|
269
|
+
## ActiveReporting::Report
|
270
|
+
|
271
|
+
A `Report` takes an `ActiveReporting::Metric` and ties everything together. It is responsible for building and executing the query to generate a result. The result is an simple array of hashing.
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
metric = ActiveReporting::Metric.new(
|
275
|
+
:order_count,
|
276
|
+
fact_model: OrderFactModel,
|
277
|
+
dimension: [:sales_rep],
|
278
|
+
dimension_filter: {months_ago: 1}
|
279
|
+
)
|
280
|
+
|
281
|
+
report = ActiveReporting.new(metric)
|
282
|
+
report.run
|
283
|
+
=> [{order_count: 12, sales_rep: 'Fred Jones', sales_rep_identifier: 123},{order_count: 17, sales_rep: 'Mary Sue', sales_rep_identifier: 123}]
|
284
|
+
```
|
285
|
+
|
286
|
+
A `Report` may also take additional arguments to merge with the `Metric`'s information. This can be user input for additional filters, or to expand on a base `Metric`.
|
287
|
+
|
288
|
+
`dimension_identifiers` - When true, the result will include the database identifier columns of the dimensions. For example, when running a report for the total number of orders dimensioned by sales rep, the rep's IDs from the `sales_reps` table will be included. (Default `true`)
|
289
|
+
|
290
|
+
`dimension_filter` - A hash that will be merged with the `Metric`'s dimension filters.
|
291
|
+
|
292
|
+
`dimensions` - An array of additional dimensions which are merged with the `Metric`'s dimensions.
|
293
|
+
|
294
|
+
`metric_filter` - Sets the HAVING clause of the final query and is merged with the `Metric`'s metric filter.
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
metric = ActiveReporting::Metric.new(
|
298
|
+
:order_count,
|
299
|
+
fact_model: OrderFactModel,
|
300
|
+
dimension: [:sales_rep],
|
301
|
+
dimension_filter: {months_ago: 1}
|
302
|
+
)
|
303
|
+
|
304
|
+
report = ActiveReporting.new(metric, dimension_filter: {from_region: 'North'}, dimension_identifiers: false)
|
305
|
+
report.run
|
306
|
+
=> [{order_count: 17, sales_rep: 'Mary Sue'}]
|
307
|
+
```
|
308
|
+
|
309
|
+
It may be more DRY to store ready-made metrics in a database table or stored in memory to use as the bases for various reports. You can pass a string or symbol into a `Report` instead of a `Metric` to look up an pre-made metric. This is done by passing the symbol or string into the `lookup` class method on the constant defined in `ActiveReporting::Configuration.metric_lookup_class`.
|
310
|
+
|
311
|
+
```ruby
|
312
|
+
class StoredMetrics
|
313
|
+
def lookup(metric_name)
|
314
|
+
# Code to construct and return an `ActiveReporting::Metric` object
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
ActiveReporting::Configuration.metric_lookup_class = StoredMetrics
|
319
|
+
|
320
|
+
report = ActiveReporting::Report.new(:a_stored_metric, ...)
|
321
|
+
```
|
26
322
|
|
27
323
|
## Development
|
28
324
|
|
@@ -32,7 +328,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
32
328
|
|
33
329
|
## Contributing
|
34
330
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_reporting.
|
331
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_reporting. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
332
|
|
37
333
|
|
38
334
|
## License
|