defra_ruby_features 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +8 -0
  3. data/README.md +92 -0
  4. data/Rakefile +40 -0
  5. data/app/controllers/defra_ruby_features/application_controller.rb +7 -0
  6. data/app/controllers/defra_ruby_features/feature_toggles_controller.rb +58 -0
  7. data/app/helpers/defra_ruby_features/application_helper.rb +6 -0
  8. data/app/helpers/delete_link_helper.rb +16 -0
  9. data/app/views/defra_ruby_features/feature_toggles/index.html.erb +48 -0
  10. data/app/views/defra_ruby_features/feature_toggles/new.html.erb +35 -0
  11. data/app/views/shared/_back.html.erb +1 -0
  12. data/config/locales/defra_ruby_features.en.yml +24 -0
  13. data/config/routes.rb +5 -0
  14. data/lib/defra_ruby_features.rb +26 -0
  15. data/lib/defra_ruby_features/configuration.rb +7 -0
  16. data/lib/defra_ruby_features/engine.rb +13 -0
  17. data/lib/defra_ruby_features/version.rb +5 -0
  18. data/lib/tasks/changelog.rake +8 -0
  19. data/spec/dummy/Rakefile +8 -0
  20. data/spec/dummy/app/assets/config/manifest.js +3 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  22. data/spec/dummy/app/channels/application_cable/channel.rb +6 -0
  23. data/spec/dummy/app/channels/application_cable/connection.rb +6 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  25. data/spec/dummy/app/controllers/test_controller.rb +7 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +4 -0
  27. data/spec/dummy/app/javascript/packs/application.js +14 -0
  28. data/spec/dummy/app/jobs/application_job.rb +9 -0
  29. data/spec/dummy/app/models/ability.rb +9 -0
  30. data/spec/dummy/app/models/application_record.rb +5 -0
  31. data/spec/dummy/app/models/feature_toggle.rb +4 -0
  32. data/spec/dummy/app/models/user.rb +8 -0
  33. data/spec/dummy/app/views/layouts/application.html.erb +12 -0
  34. data/spec/dummy/bin/rails +4 -0
  35. data/spec/dummy/bin/rake +4 -0
  36. data/spec/dummy/bin/setup +25 -0
  37. data/spec/dummy/config.ru +7 -0
  38. data/spec/dummy/config/application.rb +29 -0
  39. data/spec/dummy/config/boot.rb +7 -0
  40. data/spec/dummy/config/database.yml +21 -0
  41. data/spec/dummy/config/environment.rb +7 -0
  42. data/spec/dummy/config/environments/development.rb +50 -0
  43. data/spec/dummy/config/environments/test.rb +41 -0
  44. data/spec/dummy/config/initializers/application_controller_renderer.rb +10 -0
  45. data/spec/dummy/config/initializers/assets.rb +14 -0
  46. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  47. data/spec/dummy/config/initializers/content_security_policy.rb +30 -0
  48. data/spec/dummy/config/initializers/cookies_serializer.rb +7 -0
  49. data/spec/dummy/config/initializers/defra_ruby_features.rb +7 -0
  50. data/spec/dummy/config/initializers/devise.rb +311 -0
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  52. data/spec/dummy/config/initializers/inflections.rb +18 -0
  53. data/spec/dummy/config/initializers/mime_types.rb +6 -0
  54. data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
  55. data/spec/dummy/config/locales/devise.en.yml +65 -0
  56. data/spec/dummy/config/locales/en.yml +33 -0
  57. data/spec/dummy/config/routes.rb +7 -0
  58. data/spec/dummy/db/migrate/20200623125321_create_feature_toggles.rb +12 -0
  59. data/spec/dummy/db/migrate/20200624115316_devise_create_users.rb +44 -0
  60. data/spec/dummy/db/schema.rb +34 -0
  61. data/spec/dummy/db/test.sqlite3 +0 -0
  62. data/spec/dummy/log/test.log +4579 -0
  63. data/spec/dummy/public/404.html +67 -0
  64. data/spec/dummy/public/422.html +67 -0
  65. data/spec/dummy/public/500.html +66 -0
  66. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  67. data/spec/dummy/public/apple-touch-icon.png +0 -0
  68. data/spec/dummy/public/favicon.ico +0 -0
  69. data/spec/dummy/tmp/development_secret.txt +1 -0
  70. data/spec/examples.txt +13 -0
  71. data/spec/factories/feature_toggle.rb +8 -0
  72. data/spec/factories/user.rb +11 -0
  73. data/spec/lib/defra_ruby_mocks/configuration_spec.rb +13 -0
  74. data/spec/rails_helper.rb +54 -0
  75. data/spec/requests/defra_ruby_features/feature_toggles_spec.rb +145 -0
  76. data/spec/spec_helper.rb +83 -0
  77. data/spec/support/database_cleaner.rb +19 -0
  78. data/spec/support/devise.rb +5 -0
  79. data/spec/support/factory_bot.rb +8 -0
  80. data/spec/support/migrations.rb +10 -0
  81. data/spec/support/pry.rb +7 -0
  82. data/spec/support/rails_controller_testing.rb +5 -0
  83. data/spec/support/simplecov.rb +17 -0
  84. metadata +364 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b9b0b518a146ea7a2c3ac516a564d94f9791c5ffd758b03240924bd4178d544c
4
+ data.tar.gz: e197f19c6013c8225958f51145159bcf27767b54087e28eff5243e335b4393f2
5
+ SHA512:
6
+ metadata.gz: 6fe6cee2c6aeab187d325491773d93a4eb2637e006315f973060a3af84dedefe98688825787753f090105a69438832b7a6fd7b6878c78f4defd24e7bad0d562f
7
+ data.tar.gz: e9a3c942ad37d26e0ff1dc99d4723641ff3f260be201220a8de34abba0fd15fb779c003b9efb1652bab0b83cbc4bc380f7e1aefcf4f87d310bf168dbb540f9e6
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ The Open Government Licence (OGL) Version 3
2
+
3
+ Copyright (c) 2020 Defra
4
+
5
+ This source code is licensed under the Open Government Licence v3.0. To view this
6
+ licence, visit www.nationalarchives.gov.uk/doc/open-government-licence/version/3
7
+ or write to the Information Policy Team, The National Archives, Kew, Richmond,
8
+ Surrey, TW9 4DU.
@@ -0,0 +1,92 @@
1
+ # Defra Ruby Features
2
+
3
+ [![Build Status](https://travis-ci.com/DEFRA/defra-ruby-features.svg?branch=main)](https://travis-ci.com/DEFRA/defra-ruby-features)
4
+ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=DEFRA_defra-ruby-features&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-features)
5
+ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=DEFRA_defra-ruby-features&metric=coverage)](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-features)
6
+ [![security](https://hakiri.io/github/DEFRA/defra-ruby-features/main.svg)](https://hakiri.io/github/DEFRA/defra-ruby-features/main)
7
+ [![Gem Version](https://badge.fury.io/rb/defra_ruby_features.svg)](https://badge.fury.io/rb/defra_ruby_features)
8
+ [![Licence](https://img.shields.io/badge/Licence-OGLv3-blue.svg)](http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3)
9
+
10
+ A Rails Engine used by the [Ruby services team](https://github.com/DEFRA/ruby-services-team) in their digital services.
11
+
12
+ We use it to set, update and delete feature toggles on our rails services.
13
+
14
+ When mounted in an app, it will add additional endpoints and views to manage feature toggles.
15
+
16
+ Things to note
17
+
18
+ - We have gone with an engine rather than an additional service, to simplify management of feature toggles in our environments
19
+
20
+ ## Prerequisites
21
+
22
+ Make sure you already have:
23
+
24
+ - Ruby 2.7.1
25
+ - [Bundler](http://bundler.io/) – for installing Ruby gems
26
+
27
+ ## Mounting the engine
28
+
29
+ Add the engine to your Gemfile:
30
+
31
+ ```ruby
32
+ gem "defra_ruby_features",
33
+ git: "https://github.com/DEFRA/defra-ruby-features"
34
+ ```
35
+
36
+ Install it with `bundle install`.
37
+
38
+ Then mount the engine in your routes.rb file:
39
+
40
+ ```ruby
41
+ Rails.application.routes.draw do
42
+ mount DefraRubyFeatures::Engine => "/defra-ruby-featuress"
43
+ end
44
+ ```
45
+
46
+ The engine should now be mounted at `/defra-ruby-features` of your project. You can change `"/defra-ruby-features"` to a different route if you'd prefer it to be elsewhere.
47
+
48
+ ## Configuration
49
+
50
+ For the feature toggles to be persisted, you need to create a model class with a `String` attribute called `key` and a `Boolean` attribute named `active`.
51
+
52
+ *This will work with MongoId models as well as ActiveRecord models.*
53
+
54
+
55
+ ```ruby
56
+ # config/initializers/defra_ruby_features.rb
57
+ require "defra_ruby_features"
58
+
59
+ DefraRubyFeatures.configure do |config|
60
+ config.feature_toggle_model_name = "FeatureToggle"
61
+ end
62
+ ```
63
+
64
+ ## Testing the engine
65
+
66
+ The engine is mounted in a dummy Rails 6 app (in /spec/dummy) so we can properly test its behaviour.
67
+
68
+ The test suite is written in RSpec.
69
+
70
+ To run all the tests, use `bundle exec rspec`.
71
+
72
+ ## Contributing to this project
73
+
74
+ If you have an idea you'd like to contribute please log an issue.
75
+
76
+ All contributions should be submitted via a pull request.
77
+
78
+ ## License
79
+
80
+ THIS INFORMATION IS LICENSED UNDER THE CONDITIONS OF THE OPEN GOVERNMENT LICENCE found at:
81
+
82
+ <http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3>
83
+
84
+ The following attribution statement MUST be cited in your products and applications when using this information.
85
+
86
+ > Contains public sector information licensed under the Open Government license v3
87
+
88
+ ### About the license
89
+
90
+ The Open Government Licence (OGL) was developed by the Controller of Her Majesty's Stationery Office (HMSO) to enable information providers in the public sector to license the use and re-use of their information under a common open licence.
91
+
92
+ It is designed to encourage use and re-use of information freely and flexibly, with only a few conditions.
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "bundler/setup"
5
+ rescue LoadError
6
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
7
+ end
8
+
9
+ require "rdoc/task"
10
+
11
+ RDoc::Task.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_dir = "rdoc"
13
+ rdoc.title = "DefraRubyFeatures"
14
+ rdoc.options << "--line-numbers"
15
+ rdoc.rdoc_files.include("README.md")
16
+ rdoc.rdoc_files.include("lib/**/*.rb")
17
+ end
18
+
19
+ load "rails/tasks/statistics.rake"
20
+
21
+ Bundler::GemHelper.install_tasks
22
+
23
+ Dir[File.join(File.dirname(__FILE__), "lib/tasks/**/*.rake")].each { |f| load f }
24
+
25
+ require "bundler/gem_tasks"
26
+
27
+ require "rake/testtask"
28
+
29
+ # This is wrapped to prevent an error when rake is called in environments where
30
+ # rspec may not be available, e.g. production. As such we don't need to handle
31
+ # the error.
32
+ begin
33
+ require "rspec/core/rake_task"
34
+
35
+ RSpec::Core::RakeTask.new(:spec)
36
+
37
+ task default: :spec
38
+ rescue LoadError
39
+ # no rspec available
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyFeatures
4
+ class ApplicationController < ::ApplicationController
5
+
6
+ end
7
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyFeatures
4
+ class FeatureTogglesController < ::DefraRubyFeatures::ApplicationController
5
+ before_action :authenticate_user!
6
+ before_action :authorize_user!
7
+
8
+ helper DeleteLinkHelper
9
+
10
+ def index
11
+ # MongoId
12
+ @feature_toggles = model.order(key: "ASC") if model.respond_to?(:order)
13
+
14
+ # ActiveRecord
15
+ @feature_toggles = model.order_by(key: "ASC").all if model.respond_to?(:order_by)
16
+ end
17
+
18
+ def create
19
+ model.create!(feature_toggle_params)
20
+
21
+ redirect_to feature_toggles_path
22
+ end
23
+
24
+ def update
25
+ @feature_toggle = model.find_by(id: params[:id])
26
+
27
+ @feature_toggle.update!(feature_toggle_params)
28
+
29
+ redirect_to feature_toggles_path
30
+ end
31
+
32
+ def new
33
+ @feature_toggle = model.new
34
+ end
35
+
36
+ def destroy
37
+ @feature_toggle = model.find_by(id: params[:id])
38
+
39
+ @feature_toggle.destroy!
40
+
41
+ redirect_to feature_toggles_path
42
+ end
43
+
44
+ private
45
+
46
+ def authorize_user!
47
+ authorize! :manage, model
48
+ end
49
+
50
+ def model
51
+ @_model ||= DefraRubyFeatures.configuration.feature_toggle_model_name.constantize
52
+ end
53
+
54
+ def feature_toggle_params
55
+ params.fetch(:feature_toggle, {}).permit(:key, :active)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyFeatures
4
+ module ApplicationHelper
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Credits: viget.com/articles/delete-in-rails-without-jquery-and-ujs/
4
+
5
+ module DeleteLinkHelper
6
+ def delete_button_to(title, url, options = {})
7
+ html_options = {
8
+ class: "delete_button_to",
9
+ method: "delete"
10
+ }.merge(options.delete(:html_options) || {})
11
+
12
+ form_for :delete, url: url, html: html_options do |f|
13
+ f.submit title, options
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,48 @@
1
+ <div class="text">
2
+ <h1 class="heading-large"><%= t(".heading") %></h1>
3
+
4
+ <table>
5
+ <thead>
6
+ <tr>
7
+ <th scope="col"><%= t(".table.key") %></th>
8
+ <th scope="col"><%= t(".table.status") %></th>
9
+ <th scope="col"></th>
10
+ </tr>
11
+ </thead>
12
+
13
+ <tbody>
14
+ <% @feature_toggles.each do |feature_toggle| %>
15
+ <tr>
16
+ <td><%= feature_toggle.key %></th>
17
+ <td>
18
+ <span class="status-tag <%= feature_toggle.active ? "status-tag--active" : "status-tag--inactive" %>">
19
+ <%= feature_toggle.active ? t(".status.enabled") : t(".status.disabled") %>
20
+ </span>
21
+ </td>
22
+
23
+ <td>
24
+ <ul class="list">
25
+ <li>
26
+ <%= form_for feature_toggle do |f| %>
27
+ <%= f.hidden_field :active, value: !feature_toggle.active %>
28
+ <%= f.submit t(".actions.toggle"), class: "button" %>
29
+ <% end %>
30
+ </li>
31
+ <li>
32
+ <%= delete_button_to t(".actions.delete"), feature_toggle_path(feature_toggle), {
33
+ data: { confirm: "Are you sure?" },
34
+ class: "button button-red"
35
+ } %>
36
+
37
+ </li>
38
+ </ul>
39
+ </td>
40
+ </tr>
41
+ <% end %>
42
+ </tbody>
43
+ </table>
44
+
45
+ <p>
46
+ <%= link_to t(".new"), new_feature_toggle_path, class: "button" %>
47
+ </p>
48
+ </div>
@@ -0,0 +1,35 @@
1
+ <div class="grid-row">
2
+ <div class="column-two-thirds">
3
+ <%= render("shared/back", back_path: feature_toggles_path) %>
4
+
5
+ <%= form_for(@feature_toggle) do |f| %>
6
+ <h1 class="heading-large">
7
+ <%= t(".heading") %>
8
+ </h1>
9
+
10
+ <div class="form-group <%= "form-group-error" if @feature_toggle.errors[:key].any? %>">
11
+ <% if @feature_toggle.errors[:key].any? %>
12
+ <span class="error-message"><%= @feature_toggle.errors[:key].join(", ") %></span>
13
+ <% end %>
14
+
15
+ <%= f.label :key, t(".fields.key"), class: "form-label" %>
16
+ <%= f.text_field :key, class: "form-control" %>
17
+ </div>
18
+
19
+ <div class="form-group <%= "form-group-error" if @feature_toggle.errors[:active].any? %>">
20
+ <% if @feature_toggle.errors[:key].any? %>
21
+ <span class="error-message"><%= @feature_toggle.errors[:key].join(", ") %></span>
22
+ <% end %>
23
+
24
+ <div class="multiple-choice">
25
+ <%= f.check_box :active %>
26
+ <%= f.label :active, t(".fields.active") %>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="form-group">
31
+ <%= f.submit t(".submit"), class: "button" %>
32
+ </div>
33
+ <% end %>
34
+ </div>
35
+ </div>
@@ -0,0 +1 @@
1
+ <%= link_to(t(".back_link"), back_path, class: "link-back") %>
@@ -0,0 +1,24 @@
1
+ en:
2
+ defra_ruby_features:
3
+ feature_toggles:
4
+ index:
5
+ heading: "Feature Toggles"
6
+ new: "New"
7
+ table:
8
+ key: "Name"
9
+ status: "Status"
10
+ actions:
11
+ toggle: "Toggle"
12
+ delete: "Delete"
13
+ status:
14
+ enabled: "Enabled"
15
+ disabled: "Disabled"
16
+ new:
17
+ heading: "Add a new Feature Toggle"
18
+ fields:
19
+ key: "Name"
20
+ active: "Active"
21
+ submit: "Save"
22
+ shared:
23
+ back:
24
+ back_link: "Back"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ DefraRubyFeatures::Engine.routes.draw do
4
+ resources :feature_toggles, except: %i[show edit], path: "feature-toggles"
5
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "defra_ruby_features/engine"
4
+
5
+ module DefraRubyFeatures
6
+ # Enable the ability to configure the gem from its host app, rather than
7
+ # reading directly from env vars. Derived from
8
+ # https://robots.thoughtbot.com/mygem-configure-block
9
+ class << self
10
+ attr_writer :configuration
11
+
12
+ def configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ # Added for testing. Without we cannot test both a config object with and
17
+ # with set values in the same rspec session
18
+ def reset_configuration
19
+ @configuration = nil
20
+ end
21
+ end
22
+
23
+ def self.configure
24
+ yield(configuration)
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyFeatures
4
+ class Configuration
5
+ attr_accessor :feature_toggle_model_name
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "configuration"
4
+
5
+ module DefraRubyFeatures
6
+ class Engine < ::Rails::Engine
7
+ isolate_namespace DefraRubyFeatures
8
+
9
+ config.generators do |g|
10
+ g.test_framework :rspec
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefraRubyFeatures
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "github_changelog_generator/task"
4
+
5
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
6
+ config.user = "DEFRA"
7
+ config.project = "defra-ruby-features"
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
4
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5
+
6
+ require_relative "config/application"
7
+
8
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ //= link_tree ../images
2
+ //= link_directory ../stylesheets .css
3
+ //= link defra_ruby_features_manifest.js