time_pilot 1.0.0 → 1.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +1 -0
- data/.tool-versions +1 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/docker-compose.yml +6 -0
- data/lib/time_pilot/time_pilot.rb +2 -2
- data/lib/time_pilot/version.rb +1 -1
- data/test/time_pilot_test.rb +74 -70
- data/web/views/index.html.erb +29 -25
- metadata +9 -8
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6553820e8f6b3c0a0432cbc93853d738c185d241d4600054c6004bf6bb72c8b
|
4
|
+
data.tar.gz: 6a15e38e9b53dd74c3d46b2117f6c357136b1119ea1c1d29b20950e79d27ce52
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e3ae4b4ff917dd411dc82fd56e32be43c297e7b3b0116f34dbd902d171a73c5e2a5965e1770f52933652868bd1f405a73044c128ece0d101bb5561bd6ca1e00
|
7
|
+
data.tar.gz: 1e0e68b17d22063b01aaea0ad9580e654590635e2e909d7b9c7de68b9c66350fbf51ba0afd0582ac8a7c990e89e59ef9443e2a878795e636029d3df1b802af51
|
@@ -0,0 +1,33 @@
|
|
1
|
+
name: Testing
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
strategy:
|
13
|
+
matrix:
|
14
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
15
|
+
services:
|
16
|
+
redis:
|
17
|
+
image: redis
|
18
|
+
options: >-
|
19
|
+
--health-cmd "redis-cli ping"
|
20
|
+
--health-interval 10s
|
21
|
+
--health-timeout 5s
|
22
|
+
--health-retries 5
|
23
|
+
ports:
|
24
|
+
- 6379:6379
|
25
|
+
steps:
|
26
|
+
- uses: actions/checkout@v2
|
27
|
+
- name: Set up Ruby
|
28
|
+
uses: ruby/setup-ruby@v1
|
29
|
+
with:
|
30
|
+
ruby-version: ${{ matrix.ruby-version }}
|
31
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
32
|
+
- name: Run tests
|
33
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.0.2
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2014 Matthijs Langenberg and Mark Oude Veldhuis
|
3
|
+
Copyright (c) 2014-2021 Matthijs Langenberg and Mark Oude Veldhuis
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# TimePilot [](https://github.com/nedap/time_pilot/actions/workflows/ruby.yml) 
|
2
2
|
|
3
3
|
TimePilot is a RubyGem that makes it possible to define features that can be enabled for a certain group of users. It requires very little configuration, and is designed to work stand-alone on any object. TimePilot uses Redis for storage.
|
4
4
|
|
data/docker-compose.yml
ADDED
@@ -90,9 +90,9 @@ module TimePilot
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def pilot_member_in_any_group?(feature_name)
|
93
|
-
TimePilot.redis.pipelined do
|
93
|
+
TimePilot.redis.pipelined do |pipeline|
|
94
94
|
self.class.time_pilot_groups.each do |group|
|
95
|
-
|
95
|
+
pipeline.sismember(
|
96
96
|
TimePilot.key("#{feature_name}:#{group}_ids"),
|
97
97
|
send(pilot_method_for_group(group))
|
98
98
|
)
|
data/lib/time_pilot/version.rb
CHANGED
data/test/time_pilot_test.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../lib/time_pilot"
|
4
|
+
require "minitest/autorun"
|
3
5
|
|
4
6
|
class Company
|
5
7
|
include TimePilot::Features
|
6
8
|
is_pilot_group
|
7
9
|
attr_reader :id
|
10
|
+
|
8
11
|
def initialize(id)
|
9
12
|
@id = id
|
10
13
|
end
|
@@ -14,6 +17,7 @@ class Team
|
|
14
17
|
include TimePilot::Features
|
15
18
|
is_pilot_group overridden_by: :company
|
16
19
|
attr_reader :company_id, :id
|
20
|
+
|
17
21
|
def initialize(company_id, id)
|
18
22
|
@company_id = company_id
|
19
23
|
@id = id
|
@@ -22,8 +26,9 @@ end
|
|
22
26
|
|
23
27
|
class Employee
|
24
28
|
include TimePilot::Features
|
25
|
-
is_pilot_group overridden_by: [
|
29
|
+
is_pilot_group overridden_by: %i[company team]
|
26
30
|
attr_reader :company_id, :team_id, :id
|
31
|
+
|
27
32
|
def initialize(company_id, team_id, id)
|
28
33
|
@company_id = company_id
|
29
34
|
@team_id = team_id
|
@@ -32,11 +37,11 @@ class Employee
|
|
32
37
|
end
|
33
38
|
|
34
39
|
TimePilot.configure do |c|
|
35
|
-
c.feature
|
40
|
+
c.feature "planning"
|
36
41
|
end
|
37
42
|
|
38
43
|
TimePilot.configure do |c|
|
39
|
-
c.feature
|
44
|
+
c.feature "secret_feature"
|
40
45
|
end
|
41
46
|
|
42
47
|
describe TimePilot do
|
@@ -50,102 +55,102 @@ describe TimePilot do
|
|
50
55
|
@jane = Employee.new(@nedap.id, @healthcare.id, 22)
|
51
56
|
end
|
52
57
|
|
53
|
-
it
|
54
|
-
TimePilot.features.must_equal [
|
55
|
-
@acme.planning_enabled
|
56
|
-
@acme.secret_feature_enabled
|
58
|
+
it "allows multiple configure blocks to add features" do
|
59
|
+
_(TimePilot.features).must_equal %w[planning secret_feature]
|
60
|
+
_(@acme.planning_enabled?).must_equal false
|
61
|
+
_(@acme.secret_feature_enabled?).must_equal false
|
57
62
|
end
|
58
63
|
|
59
|
-
it
|
60
|
-
TimePilot.redis.sadd
|
61
|
-
@acme.planning_enabled
|
62
|
-
@acme.instance_variable_get("@planning_enabled").must_equal true
|
64
|
+
it "defines a getter on company" do
|
65
|
+
TimePilot.redis.sadd "timepilot:planning:company_ids", @acme.id
|
66
|
+
_(@acme.planning_enabled?).must_equal true
|
67
|
+
_(@acme.instance_variable_get("@planning_enabled")).must_equal true
|
63
68
|
end
|
64
69
|
|
65
|
-
it
|
70
|
+
it "defines an enabler on company" do
|
66
71
|
@nedap.enable_planning
|
67
|
-
TimePilot.redis.sismember("timepilot:planning:company_ids", @nedap.id).must_equal true
|
72
|
+
_(TimePilot.redis.sismember("timepilot:planning:company_ids", @nedap.id)).must_equal true
|
68
73
|
end
|
69
74
|
|
70
|
-
it
|
75
|
+
it "defines a disabler on employee" do
|
71
76
|
@nedap.enable_planning
|
72
|
-
@nedap.planning_enabled
|
73
|
-
@nedap.instance_variable_get("@planning_enabled").must_equal true
|
77
|
+
_(@nedap.planning_enabled?).must_equal true
|
78
|
+
_(@nedap.instance_variable_get("@planning_enabled")).must_equal true
|
74
79
|
@nedap.disable_planning
|
75
|
-
@nedap.planning_enabled
|
76
|
-
@nedap.instance_variable_get("@planning_enabled").must_equal false
|
80
|
+
_(@nedap.planning_enabled?).must_equal false
|
81
|
+
_(@nedap.instance_variable_get("@planning_enabled")).must_equal false
|
77
82
|
end
|
78
83
|
|
79
|
-
it
|
80
|
-
TimePilot.redis.sadd
|
81
|
-
@healthcare.planning_enabled
|
84
|
+
it "defines a getter on team" do
|
85
|
+
TimePilot.redis.sadd "timepilot:planning:team_ids", @healthcare.id
|
86
|
+
_(@healthcare.planning_enabled?).must_equal true
|
82
87
|
end
|
83
88
|
|
84
|
-
it
|
89
|
+
it "defines an enabler on team" do
|
85
90
|
@retail.enable_planning
|
86
|
-
TimePilot.redis.sismember("timepilot:planning:team_ids", @retail.id).must_equal true
|
91
|
+
_(TimePilot.redis.sismember("timepilot:planning:team_ids", @retail.id)).must_equal true
|
87
92
|
end
|
88
93
|
|
89
|
-
it
|
94
|
+
it "defines a disabler on team" do
|
90
95
|
@retail.enable_planning
|
91
|
-
@retail.planning_enabled
|
92
|
-
@retail.instance_variable_get("@planning_enabled").must_equal true
|
96
|
+
_(@retail.planning_enabled?).must_equal true
|
97
|
+
_(@retail.instance_variable_get("@planning_enabled")).must_equal true
|
93
98
|
@retail.disable_planning
|
94
|
-
@retail.planning_enabled
|
95
|
-
@retail.instance_variable_get("@planning_enabled").must_equal false
|
99
|
+
_(@retail.planning_enabled?).must_equal false
|
100
|
+
_(@retail.instance_variable_get("@planning_enabled")).must_equal false
|
96
101
|
end
|
97
102
|
|
98
|
-
it
|
99
|
-
TimePilot.redis.sadd
|
100
|
-
@john.planning_enabled
|
101
|
-
@john.instance_variable_get("@planning_enabled").must_equal true
|
103
|
+
it "defines a getter on employee" do
|
104
|
+
TimePilot.redis.sadd "timepilot:planning:employee_ids", @john.id
|
105
|
+
_(@john.planning_enabled?).must_equal true
|
106
|
+
_(@john.instance_variable_get("@planning_enabled")).must_equal true
|
102
107
|
end
|
103
108
|
|
104
|
-
it
|
109
|
+
it "defines an enabler on employee" do
|
105
110
|
@jane.enable_planning
|
106
|
-
TimePilot.redis.sismember("timepilot:planning:employee_ids", @jane.id).must_equal true
|
111
|
+
_(TimePilot.redis.sismember("timepilot:planning:employee_ids", @jane.id)).must_equal true
|
107
112
|
end
|
108
113
|
|
109
|
-
it
|
114
|
+
it "defines a disabler on employee" do
|
110
115
|
@jane.enable_planning
|
111
|
-
@jane.planning_enabled
|
112
|
-
@jane.instance_variable_get("@planning_enabled").must_equal true
|
116
|
+
_(@jane.planning_enabled?).must_equal true
|
117
|
+
_(@jane.instance_variable_get("@planning_enabled")).must_equal true
|
113
118
|
@jane.disable_planning
|
114
|
-
@jane.planning_enabled
|
115
|
-
@jane.instance_variable_get("@planning_enabled").must_equal false
|
119
|
+
_(@jane.planning_enabled?).must_equal false
|
120
|
+
_(@jane.instance_variable_get("@planning_enabled")).must_equal false
|
116
121
|
end
|
117
122
|
|
118
|
-
it
|
123
|
+
it "defines a cardinality count on the classes" do
|
119
124
|
@nedap.enable_planning
|
120
125
|
@john.enable_planning
|
121
126
|
@jane.enable_planning
|
122
|
-
Company.pilot_feature_cardinality(:planning).must_equal 1
|
123
|
-
Team.pilot_feature_cardinality(:planning).must_equal 0
|
124
|
-
Employee.pilot_feature_cardinality(:planning).must_equal 2
|
127
|
+
_(Company.pilot_feature_cardinality(:planning)).must_equal 1
|
128
|
+
_(Team.pilot_feature_cardinality(:planning)).must_equal 0
|
129
|
+
_(Employee.pilot_feature_cardinality(:planning)).must_equal 2
|
125
130
|
end
|
126
131
|
|
127
|
-
it
|
128
|
-
TimePilot.group_classes.must_include Company
|
129
|
-
TimePilot.group_classes.must_include Team
|
130
|
-
TimePilot.group_classes.must_include Employee
|
132
|
+
it "registers classes that include TimePilot::Features" do
|
133
|
+
_(TimePilot.group_classes).must_include Company
|
134
|
+
_(TimePilot.group_classes).must_include Team
|
135
|
+
_(TimePilot.group_classes).must_include Employee
|
131
136
|
end
|
132
137
|
|
133
|
-
specify
|
138
|
+
specify "company overrides team" do
|
134
139
|
@nedap.enable_planning
|
135
|
-
@retail.planning_enabled
|
136
|
-
@retail.instance_variable_get("@planning_enabled").must_equal true
|
140
|
+
_(@retail.planning_enabled?).must_equal true
|
141
|
+
_(@retail.instance_variable_get("@planning_enabled")).must_equal true
|
137
142
|
end
|
138
143
|
|
139
|
-
specify
|
144
|
+
specify "team overrides employee" do
|
140
145
|
@healthcare.enable_planning
|
141
|
-
@john.planning_enabled
|
142
|
-
@john.instance_variable_get("@planning_enabled").must_equal true
|
146
|
+
_(@john.planning_enabled?).must_equal true
|
147
|
+
_(@john.instance_variable_get("@planning_enabled")).must_equal true
|
143
148
|
end
|
144
149
|
|
145
|
-
specify
|
150
|
+
specify "company overrides employee" do
|
146
151
|
@nedap.enable_planning
|
147
|
-
@jane.planning_enabled
|
148
|
-
@jane.instance_variable_get("@planning_enabled").must_equal true
|
152
|
+
_(@jane.planning_enabled?).must_equal true
|
153
|
+
_(@jane.instance_variable_get("@planning_enabled")).must_equal true
|
149
154
|
end
|
150
155
|
end
|
151
156
|
|
@@ -154,9 +159,9 @@ class CamelCasedModel
|
|
154
159
|
is_pilot_group
|
155
160
|
end
|
156
161
|
|
157
|
-
describe TimePilot,
|
162
|
+
describe TimePilot, "converting CamelCase to camel_case" do
|
158
163
|
it do
|
159
|
-
CamelCasedModel.time_pilot_groups.must_equal [
|
164
|
+
_(CamelCasedModel.time_pilot_groups).must_equal ["camel_cased_model"]
|
160
165
|
end
|
161
166
|
end
|
162
167
|
|
@@ -176,34 +181,33 @@ describe TimePilot do
|
|
176
181
|
end
|
177
182
|
end
|
178
183
|
|
179
|
-
specify
|
184
|
+
specify "calls redis only once" do
|
180
185
|
@mock.expect(:pipelined, [true])
|
181
186
|
|
182
|
-
@acme.planning_enabled
|
187
|
+
_(@acme.planning_enabled?).must_equal true
|
183
188
|
# Call the feature a second time
|
184
189
|
# If it calls redis you will get
|
185
190
|
# MockExpectationError: No more expects available for :pipelined: []
|
186
|
-
@acme.planning_enabled
|
191
|
+
_(@acme.planning_enabled?).must_equal true
|
187
192
|
end
|
188
193
|
|
189
|
-
specify
|
190
|
-
@mock.expect(:srem, true, [
|
194
|
+
specify "does not call redis to get status after disabling" do
|
195
|
+
@mock.expect(:srem, true, ["timepilot:planning:company_ids", @acme.id])
|
191
196
|
|
192
197
|
@acme.disable_planning
|
193
198
|
# Call the feature again it should hit the instance variable
|
194
199
|
# If it calls redis you will get
|
195
200
|
# MockExpectationError: No more expects available for :pipelined: []
|
196
|
-
@acme.planning_enabled
|
201
|
+
_(@acme.planning_enabled?).must_equal false
|
197
202
|
end
|
198
203
|
|
199
|
-
specify
|
200
|
-
@mock.expect(:sadd, true, [
|
204
|
+
specify "does not call redis to get status after enabling" do
|
205
|
+
@mock.expect(:sadd, true, ["timepilot:planning:company_ids", @acme.id])
|
201
206
|
|
202
207
|
@acme.enable_planning
|
203
208
|
# Call the feature again it should hit the instance variable
|
204
209
|
# If it calls redis you will get
|
205
210
|
# MockExpectationError: No more expects available for :pipelined: []
|
206
|
-
@acme.planning_enabled
|
211
|
+
_(@acme.planning_enabled?).must_equal true
|
207
212
|
end
|
208
213
|
end
|
209
|
-
|
data/web/views/index.html.erb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html>
|
2
|
+
<html lang="en">
|
3
3
|
<head>
|
4
4
|
<meta charset="utf8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
6
|
<title>Time Pilot Web</title>
|
7
7
|
|
8
8
|
<link rel="stylesheet" href="<%= root_path %>stylesheets/pure-min.css">
|
9
|
-
<style
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
9
|
+
<style >
|
10
|
+
h1 {
|
11
|
+
text-align: center;
|
12
|
+
}
|
13
|
+
|
14
|
+
body {
|
15
|
+
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
16
|
+
font-weight: 300;
|
17
|
+
}
|
18
|
+
|
19
|
+
.centered {
|
20
|
+
margin-left: auto;
|
21
|
+
margin-right: auto;
|
22
|
+
}
|
21
23
|
</style>
|
22
24
|
</head>
|
23
25
|
<body>
|
@@ -26,20 +28,22 @@
|
|
26
28
|
<h1>Time Pilot Web</h1>
|
27
29
|
<table class="pure-table pure-table-horizontal pure-table-striped centered">
|
28
30
|
<thead>
|
29
|
-
|
30
|
-
|
31
|
-
</
|
31
|
+
<tr>
|
32
|
+
<th>Feature</th>
|
33
|
+
<th>Group</th>
|
34
|
+
<th>Count</th>
|
35
|
+
</tr>
|
32
36
|
</thead>
|
33
37
|
<tbody>
|
34
|
-
<% @total_count.each do |feature, groups| %>
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
<% end %>
|
38
|
+
<% @total_count.each do |feature, groups| %>
|
39
|
+
<% groups.each do |group, count| %>
|
40
|
+
<tr>
|
41
|
+
<td><%= feature %></td>
|
42
|
+
<td><%= group %></td>
|
43
|
+
<td><%= count %></td>
|
44
|
+
</tr>
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
43
47
|
</tbody>
|
44
48
|
</table>
|
45
49
|
</div>
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: time_pilot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "@mlangenberg"
|
8
8
|
- "@markoudev"
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-02-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
@@ -61,13 +61,15 @@ executables: []
|
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
+
- ".github/workflows/ruby.yml"
|
64
65
|
- ".gitignore"
|
65
|
-
- ".
|
66
|
+
- ".tool-versions"
|
66
67
|
- AUTHORS
|
67
68
|
- Gemfile
|
68
69
|
- LICENSE
|
69
70
|
- README.md
|
70
71
|
- Rakefile
|
72
|
+
- docker-compose.yml
|
71
73
|
- lib/time_pilot.rb
|
72
74
|
- lib/time_pilot/configuration.rb
|
73
75
|
- lib/time_pilot/time_pilot.rb
|
@@ -84,7 +86,7 @@ homepage: https://github.com/nedap/time_pilot
|
|
84
86
|
licenses:
|
85
87
|
- MIT
|
86
88
|
metadata: {}
|
87
|
-
post_install_message:
|
89
|
+
post_install_message:
|
88
90
|
rdoc_options: []
|
89
91
|
require_paths:
|
90
92
|
- lib
|
@@ -99,9 +101,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
101
|
- !ruby/object:Gem::Version
|
100
102
|
version: '0'
|
101
103
|
requirements: []
|
102
|
-
|
103
|
-
|
104
|
-
signing_key:
|
104
|
+
rubygems_version: 3.2.26
|
105
|
+
signing_key:
|
105
106
|
specification_version: 4
|
106
107
|
summary: Configure enabled features for a specific set of users.
|
107
108
|
test_files: []
|