redtastic 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NGQzNDc2Y2UwNTk1NThlMTNjZDUxMGFlOGQyZGFiZjVlYTFmYzJmYQ==
4
+ MGFlYTFiMjY1ZmY5NDI3ZjQ5YWJmM2UzMTMyYzFhNGY1NThlNmFiZg==
5
5
  data.tar.gz: !binary |-
6
- OGMxYTVjM2Q4NDJjMDNjNGFhNDIwY2NmOTUwMTEyNmQ2N2VjODcyYw==
6
+ ZmM3MzRmN2UyZWRmOGYzOTkwM2I4NDg1NzcxNWRiNDZlODQ2NmI3MA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YjAxNjc4YzhkYWZmY2EwZjUyNjk4ZDk3YWVlOWEwODBiY2JiZDE5OTYzNTJl
10
- MGRlYmQyMDg3OGZiYWQ0NGJhMWVhNzViZDExZjA4OWI2MWNhNTdhNWE0NDZh
11
- M2IyYTBjMmY2MjQ2Mjg1MjM3MDExZDE5OWI0MTZhMTA0ZWZkMmU=
9
+ MjJlMTI2OWQ4YjZiMjg2ZDBhZWYwMGI1NmNiNTI0MGI4NjViMjc2YTc5OWVk
10
+ ZGJiYjAzODJmN2E2Y2UxNGJiYWM2ZGNkYWYxNTAwZjFjOGZhZmRkMmQxNmE4
11
+ Njg3NGNkOWQwNjQwNDlhNjA0MmRjNDU2YTFhNzg0NjM2NDA3ZTA=
12
12
  data.tar.gz: !binary |-
13
- NTYwM2Q1OTdhM2Y4YjJkODg3NzY0MTE5Mzg5OThiM2FlYjM2YzY4MmQ4YmQ2
14
- ZTM4Zjg1ODgxZmE5ZTcwNGQ0YTNlNTE2YzgxOGViN2Q4NjUyNjcyNjYyNTJk
15
- NTVkNjI3YTU2N2FkNzc2NGY0ZjMzYmY2NjMxZmEyMmM0NzA1MjI=
13
+ NTIzMTBjOWY5NWNlNGJkMjUxMGNmYjk2NjQ2YzU5OTRmZTQ4OWEwNDRmODkx
14
+ NmRkNWZkOTE1MTJiMmVmMmM3MjNiMTNjNDAzNzhhZWQyMDA5YzhiYWMzNTA4
15
+ MTExZTI4OGRkYjQzNGU3ZGIyYzc4MDZkYzEyOTgxOTI0NzVhOTc=
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ .ruby-gemset
2
+ .ruby-version
3
+ .DS_Store
4
+ .rspec
5
+ .env
6
+ .env.*
7
+ log/*
8
+ tmp/*
9
+ !.keep
10
+ coverage
data/.rubocop.yml ADDED
@@ -0,0 +1,17 @@
1
+ Documentation:
2
+ Enabled: false
3
+
4
+ Encoding:
5
+ Enabled: false
6
+
7
+ HandleExceptions:
8
+ Enabled: false
9
+
10
+ LineLength:
11
+ Max: 120
12
+
13
+ NumericLiterals:
14
+ Enabled: false
15
+
16
+ MethodLength:
17
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ ---
2
+ language: ruby
3
+
4
+ cache:
5
+ - bundler
6
+
7
+ rvm:
8
+ - 2.0.0
9
+
10
+ services:
11
+ - redis-server
12
+
13
+ script: "bundle exec rspec"
14
+
15
+ env:
16
+ global:
17
+ REDIS_PORT=6379
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in napa.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,84 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ redtastic (0.2.2)
5
+ activesupport
6
+ redis
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.2)
12
+ i18n (~> 0.6, >= 0.6.4)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ ast (1.1.0)
18
+ atomic (1.1.14)
19
+ coderay (1.1.0)
20
+ coveralls (0.7.0)
21
+ multi_json (~> 1.3)
22
+ rest-client
23
+ simplecov (>= 0.7)
24
+ term-ansicolor
25
+ thor
26
+ diff-lcs (1.2.5)
27
+ docile (1.1.1)
28
+ dotenv (0.9.0)
29
+ git (1.2.6)
30
+ i18n (0.6.9)
31
+ method_source (0.8.2)
32
+ mime-types (2.0)
33
+ minitest (4.7.5)
34
+ multi_json (1.8.4)
35
+ parser (2.1.4)
36
+ ast (~> 1.1)
37
+ slop (~> 3.4, >= 3.4.5)
38
+ powerpack (0.0.9)
39
+ pry (0.9.12.4)
40
+ coderay (~> 1.0)
41
+ method_source (~> 0.8)
42
+ slop (~> 3.4)
43
+ rainbow (1.99.1)
44
+ redis (3.0.7)
45
+ rest-client (1.6.7)
46
+ mime-types (>= 1.16)
47
+ rspec (2.14.1)
48
+ rspec-core (~> 2.14.0)
49
+ rspec-expectations (~> 2.14.0)
50
+ rspec-mocks (~> 2.14.0)
51
+ rspec-core (2.14.7)
52
+ rspec-expectations (2.14.4)
53
+ diff-lcs (>= 1.1.3, < 2.0)
54
+ rspec-mocks (2.14.4)
55
+ rubocop (0.16.0)
56
+ parser (~> 2.1)
57
+ powerpack (~> 0.0.6)
58
+ rainbow (>= 1.1.4)
59
+ simplecov (0.8.2)
60
+ docile (~> 1.1.0)
61
+ multi_json
62
+ simplecov-html (~> 0.8.0)
63
+ simplecov-html (0.8.0)
64
+ slop (3.4.7)
65
+ term-ansicolor (1.2.2)
66
+ tins (~> 0.8)
67
+ thor (0.18.1)
68
+ thread_safe (0.1.3)
69
+ atomic
70
+ tins (0.13.1)
71
+ tzinfo (0.3.38)
72
+
73
+ PLATFORMS
74
+ ruby
75
+
76
+ DEPENDENCIES
77
+ coveralls
78
+ dotenv
79
+ git
80
+ pry
81
+ redtastic!
82
+ rspec
83
+ rubocop
84
+ simplecov
data/README.md ADDED
@@ -0,0 +1,275 @@
1
+ Redtastic [![Build Status](https://travis-ci.org/bellycard/redtastic.png?branch=master)](https://travis-ci.org/bellycard/redtastic) [![Coverage Status](https://coveralls.io/repos/bellycard/redtastic/badge.png?branch=master)](https://coveralls.io/r/bellycard/redtastic?branch=master)
2
+ ========
3
+
4
+ Redtastic! Why? Because using Redis for analytics is fantastic!
5
+
6
+ Redtastic provides a interface for storing, retriveing, and aggregating time intervalled data. Applications of Redtastic include developing snappy dashboards containing daily / monthly / yearly counts over time. Additionally Redtastic allows for the "mashing-up" of different statistics, allowing the drilling down of data into specific subgroups (such as "Number of unique customers who are also male, android users...etc").
7
+
8
+ Redtastic is backed by [Redis](http://redis.io) - so it's super fast.
9
+
10
+ Installation
11
+ ------------
12
+
13
+ ```
14
+ $ gem install redtastic
15
+ ```
16
+
17
+ or in your **Gemfile**
18
+
19
+ ``` ruby
20
+ gem 'redtastic'
21
+ ```
22
+
23
+ and run:
24
+
25
+ ```
26
+ $ bundle install
27
+ ```
28
+
29
+ Then initialize Redtastic in your application & connect it with a redis instance:
30
+
31
+ ``` ruby
32
+ $redis = Redis.new
33
+ Redtastic::Connection.establish_connection($redis, 'namespace')
34
+ ```
35
+ \* *specifying a namespace is optional and is used to avoid key collisions if multiple applications are using the same instance of Redis.*
36
+
37
+ Usage
38
+ -----
39
+
40
+ ### Defining a Redtastic Model
41
+
42
+ First, create a Redtastic Model:
43
+
44
+ ``` ruby
45
+ class Checkins < Redtastic::Model
46
+ type :counter
47
+ resolution :days
48
+ end
49
+ ```
50
+
51
+ The class must inherit from Redtastic::Model and provide some attributes:
52
+ * **type:** *Required*. The data type of the model. Valid values include:
53
+ * :counter
54
+ * :unique (more on types [below](https://github.com/bellycard/Redtastic#Redtastic-types)).
55
+ * **resolution:** *Optional*. The degree of fidelity you would like to store this model at. Valid values include:
56
+ * :days
57
+ * :weeks
58
+ * :months
59
+ * :years
60
+ * nil
61
+
62
+ \* *Note that methods requesting results at a higher resolution than that of the model will not work. Using a lower resolution will result in less memory utilization and will generally see faster query response times.*
63
+
64
+ ### Using Redtastic Models
65
+
66
+ Incrementing / decrementing counters:
67
+
68
+ ``` ruby
69
+ Checkins.increment(id: 1, timestamp: '2014-01-01')
70
+ Checkins.decrement(id: 1, timestamp: '2014-01-01')
71
+ ```
72
+
73
+ Find a value of a counter for a single / day / week / month / year:
74
+
75
+ ``` ruby
76
+ Checkins.find(id: 1, year: 2014, month: 1, day: 5) # Day
77
+ Checkins.find(id: 1, year: 2014, week: 2) # Week
78
+ Checkins.find(id: 1, year: 2014, month: 1) # Month
79
+ Checkins.find(id: 1, year: 2014) # Year
80
+ ```
81
+
82
+ Find the aggregate total over a dataspan:
83
+
84
+ ``` ruby
85
+ Checkins.aggregate(id: 1, start_date: '2014-01-01', end_date: '2014-01-05')
86
+ ```
87
+
88
+ Get the aggregate total + data points for each date at the specified interval:
89
+
90
+ ``` ruby
91
+ Checkins.aggregate(id: 1, start_date: '2014-01-01', end_date: '2014-01-05', interval: :days)
92
+ ```
93
+
94
+ ### Multiple Ids
95
+
96
+ The above methods also have support for multiple ids.
97
+
98
+ Incrementing / decrementing multiple ids in one request:
99
+
100
+ ``` ruby
101
+ Checkins.increment(id: [1001, 1002, 2003], timestamp: '2014-01-01')
102
+ Checkins.decrement(id: [1001, 1002, 2003], timestamp: '2014-01-01')
103
+ ```
104
+
105
+ Find for mutiple ids at once:
106
+
107
+ ``` ruby
108
+ Checkins.find(id: [1001, 1002, 2003], year: 2014, month: 1, day: 5)
109
+ ```
110
+
111
+ Aggregations across mutiple ids can be quite powerful:
112
+
113
+ ``` ruby
114
+ Checkins.aggregate(id: [1001, 1002, 2003], start_date: '2014-01-01', end_date: '2014-01-05')
115
+ ```
116
+
117
+ As well as aggregating across mutiple ids w/ data points at the specified interval:
118
+
119
+ ``` ruby
120
+ Checkins.aggregate(id: [1001, 1002, 2003], start_date: '2013-01-01', end_date: '2014-01-05', interval: :days)
121
+ ```
122
+
123
+ ### Redtastic Types
124
+
125
+ #### Counters
126
+
127
+ Counters are just what they appear to be - counters of things. Examples of using counters is shown in the previous two sections.
128
+
129
+ #### Unique Counters
130
+
131
+ Unique counters are used when an event with the same unique_id should not be counted twice. A general example of this could be a counter for the number of users that visited a place. In this case the "id" parameter would represent the id of the place and the unique_id would be the users id.
132
+
133
+ **Examples**
134
+
135
+ Incrementing / Decrementing (adding / removing a unique_id from a set for a particular id / time):
136
+ ``` ruby
137
+ Customers.increment(id: 1, timestamp: '2014-01-05', unique_id: 1000)
138
+ Customers.decrement(id: 1, timestamp: '2014-01-05', unique_id: 1000)
139
+ ```
140
+
141
+ Find:
142
+ ``` ruby
143
+ Customers.find(id: 1, year: 2014, month: 1, day: 5, unique_id: 1000) # Returns true or false
144
+ ```
145
+
146
+ Find the aggregate total over a datespan (this would only return the *unique* aggregate total):
147
+ ``` ruby
148
+ Customers.aggregate(id: 1, start_date: '2014-01-01', end_date: '2014-01-05')
149
+ ```
150
+
151
+ Find the aggregate total + data points for each point at a specified interval (again, this returns not only the unique aggregate total, but also the unique total for each interval data point ~ being each day / week / month / year...etc)
152
+ ``` ruby
153
+ Customers.aggregate(id: 1, start_date: '2014-01-01', end_date: '2014-01-05', interval: :days)
154
+ ```
155
+
156
+ Unique counters also support querying mutiple ids. For example, we can find the unique aggregate totals across multiple ids by doing:
157
+ ``` ruby
158
+ Customers.aggregate(id: [1,2,3], start_date: '2014-01-01', end_date: '2014-01-05', interval: :days)
159
+ ```
160
+
161
+ #### Attributes
162
+
163
+ Attributes are unique counters that are not associated with an id, and can be thought of as a type of "global" group. This can be mashed up with other unique counters that are associated with ids to give the same result as if they were associated with that id. The main advantages to using this technique are:
164
+
165
+ * Save a tremendous amount of memory by not storing this data, for ever resolution interval, for every id
166
+ * Easier to update / maintain / rebuilding data is much quicker ( instead of having to update at every interval / id, you can just update it once at the "global" level)
167
+
168
+ This is best explained with the example below.
169
+
170
+ Say you have a unique counter "Customers", and a unique couner "Males". Instead of storing them both at the id & daily level we can get the number of males / day / id with the following:
171
+
172
+ ```ruby
173
+ class Customers < Redtastic::Model
174
+ type :unique
175
+ resolution :days
176
+ end
177
+
178
+ class Males < Redtastic::Model
179
+ type :unique
180
+ end
181
+ ```
182
+
183
+ then to mash up Customers against Males, just use the attributes parameter:
184
+
185
+ ```ruby
186
+ Customers.aggregate(id: 1, start_date: '2014-01-01', end_date: '204-01-09', attrbiutes: [:males])
187
+ ```
188
+
189
+ You can even mash up multiple attributes. Suppose I want to see all the customers who are Male and Android Users. First add the AndroidUsers class:
190
+
191
+ ```ruby
192
+ class AndroidUsers < Redtastic::Model
193
+ type :unique
194
+ end
195
+ ```
196
+
197
+ then just add that into the query:
198
+
199
+ ```ruby
200
+ Customers.aggregate(id: 1, start_date: '2014-01-01', end_date: '2014-01-09', attributes: [:males, :android_users])
201
+ ```
202
+
203
+ and just like every other example, attributes can be used in aggregations accross multiple ids:
204
+
205
+ ```ruby
206
+ Customers.aggregate(id: [1,2,3], start_date: '2014-01-01', end_date: '2014-01-09', attributes: [:males, :android_users])
207
+ ```
208
+
209
+ All the methods available to unique counters can be used for unique counters acting as global attributes, with a few simplifications. Obviously, if it does not have a resolution and is not associated with an id, then there is no need to pass those parameters into any of those.
210
+
211
+ For example, adding / removing a unique_id to a global attribute set:
212
+
213
+ ```ruby
214
+ Males.increment(unique_id: 1000)
215
+ Males.decrement(unique_id: 1000)
216
+ ```
217
+
218
+ or seeing if a unique_id is in a global set:
219
+
220
+ ```ruby
221
+ Males.find(unique_id: 1000)
222
+ ```
223
+
224
+ ### Misc
225
+
226
+ #### Script Manager
227
+
228
+ Redtastic also provides access to the *Redtastic::ScriptManager* class which it uses internally to pre-load & provide an interface to running Lua Scripts on Redis. Although it is used by Redtastic to run its own scripts, anybody can use it to run their own custom scripts defined in their application:
229
+
230
+ Create a script: *./lib/scripts/hello.lua*
231
+ ``` lua
232
+ return 'hello'
233
+ ```
234
+ Tell ScriptManager to pre-load your scripts (after initializing Redtastic)
235
+ ``` ruby
236
+ Redtastic::ScriptManager.load_scripts('./lib/scripts')
237
+ ```
238
+
239
+ Now you can easily use your script anywhere in your application:
240
+ ``` ruby
241
+ puts Redtastic::ScriptManager.hello # prints 'hello'
242
+ ```
243
+
244
+ with every script having the ability to accept parameters for the KEYS & ARGV arrays:
245
+ ```ruby
246
+ keys = []
247
+ argv = []
248
+ Redtastic::ScriptManager.hello(keys, argv)
249
+ ```
250
+
251
+ Performance
252
+ -----------
253
+
254
+ Contributing
255
+ ------------
256
+
257
+ 1. Fork it
258
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
259
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
260
+ 4. Push to the branch (`git push origin my-new-feature`)
261
+ 5. Create new Pull Request
262
+
263
+ TODOS
264
+ -----
265
+ * Set elapsed expiration times for each resolution of a model (ie. keys of resolution days expire in 1 year, months expire in 2 years...etc).
266
+ * For large, multi-id aggregations, set batch size & do aggregations in serveral batches rather than all in one lua run to prevent long running lua scripts from blocking any other redis operation.
267
+ * Support for hourly resolutions
268
+
269
+
270
+
271
+
272
+
273
+
274
+
275
+
@@ -0,0 +1,14 @@
1
+ module Redtastic
2
+ class Connection
3
+ class << self
4
+ attr_accessor :namespace
5
+ attr_accessor :redis
6
+
7
+ def establish_connection(connection, namespace = nil)
8
+ @redis = connection
9
+ @namespace = namespace
10
+ Redtastic::ScriptManager.load_scripts(File.join(File.dirname(__FILE__),'/scripts'))
11
+ end
12
+ end
13
+ end
14
+ end