goalkeeper 0.0.1 → 0.2
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.
- checksums.yaml +4 -4
- data/LICENSE +21 -0
- data/Rakefile +2 -0
- data/goalkeeper.gemspec +2 -2
- data/lib/goalkeeper.rb +117 -31
- data/lib/goalkeeper/version.rb +1 -1
- data/test/goalkeeper_test.rb +102 -59
- data/test/support/redis_instance.rb +136 -0
- data/test/test_helper.rb +6 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 151b87201e8fbaa657d6aad6ef7aed33f8bf328a
|
4
|
+
data.tar.gz: 5a2b70b08cc0aa919d13cd3283300240d1895cdc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bb9d5d7819f6f778525ce4d1fbb638ac986f4a3644b26c306e07292396d8207adbb9499421fd33ce5f17034f03dc71c97cb3e893be537b3e761af16da1b3270
|
7
|
+
data.tar.gz: 55e43ca0fe7926ef24984da40b29f82af3dd8e9ff48d111b01e932e130a73dc9379ec16b59044d2802b6d1bdaf3bdbd016fde60bef2863cb0f65f8ac85c4dccd
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 John Weir, Pharos Enterprise Intelligence LLC
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
CHANGED
data/goalkeeper.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["John Weir"]
|
10
10
|
spec.email = ["john@smokinggun.com"]
|
11
11
|
spec.summary = %q{A Todo App for your application.}
|
12
|
-
spec.description = %q{Goalkeeper is a system for
|
13
|
-
spec.homepage = ""
|
12
|
+
spec.description = %q{Goalkeeper is a system for verifing if specific goals have been met by an application.}
|
13
|
+
spec.homepage = "https://github.com/jweir/goalkeeper"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
data/lib/goalkeeper.rb
CHANGED
@@ -1,17 +1,101 @@
|
|
1
1
|
require "goalkeeper/version"
|
2
2
|
require 'forwardable'
|
3
3
|
require 'redis'
|
4
|
-
|
4
|
+
require 'time' # for Time.parse
|
5
|
+
|
6
|
+
# Goalkeeper provides methods to track if specific events(Goals) have been completed(met).
|
7
|
+
#
|
8
|
+
# It is not a complicated system and it is easy enough to roll your own. This
|
9
|
+
# is an extraction from a system Pharos EI has been using.
|
10
|
+
#
|
11
|
+
# A Goal is just a unique string. It is up to your application to
|
12
|
+
# define any schema for the Goal's label.
|
13
|
+
#
|
14
|
+
# For example you might have your Goals labeled by date and company id:
|
15
|
+
# "job:2016-01-17:company:7"
|
16
|
+
#
|
17
|
+
# When a Goal is met a record is created in Redis with a timestamp, this is the only
|
18
|
+
# persistent layer.
|
19
|
+
# Goalkeeper.met!("jobkey")
|
20
|
+
# # or
|
21
|
+
# Goalkeeper::Goal.new("jobkey").met!
|
22
|
+
#
|
23
|
+
# To check if a Goal as been met
|
24
|
+
# Goalkeeper::Goal.new("jobkey").met?
|
25
|
+
#
|
26
|
+
# Customize the redis client by setting it in your application
|
27
|
+
# Goalkeeper.redis = your_redis_client
|
28
|
+
#
|
29
|
+
# Each record has a default expiration of 24 hours, but this can be modified.
|
30
|
+
# Goalkeeper.expiration = number_of_seconds
|
31
|
+
#
|
32
|
+
# Redis keys are stored under the default namespace of "Goalkeeper:". The namespace can be configured:
|
33
|
+
#
|
34
|
+
# Goalkeeper.namespace = string
|
35
|
+
#
|
5
36
|
class Goalkeeper
|
6
37
|
|
38
|
+
# Set the Redis client to a non default setting
|
39
|
+
def self.redis=(redis)
|
40
|
+
@redis = redis
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.redis
|
44
|
+
@redis ||= Redis.new
|
45
|
+
end
|
46
|
+
|
7
47
|
# Creates a persistent Goal market with the given label.
|
8
48
|
def self.met!(label)
|
9
49
|
Goal.new(label).met!
|
10
50
|
end
|
11
51
|
|
52
|
+
# The TTL set for each met Goal record created in Redis
|
53
|
+
# Default is 24 hours
|
54
|
+
def self.expiration
|
55
|
+
@expiration ||= 24 * 60 * 60
|
56
|
+
end
|
57
|
+
|
58
|
+
# Overwrite the default expiration
|
59
|
+
def self.expiration=(number_of_seconds)
|
60
|
+
@expiration = number_of_seconds
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.namespace
|
64
|
+
@namespace ||= "Goalkeeper"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.namespace=(ns)
|
68
|
+
@namespace = ns
|
69
|
+
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| ...}
|
12
96
|
class List
|
13
97
|
extend Forwardable
|
14
|
-
def_delegators :@list, :size, :[]
|
98
|
+
def_delegators :@list, :size, :[], :each, :map
|
15
99
|
|
16
100
|
def initialize
|
17
101
|
@list = []
|
@@ -24,6 +108,7 @@ class Goalkeeper
|
|
24
108
|
self
|
25
109
|
end
|
26
110
|
|
111
|
+
# met? returns true if all Goals in the set have been met.
|
27
112
|
def met?
|
28
113
|
unmet.empty?
|
29
114
|
end
|
@@ -38,61 +123,62 @@ class Goalkeeper
|
|
38
123
|
end
|
39
124
|
|
40
125
|
class Goal
|
126
|
+
# The unique label to identify this Goal
|
41
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.
|
42
131
|
attr_reader :ref
|
43
132
|
|
44
|
-
#
|
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.
|
45
137
|
# There is no checking if it is truly unique.
|
46
138
|
#
|
47
139
|
# +ref+ is an optional reference to any object. This
|
48
140
|
# would be used by the end user's application.
|
49
|
-
|
141
|
+
#
|
142
|
+
# +expiration+ can be set to override the gobal expiratin.
|
143
|
+
def initialize(label, ref: nil, expiration: Goalkeeper.expiration)
|
50
144
|
@label = label
|
51
145
|
@ref = ref
|
146
|
+
@expiration = expiration
|
52
147
|
end
|
53
148
|
|
54
149
|
def met!
|
55
|
-
|
150
|
+
write
|
56
151
|
self
|
57
152
|
end
|
58
153
|
|
59
154
|
def met?
|
60
|
-
!
|
155
|
+
! read.nil?
|
61
156
|
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class Store
|
65
|
-
EXPIRATION = 60 * 60 * 24 # 1 day
|
66
157
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
71
166
|
end
|
72
167
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.remove(label)
|
79
|
-
nl = ns(label)
|
80
|
-
client.del nl
|
168
|
+
# a namespaced key for the goal
|
169
|
+
def key
|
170
|
+
"#{Goalkeeper.namespace}:#{label}"
|
81
171
|
end
|
82
172
|
|
83
173
|
protected
|
84
174
|
|
85
|
-
def
|
86
|
-
|
175
|
+
def write
|
176
|
+
Goalkeeper.redis.set(self.key, Time.now)
|
177
|
+
Goalkeeper.redis.expire(self.key, self.expiration)
|
87
178
|
end
|
88
179
|
|
89
|
-
def
|
90
|
-
|
180
|
+
def read
|
181
|
+
Goalkeeper.redis.get self.key
|
91
182
|
end
|
92
|
-
|
93
|
-
def self.client
|
94
|
-
@client ||= Redis.new
|
95
|
-
end
|
96
|
-
|
97
183
|
end
|
98
184
|
end
|
data/lib/goalkeeper/version.rb
CHANGED
data/test/goalkeeper_test.rb
CHANGED
@@ -1,23 +1,33 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
# API
|
4
|
-
# Goal.configure
|
5
|
-
# Goal.met label, ttl
|
6
|
-
# Goal::List.new
|
7
|
-
# .add
|
8
|
-
# .met
|
9
|
-
# .unmet
|
10
|
-
# .met?
|
11
|
-
#
|
12
|
-
# configure
|
13
|
-
# redis client
|
14
|
-
# namespace
|
15
|
-
#
|
16
|
-
# Goal -> score, completed, met
|
17
|
-
#
|
18
|
-
# was a Goal met?
|
19
|
-
# when was it met?
|
20
3
|
describe Goalkeeper do
|
4
|
+
before do
|
5
|
+
Goalkeeper.redis.flushdb
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "::redis" do
|
9
|
+
it "returns the Redis client" do
|
10
|
+
assert Goalkeeper.redis.is_a?(Redis)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "::namespace" do
|
15
|
+
it "defaults to Goalkeeper" do
|
16
|
+
assert_equal "Goalkeeper", Goalkeeper.namespace
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can be user defined" do
|
20
|
+
ns = Goalkeeper.namespace
|
21
|
+
|
22
|
+
Goalkeeper.namespace = "NewNamespace"
|
23
|
+
assert_equal "NewNamespace", Goalkeeper.namespace
|
24
|
+
goal = Goalkeeper::Goal.new("x")
|
25
|
+
assert_equal "NewNamespace:x", goal.key
|
26
|
+
|
27
|
+
# reset
|
28
|
+
Goalkeeper.namespace = ns
|
29
|
+
end
|
30
|
+
end
|
21
31
|
|
22
32
|
describe Goalkeeper::List do
|
23
33
|
before do
|
@@ -25,7 +35,7 @@ describe Goalkeeper do
|
|
25
35
|
end
|
26
36
|
|
27
37
|
describe "#add" do
|
28
|
-
it "
|
38
|
+
it "creates a Goal" do
|
29
39
|
@goals.add("a:1")
|
30
40
|
assert_equal 1, @goals.size
|
31
41
|
assert_equal "a:1", @goals[0].label
|
@@ -42,61 +52,94 @@ describe Goalkeeper do
|
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
|
-
describe "
|
46
|
-
|
47
|
-
|
55
|
+
describe "with goals" do
|
56
|
+
before do
|
57
|
+
@goals.add("x").add("y")
|
58
|
+
end
|
48
59
|
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
52
69
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
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
|
57
79
|
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
61
87
|
end
|
62
|
-
|
63
|
-
it "has a default ttl expiration"
|
64
|
-
it "takes an optional at: timestamp"
|
65
|
-
it "takes an optional ttl for expiration"
|
66
88
|
end
|
67
89
|
|
68
|
-
describe
|
69
|
-
|
90
|
+
describe Goalkeeper::Goal do
|
91
|
+
before do
|
92
|
+
@goal = Goalkeeper::Goal.new("b")
|
93
|
+
end
|
70
94
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
end
|
95
|
+
it "has a label" do
|
96
|
+
assert_equal "b", @goal.label
|
97
|
+
end
|
75
98
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
Redis.new.flushdb
|
80
|
-
end
|
99
|
+
it "has a namespaced key" do
|
100
|
+
assert_equal "Goalkeeper:b", @goal.key
|
101
|
+
end
|
81
102
|
|
82
|
-
|
83
|
-
|
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
|
84
108
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
89
113
|
|
90
|
-
|
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
|
91
120
|
|
92
|
-
|
93
|
-
|
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
|
94
127
|
|
95
|
-
|
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
|
96
133
|
|
97
|
-
|
134
|
+
describe "#expiration" do
|
135
|
+
it "has a default of 24 hours" do
|
136
|
+
assert_equal 24 * 60 * 60, @goal.expiration
|
137
|
+
end
|
98
138
|
|
99
|
-
|
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
|
100
144
|
end
|
101
145
|
end
|
102
|
-
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# RedisInstance is copied from
|
2
|
+
# https://github.com/resque/resque-scheduler/blob/master/test/support/redis_instance.rb
|
3
|
+
# released under an MIT license
|
4
|
+
# modifications have been made
|
5
|
+
require 'socket'
|
6
|
+
require 'timeout'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
class RedisInstance
|
10
|
+
class << self
|
11
|
+
@running = false
|
12
|
+
@port = nil
|
13
|
+
@pid = nil
|
14
|
+
@waiting = false
|
15
|
+
|
16
|
+
def run_if_needed!
|
17
|
+
run! unless @running
|
18
|
+
end
|
19
|
+
|
20
|
+
def run!
|
21
|
+
ensure_redis_server_present!
|
22
|
+
ensure_pid_directory
|
23
|
+
start_redis_server
|
24
|
+
post_boot_waiting_and_such
|
25
|
+
|
26
|
+
@running = true
|
27
|
+
client
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop!
|
31
|
+
$stdout.puts "Sending TERM to Redis (#{pid})..." if $stdout.tty?
|
32
|
+
Process.kill('TERM', pid)
|
33
|
+
|
34
|
+
@port = nil
|
35
|
+
@running = false
|
36
|
+
@pid = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def client
|
40
|
+
Redis.new(
|
41
|
+
hostname: '127.0.0.1', port: port, thread_safe: true
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def post_boot_waiting_and_such
|
48
|
+
wait_for_pid
|
49
|
+
puts "Booted isolated Redis on #{port} with PID #{pid}."
|
50
|
+
|
51
|
+
wait_for_redis_boot
|
52
|
+
|
53
|
+
# Ensure we tear down Redis on Ctrl+C / test failure.
|
54
|
+
at_exit { stop! }
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_redis_server_present!
|
58
|
+
unless system('redis-server -v')
|
59
|
+
fail "** can't find `redis-server` in your path"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def wait_for_redis_boot
|
64
|
+
Timeout.timeout(10) do
|
65
|
+
loop do
|
66
|
+
begin
|
67
|
+
break if client.ping == "PONG"
|
68
|
+
rescue Redis::CannotConnectError
|
69
|
+
@waiting = true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
@waiting = false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def ensure_pid_directory
|
77
|
+
FileUtils.mkdir_p(File.dirname(pid_file))
|
78
|
+
end
|
79
|
+
|
80
|
+
def start_redis_server
|
81
|
+
IO.popen('redis-server -', 'w+') do |server|
|
82
|
+
server.write(config)
|
83
|
+
server.close_write
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def pid
|
88
|
+
@pid ||= File.read(pid_file).to_i
|
89
|
+
end
|
90
|
+
|
91
|
+
def wait_for_pid
|
92
|
+
Timeout.timeout(10) do
|
93
|
+
loop { break if File.exist?(pid_file) }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def port
|
98
|
+
@port ||= random_port
|
99
|
+
end
|
100
|
+
|
101
|
+
def pid_file
|
102
|
+
'/tmp/redis-scheduler-test.pid'
|
103
|
+
end
|
104
|
+
|
105
|
+
def config
|
106
|
+
<<-EOF
|
107
|
+
daemonize yes
|
108
|
+
pidfile #{pid_file}
|
109
|
+
port #{port}
|
110
|
+
EOF
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns a random port in the upper (10000-65535) range.
|
114
|
+
def random_port
|
115
|
+
ports = (10_000..65_535).to_a
|
116
|
+
|
117
|
+
loop do
|
118
|
+
port = ports[rand(ports.size)]
|
119
|
+
return port if port_available?('127.0.0.1', port)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def port_available?(ip, port, seconds = 1)
|
124
|
+
Timeout.timeout(seconds) do
|
125
|
+
begin
|
126
|
+
TCPSocket.new(ip, port).close
|
127
|
+
false
|
128
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
129
|
+
true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
rescue Timeout::Error
|
133
|
+
true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: goalkeeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.2'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Weir
|
@@ -52,8 +52,8 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '10.0'
|
55
|
-
description: Goalkeeper is a system for
|
56
|
-
|
55
|
+
description: Goalkeeper is a system for verifing if specific goals have been met by
|
56
|
+
an application.
|
57
57
|
email:
|
58
58
|
- john@smokinggun.com
|
59
59
|
executables: []
|
@@ -62,6 +62,7 @@ extra_rdoc_files: []
|
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
64
|
- Gemfile
|
65
|
+
- LICENSE
|
65
66
|
- LICENSE.txt
|
66
67
|
- README.md
|
67
68
|
- Rakefile
|
@@ -69,8 +70,9 @@ files:
|
|
69
70
|
- lib/goalkeeper.rb
|
70
71
|
- lib/goalkeeper/version.rb
|
71
72
|
- test/goalkeeper_test.rb
|
73
|
+
- test/support/redis_instance.rb
|
72
74
|
- test/test_helper.rb
|
73
|
-
homepage:
|
75
|
+
homepage: https://github.com/jweir/goalkeeper
|
74
76
|
licenses:
|
75
77
|
- MIT
|
76
78
|
metadata: {}
|
@@ -96,4 +98,5 @@ specification_version: 4
|
|
96
98
|
summary: A Todo App for your application.
|
97
99
|
test_files:
|
98
100
|
- test/goalkeeper_test.rb
|
101
|
+
- test/support/redis_instance.rb
|
99
102
|
- test/test_helper.rb
|