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.
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