rack-flags 0.1.4 → 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.
- data/lib/rack-flags/admin_app.rb +11 -1
- data/lib/rack-flags/version.rb +1 -1
- data/spec/acceptance/administering_feature_flags_spec.rb +25 -49
- data/spec/acceptance/end_to_end_flow_spec.rb +44 -0
- data/spec/acceptance/support/page_objects/admin_page.rb +66 -0
- data/spec/acceptance/support/page_objects/reader_page.rb +27 -0
- metadata +71 -19
data/lib/rack-flags/admin_app.rb
CHANGED
@@ -52,7 +52,12 @@ module RackFlags
|
|
52
52
|
overrides
|
53
53
|
end
|
54
54
|
|
55
|
-
response.set_cookie(
|
55
|
+
response.set_cookie(
|
56
|
+
CookieCodec::COOKIE_KEY,
|
57
|
+
value: CookieCodec.new.generate_cookie_from(overrides),
|
58
|
+
path: '/',
|
59
|
+
expires: cookie_expiration
|
60
|
+
)
|
56
61
|
redirect to('/'), 303
|
57
62
|
end
|
58
63
|
|
@@ -67,5 +72,10 @@ module RackFlags
|
|
67
72
|
flag_states[form_param_flag_state.to_sym]
|
68
73
|
end
|
69
74
|
|
75
|
+
def cookie_expiration
|
76
|
+
# store overrides in the cookie for around for ~5 years from the last time they were modified
|
77
|
+
Time.new( Time.now.year + 5 )
|
78
|
+
end
|
79
|
+
|
70
80
|
end
|
71
81
|
end
|
data/lib/rack-flags/version.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative 'spec_helper'
|
2
|
+
require_relative 'support/page_objects/admin_page'
|
2
3
|
|
3
4
|
describe 'displaying flags in admin app' do
|
4
|
-
include Capybara::DSL
|
5
5
|
|
6
6
|
let( :feature_flag_config ) do
|
7
7
|
{
|
@@ -29,65 +29,41 @@ describe 'displaying flags in admin app' do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
let(:on_flag_section){ page.find('section[data-flag-name="on_by_default"]') }
|
33
|
-
let(:off_flag_section){ page.find('section[data-flag-name="off_by_default"]') }
|
34
|
-
let(:update_button){ page.find('input[type="submit"]') }
|
35
|
-
|
36
32
|
it 'successfully GETs the admin page' do
|
37
|
-
|
38
|
-
|
33
|
+
AdminPage.visiting do |page|
|
34
|
+
page.verify_status_code_is 200
|
35
|
+
end
|
39
36
|
end
|
40
37
|
|
41
38
|
it 'renders the feature flag name, default and description' do
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
39
|
+
AdminPage.visiting do |page|
|
40
|
+
on_flag_section = page.section_for_flag_named('on_by_default')
|
41
|
+
on_flag_section.should_not be_nil
|
42
|
+
on_flag_section.find('h3').text.should == 'on_by_default'
|
43
|
+
on_flag_section.find('p').text.should == 'this flag on by default'
|
44
|
+
on_flag_section.find('label.default').text.should include('Default (On)')
|
45
|
+
|
46
|
+
off_flag_section = page.section_for_flag_named('off_by_default')
|
47
|
+
off_flag_section.should_not be_nil
|
48
|
+
off_flag_section.find('h3').text.should == 'off_by_default'
|
49
|
+
off_flag_section.find('p').text.should == 'this flag off by default'
|
50
|
+
off_flag_section.find('label.default').text.should include('Default (Off)')
|
51
|
+
end
|
53
52
|
end
|
54
53
|
|
55
54
|
it 'selects the default option if there are no cookies present' do
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
55
|
+
AdminPage.visiting do |page|
|
56
|
+
page.verify_flag_section( 'on_by_default', :default )
|
57
|
+
page.verify_flag_section( 'off_by_default', :default )
|
58
|
+
end
|
60
59
|
end
|
61
60
|
|
62
61
|
it 'allows switching off a flag defaulted to on' do
|
63
|
-
|
64
|
-
|
65
|
-
on_flag_section.choose( 'Off' )
|
66
|
-
update_button.click
|
67
|
-
|
68
|
-
verify_flag_section( on_flag_section, :off )
|
69
|
-
verify_flag_section( off_flag_section, :default )
|
70
|
-
end
|
71
|
-
|
72
|
-
def verify_flag_section( section, expected_state )
|
73
|
-
case expected_state.to_sym
|
74
|
-
when :default
|
75
|
-
section.find('label.default input').should be_checked
|
76
|
-
|
77
|
-
section.find('label.on input').should_not be_checked
|
78
|
-
section.find('label.off input').should_not be_checked
|
79
|
-
when :on
|
80
|
-
section.find('label.on input').should be_checked
|
81
|
-
|
82
|
-
section.find('label.default input').should_not be_checked
|
83
|
-
section.find('label.off input').should_not be_checked
|
84
|
-
when :off
|
85
|
-
section.find('label.off input').should be_checked
|
62
|
+
AdminPage.visiting do |page|
|
63
|
+
page.turn_off_flag_name('on_by_default')
|
86
64
|
|
87
|
-
|
88
|
-
|
89
|
-
else
|
90
|
-
raise "unrecognized state '#{expected_state}'"
|
65
|
+
page.verify_flag_section( 'on_by_default', :off )
|
66
|
+
page.verify_flag_section( 'off_by_default', :default )
|
91
67
|
end
|
92
68
|
end
|
93
69
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative 'support/page_objects/admin_page'
|
3
|
+
require_relative 'support/page_objects/reader_page'
|
4
|
+
|
5
|
+
describe 'end-to-end flow of setting a feature flag in the admin app and seeing its effect' do
|
6
|
+
|
7
|
+
let( :app ) do
|
8
|
+
yaml_path = ff_config_file_path
|
9
|
+
Rack::Builder.new do
|
10
|
+
use RackFlags::RackMiddleware, yaml_path: yaml_path
|
11
|
+
map('/reader'){ run ReaderApp }
|
12
|
+
map('/feature_flags'){ run RackFlags::AdminApp.new }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let( :feature_flag_config ) do
|
17
|
+
{
|
18
|
+
foo: { default: true },
|
19
|
+
bar: { default: false }
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
before :each do
|
24
|
+
ff_config_file_contains( feature_flag_config )
|
25
|
+
Capybara.app = app
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'overriding an off flag to on' do
|
29
|
+
ReaderPage.visiting('/reader') do |page|
|
30
|
+
page.verify_flag_is_on('foo')
|
31
|
+
page.verify_flag_is_off('bar')
|
32
|
+
end
|
33
|
+
|
34
|
+
AdminPage.visiting('/feature_flags') do |page|
|
35
|
+
page.turn_on_flag_name('bar')
|
36
|
+
end
|
37
|
+
|
38
|
+
ReaderPage.visiting('/reader') do |page|
|
39
|
+
page.verify_flag_is_on('foo')
|
40
|
+
page.verify_flag_is_on('bar')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class AdminPage
|
2
|
+
include RSpec::Matchers
|
3
|
+
include Capybara::DSL
|
4
|
+
|
5
|
+
def self.visiting( path = "/" )
|
6
|
+
page = self.new(path)
|
7
|
+
page.visit_page
|
8
|
+
yield page
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize( path )
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_page
|
16
|
+
visit @path
|
17
|
+
end
|
18
|
+
|
19
|
+
def turn_on_flag_name(flag_name)
|
20
|
+
section_for_flag_named(flag_name).choose('On')
|
21
|
+
update_button.click
|
22
|
+
end
|
23
|
+
|
24
|
+
def turn_off_flag_name(flag_name)
|
25
|
+
section_for_flag_named(flag_name).choose('Off')
|
26
|
+
update_button.click
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def update_button
|
31
|
+
page.find('input[type="submit"]')
|
32
|
+
end
|
33
|
+
|
34
|
+
def section_for_flag_named(flag_name)
|
35
|
+
page.find(%Q|section[data-flag-name="#{flag_name}"]|)
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_flag_section( flag_name, expected_state )
|
39
|
+
section = section_for_flag_named( flag_name )
|
40
|
+
|
41
|
+
case expected_state.to_sym
|
42
|
+
when :default
|
43
|
+
section.find('label.default input').should be_checked
|
44
|
+
|
45
|
+
section.find('label.on input').should_not be_checked
|
46
|
+
section.find('label.off input').should_not be_checked
|
47
|
+
when :on
|
48
|
+
section.find('label.on input').should be_checked
|
49
|
+
|
50
|
+
section.find('label.default input').should_not be_checked
|
51
|
+
section.find('label.off input').should_not be_checked
|
52
|
+
when :off
|
53
|
+
section.find('label.off input').should be_checked
|
54
|
+
|
55
|
+
section.find('label.default input').should_not be_checked
|
56
|
+
section.find('label.on input').should_not be_checked
|
57
|
+
else
|
58
|
+
raise "unrecognized state '#{expected_state}'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def verify_status_code_is(expected_status_code)
|
63
|
+
status_code.should == expected_status_code
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ReaderPage
|
2
|
+
include RSpec::Matchers
|
3
|
+
include Capybara::DSL
|
4
|
+
|
5
|
+
def self.visiting( path = "/" )
|
6
|
+
page = self.new(path)
|
7
|
+
page.visit_page
|
8
|
+
yield page
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize( path )
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_page
|
16
|
+
visit(@path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def verify_flag_is_off(flag_name)
|
20
|
+
page.should have_content("#{flag_name} is off")
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify_flag_is_on(flag_name)
|
24
|
+
page.should have_content("#{flag_name} is on")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-flags
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-08-
|
13
|
+
date: 2013-08-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rake
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: rack
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ~>
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: '1.4'
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.4'
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: sinatra
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ~>
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '1.3'
|
45
55
|
type: :runtime
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.3'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: pry-debugger
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ! '>='
|
@@ -55,10 +70,15 @@ dependencies:
|
|
55
70
|
version: '0'
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
59
79
|
- !ruby/object:Gem::Dependency
|
60
80
|
name: rspec-core
|
61
|
-
requirement:
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
62
82
|
none: false
|
63
83
|
requirements:
|
64
84
|
- - ! '>='
|
@@ -66,10 +86,15 @@ dependencies:
|
|
66
86
|
version: '0'
|
67
87
|
type: :development
|
68
88
|
prerelease: false
|
69
|
-
version_requirements:
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
70
95
|
- !ruby/object:Gem::Dependency
|
71
96
|
name: rspec-expectations
|
72
|
-
requirement:
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
73
98
|
none: false
|
74
99
|
requirements:
|
75
100
|
- - ! '>='
|
@@ -77,10 +102,15 @@ dependencies:
|
|
77
102
|
version: '0'
|
78
103
|
type: :development
|
79
104
|
prerelease: false
|
80
|
-
version_requirements:
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
81
111
|
- !ruby/object:Gem::Dependency
|
82
112
|
name: rr
|
83
|
-
requirement:
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
84
114
|
none: false
|
85
115
|
requirements:
|
86
116
|
- - ! '>='
|
@@ -88,10 +118,15 @@ dependencies:
|
|
88
118
|
version: '0'
|
89
119
|
type: :development
|
90
120
|
prerelease: false
|
91
|
-
version_requirements:
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
92
127
|
- !ruby/object:Gem::Dependency
|
93
128
|
name: capybara
|
94
|
-
requirement:
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
95
130
|
none: false
|
96
131
|
requirements:
|
97
132
|
- - ! '>='
|
@@ -99,7 +134,12 @@ dependencies:
|
|
99
134
|
version: '0'
|
100
135
|
type: :development
|
101
136
|
prerelease: false
|
102
|
-
version_requirements:
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
103
143
|
description: This is a simple lightweight way to expose work-in-progress functionality
|
104
144
|
to developers, testers or other internal users.
|
105
145
|
email:
|
@@ -118,8 +158,11 @@ files:
|
|
118
158
|
- resources/admin_app/index.erb
|
119
159
|
- resources/admin_app/style.css
|
120
160
|
- spec/acceptance/administering_feature_flags_spec.rb
|
161
|
+
- spec/acceptance/end_to_end_flow_spec.rb
|
121
162
|
- spec/acceptance/reading_feature_flags_spec.rb
|
122
163
|
- spec/acceptance/spec_helper.rb
|
164
|
+
- spec/acceptance/support/page_objects/admin_page.rb
|
165
|
+
- spec/acceptance/support/page_objects/reader_page.rb
|
123
166
|
- spec/acceptance/support/reader_app.rb
|
124
167
|
- spec/spec_helper.rb
|
125
168
|
- spec/unit/admin_app_spec.rb
|
@@ -141,22 +184,31 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
141
184
|
- - ! '>='
|
142
185
|
- !ruby/object:Gem::Version
|
143
186
|
version: '0'
|
187
|
+
segments:
|
188
|
+
- 0
|
189
|
+
hash: -1587276137499646704
|
144
190
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
191
|
none: false
|
146
192
|
requirements:
|
147
193
|
- - ! '>='
|
148
194
|
- !ruby/object:Gem::Version
|
149
195
|
version: '0'
|
196
|
+
segments:
|
197
|
+
- 0
|
198
|
+
hash: -1587276137499646704
|
150
199
|
requirements: []
|
151
200
|
rubyforge_project:
|
152
|
-
rubygems_version: 1.8.
|
201
|
+
rubygems_version: 1.8.25
|
153
202
|
signing_key:
|
154
203
|
specification_version: 3
|
155
204
|
summary: Simple cookie-based feature flags using Rack.
|
156
205
|
test_files:
|
157
206
|
- spec/acceptance/administering_feature_flags_spec.rb
|
207
|
+
- spec/acceptance/end_to_end_flow_spec.rb
|
158
208
|
- spec/acceptance/reading_feature_flags_spec.rb
|
159
209
|
- spec/acceptance/spec_helper.rb
|
210
|
+
- spec/acceptance/support/page_objects/admin_page.rb
|
211
|
+
- spec/acceptance/support/page_objects/reader_page.rb
|
160
212
|
- spec/acceptance/support/reader_app.rb
|
161
213
|
- spec/spec_helper.rb
|
162
214
|
- spec/unit/admin_app_spec.rb
|