sleek 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +256 -0
- data/Rakefile +6 -0
- data/lib/sleek/base.rb +52 -0
- data/lib/sleek/core_ext/range.rb +44 -0
- data/lib/sleek/core_ext/time.rb +2 -0
- data/lib/sleek/event.rb +37 -0
- data/lib/sleek/filter.rb +24 -0
- data/lib/sleek/interval.rb +41 -0
- data/lib/sleek/queries/average.rb +21 -0
- data/lib/sleek/queries/count.rb +17 -0
- data/lib/sleek/queries/count_unique.rb +21 -0
- data/lib/sleek/queries/maximum.rb +21 -0
- data/lib/sleek/queries/minimum.rb +21 -0
- data/lib/sleek/queries/query.rb +105 -0
- data/lib/sleek/queries/sum.rb +21 -0
- data/lib/sleek/queries/targetable.rb +13 -0
- data/lib/sleek/queries.rb +8 -0
- data/lib/sleek/query_collection.rb +25 -0
- data/lib/sleek/timeframe.rb +85 -0
- data/lib/sleek/version.rb +3 -0
- data/lib/sleek.rb +23 -0
- data/sleek.gemspec +28 -0
- data/sleek.png +0 -0
- data/spec/lib/sleek/base_spec.rb +48 -0
- data/spec/lib/sleek/event_spec.rb +21 -0
- data/spec/lib/sleek/filter_spec.rb +26 -0
- data/spec/lib/sleek/interval_spec.rb +24 -0
- data/spec/lib/sleek/queries/average_spec.rb +13 -0
- data/spec/lib/sleek/queries/count_spec.rb +13 -0
- data/spec/lib/sleek/queries/count_unique_spec.rb +15 -0
- data/spec/lib/sleek/queries/maximum_spec.rb +13 -0
- data/spec/lib/sleek/queries/minimum_spec.rb +13 -0
- data/spec/lib/sleek/queries/query_spec.rb +226 -0
- data/spec/lib/sleek/queries/sum_spec.rb +13 -0
- data/spec/lib/sleek/queries/targetable_spec.rb +29 -0
- data/spec/lib/sleek/query_collection_spec.rb +27 -0
- data/spec/lib/sleek/timeframe_spec.rb +86 -0
- data/spec/lib/sleek_spec.rb +10 -0
- data/spec/spec_helper.rb +17 -0
- metadata +203 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Gosha Arinich
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
![Sleek](sleek.png)
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/goshakkk/sleek.png)](https://travis-ci.org/goshakkk/sleek)
|
4
|
+
|
5
|
+
Sleek is a gem for doing analytics. It allows you to easily collect and
|
6
|
+
analyze events that happen in your app.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
The easiest way to install Sleek is to add it to your Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem "sleek"
|
14
|
+
```
|
15
|
+
|
16
|
+
Then, install it:
|
17
|
+
|
18
|
+
```
|
19
|
+
$ bundle install
|
20
|
+
```
|
21
|
+
|
22
|
+
Sleek requires MongoDB to work and assumes that you have Mongoid
|
23
|
+
configured already.
|
24
|
+
|
25
|
+
## Getting started
|
26
|
+
|
27
|
+
### Namespacing
|
28
|
+
|
29
|
+
Namespaces are a great way to organize entirely different buckets of
|
30
|
+
data inside a single application. In Sleek, everything is namespaced.
|
31
|
+
|
32
|
+
Creating a namespaced instance of Sleek is easy:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
sleek = Sleek[:my_namespace]
|
36
|
+
```
|
37
|
+
|
38
|
+
You then would just call everything on this instance.
|
39
|
+
|
40
|
+
### Sending an Event
|
41
|
+
|
42
|
+
The heart of analytics is in recording events. Events are things that
|
43
|
+
happen in your app that you want to track. Events are stored in event
|
44
|
+
buckets.
|
45
|
+
|
46
|
+
In order to send an event, you would simply need to call
|
47
|
+
`sleek.record`, passing the event bucket name and the event
|
48
|
+
payload.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
sleek.record(:purchases, {
|
52
|
+
customer: { id: 1, name: "First Last", email: "first@last.com" },
|
53
|
+
items: [{ sku: "TSTITM1", name: "Test Item 1", price: 1999 }],
|
54
|
+
total: 1999
|
55
|
+
})
|
56
|
+
```
|
57
|
+
|
58
|
+
### Analyzing Events
|
59
|
+
|
60
|
+
#### Simple count
|
61
|
+
|
62
|
+
There are a few methods of analyzing your data. The simplest one is
|
63
|
+
counting. It, you guessed it, would count how many times the event has
|
64
|
+
occurred.
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
sleek.queries.count(:purchases)
|
68
|
+
# => 42
|
69
|
+
```
|
70
|
+
|
71
|
+
#### Average
|
72
|
+
|
73
|
+
In order to calculate average value, it's needed to additionally specify
|
74
|
+
what property should the average be calculated based on:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
sleek.queries.average(:purchases, target_property: :total)
|
78
|
+
# => 1999
|
79
|
+
```
|
80
|
+
|
81
|
+
#### Query with timeframe
|
82
|
+
|
83
|
+
You can limit the scope of events that analysis is run on by adding the
|
84
|
+
`:timeframe` option to any query call.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
sleek.queries.count(:purchases, timeframe: :this_day)
|
88
|
+
# => 10
|
89
|
+
```
|
90
|
+
|
91
|
+
#### Query with interval
|
92
|
+
|
93
|
+
Some kinds of applications may need to analyze trends in the data. Using
|
94
|
+
intervals, you can break a timeframe into minutes, hours, days, weeks,
|
95
|
+
or months. One can do so by passing the `:interval` option to any query
|
96
|
+
call. Using `:interval` also requires that you specify `:timeframe`.
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
sleek.queries.count(:purchases, timeframe: :this_2_days, interval: :daily)
|
100
|
+
# => [
|
101
|
+
# {:timeframe=>#<Sleek::Timeframe 2013-01-01 00:00:00 UTC..2013-01-02 00:00:00 UTC>, :value=>10},
|
102
|
+
# {:timeframe=>#<Sleek::Timeframe 2013-01-02 00:00:00 UTC..2013-01-03 00:00:00 UTC>, :value=>24}
|
103
|
+
# ]
|
104
|
+
```
|
105
|
+
|
106
|
+
## Data analysis in more detail
|
107
|
+
|
108
|
+
### Metrics
|
109
|
+
|
110
|
+
The word "metrics" is used to describe analysis queries which return a
|
111
|
+
single numeric value.
|
112
|
+
|
113
|
+
### Count
|
114
|
+
|
115
|
+
Count just counts the number of events recorded.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
sleek.queries.count(:bucket)
|
119
|
+
# => 42
|
120
|
+
```
|
121
|
+
|
122
|
+
### Count unique
|
123
|
+
|
124
|
+
It counts how many events have an unique value for a given property.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
sleek.queries.count_unique(:bucket, params)
|
128
|
+
```
|
129
|
+
|
130
|
+
You must pass the target property name in params like this:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
sleek.queries.count_unique(:purchases, target_property: "customer.id")
|
134
|
+
# => 30
|
135
|
+
```
|
136
|
+
|
137
|
+
### Minimum
|
138
|
+
|
139
|
+
It finds the minimum numeric value for a given property. All non-numeric
|
140
|
+
values are ignored. If none of property values are numeric, the
|
141
|
+
exception will be raised.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
sleek.queries.minimum(:bucket, params)
|
145
|
+
```
|
146
|
+
|
147
|
+
You must pass the target property name in params like this:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
sleek.queries.minimum(:purchases, target_property: "total")
|
151
|
+
# => 10_99
|
152
|
+
```
|
153
|
+
|
154
|
+
### Maximum
|
155
|
+
|
156
|
+
It finds the maximum numeric value for a given property. All non-numeric
|
157
|
+
values are ignored. If none of property values are numeric, the
|
158
|
+
exception will be raised.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
sleek.queries.maximum(:bucket, params)
|
162
|
+
```
|
163
|
+
|
164
|
+
You must pass the target property name in params like this:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
sleek.queries.maximum(:purchases, target_property: "total")
|
168
|
+
# => 199_99
|
169
|
+
```
|
170
|
+
|
171
|
+
### Average
|
172
|
+
|
173
|
+
The average query finds the average value for a given property. All
|
174
|
+
non-numeric values are ignored. If none of property values are numeric,
|
175
|
+
the exception will be raised.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
sleek.queries.average(:bucket, params)
|
179
|
+
```
|
180
|
+
|
181
|
+
You must pass the target property name in params like this:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
sleek.queries.average(:purchases, target_property: "total")
|
185
|
+
# => 49_35
|
186
|
+
```
|
187
|
+
|
188
|
+
### Sum
|
189
|
+
|
190
|
+
The sum query sums all the numeric values for a given property. All
|
191
|
+
non-numeric values are ignored. If none of property values are numeric,
|
192
|
+
the exception will be raised.
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
sleek.queries.sum(:bucket, params)
|
196
|
+
```
|
197
|
+
|
198
|
+
You must pass the target property name in params like this:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
sleek.queries.sum(:purchases, target_property: "total")
|
202
|
+
# => 2_072_70
|
203
|
+
```
|
204
|
+
|
205
|
+
## Filters
|
206
|
+
|
207
|
+
To limit the scope of events used in analysis you can use a filter. To
|
208
|
+
do so, you just pass the `:filter` option to the query.
|
209
|
+
|
210
|
+
A single filter is a 3-element array, consisting of:
|
211
|
+
|
212
|
+
* `property_name` - the property name to filter.
|
213
|
+
* `operator` - the name of the operator to apply.
|
214
|
+
* `value` - the value used in operator to compare to property value.
|
215
|
+
|
216
|
+
Operators: eq, ne, lt, lte, gt, gte, in.
|
217
|
+
|
218
|
+
You can pass either a single filter or an array of filters.
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
sleek.queries.count(:purchases, filters: [:total, :gt, 1599])
|
222
|
+
# => 20
|
223
|
+
```
|
224
|
+
|
225
|
+
## Series
|
226
|
+
|
227
|
+
Series allow you to analyze trends in metrics over time. They break a
|
228
|
+
timeframe into intervals and compute the metric for those intervals.
|
229
|
+
|
230
|
+
Calculating series is simply done by adding the `:timeframe` and
|
231
|
+
`:interval` options to the metric query.
|
232
|
+
|
233
|
+
Valid intervals are:
|
234
|
+
|
235
|
+
* `:hourly`
|
236
|
+
* `:daily`
|
237
|
+
* `:weekly`
|
238
|
+
* `:monthly`
|
239
|
+
|
240
|
+
## Other
|
241
|
+
|
242
|
+
### Deleting buckets
|
243
|
+
|
244
|
+
```ruby
|
245
|
+
sleek.delete_bucket(:purchases)
|
246
|
+
```
|
247
|
+
|
248
|
+
### Deleting property from all events in the bucket
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
sleek.delete_property(:purchases, :some_property)
|
252
|
+
```
|
253
|
+
|
254
|
+
## License
|
255
|
+
|
256
|
+
[MIT](LICENSE).
|
data/Rakefile
ADDED
data/lib/sleek/base.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Sleek
|
2
|
+
class Base
|
3
|
+
attr_reader :namespace
|
4
|
+
|
5
|
+
# Internal: Initialize Sleek with namespace.
|
6
|
+
#
|
7
|
+
# namespace - the Symbol namespace name.
|
8
|
+
def initialize(namespace)
|
9
|
+
@namespace = namespace
|
10
|
+
end
|
11
|
+
|
12
|
+
# Public: Record an event.
|
13
|
+
#
|
14
|
+
# bucket - the String name of bucket.
|
15
|
+
# payload - the Hash of event data.
|
16
|
+
def record(bucket, payload)
|
17
|
+
Event.create_with_namespace(namespace, bucket, payload)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Get `QueriesCollection` for the namespace.
|
21
|
+
def queries
|
22
|
+
@queries ||= QueryCollection.new(namespace)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: Delete event bucket.
|
26
|
+
#
|
27
|
+
# bucket - the String bucket name.
|
28
|
+
def delete_bucket(bucket)
|
29
|
+
events(bucket).delete_all
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Delete specific property from all events in the bucket.
|
33
|
+
#
|
34
|
+
# bucket - the String bucket name.
|
35
|
+
# property - the String property name.
|
36
|
+
def delete_property(bucket, property)
|
37
|
+
events(bucket).unset("d.#{property}")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Internal: Get events associated with current namespace and,
|
41
|
+
# optionally, specified bucket.
|
42
|
+
def events(bucket = nil)
|
43
|
+
evts = Event.where(namespace: namespace)
|
44
|
+
evts = evts.where(bucket: bucket) if bucket.present?
|
45
|
+
evts
|
46
|
+
end
|
47
|
+
|
48
|
+
def inspect
|
49
|
+
"#<Sleek::Base ns=#{namespace}>"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Range
|
2
|
+
# Public: Convert both ends of range to integers.
|
3
|
+
def to_i_range
|
4
|
+
self.begin.to_i..self.end.to_i
|
5
|
+
end
|
6
|
+
|
7
|
+
# Public: Convert both ends of range to times.
|
8
|
+
def to_time_range
|
9
|
+
Time.at(self.begin)..Time.at(self.end)
|
10
|
+
end
|
11
|
+
|
12
|
+
def int_range?
|
13
|
+
self.begin.is_a?(Integer)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Public: Check if range elements are times.
|
17
|
+
def time_range?
|
18
|
+
self.begin.is_a?(Time)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Calculate the differentce between ends of the range.
|
22
|
+
def difference
|
23
|
+
self.end - self.begin
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Make up a range for previous n periods.
|
27
|
+
# Start of new range would be start of current - difference between
|
28
|
+
# start and end * number of periods, end of new range would be start of
|
29
|
+
# current.
|
30
|
+
#
|
31
|
+
# Example
|
32
|
+
#
|
33
|
+
# (1200..1300).previous
|
34
|
+
# # => 1100..1200
|
35
|
+
def previous(n = 1)
|
36
|
+
new_begin = self.begin - difference * n
|
37
|
+
new_end = self.end - difference * n
|
38
|
+
new_begin..new_end
|
39
|
+
end
|
40
|
+
|
41
|
+
def -(what)
|
42
|
+
(self.begin - what)..(self.end - what)
|
43
|
+
end
|
44
|
+
end
|
data/lib/sleek/event.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sleek
|
2
|
+
class EventMetadata
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps::Created::Short
|
5
|
+
|
6
|
+
field :t, type: Time, as: :timestamp
|
7
|
+
embedded_in :event
|
8
|
+
|
9
|
+
before_create :set_timestamp
|
10
|
+
|
11
|
+
def set_timestamp
|
12
|
+
self.timestamp = created_at unless timestamp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Event
|
17
|
+
include Mongoid::Document
|
18
|
+
|
19
|
+
field :ns, type: Symbol, as: :namespace
|
20
|
+
field :b, type: String, as: :bucket
|
21
|
+
field :d, type: Hash, as: :data
|
22
|
+
embeds_one :sleek, store_as: "s", class_name: 'Sleek::EventMetadata', cascade_callbacks: true
|
23
|
+
accepts_nested_attributes_for :sleek
|
24
|
+
|
25
|
+
validates :namespace, presence: true
|
26
|
+
validates :bucket, presence: true
|
27
|
+
|
28
|
+
after_initialize { build_sleek }
|
29
|
+
|
30
|
+
def self.create_with_namespace(namespace, bucket, payload)
|
31
|
+
sleek = payload.delete(:sleek)
|
32
|
+
event = create(namespace: namespace, bucket: bucket, data: payload)
|
33
|
+
event.sleek.update_attributes sleek
|
34
|
+
event
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/sleek/filter.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Sleek
|
2
|
+
class Filter
|
3
|
+
attr_reader :property_name, :operator, :value
|
4
|
+
|
5
|
+
def initialize(property_name, operator, value)
|
6
|
+
@property_name = "d.#{property_name}"
|
7
|
+
@operator = operator.to_sym
|
8
|
+
@value = value
|
9
|
+
|
10
|
+
unless [:eq, :ne, :lt, :lte, :gt, :gte, :in].include? @operator
|
11
|
+
raise ArgumentError, "unsupported operator - #{operator}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def apply(criteria)
|
16
|
+
criteria.send(operator, property_name => value)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
other.is_a?(Filter) && property_name == other.property_name &&
|
21
|
+
operator == other.operator && value == other.value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Sleek
|
2
|
+
class Interval
|
3
|
+
def self.interval_value(desc)
|
4
|
+
case desc
|
5
|
+
when :hourly
|
6
|
+
1.hour
|
7
|
+
when :daily
|
8
|
+
1.day
|
9
|
+
when :weekly
|
10
|
+
1.week
|
11
|
+
when :monthly
|
12
|
+
1.month
|
13
|
+
else
|
14
|
+
raise ArgumentError, "invalid interval description"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :interval, :timeframe
|
19
|
+
|
20
|
+
# Internal: Initialize an interval.
|
21
|
+
#
|
22
|
+
# interval_desc - the Symbol description of the interval.
|
23
|
+
# Possible values: :hourly, :daily, :weekly,
|
24
|
+
# :monthly.
|
25
|
+
# timeframe - the Timeframe object.
|
26
|
+
def initialize(interval_desc, timeframe)
|
27
|
+
@interval = self.class.interval_value(interval_desc)
|
28
|
+
@timeframe = timeframe
|
29
|
+
end
|
30
|
+
|
31
|
+
# Internal: Split the timeframe into intervals.
|
32
|
+
#
|
33
|
+
# Returns an Array of Timeframe objects.
|
34
|
+
def timeframes
|
35
|
+
@timeframes ||= timeframe.to_time_range.to_i_range.each_slice(interval)
|
36
|
+
.to_a[0..-2]
|
37
|
+
.map { |tf| (tf.first..(tf.first + interval)).to_time_range }
|
38
|
+
.map { |tf| Timeframe.new(tf) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sleek
|
2
|
+
module Queries
|
3
|
+
# Internal: Average query.
|
4
|
+
#
|
5
|
+
# Finds the average value for a given property.
|
6
|
+
#
|
7
|
+
# target_property - the String name of target property on event.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# sleek.queries.average(:purchases, target_property: "total")
|
12
|
+
# # => 49_35
|
13
|
+
class Average < Query
|
14
|
+
include Targetable
|
15
|
+
|
16
|
+
def perform(events)
|
17
|
+
events.avg target_property
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sleek
|
2
|
+
module Queries
|
3
|
+
# Internal: Count unique query.
|
4
|
+
#
|
5
|
+
# Counts how many events have unique value for a given property.
|
6
|
+
#
|
7
|
+
# target_property - the String name of target property on event.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# sleek.queries.count_unique(:purchases, target_property: "customer.email")
|
12
|
+
# # => 4
|
13
|
+
class CountUnique < Query
|
14
|
+
include Targetable
|
15
|
+
|
16
|
+
def perform(events)
|
17
|
+
events.distinct(target_property).count
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sleek
|
2
|
+
module Queries
|
3
|
+
# Internal: Maximum query.
|
4
|
+
#
|
5
|
+
# Finds the maximum value for a given property.
|
6
|
+
#
|
7
|
+
# target_property - the String name of target property on event.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# sleek.queries.maximum(:purchases, target_property: "total")
|
12
|
+
# # => 199_99
|
13
|
+
class Maximum < Query
|
14
|
+
include Targetable
|
15
|
+
|
16
|
+
def perform(events)
|
17
|
+
events.max target_property
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sleek
|
2
|
+
module Queries
|
3
|
+
# Internal: Minimum query.
|
4
|
+
#
|
5
|
+
# Finds the minimum value for a given property.
|
6
|
+
#
|
7
|
+
# target_property - the String name of target property on event.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# sleek.queries.minimum(:purchases, target_property: "total")
|
12
|
+
# # => 19_99
|
13
|
+
class Minimum < Query
|
14
|
+
include Targetable
|
15
|
+
|
16
|
+
def perform(events)
|
17
|
+
events.min target_property
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|