green_flag 0.1.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 +7 -0
- data/.gitignore +7 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +138 -0
- data/MIT-LICENSE +20 -0
- data/README.md +43 -0
- data/Rakefile +38 -0
- data/app/assets/images/green_flag/.gitkeep +0 -0
- data/app/assets/javascripts/green_flag/admin/features.js +353 -0
- data/app/assets/javascripts/green_flag/admin/rules.js +2 -0
- data/app/assets/javascripts/green_flag/application.js +14 -0
- data/app/assets/stylesheets/green_flag/admin/features.css.scss +36 -0
- data/app/assets/stylesheets/green_flag/admin/rules.css.scss +3 -0
- data/app/assets/stylesheets/green_flag/application.css +13 -0
- data/app/controllers/green_flag/admin/feature_decision_summaries_controller.rb +33 -0
- data/app/controllers/green_flag/admin/features_controller.rb +32 -0
- data/app/controllers/green_flag/admin/rule_lists_controller.rb +28 -0
- data/app/controllers/green_flag/admin/white_list_users_controller.rb +39 -0
- data/app/controllers/green_flag/site_visitor_management.rb +51 -0
- data/app/helpers/green_flag/application_helper.rb +4 -0
- data/app/models/green_flag/feature.rb +84 -0
- data/app/models/green_flag/feature_decision.rb +77 -0
- data/app/models/green_flag/feature_event.rb +9 -0
- data/app/models/green_flag/rule.rb +66 -0
- data/app/models/green_flag/site_visitor.rb +49 -0
- data/app/models/green_flag/user_group.rb +8 -0
- data/app/models/green_flag/visitor_group.rb +60 -0
- data/app/views/green_flag/admin/features/index.html.erb +14 -0
- data/app/views/green_flag/admin/features/show.html.erb +144 -0
- data/app/views/layouts/green_flag/application.html.erb +25 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20140502112602_create_green_flag_site_visitors.rb +9 -0
- data/db/migrate/20140502221059_create_green_flag_features.rb +10 -0
- data/db/migrate/20140502221423_create_green_flag_feature_decisions.rb +13 -0
- data/db/migrate/20140505204611_add_visitor_code_to_site_visitors.rb +8 -0
- data/db/migrate/20140511045110_create_green_flag_rules.rb +12 -0
- data/db/migrate/20140513203728_set_default_percentage_in_green_flag_rules.rb +10 -0
- data/db/migrate/20140514202337_require_ordering_for_green_flag_rules.rb +13 -0
- data/db/migrate/20140516214909_add_restrictions_to_green_flag_rules.rb +13 -0
- data/db/migrate/20150211214159_create_green_flag_feature_events.rb +13 -0
- data/db/migrate/20150213191101_add_rule_id_to_green_flag_feature_decisions.rb +5 -0
- data/db/migrate/20150218035000_add_version_number_to_green_flag_rules.rb +9 -0
- data/db/migrate/20150218035805_add_version_number_to_green_flag_features.rb +9 -0
- data/db/migrate/20150218171852_add_version_number_to_green_flag_rules_indices.rb +19 -0
- data/green_flag.gemspec +34 -0
- data/green_flag.png +0 -0
- data/green_flag_small.png +0 -0
- data/lib/green_flag/engine.rb +16 -0
- data/lib/green_flag/version.rb +3 -0
- data/lib/green_flag.rb +4 -0
- data/lib/tasks/green_flag_tasks.rake +4 -0
- data/script/rails +8 -0
- data/spec/controllers/admin/feature_decision_summaries_controller_spec.rb +17 -0
- data/spec/controllers/admin/features_controller_spec.rb +21 -0
- data/spec/controllers/admin/rule_lists_controller_spec.rb +25 -0
- data/spec/controllers/admin/white_list_users_controller_spec.rb +15 -0
- data/spec/controllers/site_visitor_management_spec.rb +54 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +7 -0
- data/spec/dummy/app/controllers/feature_check_controller.rb +10 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +43 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20150726195118_create_users.rb +8 -0
- data/spec/dummy/db/migrate/20150726204409_add_email_to_users.rb +5 -0
- data/spec/dummy/db/schema.rb +78 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/log/development.log +8341 -0
- data/spec/dummy/log/test.log +34578 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/tmp/cache/assets/C47/3D0/sprockets%2F8f17c33229239b023190617bf2e915a3 +0 -0
- data/spec/dummy/tmp/cache/assets/C81/770/sprockets%2Fdbcf34796b062155788f0b550808541a +0 -0
- data/spec/dummy/tmp/cache/assets/C89/D60/sprockets%2F73cd073739a0655341b7278fae57518f +0 -0
- data/spec/dummy/tmp/cache/assets/CB6/8F0/sprockets%2F5ea0f1f2583683678e122a9a9391a80f +0 -0
- data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/CE7/FF0/sprockets%2Fe45f3a7675a8c5a5b064117792bf5e28 +0 -0
- data/spec/dummy/tmp/cache/assets/D07/670/sprockets%2F761d03a66b753d628feccd12072c814c +0 -0
- data/spec/dummy/tmp/cache/assets/D10/860/sprockets%2F4582878dbb5b72bfa76615d31b34ed51 +0 -0
- data/spec/dummy/tmp/cache/assets/D13/270/sprockets%2F701a30cd450ae3cfa114092bafc16004 +0 -0
- data/spec/dummy/tmp/cache/assets/D15/C10/sprockets%2F6f42f843c916d7864a0dfa912fb3194a +0 -0
- data/spec/dummy/tmp/cache/assets/D18/860/sprockets%2Fce00769800ae939cebb28501947ea96f +0 -0
- data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/D4A/970/sprockets%2F2a7d3b403cbdd8b59d57d26964ea5768 +0 -0
- data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/D59/090/sprockets%2F88788ba6a64e6279624e5c2ff7eead53 +0 -0
- data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/D5C/330/sprockets%2Fd1b1c9a53f4a8a5827e5b02bf0e100e8 +0 -0
- data/spec/dummy/tmp/cache/assets/D68/760/sprockets%2Fea24808c41a3dff75b995b0f090e1a6a +0 -0
- data/spec/dummy/tmp/cache/assets/D6A/580/sprockets%2F18c12847aa1bb46ce9b5661f0e9e5fb0 +0 -0
- data/spec/dummy/tmp/cache/assets/DA1/210/sprockets%2F25a2979e392c8bc3adce7075cf19ab4b +0 -0
- data/spec/dummy/tmp/cache/assets/DA9/2C0/sprockets%2Ff95d82b2bbb6db8ffe1a87f67b415291 +0 -0
- data/spec/dummy/tmp/cache/assets/DB2/0C0/sprockets%2F95cf35cd3e97774df3c41ee0ef564a8d +0 -0
- data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/E07/200/sprockets%2F82a8ce7f5bcfb07f773df4cbfeb04762 +0 -0
- data/spec/dummy/tmp/cache/assets/E34/D30/sprockets%2F99c2d0bbd78f1b867beeb3a2eefda618 +0 -0
- data/spec/factories/green_flag/feature.rb +7 -0
- data/spec/factories/green_flag/rule.rb +9 -0
- data/spec/features/admin_spec.rb +16 -0
- data/spec/features/visitor_spec.rb +36 -0
- data/spec/models/green_flag/feature_decision_spec.rb +102 -0
- data/spec/models/green_flag/feature_event_spec.rb +4 -0
- data/spec/models/green_flag/feature_spec.rb +135 -0
- data/spec/models/green_flag/rule_spec.rb +107 -0
- data/spec/models/green_flag/site_visitor_spec.rb +95 -0
- data/spec/models/green_flag/user_group_spec.rb +24 -0
- data/spec/models/green_flag/visitor_group_spec.rb +81 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/controller_helpers.rb +7 -0
- metadata +359 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<div class="header clearfix">
|
|
2
|
+
<h1><%= link_to 'Green Flag', admin_features_path %> - <%= @feature.code %></h1>
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<%# Document structure %>
|
|
6
|
+
<div class="row">
|
|
7
|
+
<div id='rules' class="col-md-12 "></div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="row">
|
|
11
|
+
<div id='decisions' class="col-md-6 panel "></div>
|
|
12
|
+
<div id='white-list' class="col-md-6 panel "></div>
|
|
13
|
+
<div id='your-status' class="col-md-6 panel "></div>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="row">
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
<%# Templates - rules %>
|
|
22
|
+
<script id='rules-template' type='text/html'>
|
|
23
|
+
<h2>Rules</h2>
|
|
24
|
+
|
|
25
|
+
<div class="panel panel-default">
|
|
26
|
+
<table class="table">
|
|
27
|
+
<thead>
|
|
28
|
+
<th>Group Key</th>
|
|
29
|
+
<th>Group Description</th>
|
|
30
|
+
<th>Percentage</th>
|
|
31
|
+
<th></th>
|
|
32
|
+
</thead>
|
|
33
|
+
<tbody class="current-rules"></tbody>
|
|
34
|
+
<tfoot>
|
|
35
|
+
<th colspan="4" >
|
|
36
|
+
<div class="input-group">
|
|
37
|
+
<select class="group-keys form-control">
|
|
38
|
+
<option disabled>New Rule For...</option>
|
|
39
|
+
</select>
|
|
40
|
+
<span class="input-group-btn">
|
|
41
|
+
<input class="add-rule btn btn-primary" type="button" value="Add Rule" />
|
|
42
|
+
</span>
|
|
43
|
+
</div>
|
|
44
|
+
</th>
|
|
45
|
+
|
|
46
|
+
</tfoot>
|
|
47
|
+
</table>
|
|
48
|
+
|
|
49
|
+
<div class="invalid-message panel panel-danger">
|
|
50
|
+
<div class="panel-heading">These rules cannot be saved.</div>
|
|
51
|
+
<div class="panel-body">
|
|
52
|
+
There is probably a good reason.
|
|
53
|
+
<ul>
|
|
54
|
+
<li>Percentages should be between 0 and 100</li>
|
|
55
|
+
<li>Each group can only be included once.</li>
|
|
56
|
+
</ul>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="save-buttons panel">
|
|
61
|
+
<button class="save-rules btn btn-success btn-lg">Save Rules</button>
|
|
62
|
+
<button class="revert-rules btn btn-default btn-lg">Revert</button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<script id='current-rule-template' type='text/html'>
|
|
68
|
+
<th>{{group_key}}</th>
|
|
69
|
+
<td>{{group_description}}</th>
|
|
70
|
+
<td class="percentage">
|
|
71
|
+
<input type="number" class="rule-percent" value="{{percentage}}" min="0" max="100" />%
|
|
72
|
+
</td>
|
|
73
|
+
<td>
|
|
74
|
+
<button type="button" class="close remove-rule" aria-hidden="true">×</button>
|
|
75
|
+
</td>
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<%# Templates - decisions %>
|
|
79
|
+
<script id='decisions-template' type='text/html'>
|
|
80
|
+
<h2>Decisions Made</h2>
|
|
81
|
+
<div class="panel panel-default col-md-6">
|
|
82
|
+
|
|
83
|
+
<ul class="decisions-made-summary">
|
|
84
|
+
<li>
|
|
85
|
+
<span class="decision-number">{{enabled}}</span><br>Enabled
|
|
86
|
+
</li>
|
|
87
|
+
<li>
|
|
88
|
+
<button class="forget-decisions-enabled btn btn-lg">Forget</button>
|
|
89
|
+
</li>
|
|
90
|
+
</ul>
|
|
91
|
+
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="panel panel-default col-md-6">
|
|
95
|
+
|
|
96
|
+
<ul class="decisions-made-summary">
|
|
97
|
+
<li>
|
|
98
|
+
<span class="decision-number">{{disabled}}</span><br>Disabled
|
|
99
|
+
</li>
|
|
100
|
+
<li>
|
|
101
|
+
<button class="forget-decisions-disabled btn btn-lg">Forget</button>
|
|
102
|
+
</li>
|
|
103
|
+
</ul>
|
|
104
|
+
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
</script>
|
|
109
|
+
|
|
110
|
+
<%# Templates - white list %>
|
|
111
|
+
<script id='white-list-template' type='text/html'>
|
|
112
|
+
<h2>White List</h2>
|
|
113
|
+
|
|
114
|
+
<div class="list-group">
|
|
115
|
+
<div class='white-list-user-list'></div>
|
|
116
|
+
<div class="list-group-item">
|
|
117
|
+
<div class="alert alert-warning error-message" style="display:none">User not found</div>
|
|
118
|
+
<div class="input-group">
|
|
119
|
+
<input class="user-email form-control" placeholder="Email" />
|
|
120
|
+
<span class="input-group-btn">
|
|
121
|
+
<input class="add-user btn btn-primary" type="button" value="Add User">
|
|
122
|
+
</span>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<script id='white-list-user-template' type='text/html'>
|
|
129
|
+
<button type="button" class="close destroy-user" aria-hidden="true">×</button>
|
|
130
|
+
<strong>{{name}}</strong> {{email}}
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<script id='your-status-template' type='text/html'>
|
|
134
|
+
<h2>Your Status <span class="label {{labelClass}}">{{status}}</span></h2>
|
|
135
|
+
</script>
|
|
136
|
+
|
|
137
|
+
<%# Javascript kickstart %>
|
|
138
|
+
<%= javascript_include_tag 'green_flag/admin/features' %>
|
|
139
|
+
<script>
|
|
140
|
+
GreenFlag.FeatureEditor.start({
|
|
141
|
+
feature: <%= @feature.to_json.html_safe %>,
|
|
142
|
+
allVisitorGroups: <%= @visitor_groups.to_json.html_safe %>,
|
|
143
|
+
});
|
|
144
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>GreenFlag</title>
|
|
5
|
+
<%= stylesheet_link_tag "green_flag/application", :media => "all" %>
|
|
6
|
+
<%= stylesheet_link_tag '//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css' %>
|
|
7
|
+
<%= stylesheet_link_tag 'green_flag/admin/features' %>
|
|
8
|
+
|
|
9
|
+
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.1/underscore-min.js"></script>
|
|
11
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
|
|
12
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone.js"></script>
|
|
13
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.marionette/1.0.4/backbone.marionette.min.js"></script>
|
|
14
|
+
|
|
15
|
+
<%= javascript_include_tag "green_flag/application" %>
|
|
16
|
+
<%= csrf_meta_tags %>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
|
|
20
|
+
<div class="container">
|
|
21
|
+
<%= yield %>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateGreenFlagFeatureDecisions < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :green_flag_feature_decisions do |t|
|
|
4
|
+
t.integer :feature_id, null: false
|
|
5
|
+
t.integer :site_visitor_id, null: false
|
|
6
|
+
t.boolean :enabled
|
|
7
|
+
t.boolean :manual
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
add_index :green_flag_feature_decisions, [:site_visitor_id, :feature_id],
|
|
11
|
+
{unique: true, name: 'index_gf_feature_decisions_on_site_visitor_id_feature_id'}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class AddVisitorCodeToSiteVisitors < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
add_column :green_flag_site_visitors, :visitor_code, :string
|
|
4
|
+
execute 'update green_flag_site_visitors set visitor_code = id'
|
|
5
|
+
change_column :green_flag_site_visitors, :visitor_code, :string, null: false
|
|
6
|
+
add_index :green_flag_site_visitors, :visitor_code, unique: true
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CreateGreenFlagRules < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :green_flag_rules do |t|
|
|
4
|
+
t.string :group_key
|
|
5
|
+
t.integer :feature_id
|
|
6
|
+
t.integer :order_by
|
|
7
|
+
t.integer :percentage
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
add_index :green_flag_rules, [:feature_id, :order_by]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
class SetDefaultPercentageInGreenFlagRules < ActiveRecord::Migration
|
|
2
|
+
def up
|
|
3
|
+
execute 'update green_flag_rules set percentage=0 where percentage is null'
|
|
4
|
+
change_column :green_flag_rules, :percentage, :integer, default: 0, null: false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def down
|
|
8
|
+
change_column :green_flag_rules, :percentage, :integer, default: nil, null: true
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class RequireOrderingForGreenFlagRules < ActiveRecord::Migration
|
|
2
|
+
def up
|
|
3
|
+
change_column :green_flag_rules, :order_by, :integer, null: false
|
|
4
|
+
remove_index :green_flag_rules, [:feature_id, :order_by]
|
|
5
|
+
add_index :green_flag_rules, [:feature_id, :order_by], :unique => true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def down
|
|
9
|
+
remove_index :green_flag_rules, [:feature_id, :order_by]
|
|
10
|
+
add_index :green_flag_rules, [:feature_id, :order_by]
|
|
11
|
+
change_column :green_flag_rules, :order_by, :integer, null: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class AddRestrictionsToGreenFlagRules < ActiveRecord::Migration
|
|
2
|
+
def up
|
|
3
|
+
change_column :green_flag_rules, :group_key, :string, null: false
|
|
4
|
+
change_column :green_flag_rules, :feature_id, :integer, null: false
|
|
5
|
+
add_index :green_flag_rules, [:feature_id, :group_key], :unique => true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def down
|
|
9
|
+
remove_index :green_flag_rules, [:feature_id, :group_key]
|
|
10
|
+
change_column :green_flag_rules, :group_key, :string, null: true
|
|
11
|
+
change_column :green_flag_rules, :feature_id, :integer, null: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateGreenFlagFeatureEvents < ActiveRecord::Migration
|
|
2
|
+
def change
|
|
3
|
+
create_table :green_flag_feature_events do |t|
|
|
4
|
+
t.integer :feature_id, :null => false
|
|
5
|
+
t.integer :event_type_code, :null => false
|
|
6
|
+
t.integer :count, :null => false
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :green_flag_feature_events, :feature_id
|
|
11
|
+
add_index :green_flag_feature_events, :event_type_code
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'activerecord-concurrent-index'
|
|
2
|
+
|
|
3
|
+
class AddVersionNumberToGreenFlagRulesIndices < ActiveRecord::Migration
|
|
4
|
+
def up
|
|
5
|
+
remove_index :green_flag_rules, [:feature_id, :group_key]
|
|
6
|
+
remove_index :green_flag_rules, [:feature_id, :order_by]
|
|
7
|
+
|
|
8
|
+
add_index_concurrently :green_flag_rules, [:feature_id, :group_key, :version_number], :unique => true, :name => 'index_green_flag_rules_on_feature_version_and_group_key'
|
|
9
|
+
add_index_concurrently :green_flag_rules, [:feature_id, :order_by, :version_number], :unique => true, :name => 'index_green_flag_rules_on_feature_version_and_order_by'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def down
|
|
13
|
+
add_index_concurrently :green_flag_rules, [:feature_id, :group_key], :unique => true
|
|
14
|
+
add_index_concurrently :green_flag_rules, [:feature_id, :order_by], :unique => true
|
|
15
|
+
|
|
16
|
+
remove_index :green_flag_rules, [:feature_id, :group_key, :version_number]
|
|
17
|
+
remove_index :green_flag_rules, [:feature_id, :order_by, :version_number]
|
|
18
|
+
end
|
|
19
|
+
end
|
data/green_flag.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
2
|
+
|
|
3
|
+
# Maintain your gem's version:
|
|
4
|
+
require "green_flag/version"
|
|
5
|
+
|
|
6
|
+
# Describe your gem and declare its dependencies:
|
|
7
|
+
Gem::Specification.new do |s|
|
|
8
|
+
s.name = "green_flag"
|
|
9
|
+
s.version = GreenFlag::VERSION
|
|
10
|
+
s.authors = ["Tim Morton"]
|
|
11
|
+
s.email = ["tim@timothymorton.com"]
|
|
12
|
+
s.homepage = "https://github.com/websdotcom/green_flag"
|
|
13
|
+
s.summary = "A feature-flag system."
|
|
14
|
+
s.description = <<-EOF
|
|
15
|
+
A feature-flag system that makes it *really easy* for application developers
|
|
16
|
+
to add and manage features. You can roll out features to a percentage of
|
|
17
|
+
your users, or you can define groups to see the new feature.
|
|
18
|
+
Want to test a new feature on 50% of your premium-plan users named "Fred"?
|
|
19
|
+
GreenFlag can do that.
|
|
20
|
+
EOF
|
|
21
|
+
s.license = "MIT"
|
|
22
|
+
|
|
23
|
+
s.files = `git ls-files`.split($/)
|
|
24
|
+
s.test_files = Dir["spec/**/*"]
|
|
25
|
+
|
|
26
|
+
s.add_dependency "rails", "~> 3.2"
|
|
27
|
+
s.add_dependency "pg", "~> 0.18"
|
|
28
|
+
s.add_dependency "activerecord-concurrent-index", "~> 0.0.1"
|
|
29
|
+
s.add_dependency "sass-rails", "~> 3.2"
|
|
30
|
+
|
|
31
|
+
s.add_development_dependency 'rspec-rails'
|
|
32
|
+
s.add_development_dependency 'factory_girl_rails'
|
|
33
|
+
s.add_development_dependency 'capybara'
|
|
34
|
+
end
|
data/green_flag.png
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module GreenFlag
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace GreenFlag
|
|
4
|
+
|
|
5
|
+
config.generators do |g|
|
|
6
|
+
g.test_framework :rspec, :fixture => false
|
|
7
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
|
8
|
+
g.assets false
|
|
9
|
+
g.helper false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
initializer "green_flag.site_visitor_management" do |app|
|
|
13
|
+
ActionController::Base.send :include, GreenFlag::SiteVisitorManagement
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/green_flag.rb
ADDED
data/script/rails
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
|
3
|
+
|
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/green_flag/engine', __FILE__)
|
|
6
|
+
|
|
7
|
+
require 'rails/all'
|
|
8
|
+
require 'rails/engine/commands'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GreenFlag::Admin::FeatureDecisionSummariesController do
|
|
4
|
+
|
|
5
|
+
let(:feature) { GreenFlag::Feature.create(code: 'asdf') }
|
|
6
|
+
|
|
7
|
+
describe '#show' do
|
|
8
|
+
subject { get :show, feature_id: feature.id, format: 'js' }
|
|
9
|
+
it { should be_successful }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#update' do
|
|
13
|
+
subject { post :update, feature_id: feature.id, format: 'js' }
|
|
14
|
+
it { should be_successful }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GreenFlag::Admin::FeaturesController do
|
|
4
|
+
|
|
5
|
+
let(:feature) { GreenFlag::Feature.create(code: 'foo') }
|
|
6
|
+
|
|
7
|
+
describe '#index' do
|
|
8
|
+
it 'is successful' do
|
|
9
|
+
get :index
|
|
10
|
+
expect(response).to be_success
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe '#show' do
|
|
15
|
+
it 'is successful' do
|
|
16
|
+
get :show, :id => feature.id
|
|
17
|
+
expect(response).to be_success
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GreenFlag::Admin::RuleListsController do
|
|
4
|
+
|
|
5
|
+
let(:feature) { GreenFlag::Feature.create(code: 'asdf') }
|
|
6
|
+
|
|
7
|
+
describe '#show' do
|
|
8
|
+
subject { get :show, feature_id: feature.id, format: 'js' }
|
|
9
|
+
it { should be_successful }
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#update' do
|
|
13
|
+
let(:empty_rules_json) { [] }
|
|
14
|
+
subject do
|
|
15
|
+
post :update, feature_id: feature.id, format: 'js',
|
|
16
|
+
'_json' => empty_rules_json
|
|
17
|
+
end
|
|
18
|
+
it { should be_successful }
|
|
19
|
+
it 'sends the rules to a model' do
|
|
20
|
+
expect(GreenFlag::Rule).to receive(:set_rules!).with(feature.id, [])
|
|
21
|
+
subject
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GreenFlag::Admin::WhiteListUsersController do
|
|
4
|
+
|
|
5
|
+
describe '#index' do
|
|
6
|
+
subject { get :index, feature_id: 1, format: 'js' }
|
|
7
|
+
context 'with no whitelisted users' do
|
|
8
|
+
before(:each) do
|
|
9
|
+
allow(GreenFlag::FeatureDecision).to receive(:whitelisted_users) { [] }
|
|
10
|
+
end
|
|
11
|
+
it { should be_successful }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GreenFlag::SiteVisitorManagement do
|
|
4
|
+
|
|
5
|
+
controller(ActionController::Base) do
|
|
6
|
+
include GreenFlag::SiteVisitorManagement
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
render :text => 'foo'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def current_user
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
let(:cookie_name) { GreenFlag::SiteVisitorManagement::COOKIE_NAME }
|
|
18
|
+
|
|
19
|
+
it 'does not prevent the controller from rendering' do
|
|
20
|
+
get :index
|
|
21
|
+
expect(response).to be_success
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
context '#set_site_visitor' do
|
|
25
|
+
context 'when there is no vistor cookie' do
|
|
26
|
+
it 'does not create a new visitor' do
|
|
27
|
+
expect{ get :index }.to_not change { GreenFlag::SiteVisitor.count }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'sets the visitor cookie' do
|
|
31
|
+
get :index
|
|
32
|
+
expect(response.cookies[cookie_name]).to be_present
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'when there is a visitor cookie' do
|
|
37
|
+
before(:each) do
|
|
38
|
+
request.cookies[cookie_name] = '2d931510-d99f-494a-8c67-87feb05e1594'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'does not create a new visitor' do
|
|
42
|
+
expect{ get :index }.to_not change { GreenFlag::SiteVisitor.count }
|
|
43
|
+
end
|
|
44
|
+
it 'calls record_login when there is a current_user' do
|
|
45
|
+
user = double
|
|
46
|
+
controller.stub(:current_user) { user }
|
|
47
|
+
expect(controller).to receive(:record_login).with(user)
|
|
48
|
+
get :index
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
end
|