red_trend 0.0.1
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/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +23 -0
- data/README.md +48 -0
- data/Rakefile +6 -0
- data/lib/red_trend/version.rb +3 -0
- data/lib/red_trend.rb +132 -0
- data/red_trend.gemspec +26 -0
- data/spec/red_trend_spec.rb +275 -0
- data/spec/spec_helper.rb +16 -0
- metadata +140 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012 Brendon Murphy
|
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.
|
23
|
+
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# RedTrend
|
2
|
+
|
3
|
+
Store your trend data in redis.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'red_trend'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install red_trend
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'red_trend'
|
23
|
+
|
24
|
+
red_trend = RedTrend.new(:prefix => "project:9")
|
25
|
+
red_trend.record("post_views", 42)
|
26
|
+
red_trend.record("post_views", 53)
|
27
|
+
red_trend.record("post_views", 53)
|
28
|
+
red_trend.top("post_views") # => ["53", "42"]
|
29
|
+
```
|
30
|
+
|
31
|
+
TODO add configuration examples here
|
32
|
+
|
33
|
+
## How it works
|
34
|
+
|
35
|
+
TODO
|
36
|
+
|
37
|
+
## Todos
|
38
|
+
|
39
|
+
Rip ActiveSupport entirely if possible; otherwise narrow the inclusion.
|
40
|
+
|
41
|
+
## Contributing
|
42
|
+
|
43
|
+
1. Fork it
|
44
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
45
|
+
3. Create your specs and feature code
|
46
|
+
4. Commit your changes (`git commit -am 'Added some feature'`)
|
47
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
48
|
+
6. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/red_trend.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'redis'
|
2
|
+
require "red_trend/version"
|
3
|
+
require 'active_support/all'
|
4
|
+
|
5
|
+
class RedTrend
|
6
|
+
CYCLE_UNITS = {
|
7
|
+
:minute => 60,
|
8
|
+
:hour => 3600,
|
9
|
+
:day => 86400
|
10
|
+
}.freeze
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer :redis
|
14
|
+
|
15
|
+
def redis
|
16
|
+
@redis ||= Redis.new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :options
|
21
|
+
|
22
|
+
def initialize(options = {})
|
23
|
+
@options = options
|
24
|
+
unless CYCLE_UNITS.include?(cycle_unit)
|
25
|
+
raise ArgumentError, "cycle unit must be in #{CYCLE_UNITS.keys}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# How many cycles we'll persist data for
|
30
|
+
def cycles_count
|
31
|
+
@options.fetch(:cycles_count, 3)
|
32
|
+
end
|
33
|
+
|
34
|
+
def cycle_length
|
35
|
+
CYCLE_UNITS[cycle_unit]
|
36
|
+
end
|
37
|
+
|
38
|
+
# The length of 1 cycle
|
39
|
+
def cycle_unit
|
40
|
+
@options.fetch(:cycle_unit, :hour)
|
41
|
+
end
|
42
|
+
|
43
|
+
# The interval length of each cycle, probably
|
44
|
+
# days for production
|
45
|
+
def cycle_interval
|
46
|
+
cycle_length * cycles_count
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_cycle
|
50
|
+
method = case cycle_unit
|
51
|
+
when :minute then :min
|
52
|
+
when :hour then :hour
|
53
|
+
when :day then :yday
|
54
|
+
end
|
55
|
+
|
56
|
+
time = @time || Time.now
|
57
|
+
(time.send(method) % cycles_count) + 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def cycle_positions
|
61
|
+
1.upto(cycles_count - 1).inject([current_cycle]) do |n_cycles, offset|
|
62
|
+
n = current_cycle - offset
|
63
|
+
n = n + cycles_count if n < 1
|
64
|
+
n_cycles << n
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Increment the leaderboard score for the object on
|
69
|
+
# the sorted set for the current cycle. Make the key
|
70
|
+
# volitile so that it doesn't persist past the number
|
71
|
+
# of cycles. Store the union score set after each
|
72
|
+
# new score
|
73
|
+
def record(key, member)
|
74
|
+
@time = Time.now
|
75
|
+
|
76
|
+
n_key = make_key(key, current_cycle)
|
77
|
+
|
78
|
+
zcard = redis.zcard(n_key)
|
79
|
+
|
80
|
+
redis.multi do
|
81
|
+
redis.zincrby n_key, 1, member
|
82
|
+
# FIXME I'm not certain the second redis.ttl is working in the multi
|
83
|
+
if zcard < 1 || redis.ttl(n_key) == -1
|
84
|
+
redis.expire n_key, calculate_expire_seconds
|
85
|
+
end
|
86
|
+
unionize_sets(key)
|
87
|
+
end
|
88
|
+
|
89
|
+
@time = nil
|
90
|
+
|
91
|
+
true
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns top scoring ids for the current interval
|
95
|
+
def top(key, limit = 10)
|
96
|
+
redis.zrevrange make_key(key), 0, limit - 1
|
97
|
+
end
|
98
|
+
|
99
|
+
def weight_offset
|
100
|
+
("%0.1f" % (1 / cycles_count.to_f)).to_f
|
101
|
+
end
|
102
|
+
|
103
|
+
def cycle_weights
|
104
|
+
cycles_count.times.inject([]) do |weights, n|
|
105
|
+
weights << 1 - n * weight_offset
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def make_key(*args)
|
112
|
+
key_parts = [options[:prefix]]
|
113
|
+
key_parts.concat Array(args)
|
114
|
+
key_parts.compact.join(":")
|
115
|
+
end
|
116
|
+
|
117
|
+
def redis
|
118
|
+
self.class.redis
|
119
|
+
end
|
120
|
+
|
121
|
+
# The time in seconds which the current cycle key should expire in
|
122
|
+
def calculate_expire_seconds
|
123
|
+
((@time.to_f / cycle_length).floor * cycle_length + cycle_interval) - @time.to_i
|
124
|
+
end
|
125
|
+
|
126
|
+
# Take the scores from all current cycle sets and store
|
127
|
+
# them as a union
|
128
|
+
def unionize_sets(key)
|
129
|
+
keys = cycle_positions.collect { |n| make_key(key, n) }
|
130
|
+
redis.zunionstore(make_key(key), keys, :weights => cycle_weights)
|
131
|
+
end
|
132
|
+
end
|
data/red_trend.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/red_trend/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Brendon Murphy"]
|
6
|
+
gem.email = ["xternal1+github@gmail.com"]
|
7
|
+
gem.description = %q{Store your trend data in redis}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "red_trend"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = RedTrend::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "redis"
|
19
|
+
gem.add_dependency "active_support"
|
20
|
+
gem.add_dependency "i18n"
|
21
|
+
|
22
|
+
gem.add_development_dependency "rake"
|
23
|
+
gem.add_development_dependency "rspec"
|
24
|
+
gem.add_development_dependency "integration_test_redis"
|
25
|
+
gem.add_development_dependency "timecop"
|
26
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RedTrend, "redis connection config at the class level" do
|
4
|
+
let(:redis) { stub }
|
5
|
+
|
6
|
+
it "can instantiate a default localhost redis instance" do
|
7
|
+
RedTrend.redis = nil
|
8
|
+
Redis.should_receive(:new).with().and_return(redis)
|
9
|
+
RedTrend.redis.should == redis
|
10
|
+
end
|
11
|
+
|
12
|
+
it "can be overrided with a redis connection" do
|
13
|
+
RedTrend.redis = redis
|
14
|
+
RedTrend.redis.should == redis
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe RedTrend, "defaults" do
|
19
|
+
subject { RedTrend.new }
|
20
|
+
|
21
|
+
it "cycles_count to 3" do
|
22
|
+
subject.cycles_count.should == 3
|
23
|
+
end
|
24
|
+
|
25
|
+
it "cycle_unit to :hour" do
|
26
|
+
subject.cycle_unit.should == :hour
|
27
|
+
end
|
28
|
+
|
29
|
+
it "cycle_length to 3 hours" do
|
30
|
+
subject.cycle_interval.to_i.should == 3 * 60 * 60
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe RedTrend, "overriding defaults" do
|
35
|
+
subject { RedTrend.new(:cycles_count => 2, :cycle_unit => :minute) }
|
36
|
+
|
37
|
+
it "allows setting the cycles_count" do
|
38
|
+
subject.cycles_count.should == 2
|
39
|
+
end
|
40
|
+
|
41
|
+
it "allows setting the cycle_length" do
|
42
|
+
subject.cycle_length.should == 60
|
43
|
+
end
|
44
|
+
|
45
|
+
it "accepts the cycle_length of :minute, :hour, or :day" do
|
46
|
+
[:minute, :hour, :day].each do |unit|
|
47
|
+
lambda {
|
48
|
+
RedTrend.new(:cycle_length => unit)
|
49
|
+
}.should_not raise_error
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "raises an argument error for an unknown cycle length" do
|
54
|
+
lambda {
|
55
|
+
RedTrend.new(:cycle_unit => :foobar)
|
56
|
+
}.should raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe RedTrend, "cycle interval" do
|
61
|
+
context "for a cycle_count of 2 and cycle_unit of :minute" do
|
62
|
+
it "is 120" do
|
63
|
+
subject = RedTrend.new(:cycles_count => 2, :cycle_unit => :minute)
|
64
|
+
subject.cycle_interval.should == 120
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "for a cycle_count of 4 and cycle_length of 1 day" do
|
69
|
+
it "is 345600" do
|
70
|
+
subject = RedTrend.new(:cycles_count => 4, :cycle_unit => :day)
|
71
|
+
subject.cycle_interval.should == 345600
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe RedTrend, "getting the current cycle" do
|
77
|
+
before do
|
78
|
+
Timecop.freeze("2012-07-30 18:00:00 -0700")
|
79
|
+
end
|
80
|
+
|
81
|
+
context "for a default RedTrend" do
|
82
|
+
subject { RedTrend.new }
|
83
|
+
|
84
|
+
it "cycles from 1 through 3 on intervals based on the hour" do
|
85
|
+
subject.current_cycle.should == 1
|
86
|
+
Timecop.travel(3600)
|
87
|
+
subject.current_cycle.should == 2
|
88
|
+
Timecop.travel(3600)
|
89
|
+
subject.current_cycle.should == 3
|
90
|
+
Timecop.travel(3600)
|
91
|
+
subject.current_cycle.should == 1
|
92
|
+
end
|
93
|
+
|
94
|
+
it "cycles from 2, 3, 1 on intervals based on the hour" do
|
95
|
+
Timecop.freeze("2012-07-30 19:00:00 -0700")
|
96
|
+
subject.current_cycle.should == 2
|
97
|
+
Timecop.travel(3600)
|
98
|
+
subject.current_cycle.should == 3
|
99
|
+
Timecop.travel(3600)
|
100
|
+
subject.current_cycle.should == 1
|
101
|
+
Timecop.travel(3600)
|
102
|
+
subject.current_cycle.should == 2
|
103
|
+
end
|
104
|
+
|
105
|
+
it "behaves when the time does not fall on the boundary" do
|
106
|
+
subject.current_cycle.should == 1
|
107
|
+
Timecop.travel(1800)
|
108
|
+
subject.current_cycle.should == 1
|
109
|
+
Timecop.travel(1800)
|
110
|
+
subject.current_cycle.should == 2
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "for a RedTrend of 4 cycles of 60 seconds" do
|
115
|
+
subject { RedTrend.new(:cycles_count => 4, :cycle_unit => :minute) }
|
116
|
+
|
117
|
+
it "cycles from 1 through 4 on intervals based on the minute" do
|
118
|
+
subject.current_cycle.should == 1
|
119
|
+
Timecop.travel(60)
|
120
|
+
subject.current_cycle.should == 2
|
121
|
+
Timecop.travel(60)
|
122
|
+
subject.current_cycle.should == 3
|
123
|
+
Timecop.travel(60)
|
124
|
+
subject.current_cycle.should == 4
|
125
|
+
Timecop.travel(60)
|
126
|
+
subject.current_cycle.should == 1
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "for a RedTrend of 2 cycles of 1 day" do
|
131
|
+
subject { RedTrend.new(:cycles_count => 2, :cycle_unit => :day) }
|
132
|
+
|
133
|
+
it "cycles from 1 through 2 on intervals based on the day" do
|
134
|
+
subject.current_cycle.should == 1
|
135
|
+
Timecop.travel(86400)
|
136
|
+
subject.current_cycle.should == 2
|
137
|
+
Timecop.travel(86400)
|
138
|
+
subject.current_cycle.should == 1
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "for a RedTrend of 3 cycles of 1 day starting with Dec 31" do
|
143
|
+
subject { RedTrend.new(:cycles_count => 3, :cycle_unit => :day) }
|
144
|
+
|
145
|
+
it "cycles from 1 through 2 on intervals based on the day" do
|
146
|
+
Timecop.freeze("2012-12-31 18:00:00 -0700")
|
147
|
+
subject.current_cycle.should == 1
|
148
|
+
Timecop.travel(86400)
|
149
|
+
subject.current_cycle.should == 2
|
150
|
+
Timecop.travel(86400)
|
151
|
+
subject.current_cycle.should == 3
|
152
|
+
Timecop.travel(86400)
|
153
|
+
subject.current_cycle.should == 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe RedTrend, "getting an array of cycle numbers" do
|
159
|
+
subject { RedTrend.new :cycles_count => 4 }
|
160
|
+
|
161
|
+
before do
|
162
|
+
Timecop.freeze("2012-07-30 18:00:00 -0700")
|
163
|
+
end
|
164
|
+
|
165
|
+
it "starts with the current cycle to the last cycle and wraps back around" do
|
166
|
+
subject.cycle_positions.should == [3, 2, 1, 4]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe RedTrend, "recording" do
|
171
|
+
subject { RedTrend.new }
|
172
|
+
let(:redis) { IntegrationTestRedis.client }
|
173
|
+
|
174
|
+
before do
|
175
|
+
Timecop.freeze("2012-07-30 18:00:00 -0700")
|
176
|
+
end
|
177
|
+
|
178
|
+
it "increments the score for the member at the current zset key" do
|
179
|
+
subject.record("foobar", 42)
|
180
|
+
redis.zscore("foobar:1", "42").should == 1
|
181
|
+
subject.record("foobar", 42)
|
182
|
+
redis.zscore("foobar:1", "42").should == 2
|
183
|
+
end
|
184
|
+
|
185
|
+
it "prefixes the key if the prefix option was set" do
|
186
|
+
subject.options[:prefix] = "fizz:buzz"
|
187
|
+
subject.record("foobar", 42)
|
188
|
+
redis.zscore("fizz:buzz:foobar:1", "42").should == 1
|
189
|
+
end
|
190
|
+
|
191
|
+
it "sets the zset to expire when the cycle wraps back around to it" do
|
192
|
+
subject.record("foobar", 42)
|
193
|
+
redis.ttl("foobar:1").should == 10800
|
194
|
+
|
195
|
+
Timecop.travel(1800)
|
196
|
+
subject.record("foobar", 42)
|
197
|
+
redis.ttl("foobar:1").should == 10800
|
198
|
+
|
199
|
+
Timecop.travel(1800)
|
200
|
+
subject.record("foobar", 42)
|
201
|
+
redis.ttl("foobar:2").should == 10800
|
202
|
+
end
|
203
|
+
|
204
|
+
it "creates a union set from the current zsets" do
|
205
|
+
redis.exists("foobar").should be_false
|
206
|
+
subject.record("foobar", 42)
|
207
|
+
redis.exists("foobar").should be_true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe RedTrend, "storing a union off all cycles" do
|
212
|
+
subject { RedTrend.new }
|
213
|
+
let(:redis) { IntegrationTestRedis.client }
|
214
|
+
|
215
|
+
before do
|
216
|
+
Timecop.freeze("2012-07-30 18:00:00 -0700")
|
217
|
+
|
218
|
+
# Cycle 1
|
219
|
+
subject.record("foobar", 1)
|
220
|
+
Timecop.travel(1800)
|
221
|
+
subject.record("foobar", 2)
|
222
|
+
|
223
|
+
# Cycle 2
|
224
|
+
Timecop.travel(1800)
|
225
|
+
subject.record("foobar", 3)
|
226
|
+
|
227
|
+
# Cycle 3
|
228
|
+
Timecop.travel(3600)
|
229
|
+
subject.record("foobar", 4)
|
230
|
+
|
231
|
+
# Cycle 1
|
232
|
+
Timecop.travel(3600)
|
233
|
+
subject.record("foobar", 5)
|
234
|
+
|
235
|
+
# Cycle 2
|
236
|
+
Timecop.travel(3600)
|
237
|
+
subject.record("foobar", 1)
|
238
|
+
end
|
239
|
+
|
240
|
+
it "unionizes the data from all the available cycles" do
|
241
|
+
redis.zrange("foobar", 0, -1).should =~ %w[1 2 3 4 5]
|
242
|
+
end
|
243
|
+
|
244
|
+
it "weights the unionization to prefer newer data" do
|
245
|
+
redis.zrevrangebyscore("foobar", '+inf', '-inf').should == %w[1 3 5 2 4]
|
246
|
+
end
|
247
|
+
|
248
|
+
it "builds a weight offset by dividing 1 by the cycles count and rounding to 1/10 precision" do
|
249
|
+
subject.weight_offset.should == 0.3
|
250
|
+
subject.options[:cycles_count] = 4
|
251
|
+
subject.weight_offset.should == 0.2
|
252
|
+
end
|
253
|
+
|
254
|
+
it "uses the weight offset to build decreasing weights by subtracting multiples of the offset" do
|
255
|
+
redis.zrevrangebyscore("foobar", '+inf', '-inf', :with_scores => true).map(&:last).should == [1.7, 1, 0.7, 0.7, 0.4]
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe RedTrend, "getting the current top ids" do
|
260
|
+
subject { RedTrend.new }
|
261
|
+
|
262
|
+
before do
|
263
|
+
1.upto(11) do |n|
|
264
|
+
n.times { subject.record("foobar", n) }
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
it "returns the top 10 zrevrange of the union set" do
|
269
|
+
subject.top("foobar").should == %w[11 10 9 8 7 6 5 4 3 2]
|
270
|
+
end
|
271
|
+
|
272
|
+
it "can be given an optional limit" do
|
273
|
+
subject.top("foobar", 3).should == %w[11 10 9]
|
274
|
+
end
|
275
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
require File.expand_path("../../lib/red_trend", __FILE__)
|
5
|
+
|
6
|
+
require "integration_test_redis"
|
7
|
+
IntegrationTestRedis.start
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.mock_with :rspec
|
11
|
+
|
12
|
+
config.before(:each) do
|
13
|
+
RedTrend.redis = IntegrationTestRedis.client
|
14
|
+
IntegrationTestRedis.client.flushdb
|
15
|
+
end
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: red_trend
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brendon Murphy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redis
|
16
|
+
requirement: &2164433440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2164433440
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: active_support
|
27
|
+
requirement: &2164433000 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2164433000
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: i18n
|
38
|
+
requirement: &2164432580 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *2164432580
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &2164657580 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *2164657580
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rspec
|
60
|
+
requirement: &2164657120 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *2164657120
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: integration_test_redis
|
71
|
+
requirement: &2164656140 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *2164656140
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: timecop
|
82
|
+
requirement: &2164655580 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *2164655580
|
91
|
+
description: Store your trend data in redis
|
92
|
+
email:
|
93
|
+
- xternal1+github@gmail.com
|
94
|
+
executables: []
|
95
|
+
extensions: []
|
96
|
+
extra_rdoc_files: []
|
97
|
+
files:
|
98
|
+
- .gitignore
|
99
|
+
- Gemfile
|
100
|
+
- LICENSE
|
101
|
+
- README.md
|
102
|
+
- Rakefile
|
103
|
+
- lib/red_trend.rb
|
104
|
+
- lib/red_trend/version.rb
|
105
|
+
- red_trend.gemspec
|
106
|
+
- spec/red_trend_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: ''
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
segments:
|
121
|
+
- 0
|
122
|
+
hash: 3032661298513805062
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
none: false
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
segments:
|
130
|
+
- 0
|
131
|
+
hash: 3032661298513805062
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.8.15
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Store your trend data in redis
|
138
|
+
test_files:
|
139
|
+
- spec/red_trend_spec.rb
|
140
|
+
- spec/spec_helper.rb
|