Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions examples/expressions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,9 @@ class Org < Struct.new(:id, :flipper_properties)
include Flipper::Identifier
end

NOW = Time.now.to_i
DAY = 60 * 60 * 24

org = Org.new(1, {
"type" => "Org",
"id" => 1,
"now" => NOW,
})

user = User.new(1, {
Expand All @@ -46,15 +42,13 @@ class Org < Struct.new(:id, :flipper_properties)
"plan" => "basic",
"age" => 39,
"team_user" => true,
"now" => NOW,
})

admin_user = User.new(2, {
"type" => "User",
"id" => 2,
"admin" => true,
"team_user" => true,
"now" => NOW,
})

other_user = User.new(3, {
Expand All @@ -63,7 +57,6 @@ class Org < Struct.new(:id, :flipper_properties)
"plan" => "plus",
"age" => 18,
"org_admin" => true,
"now" => NOW - DAY,
})

age_expression = Flipper.property(:age).gte(21)
Expand Down Expand Up @@ -205,9 +198,40 @@ class Org < Struct.new(:id, :flipper_properties)
assert Flipper.enabled?(:something, admin_user)
refute Flipper.enabled?(:something, other_user)

puts "\n\nEnabling based on time"
scheduled_time_expression = Flipper.property(:now).gte(NOW)
Flipper.enable :something, scheduled_time_expression
puts "\n\nEnabling based on time (epoch)"
scheduled_epoch = Flipper.now.gte(Flipper.time(Time.now.to_i - 86_400))
Flipper.enable :something, scheduled_epoch
assert Flipper.enabled?(:something, user)
assert Flipper.enabled?(:something, admin_user)
refute Flipper.enabled?(:something, other_user)
assert Flipper.enabled?(:something, other_user)
reset

puts "\n\nEnabling based on time (datetime)"
past_time = (Time.now.utc - 86_400).iso8601
scheduled_datetime = Flipper.now.gte(Flipper.time(past_time))
Flipper.enable :something, scheduled_datetime
assert Flipper.enabled?(:something, user)
assert Flipper.enabled?(:something, admin_user)
assert Flipper.enabled?(:something, other_user)
reset

puts "\n\nDisabling after a time (expiring feature)"
future_time = (Time.now.utc + 86_400).iso8601
expiring_expression = Flipper.now.lt(Flipper.time(future_time))
Flipper.enable :something, expiring_expression
assert Flipper.enabled?(:something, user)
assert Flipper.enabled?(:something, admin_user)
assert Flipper.enabled?(:something, other_user)
reset

puts "\n\nEnabling within a time window"
start_time = (Time.now.utc - 86_400).iso8601
end_time = (Time.now.utc + 86_400).iso8601
time_window = Flipper.all(
Flipper.now.gte(Flipper.time(start_time)),
Flipper.now.lt(Flipper.time(end_time))
)
Flipper.enable :something, time_window
assert Flipper.enabled?(:something, user)
assert Flipper.enabled?(:something, admin_user)
assert Flipper.enabled?(:something, other_user)
12 changes: 12 additions & 0 deletions lib/flipper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ def random(max)
Expression.build({ Random: max })
end

def now
Expression.build({ Now: [] })
end

def time(value)
Expression.build({ Time: value })
end

def duration(scalar, unit = 'second')
Expression.build({ Duration: [scalar, unit] })
end

def feature_enabled(name)
Expression.build({ FeatureEnabled: name })
end
Expand Down
9 changes: 8 additions & 1 deletion lib/flipper/expressions/time.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
require "time"

module Flipper
module Expressions
class Time
def self.call(value)
::Time.parse(value)
case value
when Numeric
::Time.at(value).utc
else
::Time.parse(value)
end
end
end
end
Expand Down
8 changes: 8 additions & 0 deletions spec/flipper/expressions/time_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,13 @@
it "returns time for #iso8601 format" do
expect(described_class.call(time.iso8601)).to eq(time)
end

it "returns time for epoch integer" do
expect(described_class.call(time.to_i)).to eq(Time.at(time.to_i).utc)
end

it "returns time for epoch float" do
expect(described_class.call(time.to_f)).to be_within(0.001).of(time)
end
end
end
58 changes: 58 additions & 0 deletions spec/flipper/gates/expression_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,64 @@ def context(expression, properties: {})
expect(subject.open?(context)).to be(false)
end
end

context 'for time-based expressions' do
it 'enables when now is past a scheduled epoch' do
past_epoch = Time.now.to_i - 86_400
expression = Flipper.now.gte(Flipper.time(past_epoch))
expect(subject.open?(context(expression.value))).to be(true)
end

it 'does not enable when now is before a future epoch' do
future_epoch = Time.now.to_i + 86_400
expression = Flipper.now.gte(Flipper.time(future_epoch))
expect(subject.open?(context(expression.value))).to be(false)
end

it 'enables when now is past a scheduled datetime' do
past_time = (Time.now.utc - 86_400).iso8601
expression = Flipper.now.gte(Flipper.time(past_time))
expect(subject.open?(context(expression.value))).to be(true)
end

it 'does not enable when now is before a future datetime' do
future_time = (Time.now.utc + 86_400).iso8601
expression = Flipper.now.gte(Flipper.time(future_time))
expect(subject.open?(context(expression.value))).to be(false)
end

it 'enables expiring features with lt' do
future_time = (Time.now.utc + 86_400).iso8601
expression = Flipper.now.lt(Flipper.time(future_time))
expect(subject.open?(context(expression.value))).to be(true)
end

it 'disables expired features with lt' do
past_time = (Time.now.utc - 86_400).iso8601
expression = Flipper.now.lt(Flipper.time(past_time))
expect(subject.open?(context(expression.value))).to be(false)
end

it 'enables within a time window using all' do
start_time = (Time.now.utc - 86_400).iso8601
end_time = (Time.now.utc + 86_400).iso8601
expression = Flipper.all(
Flipper.now.gte(Flipper.time(start_time)),
Flipper.now.lt(Flipper.time(end_time))
)
expect(subject.open?(context(expression.value))).to be(true)
end

it 'does not enable outside a time window' do
start_time = (Time.now.utc + 86_400).iso8601
end_time = (Time.now.utc + 172_800).iso8601
expression = Flipper.all(
Flipper.now.gte(Flipper.time(start_time)),
Flipper.now.lt(Flipper.time(end_time))
)
expect(subject.open?(context(expression.value))).to be(false)
end
end
end

describe '#protects?' do
Expand Down