green_flag 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.hound.yml +4 -0
  4. data/.rubocop.yml +1260 -0
  5. data/CONTRIBUTING.md +28 -0
  6. data/app/assets/javascripts/green_flag/admin/feature-deletion.js +3 -0
  7. data/app/assets/javascripts/green_flag/admin/features.js +1 -1
  8. data/app/assets/stylesheets/green_flag/admin/features.css.scss +5 -1
  9. data/app/controllers/green_flag/admin/features_controller.rb +22 -2
  10. data/app/helpers/green_flag/application_helper.rb +1 -0
  11. data/app/models/green_flag/feature.rb +13 -1
  12. data/app/views/green_flag/admin/features/_delete_flag_button.html.erb +18 -0
  13. data/app/views/green_flag/admin/features/index.html.erb +5 -0
  14. data/app/views/green_flag/admin/features/show.html.erb +4 -2
  15. data/green_flag.gemspec +1 -1
  16. data/lib/green_flag/version.rb +1 -1
  17. data/spec/controllers/admin/features_controller_spec.rb +51 -2
  18. data/spec/dummy/log/development.log +54000 -7025
  19. data/spec/dummy/log/test.log +37566 -31044
  20. data/spec/dummy/tmp/cache/assets/{D68/760/sprockets%2Fea24808c41a3dff75b995b0f090e1a6a → C4A/640/sprockets%2F8a0527fb4c2402950e34518216e856b5} +0 -0
  21. data/spec/dummy/tmp/cache/assets/C6A/420/sprockets%2F10f5bf24a82184a4790391b0321bd318 +0 -0
  22. data/spec/dummy/tmp/cache/assets/CB2/480/sprockets%2Fdffc90cb8265f2042063557905188b1f +0 -0
  23. data/spec/dummy/tmp/cache/assets/CB9/C30/sprockets%2F8c5e58364159f49f2242f171653cadf1 +0 -0
  24. data/spec/dummy/tmp/cache/assets/CEA/0F0/sprockets%2F7d5273f602c66c790a79a75c088ff01d +0 -0
  25. data/spec/dummy/tmp/cache/assets/{C89/D60/sprockets%2F73cd073739a0655341b7278fae57518f → D05/9B0/sprockets%2Fa5b09f162635302e890302aed86bbd6e} +0 -0
  26. data/spec/dummy/tmp/cache/assets/D1A/760/sprockets%2Fc2f1fda4977960d755da14289d49b51b +0 -0
  27. data/spec/dummy/tmp/cache/assets/D34/660/sprockets%2Fcfd906f8a0a2386735d1dcf52cc40403 +0 -0
  28. data/spec/dummy/tmp/cache/assets/{E34/D30/sprockets%2F99c2d0bbd78f1b867beeb3a2eefda618 → D3E/110/sprockets%2Fcb870c7f25e6162da93a720e1be65c76} +0 -0
  29. data/spec/dummy/tmp/cache/assets/D3E/340/sprockets%2Fad85a878e73a3075722c09b0bdc17cd8 +0 -0
  30. data/spec/dummy/tmp/cache/assets/{E07/200/sprockets%2F82a8ce7f5bcfb07f773df4cbfeb04762 → D3E/E90/sprockets%2F07bfdbaf75a7c43c05315078db879c43} +0 -0
  31. data/spec/dummy/tmp/cache/assets/{CE7/FF0/sprockets%2Fe45f3a7675a8c5a5b064117792bf5e28 → D40/260/sprockets%2F35049c3c82baf75b9adfc9662c05e172} +0 -0
  32. data/spec/dummy/tmp/cache/assets/D62/040/sprockets%2F388ca040babbebd910f7338b606736bd +0 -0
  33. data/spec/dummy/tmp/cache/assets/D6F/650/sprockets%2F9710be3c17f826e702a9ccea335cdf83 +0 -0
  34. data/spec/dummy/tmp/cache/assets/{D07/670/sprockets%2F761d03a66b753d628feccd12072c814c → D82/990/sprockets%2F708fb789dee8ecdf5a4a39b9216c1295} +0 -0
  35. data/spec/dummy/tmp/cache/assets/D8D/1D0/sprockets%2F0e45f412d3a303eda9d7d10d8fc3a5d3 +0 -0
  36. data/spec/dummy/tmp/cache/assets/D9D/660/sprockets%2Fd306fa5f1db77c33117fbf27276b3ffd +0 -0
  37. data/spec/dummy/tmp/cache/assets/{DB2/0C0/sprockets%2F95cf35cd3e97774df3c41ee0ef564a8d → DF4/060/sprockets%2F3a6c5e41f435db6af7fb91fa0da49bb1} +0 -0
  38. data/spec/dummy/tmp/cache/assets/{D13/270/sprockets%2F701a30cd450ae3cfa114092bafc16004 → DF8/280/sprockets%2Fa245a66abbfe76d9de5aa5a40c557f0e} +0 -0
  39. data/spec/dummy/tmp/cache/assets/DFC/AB0/sprockets%2Fcbca52f6723bcc63ade1b269e299a7bf +0 -0
  40. data/spec/dummy/tmp/cache/assets/{D6A/580/sprockets%2F18c12847aa1bb46ce9b5661f0e9e5fb0 → E0D/870/sprockets%2Fc21ca057d9f87e8cb797dbe5b8fe3bd2} +0 -0
  41. data/spec/dummy/tmp/pids/server.pid +1 -0
  42. data/spec/factories/green_flag/feature_decision.rb +5 -0
  43. data/spec/factories/green_flag/feature_event.rb +6 -0
  44. data/spec/factories/green_flag/rule.rb +2 -2
  45. data/spec/models/green_flag/feature_spec.rb +166 -108
  46. metadata +60 -60
  47. data/Gemfile.lock +0 -143
  48. data/spec/dummy/tmp/cache/assets/C47/3D0/sprockets%2F8f17c33229239b023190617bf2e915a3 +0 -0
  49. data/spec/dummy/tmp/cache/assets/C81/770/sprockets%2Fdbcf34796b062155788f0b550808541a +0 -0
  50. data/spec/dummy/tmp/cache/assets/CB6/8F0/sprockets%2F5ea0f1f2583683678e122a9a9391a80f +0 -0
  51. data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
  52. data/spec/dummy/tmp/cache/assets/D10/860/sprockets%2F4582878dbb5b72bfa76615d31b34ed51 +0 -0
  53. data/spec/dummy/tmp/cache/assets/D15/C10/sprockets%2F6f42f843c916d7864a0dfa912fb3194a +0 -0
  54. data/spec/dummy/tmp/cache/assets/D18/860/sprockets%2Fce00769800ae939cebb28501947ea96f +0 -0
  55. data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  56. data/spec/dummy/tmp/cache/assets/D4A/970/sprockets%2F2a7d3b403cbdd8b59d57d26964ea5768 +0 -0
  57. data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
  58. data/spec/dummy/tmp/cache/assets/D59/090/sprockets%2F88788ba6a64e6279624e5c2ff7eead53 +0 -0
  59. data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
  60. data/spec/dummy/tmp/cache/assets/D5C/330/sprockets%2Fd1b1c9a53f4a8a5827e5b02bf0e100e8 +0 -0
  61. data/spec/dummy/tmp/cache/assets/DA1/210/sprockets%2F25a2979e392c8bc3adce7075cf19ab4b +0 -0
  62. data/spec/dummy/tmp/cache/assets/DA9/2C0/sprockets%2Ff95d82b2bbb6db8ffe1a87f67b415291 +0 -0
  63. data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
  64. data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,28 @@
1
+ # Contributing to GreenFlag
2
+
3
+ ## Developing
4
+
5
+ We're following a pretty standard process for a Github hosted project:
6
+
7
+ - Fork the project (core team members can skip this and work directly in this repo)
8
+ - Clone to your local machine
9
+ - Create a branch for your changes
10
+ - Push your branch to Github
11
+ - Submit a PR to master.
12
+ - After code review, your PR will be merged into master for inclusion in the next release.
13
+
14
+ ## Release
15
+
16
+ The `gem-release` gem is included as a development dependency. Documented here is the "normal release" process using that gem. More options can be found here: https://github.com/svenfuchs/gem-release
17
+
18
+ - You should be on the `master` branch, with all of the releasable code merged in.
19
+ - Run `rake spec` to make sure the specs pass.
20
+ - Run this command: `gem bump --version minor --tag`
21
+ - That's for a "minor" version. You can also use "major" or "patch".
22
+ - This does the following:
23
+ - Increments the version in `lib/green_flag/version.rb` and commits that change
24
+ - Creates a tag for the new version
25
+ - Pushes the commits and tag to Github
26
+ - Run this command: `gem release`
27
+ - This actually pushes the gem to RubyGems.
28
+ - In your project that uses GreenFlag, update to the latest: `bundle update green_flag`
@@ -0,0 +1,3 @@
1
+ $(function() {
2
+ $('.tooltip-wrapper').tooltip({position: 'bottom'});
3
+ });
@@ -350,4 +350,4 @@ GreenFlag.DecisionsView = Backbone.Marionette.ItemView.extend({
350
350
  this.model.save({ forget_disabled: true });
351
351
  },
352
352
 
353
- });
353
+ });
@@ -37,4 +37,8 @@ ul.decisions-made-summary {
37
37
  font-weight: 500;
38
38
  font-size: 24px;
39
39
  }
40
- }
40
+ }
41
+
42
+ .tooltip-wrapper {
43
+ display: inline-block;
44
+ }
@@ -1,16 +1,29 @@
1
1
  class GreenFlag::Admin::FeaturesController < ApplicationController
2
-
3
2
  layout 'green_flag/application'
3
+ helper_method :flash_class
4
4
 
5
5
  def index
6
6
  @features = GreenFlag::Feature.order(:created_at).all
7
7
  end
8
8
 
9
9
  def show
10
- @feature = GreenFlag::Feature.find(params[:id])
10
+ @feature = GreenFlag::Feature.where(id: params[:id]).first
11
+
11
12
  @visitor_groups = GreenFlag::VisitorGroup.all.map { |group| { key: group.key, description: group.description } }
12
13
  end
13
14
 
15
+ def destroy
16
+ @feature = GreenFlag::Feature.where(id: params[:id]).first
17
+
18
+ if @feature.present?
19
+ destroy_feature
20
+ else
21
+ flash[:error] = "The feature could not be found."
22
+ end
23
+
24
+ redirect_to action: :index
25
+ end
26
+
14
27
  def current_visitor_status
15
28
  @feature = GreenFlag::Feature.find(params[:id])
16
29
  fd = GreenFlag::FeatureDecision.for_feature(@feature.id).where(site_visitor_id: current_site_visitor.id).first
@@ -19,6 +32,13 @@ class GreenFlag::Admin::FeaturesController < ApplicationController
19
32
 
20
33
  private
21
34
 
35
+ def destroy_feature
36
+ flash[:notice] = "Feature \"#{@feature.code}\" has been successfully deleted."
37
+
38
+ @feature.delete_associated_data
39
+ @feature.destroy
40
+ end
41
+
22
42
  def status_text(feature_decison)
23
43
  if feature_decison.nil? || feature_decison.undecided?
24
44
  "Undecided"
@@ -2,3 +2,4 @@ module GreenFlag
2
2
  module ApplicationHelper
3
3
  end
4
4
  end
5
+
@@ -61,7 +61,19 @@ class GreenFlag::Feature < ActiveRecord::Base
61
61
  def fully_disabled?
62
62
  rules.count == 0 || rules.all? { |rule| rule.percentage == 0 }
63
63
  end
64
-
64
+
65
+ def destroyable?
66
+ fully_enabled? || fully_disabled?
67
+ end
68
+
69
+ def delete_associated_data
70
+ GreenFlag::Feature.transaction do
71
+ GreenFlag::Rule.where(feature_id: id).delete_all
72
+ GreenFlag::FeatureDecision.where(feature_id: id).delete_all
73
+ GreenFlag::FeatureEvent.where(feature_id: id).delete_all
74
+ end
75
+ end
76
+
65
77
  private
66
78
 
67
79
  def decide_feature_decision(feature_decision)
@@ -0,0 +1,18 @@
1
+ <%
2
+ button_class = @feature.destroyable? ? 'btn btn-danger' : 'btn btn-danger disabled'
3
+
4
+ confirmation_message = "Are you sure? The feature and all associated rules and \
5
+ decisions will be permanently deleted."
6
+ %>
7
+
8
+ <% unless @feature.destroyable? %>
9
+ <div class="tooltip-wrapper" data-title="For deletion, the rules must be set to
10
+ 100% or 0% for all groups. After setting the rule percentages, refresh this page.">
11
+ <% end %>
12
+
13
+ <%= link_to "Delete Flag", admin_feature_path(@feature.id), class: button_class,
14
+ data: { confirm: confirmation_message }, method: :delete %>
15
+
16
+ <% unless @feature.destroyable? %>
17
+ </div>
18
+ <% end %>
@@ -1,3 +1,8 @@
1
+ <% flash.each do |key, value| %>
2
+ <div class="alert alert-info">
3
+ <%= value %>
4
+ </div>
5
+ <% end %>
1
6
 
2
7
  <div class="header clearfix">
3
8
  <h1><%= link_to 'Green Flag', admin_features_path %></h1>
@@ -103,8 +103,6 @@
103
103
  </ul>
104
104
 
105
105
  </div>
106
-
107
-
108
106
  </script>
109
107
 
110
108
  <%# Templates - white list %>
@@ -134,8 +132,12 @@
134
132
  <h2>Your Status <span class="label {{labelClass}}">{{status}}</span></h2>
135
133
  </script>
136
134
 
135
+ <%= render 'delete_flag_button' %>
136
+
137
137
  <%# Javascript kickstart %>
138
138
  <%= javascript_include_tag 'green_flag/admin/features' %>
139
+ <%= javascript_include_tag 'green_flag/admin/feature-deletion' %>
140
+
139
141
  <script>
140
142
  GreenFlag.FeatureEditor.start({
141
143
  feature: <%= @feature.to_json.html_safe %>,
data/green_flag.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_dependency "activerecord-concurrent-index"
29
29
  s.add_dependency "sass-rails"
30
30
 
31
- s.add_development_dependency 'rspec-rails'
31
+ s.add_development_dependency 'rspec-rails', "~> 2"
32
32
  s.add_development_dependency 'factory_girl_rails'
33
33
  s.add_development_dependency 'capybara'
34
34
  s.add_development_dependency 'gem-release'
@@ -1,3 +1,3 @@
1
1
  module GreenFlag
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GreenFlag::Admin::FeaturesController do
4
-
5
4
  let(:feature) { GreenFlag::Feature.create(code: 'foo') }
6
5
 
7
6
  describe '#index' do
@@ -16,6 +15,56 @@ describe GreenFlag::Admin::FeaturesController do
16
15
  get :show, :id => feature.id
17
16
  expect(response).to be_success
18
17
  end
19
- end
18
+ end
19
+
20
+ describe "#destroy" do
21
+ subject { delete :destroy, id: feature.id }
22
+
23
+ it "redirects to the index action" do
24
+ expect(subject).to redirect_to action: :index
25
+ end
26
+
27
+ context "when the feature can be deleted" do
28
+ before(:each) do
29
+ 5.times do
30
+ FactoryGirl.create(:green_flag_rule, feature_id: feature.id)
31
+ FactoryGirl.create(:green_flag_feature_event, feature_id: feature.id)
32
+ FactoryGirl.create(:green_flag_feature_decision, feature_id: feature.id)
33
+ end
34
+
35
+ subject
36
+ end
37
+
38
+ it "deletes the feature" do
39
+ expect(GreenFlag::Feature.find_by_id(feature.id)).to be_nil
40
+ end
20
41
 
42
+ it "deletes the feature's rules" do
43
+ expect(GreenFlag::Rule.where(feature_id: feature.id)).to eq []
44
+ end
45
+
46
+ it "deletes the feature's events" do
47
+ expect(GreenFlag::FeatureEvent.where(feature_id: feature.id)).to eq []
48
+ end
49
+
50
+ it "deletes the feature's feature decisions" do
51
+ expect(GreenFlag::FeatureDecision.where(feature_id: feature.id)).to eq []
52
+ end
53
+
54
+ it "sets a successful flash notice" do
55
+ expect(flash[:notice]).to eq "Feature \"#{feature.code}\" has been \
56
+ successfully deleted."
57
+ end
58
+ end
59
+
60
+ context "when the feature cannot be found" do
61
+ it "sets a flash error indicating the error" do
62
+ feature.destroy
63
+
64
+ subject
65
+
66
+ expect(flash[:error]).to eq "The feature could not be found."
67
+ end
68
+ end
69
+ end
21
70
  end