star_gates 0.1.0 → 0.2.0
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/CHANGELOG.md +8 -0
- data/README.md +14 -7
- data/lib/star_gates/events/base_event.rb +0 -1
- data/lib/star_gates/events/feature_events.rb +3 -3
- data/lib/star_gates/feature.rb +36 -56
- data/lib/star_gates/hexagonal/feature_aggregate.rb +3 -3
- data/lib/star_gates/hexagonal/feature_service.rb +2 -2
- data/lib/star_gates/jsonrpc_rails_adapter.rb +4 -4
- data/lib/star_gates/telemetry/opentelemetry_adapter.rb +2 -4
- data/lib/star_gates/telemetry/prometheus_adapter.rb +3 -4
- data/lib/star_gates/telemetry/telemetry_adapter.rb +2 -16
- data/lib/star_gates/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9150966ca03ffe47ce938c669d38ab3291e25b6b7aaea9560e89099ec255a65d
|
|
4
|
+
data.tar.gz: 0454b43be544e3c50026811f5d44ec3a28903c4ff824fcf3b855aaf70a733aba
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d7802feb0b5ebc3aefcd585224cfd5dda9f95e9e8f23ee5653a087f5326d14764ce7e6a06d16ca01cafd60570325c910cb776464b2abce14b2ef05b0a202407
|
|
7
|
+
data.tar.gz: 3e57ca2506afe07616e9c884f1f74228c2f380601a0ca4779b311a422aa0c215d228835e9160b0689db744ec788252b752f5133703992063a06c6c7db87254d7
|
data/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.0 (2026-10-28)
|
|
6
|
+
|
|
5
7
|
### Changed
|
|
6
8
|
|
|
9
|
+
- Use explicit rollout `key:` and `groups:` context. Group predicates receive the
|
|
10
|
+
full context; simulation, debugging, and scenario analysis use key-based inputs
|
|
11
|
+
and results. Remove user-specific helpers and telemetry labels.
|
|
7
12
|
- Require Ruby 4.0 or newer.
|
|
8
13
|
- Require StateMachines 0.202.0 or newer.
|
|
9
14
|
- Change the license to BSD-3-Clause.
|
|
@@ -17,6 +22,9 @@
|
|
|
17
22
|
|
|
18
23
|
### Fixed
|
|
19
24
|
|
|
25
|
+
- Keep percentage rollout cohorts stable across workers and restarts with CRC32
|
|
26
|
+
key buckets. Upgrading reassigns cohorts previously based on Ruby's
|
|
27
|
+
process-randomized string hashes.
|
|
20
28
|
- Correct dummy-app feature routing, persistent feature transitions, and circuit statistics.
|
|
21
29
|
- Make weather demo failures explicit instead of random.
|
|
22
30
|
|
data/README.md
CHANGED
|
@@ -65,12 +65,20 @@ end
|
|
|
65
65
|
|
|
66
66
|
gate = StarGates[:payments]
|
|
67
67
|
gate.feature.rollout_canary
|
|
68
|
-
gate.call(
|
|
68
|
+
gate.call(key: 'account:42', amount: 100)
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
Features start disabled. Use `rollout_canary` for percentage/group targeting,
|
|
72
|
-
`enable` for full rollout, and `disable` to turn a feature off.
|
|
73
|
-
|
|
72
|
+
`enable` for full rollout, and `disable` to turn a feature off. Pass a stable
|
|
73
|
+
string or integer `key:` for percentage targeting, such as an account, resident,
|
|
74
|
+
or device identifier.
|
|
75
|
+
|
|
76
|
+
Percentage targeting stays consistent across workers and restarts. Increasing
|
|
77
|
+
the rollout percentage retains keys already included.
|
|
78
|
+
|
|
79
|
+
For group targeting, pass `groups: [:beta]` to match `feature group: :beta`.
|
|
80
|
+
A predicate such as `feature group: ->(context) { context[:region] == 'eu' }`
|
|
81
|
+
receives the full context passed to the gate.
|
|
74
82
|
|
|
75
83
|
When a circuit opens, its feature rolls back from enabled to canary, or from canary
|
|
76
84
|
to disabled. Gates use their fallback when the feature is unavailable or execution
|
|
@@ -101,7 +109,7 @@ Define gates in an initializer and call them from controllers or services:
|
|
|
101
109
|
```ruby
|
|
102
110
|
class PaymentsController < ApplicationController
|
|
103
111
|
def create
|
|
104
|
-
result = StarGates[:payments].call(
|
|
112
|
+
result = StarGates[:payments].call(key: "account:#{current_account.id}", amount: params[:amount])
|
|
105
113
|
render json: result
|
|
106
114
|
end
|
|
107
115
|
end
|
|
@@ -153,15 +161,14 @@ require_relative 'test/dummy/app/models/spaceship'
|
|
|
153
161
|
ship = Spaceship.new('SG Atlas', 'SG-1')
|
|
154
162
|
warp = ship.gate(:warp_drive) # A StarGates::Gate, with a feature and a circuit.
|
|
155
163
|
warp.feature.rollout_canary
|
|
156
|
-
|
|
157
|
-
ship.engage_warp('Alpha Centauri', pilot: pilot)
|
|
164
|
+
ship.engage_warp('Alpha Centauri', key: 'crew:1', groups: [:pilots])
|
|
158
165
|
```
|
|
159
166
|
|
|
160
167
|
### Application examples
|
|
161
168
|
|
|
162
169
|
- [Payment feature](test/dummy/demos/payment_feature.rb): state-specific feature behavior.
|
|
163
170
|
- [Transition history](test/dummy/demos/transition_demo.rb): recorded feature transitions.
|
|
164
|
-
- [Introspection](test/dummy/demos/introspection_demo.rb): status, analytics, and
|
|
171
|
+
- [Introspection](test/dummy/demos/introspection_demo.rb): status, analytics, and context simulation.
|
|
165
172
|
- [Application payment feature](test/dummy/demos/application_payment_feature.rb): application-level features.
|
|
166
173
|
- [JSON-RPC controller](test/dummy/demos/jsonrpc_rails_controller.rb): optional JSON-RPC integration.
|
|
167
174
|
|
|
@@ -13,7 +13,6 @@ module StarGates
|
|
|
13
13
|
@event_type = self.class.name.split('::').last
|
|
14
14
|
@data = data.freeze
|
|
15
15
|
@metadata = metadata.merge(
|
|
16
|
-
created_by: Thread.current[:current_user_id],
|
|
17
16
|
source: 'star_gates',
|
|
18
17
|
correlation_id: Thread.current[:correlation_id] || SecureRandom.uuid
|
|
19
18
|
).freeze
|
|
@@ -58,11 +58,11 @@ module StarGates
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
class FeatureEvaluated < BaseEvent
|
|
61
|
-
def initialize(aggregate_id:,
|
|
61
|
+
def initialize(aggregate_id:, context:, eligible:, reason:, **opts)
|
|
62
62
|
super(
|
|
63
63
|
aggregate_id: aggregate_id,
|
|
64
64
|
data: {
|
|
65
|
-
|
|
65
|
+
context: context,
|
|
66
66
|
eligible: eligible,
|
|
67
67
|
reason: reason
|
|
68
68
|
},
|
|
@@ -70,7 +70,7 @@ module StarGates
|
|
|
70
70
|
)
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
def
|
|
73
|
+
def context = data[:context]
|
|
74
74
|
def eligible = data[:eligible]
|
|
75
75
|
def reason = data[:reason]
|
|
76
76
|
end
|
data/lib/star_gates/feature.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zlib'
|
|
3
4
|
require 'state_machines'
|
|
4
5
|
require 'concurrent'
|
|
5
6
|
|
|
@@ -120,17 +121,15 @@ module StarGates
|
|
|
120
121
|
!disabled?
|
|
121
122
|
end
|
|
122
123
|
|
|
123
|
-
# Check if user is eligible for rollout based on percentage and group
|
|
124
124
|
def eligible_for_rollout?(context = {})
|
|
125
125
|
return group_matches?(context) if group_matcher
|
|
126
126
|
return false if rollout_percentage.zero?
|
|
127
127
|
return true if rollout_percentage == 100
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
return false
|
|
129
|
+
key = rollout_key(context)
|
|
130
|
+
return false if key.nil?
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
(user_hash % 100) < rollout_percentage
|
|
132
|
+
(hash_key(key) % 100) < rollout_percentage
|
|
134
133
|
end
|
|
135
134
|
|
|
136
135
|
# Configuration methods for state-specific behavior
|
|
@@ -175,9 +174,9 @@ module StarGates
|
|
|
175
174
|
def summary
|
|
176
175
|
case status
|
|
177
176
|
when 'disabled'
|
|
178
|
-
"Feature '#{feature_name}' is DISABLED. No
|
|
177
|
+
"Feature '#{feature_name}' is DISABLED. No keys are eligible."
|
|
179
178
|
when 'enabled'
|
|
180
|
-
"Feature '#{feature_name}' is ENABLED for all
|
|
179
|
+
"Feature '#{feature_name}' is ENABLED for all keys (100% rollout)."
|
|
181
180
|
when 'canary'
|
|
182
181
|
if group_matcher
|
|
183
182
|
"Feature '#{feature_name}' is in CANARY mode for group #{group_matcher} (#{rollout_percentage}% rollout)."
|
|
@@ -202,20 +201,15 @@ module StarGates
|
|
|
202
201
|
}
|
|
203
202
|
end
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
# Test what a specific user would experience
|
|
209
|
-
def simulate_for_user(user_id:, user_groups: [])
|
|
210
|
-
mock_user = MockUser.new(id: user_id, groups: user_groups)
|
|
211
|
-
context = { user: mock_user }
|
|
204
|
+
def simulate(key:, groups: [], **attributes)
|
|
205
|
+
context = attributes.merge(key: key, groups: groups)
|
|
212
206
|
|
|
213
207
|
eligible = enabled_for?(context)
|
|
214
208
|
ui_component_result = ui_component(context)
|
|
215
209
|
|
|
216
210
|
{
|
|
217
|
-
|
|
218
|
-
|
|
211
|
+
key: key,
|
|
212
|
+
groups: groups,
|
|
219
213
|
eligible: eligible,
|
|
220
214
|
reason: eligibility_reason(context),
|
|
221
215
|
ui_component: ui_component_result,
|
|
@@ -224,13 +218,11 @@ module StarGates
|
|
|
224
218
|
}
|
|
225
219
|
end
|
|
226
220
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
context = { user: user }
|
|
230
|
-
user_id = extract_user_id(context)
|
|
221
|
+
def eligibility_debug(**context)
|
|
222
|
+
key = rollout_key(context)
|
|
231
223
|
|
|
232
224
|
debug_info = {
|
|
233
|
-
|
|
225
|
+
key: key,
|
|
234
226
|
feature_status: status,
|
|
235
227
|
rollout_percentage: rollout_percentage,
|
|
236
228
|
group_matcher: group_matcher,
|
|
@@ -246,18 +238,18 @@ module StarGates
|
|
|
246
238
|
if group_matcher
|
|
247
239
|
group_match = group_matches?(context)
|
|
248
240
|
debug_info[:group_match] = group_match
|
|
249
|
-
debug_info[:reason] = group_match ? '
|
|
250
|
-
elsif
|
|
251
|
-
|
|
252
|
-
debug_info[:
|
|
253
|
-
debug_info[:hash_modulo] =
|
|
254
|
-
debug_info[:reason] = if (
|
|
255
|
-
"
|
|
241
|
+
debug_info[:reason] = group_match ? 'Context matches group criteria' : 'Context does not match group criteria'
|
|
242
|
+
elsif !key.nil?
|
|
243
|
+
key_hash = hash_key(key)
|
|
244
|
+
debug_info[:key_hash] = key_hash
|
|
245
|
+
debug_info[:hash_modulo] = key_hash % 100
|
|
246
|
+
debug_info[:reason] = if (key_hash % 100) < rollout_percentage
|
|
247
|
+
"Key hash #{key_hash % 100} is within rollout #{rollout_percentage}%"
|
|
256
248
|
else
|
|
257
|
-
"
|
|
249
|
+
"Key hash #{key_hash % 100} is outside rollout #{rollout_percentage}%"
|
|
258
250
|
end
|
|
259
251
|
else
|
|
260
|
-
debug_info[:reason] = 'No
|
|
252
|
+
debug_info[:reason] = 'No rollout key found in context'
|
|
261
253
|
end
|
|
262
254
|
end
|
|
263
255
|
|
|
@@ -265,21 +257,20 @@ module StarGates
|
|
|
265
257
|
end
|
|
266
258
|
|
|
267
259
|
# Test different rollout scenarios
|
|
268
|
-
def test_rollout_scenarios(percentages,
|
|
260
|
+
def test_rollout_scenarios(percentages, sample_keys: (1..1000).to_a.sample(100))
|
|
269
261
|
scenarios = {}
|
|
270
262
|
|
|
271
263
|
percentages.each do |percentage|
|
|
272
264
|
eligible_count = 0
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
eligible_count += 1 if (user_hash % 100) < percentage
|
|
265
|
+
sample_keys.each do |key|
|
|
266
|
+
eligible_count += 1 if (hash_key(key) % 100) < percentage
|
|
276
267
|
end
|
|
277
268
|
|
|
278
269
|
scenarios[percentage] = {
|
|
279
270
|
percentage: percentage,
|
|
280
|
-
sample_size:
|
|
281
|
-
|
|
282
|
-
estimated_percentage: (eligible_count.to_f /
|
|
271
|
+
sample_size: sample_keys.size,
|
|
272
|
+
eligible_keys: eligible_count,
|
|
273
|
+
estimated_percentage: (eligible_count.to_f / sample_keys.size * 100).round(2)
|
|
283
274
|
}
|
|
284
275
|
end
|
|
285
276
|
|
|
@@ -367,7 +358,7 @@ module StarGates
|
|
|
367
358
|
when 'enabled'
|
|
368
359
|
'Feature is fully enabled'
|
|
369
360
|
when 'canary'
|
|
370
|
-
eligible_for_rollout?(context) ? '
|
|
361
|
+
eligible_for_rollout?(context) ? 'Context is eligible for rollout' : 'Context is not eligible for rollout'
|
|
371
362
|
else
|
|
372
363
|
"Unknown status: #{status}"
|
|
373
364
|
end
|
|
@@ -384,34 +375,23 @@ module StarGates
|
|
|
384
375
|
def group_matches?(context)
|
|
385
376
|
return false unless group_matcher
|
|
386
377
|
|
|
387
|
-
user = context[:user]
|
|
388
|
-
return false unless user
|
|
389
|
-
|
|
390
378
|
case group_matcher
|
|
391
379
|
when Symbol
|
|
392
|
-
|
|
380
|
+
Array(context[:groups]).include?(group_matcher)
|
|
393
381
|
when Proc
|
|
394
|
-
group_matcher.call(
|
|
382
|
+
group_matcher.call(context)
|
|
395
383
|
else
|
|
396
384
|
false
|
|
397
385
|
end
|
|
398
386
|
end
|
|
399
387
|
|
|
400
|
-
def
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if user.respond_to?(:id)
|
|
405
|
-
user.id
|
|
406
|
-
elsif user.respond_to?(:user_id)
|
|
407
|
-
user.user_id
|
|
408
|
-
elsif user.is_a?(String) || user.is_a?(Integer)
|
|
409
|
-
user
|
|
410
|
-
end
|
|
388
|
+
def rollout_key(context)
|
|
389
|
+
key = context[:key]
|
|
390
|
+
key if key.is_a?(String) || key.is_a?(Integer)
|
|
411
391
|
end
|
|
412
392
|
|
|
413
|
-
def
|
|
414
|
-
|
|
393
|
+
def hash_key(key)
|
|
394
|
+
Zlib.crc32(key.to_s)
|
|
415
395
|
end
|
|
416
396
|
|
|
417
397
|
# Record state transition with timestamp and metadata
|
|
@@ -73,10 +73,10 @@ module StarGates
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# Command: Record evaluation
|
|
76
|
-
def record_evaluation(
|
|
76
|
+
def record_evaluation(context, eligible, reason)
|
|
77
77
|
event = Events::FeatureEvaluated.new(
|
|
78
78
|
aggregate_id: @id,
|
|
79
|
-
|
|
79
|
+
context: context,
|
|
80
80
|
eligible: eligible,
|
|
81
81
|
reason: reason
|
|
82
82
|
)
|
|
@@ -213,7 +213,7 @@ module StarGates
|
|
|
213
213
|
def apply_evaluation_recorded(event)
|
|
214
214
|
@state[:evaluations] ||= []
|
|
215
215
|
@state[:evaluations] << {
|
|
216
|
-
|
|
216
|
+
context: event.context,
|
|
217
217
|
eligible: event.eligible,
|
|
218
218
|
reason: event.reason,
|
|
219
219
|
occurred_at: event.occurred_at
|
|
@@ -53,11 +53,11 @@ module StarGates
|
|
|
53
53
|
@repository.save(aggregate)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def record_feature_evaluation(feature_id,
|
|
56
|
+
def record_feature_evaluation(feature_id, context, eligible, reason)
|
|
57
57
|
aggregate = @repository.find(feature_id)
|
|
58
58
|
return unless aggregate # Silently skip if feature doesn't exist
|
|
59
59
|
|
|
60
|
-
aggregate.record_evaluation(
|
|
60
|
+
aggregate.record_evaluation(context, eligible, reason)
|
|
61
61
|
@repository.save(aggregate)
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -170,18 +170,18 @@ module StarGates
|
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
def evaluate_feature(params)
|
|
173
|
-
validate_required_params(params, %w[featureId
|
|
173
|
+
validate_required_params(params, %w[featureId context])
|
|
174
174
|
|
|
175
175
|
feature_id = params['featureId']
|
|
176
|
-
|
|
176
|
+
context = params['context']
|
|
177
177
|
eligible = params['eligible'] || false
|
|
178
178
|
reason = params['reason'] || 'No reason provided'
|
|
179
179
|
|
|
180
|
-
feature_service.record_feature_evaluation(feature_id,
|
|
180
|
+
feature_service.record_feature_evaluation(feature_id, context, eligible, reason)
|
|
181
181
|
|
|
182
182
|
{
|
|
183
183
|
featureId: feature_id,
|
|
184
|
-
|
|
184
|
+
context: context,
|
|
185
185
|
eligible: eligible,
|
|
186
186
|
reason: reason,
|
|
187
187
|
recordedAt: Time.now.utc.iso8601
|
|
@@ -15,12 +15,11 @@ module StarGates
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# OpenTelemetry-specific methods
|
|
18
|
-
def trace_feature_evaluation(feature_id,
|
|
18
|
+
def trace_feature_evaluation(feature_id, _context, &block)
|
|
19
19
|
return block.call unless enabled?
|
|
20
20
|
|
|
21
21
|
@tracer.in_span('feature.evaluate', attributes: {
|
|
22
22
|
'feature.id' => feature_id,
|
|
23
|
-
'feature.user_type' => extract_user_type(user_context),
|
|
24
23
|
'service.name' => @service_name,
|
|
25
24
|
'service.version' => @service_version
|
|
26
25
|
}) do |span|
|
|
@@ -192,13 +191,12 @@ module StarGates
|
|
|
192
191
|
end
|
|
193
192
|
|
|
194
193
|
# Override specific methods to use pre-created instruments
|
|
195
|
-
def record_feature_evaluation(feature_id, eligible,
|
|
194
|
+
def record_feature_evaluation(feature_id, eligible, _context, execution_time_ms = nil)
|
|
196
195
|
return unless enabled?
|
|
197
196
|
|
|
198
197
|
attributes = {
|
|
199
198
|
'feature.id' => feature_id.to_s,
|
|
200
199
|
'feature.eligible' => eligible.to_s,
|
|
201
|
-
'feature.user_type' => extract_user_type(user_context),
|
|
202
200
|
'service.name' => @service_name,
|
|
203
201
|
'service.version' => @service_version
|
|
204
202
|
}
|
|
@@ -48,7 +48,7 @@ module StarGates
|
|
|
48
48
|
@registry[:feature_evaluations] = @prometheus.counter(
|
|
49
49
|
:"#{@metric_prefix}_feature_evaluations_total",
|
|
50
50
|
docstring: 'Total number of feature flag evaluations',
|
|
51
|
-
labels: %i[feature_id eligible
|
|
51
|
+
labels: %i[feature_id eligible]
|
|
52
52
|
)
|
|
53
53
|
|
|
54
54
|
@registry[:feature_executions] = @prometheus.counter(
|
|
@@ -120,13 +120,12 @@ module StarGates
|
|
|
120
120
|
end
|
|
121
121
|
|
|
122
122
|
# Use pre-registered metrics for better performance
|
|
123
|
-
def record_feature_evaluation(feature_id, eligible,
|
|
123
|
+
def record_feature_evaluation(feature_id, eligible, _context, execution_time_ms = nil)
|
|
124
124
|
return unless enabled?
|
|
125
125
|
|
|
126
126
|
tags = {
|
|
127
127
|
feature_id: feature_id.to_s,
|
|
128
|
-
eligible: eligible.to_s
|
|
129
|
-
user_type: extract_user_type(user_context)
|
|
128
|
+
eligible: eligible.to_s
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
@registry[:feature_evaluations].increment(labels: tags)
|
|
@@ -40,13 +40,12 @@ module StarGates
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Feature flag specific metrics
|
|
43
|
-
def record_feature_evaluation(feature_id, eligible,
|
|
43
|
+
def record_feature_evaluation(feature_id, eligible, _context, execution_time_ms = nil)
|
|
44
44
|
return unless enabled?
|
|
45
45
|
|
|
46
46
|
tags = {
|
|
47
47
|
feature_id: feature_id,
|
|
48
|
-
eligible: eligible
|
|
49
|
-
user_type: extract_user_type(user_context)
|
|
48
|
+
eligible: eligible
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
record_counter('star_gates.feature.evaluation', 1, tags: tags)
|
|
@@ -155,19 +154,6 @@ module StarGates
|
|
|
155
154
|
|
|
156
155
|
private
|
|
157
156
|
|
|
158
|
-
def extract_user_type(user_context)
|
|
159
|
-
user = user_context[:user] if user_context.is_a?(Hash)
|
|
160
|
-
return 'unknown' unless user
|
|
161
|
-
|
|
162
|
-
if user.respond_to?(:groups) && user.groups.is_a?(Array)
|
|
163
|
-
return 'beta' if user.groups.include?(:beta_users)
|
|
164
|
-
return 'admin' if user.groups.include?(:admin)
|
|
165
|
-
return 'premium' if user.groups.include?(:premium)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
'regular'
|
|
169
|
-
end
|
|
170
|
-
|
|
171
157
|
attr_reader :config
|
|
172
158
|
end
|
|
173
159
|
end
|
data/lib/star_gates/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: star_gates
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Abdelkader Boudih
|
|
@@ -156,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
156
|
- !ruby/object:Gem::Version
|
|
157
157
|
version: '0'
|
|
158
158
|
requirements: []
|
|
159
|
-
rubygems_version: 4.0.
|
|
159
|
+
rubygems_version: 4.0.20
|
|
160
160
|
specification_version: 4
|
|
161
161
|
summary: A Ruby gem for building DSL-based star gate configurations
|
|
162
162
|
test_files: []
|