goalkeeper 0.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +29 -3
- data/Rakefile +1 -1
- data/lib/goalkeeper.rb +62 -132
- data/lib/goalkeeper/goal.rb +66 -0
- data/lib/goalkeeper/set.rb +87 -0
- data/lib/goalkeeper/version.rb +2 -2
- data/test/goalkeeper/goal_test.rb +103 -0
- data/test/goalkeeper/set_test.rb +104 -0
- data/test/goalkeeper_test.rb +0 -114
- data/test/test_helper.rb +0 -3
- metadata +8 -3
- data/LICENSE.txt +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 131ffcb82da5ac820f63710b94d5139cf249bc9d
|
4
|
+
data.tar.gz: d0ccd33c05701a7b3c5bb669008eb5bea1eedf32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ced9ac47bf52311689d02ded1b17c59067c9bd55906de495f53a4c3a59cf7242b8b3dce7f0eafb662326c6c8c3ab050d5377718dad07d95c221704fc4e2953d3
|
7
|
+
data.tar.gz: 928f802e2557c64bb046e9eec2769e66c9d4f075d34fc6b8b202111cd7b703693dfa4ac44e2bea08966ab2af33bc543e2a8172c0de9f397e5a60a84c761fc332
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -5,8 +5,7 @@ Goalkeeper is a simple system for tracking if system wide requirements have been
|
|
5
5
|
An example usage would be validating if each customer was sent a daily report.
|
6
6
|
|
7
7
|
|
8
|
-
|
9
|
-
## Installation
|
8
|
+
##### Installation
|
10
9
|
|
11
10
|
Add this line to your application's Gemfile:
|
12
11
|
|
@@ -24,7 +23,34 @@ Or install it yourself as:
|
|
24
23
|
|
25
24
|
## Usage
|
26
25
|
|
27
|
-
|
26
|
+
### Goal
|
27
|
+
| method | description | example |
|
28
|
+
| -------- | ------ | ----- |
|
29
|
+
| new(label) | Creates a new Goal with the unique label | `g = Goal.new('process')` |
|
30
|
+
| met? | Returns true if the Goal has been met | `g.met? #=> false` |
|
31
|
+
| met! | Marks the goal as completed with a timestamp | `g.met! #=> Time.now` |
|
32
|
+
| met\_at | Nil if unmet, otherwise returns the time the Goal was met. | `g.met_at #=> 2015-02-09 22:24:07 -0500` |
|
33
|
+
| ttl | The Redis ttl value if there is a record for the goal. | `g.ttl #=> 86400` |
|
34
|
+
| key | The key used to store the record in Redis. | `g.key #=> "Goalkeeper:process"` |
|
35
|
+
| clear! | Deletes the `met` record. Goal is now unmet. | `g.clear!` |
|
36
|
+
|
37
|
+
|
38
|
+
### Set
|
39
|
+
| method | description | example |
|
40
|
+
| -------- | ------ | ----- |
|
41
|
+
| new | creates an empty set of goals. | `s = set.new` |
|
42
|
+
| add(label) | Adds a new Goal the Set. _chainable_ | `s.add('process.a').add('process.b')` |
|
43
|
+
| met? | Returns true if _all_ the Set's Goals have been met. | `s.met? #=> false` |
|
44
|
+
| met! | Calls `met!` on all Goals. | `s.met! #=> Time.now` |
|
45
|
+
| met\_at | Returns the most recent met_at for the Set's Goals. Nil if no Goal met. | `s.met_at #=> 2015-02-09 22:24:07 -0500` |
|
46
|
+
| clear! | Calls `clear!` on all Goals. | `s.clear!` |
|
47
|
+
| met | Returns a new Set with all Goals which have been met. | `s.met #=> Set(...)` |
|
48
|
+
| unmet | Returns a new Set with all Goals which have not been met. | `s.unmet` #=> Set(...) |
|
49
|
+
|
50
|
+
### Goalkeeper
|
51
|
+
| method | description | example |
|
52
|
+
| -------- | ------ | ----- |
|
53
|
+
| ::met!(label) | Creates a new Goal and marks it met. | `Goalkeeper.met!('process') #=> <Goalkeepr::Goal>`
|
28
54
|
|
29
55
|
## Contributing
|
30
56
|
|
data/Rakefile
CHANGED
data/lib/goalkeeper.rb
CHANGED
@@ -1,27 +1,64 @@
|
|
1
|
-
require "goalkeeper/version"
|
2
1
|
require 'forwardable'
|
3
2
|
require 'redis'
|
4
3
|
require 'time' # for Time.parse
|
5
4
|
|
6
|
-
# Goalkeeper provides methods to track if specific events(Goals) have been
|
5
|
+
# Goalkeeper provides methods to track if specific events(Goals) have been
|
6
|
+
# completed(met).
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
8
|
+
# Its goes likes this..
|
9
|
+
#
|
10
|
+
# Lets ensure we wakeup New Years Day 2020. The goal will be called 'wakeup:2020-01-01'
|
11
|
+
# g = Goalkeeper::Goal.new('wakeup:2020-01-01')
|
12
|
+
# g.met? #=> false
|
10
13
|
#
|
11
|
-
#
|
12
|
-
#
|
14
|
+
# Time flies... it is New Years Day 2020.
|
15
|
+
# g.met! # or Goalkeeper.met!('wakeup:2020-01-01')
|
16
|
+
# g.met? #=> true
|
17
|
+
# g.met_at #=> 2020-01-01 05:01:31 -0500
|
13
18
|
#
|
14
|
-
#
|
15
|
-
#
|
19
|
+
# Now if our application checks our goal, it will be met.
|
20
|
+
# Goalkeeper::Goal.new('wakeup:2020-01-01').met? #=> true
|
21
|
+
# Goalkeeper.met?('wakeup:2020-01-01') #=> true
|
22
|
+
#
|
23
|
+
# Note: Once a Goal is 'met' the 'met_at' timestamp will not change, unless
|
24
|
+
# 'clear!' is called.
|
16
25
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# # or
|
21
|
-
# Goalkeeper::Goal.new("jobkey").met!
|
26
|
+
# We are probably only interested in this goal being complete for a limited
|
27
|
+
# time, so it will expire and be removed from Redis.
|
28
|
+
# g.ttl #=> 86400 (1 day)
|
22
29
|
#
|
23
|
-
#
|
24
|
-
# Goalkeeper
|
30
|
+
# If you need to reference the Redis key
|
31
|
+
# g.key #=> Goalkeeper:wakeup:2020-01-01
|
32
|
+
#
|
33
|
+
# Finally clear the Goal is simple
|
34
|
+
# g.clear!
|
35
|
+
#
|
36
|
+
# === Sets
|
37
|
+
#
|
38
|
+
# Perhaps you have a series of Goals you want to track, and see if they all have been met, or not.
|
39
|
+
#
|
40
|
+
# set = Goalkeeper::Set.new
|
41
|
+
# set.add('goal1').add('goal2')
|
42
|
+
# set.met? #=> false
|
43
|
+
#
|
44
|
+
# Lets have 1 goal met:
|
45
|
+
# Goalkeeper.met!('goal1')
|
46
|
+
#
|
47
|
+
# But our set is not met yet
|
48
|
+
# set.met? #=> false
|
49
|
+
#
|
50
|
+
# See which goals are met, or unmet
|
51
|
+
# set.met #=> [#<Goalkeeper::Goal @label="goal1">]
|
52
|
+
# set.unmet #=> [#<Goalkeeper::Goal @label="goal2">]
|
53
|
+
#
|
54
|
+
# Lets complete our set.
|
55
|
+
# Goalkeeper.met!('goal1')
|
56
|
+
# set.met? #=> true
|
57
|
+
#
|
58
|
+
# See the time the final goal was met
|
59
|
+
# set.met_at #=> 2015-01-01 08:02:15 -0500
|
60
|
+
#
|
61
|
+
# === Customization
|
25
62
|
#
|
26
63
|
# Customize the redis client by setting it in your application
|
27
64
|
# Goalkeeper.redis = your_redis_client
|
@@ -29,11 +66,14 @@ require 'time' # for Time.parse
|
|
29
66
|
# Each record has a default expiration of 24 hours, but this can be modified.
|
30
67
|
# Goalkeeper.expiration = number_of_seconds
|
31
68
|
#
|
32
|
-
# Redis keys are stored under the default namespace of "Goalkeeper:". The
|
33
|
-
#
|
69
|
+
# Redis keys are stored under the default namespace of "Goalkeeper:". The
|
70
|
+
# namespace can be configured:
|
34
71
|
# Goalkeeper.namespace = string
|
35
72
|
#
|
36
|
-
|
73
|
+
module Goalkeeper
|
74
|
+
require "goalkeeper/version"
|
75
|
+
require "goalkeeper/goal"
|
76
|
+
require "goalkeeper/set"
|
37
77
|
|
38
78
|
# Set the Redis client to a non default setting
|
39
79
|
def self.redis=(redis)
|
@@ -49,6 +89,10 @@ class Goalkeeper
|
|
49
89
|
Goal.new(label).met!
|
50
90
|
end
|
51
91
|
|
92
|
+
def self.met?(label)
|
93
|
+
Goal.new(label).met?
|
94
|
+
end
|
95
|
+
|
52
96
|
# The TTL set for each met Goal record created in Redis
|
53
97
|
# Default is 24 hours
|
54
98
|
def self.expiration
|
@@ -67,118 +111,4 @@ class Goalkeeper
|
|
67
111
|
def self.namespace=(ns)
|
68
112
|
@namespace = ns
|
69
113
|
end
|
70
|
-
|
71
|
-
# List is a collection of Goals to simplify tracking multiple goals.
|
72
|
-
#
|
73
|
-
# Create a new list
|
74
|
-
# mylist = Goalkeeper::List.new
|
75
|
-
#
|
76
|
-
# Add Goals you want to check for completion
|
77
|
-
# mylist.add("job1").add("job2")
|
78
|
-
# mylist.size
|
79
|
-
# #=> 2
|
80
|
-
#
|
81
|
-
# Check if all the goals are completed
|
82
|
-
# mylist.met?
|
83
|
-
# #=> false
|
84
|
-
#
|
85
|
-
# Get the unmet Goals
|
86
|
-
# mylist.unmet
|
87
|
-
# #=> [...]
|
88
|
-
#
|
89
|
-
# Get the met Goals
|
90
|
-
# mylist.met
|
91
|
-
# #=> [...]
|
92
|
-
#
|
93
|
-
# Iterate all Goals
|
94
|
-
# myslist.each {|goal| ...}
|
95
|
-
# myslist.map {|goal| ...}
|
96
|
-
class List
|
97
|
-
extend Forwardable
|
98
|
-
def_delegators :@list, :size, :[], :each, :map
|
99
|
-
|
100
|
-
def initialize
|
101
|
-
@list = []
|
102
|
-
end
|
103
|
-
|
104
|
-
# Creates a new Goal.
|
105
|
-
# see Goal#new for usage
|
106
|
-
def add(label, ref: nil)
|
107
|
-
@list.push(Goal.new(label, ref: ref))
|
108
|
-
self
|
109
|
-
end
|
110
|
-
|
111
|
-
# met? returns true if all Goals in the set have been met.
|
112
|
-
def met?
|
113
|
-
unmet.empty?
|
114
|
-
end
|
115
|
-
|
116
|
-
def unmet
|
117
|
-
@list.select {|g| ! g.met?}
|
118
|
-
end
|
119
|
-
|
120
|
-
def met
|
121
|
-
@list.select {|g| g.met?}
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class Goal
|
126
|
-
# The unique label to identify this Goal
|
127
|
-
attr_reader :label
|
128
|
-
|
129
|
-
# An optional object refrence which allows an application author to
|
130
|
-
# associate this goal to an object. The +ref+ is not used by Goalkeeper.
|
131
|
-
attr_reader :ref
|
132
|
-
|
133
|
-
# the TTL value for the Redis record. Defalts to Goalkeeper.expiration
|
134
|
-
attr_reader :expiration
|
135
|
-
|
136
|
-
# +label+ is a unique string to identify this Goal.
|
137
|
-
# There is no checking if it is truly unique.
|
138
|
-
#
|
139
|
-
# +ref+ is an optional reference to any object. This
|
140
|
-
# would be used by the end user's application.
|
141
|
-
#
|
142
|
-
# +expiration+ can be set to override the gobal expiratin.
|
143
|
-
def initialize(label, ref: nil, expiration: Goalkeeper.expiration)
|
144
|
-
@label = label
|
145
|
-
@ref = ref
|
146
|
-
@expiration = expiration
|
147
|
-
end
|
148
|
-
|
149
|
-
def met!
|
150
|
-
write
|
151
|
-
self
|
152
|
-
end
|
153
|
-
|
154
|
-
def met?
|
155
|
-
! read.nil?
|
156
|
-
end
|
157
|
-
|
158
|
-
# Time the goal was completed.
|
159
|
-
# WARNING retuns nil if the job is not met
|
160
|
-
def met_at
|
161
|
-
if met?
|
162
|
-
Time.parse(read)
|
163
|
-
else
|
164
|
-
nil
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
# a namespaced key for the goal
|
169
|
-
def key
|
170
|
-
"#{Goalkeeper.namespace}:#{label}"
|
171
|
-
end
|
172
|
-
|
173
|
-
protected
|
174
|
-
|
175
|
-
def write
|
176
|
-
Goalkeeper.redis.set(self.key, Time.now)
|
177
|
-
Goalkeeper.redis.expire(self.key, self.expiration)
|
178
|
-
end
|
179
|
-
|
180
|
-
def read
|
181
|
-
Goalkeeper.redis.get self.key
|
182
|
-
end
|
183
|
-
end
|
184
114
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Goalkeeper
|
2
|
+
# Goal represents a label which has either been +met+ or not.
|
3
|
+
#
|
4
|
+
class Goal
|
5
|
+
# The unique label to identify this Goal. There is no logic to check that the
|
6
|
+
# label is unique.
|
7
|
+
attr_reader :label
|
8
|
+
|
9
|
+
# the TTL value for the Redis record. Defalts to Goalkeeper.expiration
|
10
|
+
attr_reader :expiration
|
11
|
+
|
12
|
+
# +label+ is a unique string to identify this Goal.
|
13
|
+
# +expiration+ number secconds. This can be set to override the gobal expiration.
|
14
|
+
def initialize(label, expiration: Goalkeeper.expiration)
|
15
|
+
@label = label
|
16
|
+
@expiration = expiration
|
17
|
+
end
|
18
|
+
|
19
|
+
def met!
|
20
|
+
write unless met?
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def met?
|
25
|
+
!read.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Time the goal was completed.
|
29
|
+
# WARNING retuns nil if the job is not met
|
30
|
+
def met_at
|
31
|
+
return Time.parse(read) if met?
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# clear! removes the met state of the Goal.
|
36
|
+
def clear!
|
37
|
+
Goalkeeper.redis.del(key)
|
38
|
+
end
|
39
|
+
|
40
|
+
# a namespaced key for the goal
|
41
|
+
def key
|
42
|
+
"#{Goalkeeper.namespace}:#{label}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# ttl returns the time to live on the redis key
|
46
|
+
def ttl
|
47
|
+
Goalkeeper.redis.ttl(key)
|
48
|
+
end
|
49
|
+
|
50
|
+
# All Goalkeeper::Goals with the same label are equal
|
51
|
+
def ==(other)
|
52
|
+
other.is_a?(Goalkeeper::Goal) && other.label == label
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def write
|
58
|
+
Goalkeeper.redis.set(key, Time.now)
|
59
|
+
Goalkeeper.redis.expire(key, expiration)
|
60
|
+
end
|
61
|
+
|
62
|
+
def read
|
63
|
+
Goalkeeper.redis.get(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Goalkeeper
|
2
|
+
# Set is an Array of Goals to simplify tracking multiple goals.
|
3
|
+
# Since Set is an array, you have all Array methods available.
|
4
|
+
#
|
5
|
+
# Create a new set
|
6
|
+
# myset = Goalkeeper::Set.new
|
7
|
+
#
|
8
|
+
# Add Goals you want to check for completion
|
9
|
+
# myset.add("job1").add("job2")
|
10
|
+
# myset.size
|
11
|
+
# #=> 2
|
12
|
+
#
|
13
|
+
# Check if all the goals are completed
|
14
|
+
# myset.met?
|
15
|
+
# #=> false
|
16
|
+
#
|
17
|
+
# Get the unmet Goals
|
18
|
+
# myset.unmet
|
19
|
+
# #=> [...]
|
20
|
+
#
|
21
|
+
# Get the met Goals
|
22
|
+
# myset.met
|
23
|
+
# #=> [...]
|
24
|
+
#
|
25
|
+
# Iterate all Goals
|
26
|
+
# myset.each {|goal| ...}
|
27
|
+
# myset.map {|goal| ...}
|
28
|
+
class Set < Array
|
29
|
+
def initialize
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new Goal.
|
34
|
+
# see Goal#initialize for usage
|
35
|
+
def add(*args)
|
36
|
+
push(Goal.new(*args))
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# met? returns true if all Goals in the set have been met.
|
41
|
+
def met?
|
42
|
+
unmet.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
# unmet returns a Set with the all the Goals that have not been met.
|
46
|
+
def unmet
|
47
|
+
subset(select { |g| !g.met? })
|
48
|
+
end
|
49
|
+
|
50
|
+
# met returns a Set with the all the Goals that have been met.
|
51
|
+
def met
|
52
|
+
subset(select { |g| g.met? })
|
53
|
+
end
|
54
|
+
|
55
|
+
# nil if this set is not met?
|
56
|
+
# otherwise returns the met_at Time for the last met goal
|
57
|
+
def met_at
|
58
|
+
if met?
|
59
|
+
self.map(&:met_at).sort.last
|
60
|
+
else
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def <<(other)
|
66
|
+
if other.is_a?(Goal) && !include?(other)
|
67
|
+
super
|
68
|
+
else
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def push(*others)
|
74
|
+
others.each do |o|
|
75
|
+
self << o
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def subset(set)
|
82
|
+
Goalkeeper::Set.new.tap do |s|
|
83
|
+
set.each {|i| s << i}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/goalkeeper/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.
|
1
|
+
module Goalkeeper
|
2
|
+
VERSION = "0.3.0"
|
3
3
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Goalkeeper::Goal do
|
4
|
+
before do
|
5
|
+
Goalkeeper.redis.flushdb
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:goal) { Goalkeeper::Goal.new("test") }
|
9
|
+
|
10
|
+
it "has a label" do
|
11
|
+
assert_equal "test", goal.label
|
12
|
+
end
|
13
|
+
|
14
|
+
it "has a namespaced key" do
|
15
|
+
assert_equal "Goalkeeper:test", goal.key
|
16
|
+
end
|
17
|
+
|
18
|
+
it "is met? if the label has a Redis record" do
|
19
|
+
assert !goal.met?
|
20
|
+
Goalkeeper.redis.set goal.key, Time.now
|
21
|
+
assert goal.met?
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "met_at" do
|
25
|
+
it "is nil if the Goal is not met" do
|
26
|
+
assert_equal nil, goal.met_at
|
27
|
+
end
|
28
|
+
|
29
|
+
it "is the timestamp that the Goal was met" do
|
30
|
+
@t = Time.parse(Time.now.to_s)
|
31
|
+
goal.met!
|
32
|
+
assert_equal @t, goal.met_at
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#met!" do
|
37
|
+
it "creates a timestamp record with the Goal's key" do
|
38
|
+
Time.stub(:now, 'timestamp') do
|
39
|
+
assert_equal nil, Goalkeeper.redis.get(goal.key)
|
40
|
+
goal.met!
|
41
|
+
assert_equal 'timestamp', Goalkeeper.redis.get(goal.key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has a default ttl expiration" do
|
46
|
+
goal.met!
|
47
|
+
assert_equal goal.expiration, Goalkeeper.redis.ttl(goal.key)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "does nothing if the goal is already met" do
|
51
|
+
t = Time.now
|
52
|
+
Time.stub(:now, t) do
|
53
|
+
goal.met!
|
54
|
+
assert_equal Time.parse(t.to_s), goal.met_at
|
55
|
+
end
|
56
|
+
|
57
|
+
Time.stub(:now, t + 1000) do
|
58
|
+
goal.met!
|
59
|
+
assert_equal Time.parse(t.to_s), goal.met_at
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#clear!" do
|
65
|
+
it "removes the met state of the Goal" do
|
66
|
+
goal.met!
|
67
|
+
assert_equal true, goal.met?
|
68
|
+
goal.clear!
|
69
|
+
assert_equal false, goal.met?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#expiration" do
|
74
|
+
it "has a default of 24 hours" do
|
75
|
+
assert_equal 24 * 60 * 60, goal.expiration
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can be set at initialization" do
|
79
|
+
g = Goalkeeper::Goal.new("x", expiration: 60)
|
80
|
+
assert_equal 60, g.expiration
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "equality" do
|
85
|
+
it "should be true when the labels are the same" do
|
86
|
+
a = Goalkeeper::Goal.new("a")
|
87
|
+
b = Goalkeeper::Goal.new("b")
|
88
|
+
a2 = Goalkeeper::Goal.new("a")
|
89
|
+
|
90
|
+
assert_equal a, a2
|
91
|
+
assert a != b
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#ttl" do
|
96
|
+
it "returns the ttl on the Redis record" do
|
97
|
+
a = Goalkeeper::Goal.new("a")
|
98
|
+
assert_equal(-2, a.ttl)
|
99
|
+
a.met!
|
100
|
+
assert_equal Goalkeeper.expiration, a.ttl
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Goalkeeper::Set do
|
4
|
+
before do
|
5
|
+
Goalkeeper.redis.flushdb
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:goals) { Goalkeeper::Set.new }
|
9
|
+
|
10
|
+
describe "#add" do
|
11
|
+
it "creates a Goal from the label" do
|
12
|
+
goals.add("a:1")
|
13
|
+
assert_equal 1, goals.size
|
14
|
+
assert_equal "a:1", goals[0].label
|
15
|
+
end
|
16
|
+
|
17
|
+
it "accepts an option expiration" do
|
18
|
+
goals.add("a:1", expiration: 20)
|
19
|
+
assert_equal 20, goals.first.expiration
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return itself (so it is chainable)" do
|
23
|
+
assert_equal goals, goals.add("a:1")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "behaves as a unique set" do
|
28
|
+
goals << Goalkeeper::Goal.new("a")
|
29
|
+
goals << Goalkeeper::Goal.new("b")
|
30
|
+
goals << Goalkeeper::Goal.new("a")
|
31
|
+
goals.push Goalkeeper::Goal.new("a")
|
32
|
+
|
33
|
+
assert_equal 2, goals.size
|
34
|
+
end
|
35
|
+
|
36
|
+
it "ignores insertion of nonGoals" do
|
37
|
+
goals << "a"
|
38
|
+
goals.push 1, 2, :a
|
39
|
+
assert_equal 0, goals.size
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "with goals" do
|
43
|
+
before do
|
44
|
+
goals.add("x").add("y")
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#met" do
|
48
|
+
it "returns all Goals which have been met" do
|
49
|
+
assert goals.met.empty?
|
50
|
+
goals[0].met!
|
51
|
+
assert_equal ["x"], goals.met.map(&:label)
|
52
|
+
goals[1].met!
|
53
|
+
assert_equal(%w( x y ), goals.met.map(&:label))
|
54
|
+
|
55
|
+
assert goals.met.is_a?(Goalkeeper::Set)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#unmet" do
|
60
|
+
it "returns all Goals which have not been met" do
|
61
|
+
assert_equal(%w( x y ), goals.unmet.map(&:label))
|
62
|
+
goals[0].met!
|
63
|
+
assert_equal ["y"], goals.unmet.map(&:label)
|
64
|
+
goals[1].met!
|
65
|
+
assert goals.unmet.empty?
|
66
|
+
|
67
|
+
assert goals.unmet.is_a?(Goalkeeper::Set)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#met?" do
|
72
|
+
it "is true when all Goals have been met" do
|
73
|
+
assert !goals.met?
|
74
|
+
goals.each(&:met!)
|
75
|
+
assert goals.met?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#met_at" do
|
80
|
+
before do
|
81
|
+
goals << Goalkeeper::Goal.new("a")
|
82
|
+
goals << Goalkeeper::Goal.new("b")
|
83
|
+
goals << Goalkeeper::Goal.new("a")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "is nil unless all Goals are met" do
|
87
|
+
goals[0].met!
|
88
|
+
goals[1].met!
|
89
|
+
assert_equal nil, goals.met_at
|
90
|
+
end
|
91
|
+
|
92
|
+
it "is the most recent met_at from the Goals" do
|
93
|
+
t = Time.now
|
94
|
+
Time.stub(:now, t + 1000) do
|
95
|
+
goals.first.met!
|
96
|
+
end
|
97
|
+
# meet the reset
|
98
|
+
goals.each &:met!
|
99
|
+
|
100
|
+
assert_equal((t + 1000).to_a, goals.met_at.to_a)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/test/goalkeeper_test.rb
CHANGED
@@ -28,118 +28,4 @@ describe Goalkeeper do
|
|
28
28
|
Goalkeeper.namespace = ns
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
32
|
-
describe Goalkeeper::List do
|
33
|
-
before do
|
34
|
-
@goals = Goalkeeper::List.new
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "#add" do
|
38
|
-
it "creates a Goal" do
|
39
|
-
@goals.add("a:1")
|
40
|
-
assert_equal 1, @goals.size
|
41
|
-
assert_equal "a:1", @goals[0].label
|
42
|
-
end
|
43
|
-
|
44
|
-
it "accepts an optional reference object" do
|
45
|
-
o = Object.new
|
46
|
-
@goals.add("a:1", ref: o)
|
47
|
-
assert_equal o, @goals[0].ref
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should return itself (so it is chainable)" do
|
51
|
-
assert_equal @goals, @goals.add("a:1")
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
describe "with goals" do
|
56
|
-
before do
|
57
|
-
@goals.add("x").add("y")
|
58
|
-
end
|
59
|
-
|
60
|
-
describe "#met" do
|
61
|
-
it "returns all Goals which have been met" do
|
62
|
-
assert @goals.met.empty?
|
63
|
-
@goals[0].met!
|
64
|
-
assert_equal ["x"], @goals.met.map(&:label)
|
65
|
-
@goals[1].met!
|
66
|
-
assert_equal ["x","y"], @goals.met.map(&:label)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe "#unmet" do
|
71
|
-
it "returns all Goals which have not been met" do
|
72
|
-
assert_equal ["x","y"], @goals.unmet.map(&:label)
|
73
|
-
@goals[0].met!
|
74
|
-
assert_equal ["y"], @goals.unmet.map(&:label)
|
75
|
-
@goals[1].met!
|
76
|
-
assert @goals.unmet.empty?
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe "#met?" do
|
81
|
-
it "is true when all Goals have been met" do
|
82
|
-
assert ! @goals.met?
|
83
|
-
@goals.each(&:met!)
|
84
|
-
assert @goals.met?
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe Goalkeeper::Goal do
|
91
|
-
before do
|
92
|
-
@goal = Goalkeeper::Goal.new("b")
|
93
|
-
end
|
94
|
-
|
95
|
-
it "has a label" do
|
96
|
-
assert_equal "b", @goal.label
|
97
|
-
end
|
98
|
-
|
99
|
-
it "has a namespaced key" do
|
100
|
-
assert_equal "Goalkeeper:b", @goal.key
|
101
|
-
end
|
102
|
-
|
103
|
-
it "is met? if the label has a Redis record" do
|
104
|
-
assert ! @goal.met?
|
105
|
-
Goalkeeper.redis.set @goal.key, Time.now
|
106
|
-
assert @goal.met?
|
107
|
-
end
|
108
|
-
|
109
|
-
describe "met_at" do
|
110
|
-
it "is nil if the Goal is not met" do
|
111
|
-
assert_equal nil, @goal.met_at
|
112
|
-
end
|
113
|
-
|
114
|
-
it "is the timestamp that the Goal was met" do
|
115
|
-
@t = Time.parse(Time.now.to_s)
|
116
|
-
@goal.met!
|
117
|
-
assert_equal @t, @goal.met_at
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
describe "#met!" do
|
122
|
-
it "creates a Redis record" do
|
123
|
-
assert Goalkeeper.redis.get(@goal.key).nil?
|
124
|
-
@goal.met!
|
125
|
-
assert ! Goalkeeper.redis.get(@goal.key).nil?
|
126
|
-
end
|
127
|
-
|
128
|
-
it "has a default ttl expiration" do
|
129
|
-
@goal.met!
|
130
|
-
assert_equal @goal.expiration, Goalkeeper.redis.ttl(@goal.key)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
describe "#expiration" do
|
135
|
-
it "has a default of 24 hours" do
|
136
|
-
assert_equal 24 * 60 * 60, @goal.expiration
|
137
|
-
end
|
138
|
-
|
139
|
-
it "can be set at initialization" do
|
140
|
-
g = Goalkeeper::Goal.new("x", expiration: 60)
|
141
|
-
assert_equal 60, g.expiration
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
31
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goalkeeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Weir
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -63,12 +63,15 @@ files:
|
|
63
63
|
- ".gitignore"
|
64
64
|
- Gemfile
|
65
65
|
- LICENSE
|
66
|
-
- LICENSE.txt
|
67
66
|
- README.md
|
68
67
|
- Rakefile
|
69
68
|
- goalkeeper.gemspec
|
70
69
|
- lib/goalkeeper.rb
|
70
|
+
- lib/goalkeeper/goal.rb
|
71
|
+
- lib/goalkeeper/set.rb
|
71
72
|
- lib/goalkeeper/version.rb
|
73
|
+
- test/goalkeeper/goal_test.rb
|
74
|
+
- test/goalkeeper/set_test.rb
|
72
75
|
- test/goalkeeper_test.rb
|
73
76
|
- test/support/redis_instance.rb
|
74
77
|
- test/test_helper.rb
|
@@ -97,6 +100,8 @@ signing_key:
|
|
97
100
|
specification_version: 4
|
98
101
|
summary: A Todo App for your application.
|
99
102
|
test_files:
|
103
|
+
- test/goalkeeper/goal_test.rb
|
104
|
+
- test/goalkeeper/set_test.rb
|
100
105
|
- test/goalkeeper_test.rb
|
101
106
|
- test/support/redis_instance.rb
|
102
107
|
- test/test_helper.rb
|
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2015 John Weir
|
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.
|