green_flag 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +138 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +43 -0
  7. data/Rakefile +38 -0
  8. data/app/assets/images/green_flag/.gitkeep +0 -0
  9. data/app/assets/javascripts/green_flag/admin/features.js +353 -0
  10. data/app/assets/javascripts/green_flag/admin/rules.js +2 -0
  11. data/app/assets/javascripts/green_flag/application.js +14 -0
  12. data/app/assets/stylesheets/green_flag/admin/features.css.scss +36 -0
  13. data/app/assets/stylesheets/green_flag/admin/rules.css.scss +3 -0
  14. data/app/assets/stylesheets/green_flag/application.css +13 -0
  15. data/app/controllers/green_flag/admin/feature_decision_summaries_controller.rb +33 -0
  16. data/app/controllers/green_flag/admin/features_controller.rb +32 -0
  17. data/app/controllers/green_flag/admin/rule_lists_controller.rb +28 -0
  18. data/app/controllers/green_flag/admin/white_list_users_controller.rb +39 -0
  19. data/app/controllers/green_flag/site_visitor_management.rb +51 -0
  20. data/app/helpers/green_flag/application_helper.rb +4 -0
  21. data/app/models/green_flag/feature.rb +84 -0
  22. data/app/models/green_flag/feature_decision.rb +77 -0
  23. data/app/models/green_flag/feature_event.rb +9 -0
  24. data/app/models/green_flag/rule.rb +66 -0
  25. data/app/models/green_flag/site_visitor.rb +49 -0
  26. data/app/models/green_flag/user_group.rb +8 -0
  27. data/app/models/green_flag/visitor_group.rb +60 -0
  28. data/app/views/green_flag/admin/features/index.html.erb +14 -0
  29. data/app/views/green_flag/admin/features/show.html.erb +144 -0
  30. data/app/views/layouts/green_flag/application.html.erb +25 -0
  31. data/config/routes.rb +12 -0
  32. data/db/migrate/20140502112602_create_green_flag_site_visitors.rb +9 -0
  33. data/db/migrate/20140502221059_create_green_flag_features.rb +10 -0
  34. data/db/migrate/20140502221423_create_green_flag_feature_decisions.rb +13 -0
  35. data/db/migrate/20140505204611_add_visitor_code_to_site_visitors.rb +8 -0
  36. data/db/migrate/20140511045110_create_green_flag_rules.rb +12 -0
  37. data/db/migrate/20140513203728_set_default_percentage_in_green_flag_rules.rb +10 -0
  38. data/db/migrate/20140514202337_require_ordering_for_green_flag_rules.rb +13 -0
  39. data/db/migrate/20140516214909_add_restrictions_to_green_flag_rules.rb +13 -0
  40. data/db/migrate/20150211214159_create_green_flag_feature_events.rb +13 -0
  41. data/db/migrate/20150213191101_add_rule_id_to_green_flag_feature_decisions.rb +5 -0
  42. data/db/migrate/20150218035000_add_version_number_to_green_flag_rules.rb +9 -0
  43. data/db/migrate/20150218035805_add_version_number_to_green_flag_features.rb +9 -0
  44. data/db/migrate/20150218171852_add_version_number_to_green_flag_rules_indices.rb +19 -0
  45. data/green_flag.gemspec +34 -0
  46. data/green_flag.png +0 -0
  47. data/green_flag_small.png +0 -0
  48. data/lib/green_flag/engine.rb +16 -0
  49. data/lib/green_flag/version.rb +3 -0
  50. data/lib/green_flag.rb +4 -0
  51. data/lib/tasks/green_flag_tasks.rake +4 -0
  52. data/script/rails +8 -0
  53. data/spec/controllers/admin/feature_decision_summaries_controller_spec.rb +17 -0
  54. data/spec/controllers/admin/features_controller_spec.rb +21 -0
  55. data/spec/controllers/admin/rule_lists_controller_spec.rb +25 -0
  56. data/spec/controllers/admin/white_list_users_controller_spec.rb +15 -0
  57. data/spec/controllers/site_visitor_management_spec.rb +54 -0
  58. data/spec/dummy/README.rdoc +261 -0
  59. data/spec/dummy/Rakefile +7 -0
  60. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  61. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  62. data/spec/dummy/app/controllers/application_controller.rb +7 -0
  63. data/spec/dummy/app/controllers/feature_check_controller.rb +10 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  65. data/spec/dummy/app/mailers/.gitkeep +0 -0
  66. data/spec/dummy/app/models/.gitkeep +0 -0
  67. data/spec/dummy/app/models/user.rb +3 -0
  68. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  69. data/spec/dummy/config/application.rb +65 -0
  70. data/spec/dummy/config/boot.rb +10 -0
  71. data/spec/dummy/config/database.yml +43 -0
  72. data/spec/dummy/config/environment.rb +5 -0
  73. data/spec/dummy/config/environments/development.rb +37 -0
  74. data/spec/dummy/config/environments/production.rb +67 -0
  75. data/spec/dummy/config/environments/test.rb +37 -0
  76. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  77. data/spec/dummy/config/initializers/inflections.rb +15 -0
  78. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  79. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  80. data/spec/dummy/config/initializers/session_store.rb +8 -0
  81. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/spec/dummy/config/locales/en.yml +5 -0
  83. data/spec/dummy/config/routes.rb +6 -0
  84. data/spec/dummy/config.ru +4 -0
  85. data/spec/dummy/db/migrate/20150726195118_create_users.rb +8 -0
  86. data/spec/dummy/db/migrate/20150726204409_add_email_to_users.rb +5 -0
  87. data/spec/dummy/db/schema.rb +78 -0
  88. data/spec/dummy/lib/assets/.gitkeep +0 -0
  89. data/spec/dummy/log/.gitkeep +0 -0
  90. data/spec/dummy/log/development.log +8341 -0
  91. data/spec/dummy/log/test.log +34578 -0
  92. data/spec/dummy/public/404.html +26 -0
  93. data/spec/dummy/public/422.html +26 -0
  94. data/spec/dummy/public/500.html +25 -0
  95. data/spec/dummy/public/favicon.ico +0 -0
  96. data/spec/dummy/script/rails +6 -0
  97. data/spec/dummy/tmp/cache/assets/C47/3D0/sprockets%2F8f17c33229239b023190617bf2e915a3 +0 -0
  98. data/spec/dummy/tmp/cache/assets/C81/770/sprockets%2Fdbcf34796b062155788f0b550808541a +0 -0
  99. data/spec/dummy/tmp/cache/assets/C89/D60/sprockets%2F73cd073739a0655341b7278fae57518f +0 -0
  100. data/spec/dummy/tmp/cache/assets/CB6/8F0/sprockets%2F5ea0f1f2583683678e122a9a9391a80f +0 -0
  101. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  102. data/spec/dummy/tmp/cache/assets/CE7/FF0/sprockets%2Fe45f3a7675a8c5a5b064117792bf5e28 +0 -0
  103. data/spec/dummy/tmp/cache/assets/D07/670/sprockets%2F761d03a66b753d628feccd12072c814c +0 -0
  104. data/spec/dummy/tmp/cache/assets/D10/860/sprockets%2F4582878dbb5b72bfa76615d31b34ed51 +0 -0
  105. data/spec/dummy/tmp/cache/assets/D13/270/sprockets%2F701a30cd450ae3cfa114092bafc16004 +0 -0
  106. data/spec/dummy/tmp/cache/assets/D15/C10/sprockets%2F6f42f843c916d7864a0dfa912fb3194a +0 -0
  107. data/spec/dummy/tmp/cache/assets/D18/860/sprockets%2Fce00769800ae939cebb28501947ea96f +0 -0
  108. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  109. data/spec/dummy/tmp/cache/assets/D4A/970/sprockets%2F2a7d3b403cbdd8b59d57d26964ea5768 +0 -0
  110. data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  111. data/spec/dummy/tmp/cache/assets/D59/090/sprockets%2F88788ba6a64e6279624e5c2ff7eead53 +0 -0
  112. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  113. data/spec/dummy/tmp/cache/assets/D5C/330/sprockets%2Fd1b1c9a53f4a8a5827e5b02bf0e100e8 +0 -0
  114. data/spec/dummy/tmp/cache/assets/D68/760/sprockets%2Fea24808c41a3dff75b995b0f090e1a6a +0 -0
  115. data/spec/dummy/tmp/cache/assets/D6A/580/sprockets%2F18c12847aa1bb46ce9b5661f0e9e5fb0 +0 -0
  116. data/spec/dummy/tmp/cache/assets/DA1/210/sprockets%2F25a2979e392c8bc3adce7075cf19ab4b +0 -0
  117. data/spec/dummy/tmp/cache/assets/DA9/2C0/sprockets%2Ff95d82b2bbb6db8ffe1a87f67b415291 +0 -0
  118. data/spec/dummy/tmp/cache/assets/DB2/0C0/sprockets%2F95cf35cd3e97774df3c41ee0ef564a8d +0 -0
  119. data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  120. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  121. data/spec/dummy/tmp/cache/assets/E07/200/sprockets%2F82a8ce7f5bcfb07f773df4cbfeb04762 +0 -0
  122. data/spec/dummy/tmp/cache/assets/E34/D30/sprockets%2F99c2d0bbd78f1b867beeb3a2eefda618 +0 -0
  123. data/spec/factories/green_flag/feature.rb +7 -0
  124. data/spec/factories/green_flag/rule.rb +9 -0
  125. data/spec/features/admin_spec.rb +16 -0
  126. data/spec/features/visitor_spec.rb +36 -0
  127. data/spec/models/green_flag/feature_decision_spec.rb +102 -0
  128. data/spec/models/green_flag/feature_event_spec.rb +4 -0
  129. data/spec/models/green_flag/feature_spec.rb +135 -0
  130. data/spec/models/green_flag/rule_spec.rb +107 -0
  131. data/spec/models/green_flag/site_visitor_spec.rb +95 -0
  132. data/spec/models/green_flag/user_group_spec.rb +24 -0
  133. data/spec/models/green_flag/visitor_group_spec.rb +81 -0
  134. data/spec/spec_helper.rb +20 -0
  135. data/spec/support/controller_helpers.rb +7 -0
  136. 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">&times;</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">&times;</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,12 @@
1
+ GreenFlag::Engine.routes.draw do
2
+
3
+ namespace :admin do
4
+ resources :features do
5
+ get 'current_visitor_status', on: :member
6
+ resources :white_list_users
7
+ resource :rule_list
8
+ resource :feature_decision_summary
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,9 @@
1
+ class CreateGreenFlagSiteVisitors < ActiveRecord::Migration
2
+ def change
3
+ create_table :green_flag_site_visitors do |t|
4
+ t.integer :user_id
5
+ t.timestamps
6
+ end
7
+ add_index :green_flag_site_visitors, :user_id, unique: true
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ class CreateGreenFlagFeatures < ActiveRecord::Migration
2
+ def change
3
+ create_table :green_flag_features do |t|
4
+ t.string :code, null: false
5
+ t.string :description
6
+ t.timestamps
7
+ end
8
+ add_index :green_flag_features, :code, :unique => true
9
+ end
10
+ end
@@ -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,5 @@
1
+ class AddRuleIdToGreenFlagFeatureDecisions < ActiveRecord::Migration
2
+ def change
3
+ add_column :green_flag_feature_decisions, :rule_id, :integer
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class AddVersionNumberToGreenFlagRules < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :green_flag_rules, :version_number, :integer, null: false, default: 1
4
+ end
5
+
6
+ def self.down
7
+ remove_column :green_flag_rules, :version_number
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class AddVersionNumberToGreenFlagFeatures < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :green_flag_features, :version_number, :integer, null: false, default: 1
4
+ end
5
+
6
+ def self.down
7
+ remove_column :green_flag_features, :version_number
8
+ end
9
+ 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
@@ -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
@@ -0,0 +1,3 @@
1
+ module GreenFlag
2
+ VERSION = "0.1.0"
3
+ end
data/lib/green_flag.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "green_flag/engine"
2
+
3
+ module GreenFlag
4
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :green_flag do
3
+ # # Task goes here
4
+ # end
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