defra_ruby_features 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/LICENSE +8 -0
- data/README.md +92 -0
- data/Rakefile +40 -0
- data/app/controllers/defra_ruby_features/application_controller.rb +7 -0
- data/app/controllers/defra_ruby_features/feature_toggles_controller.rb +58 -0
- data/app/helpers/defra_ruby_features/application_helper.rb +6 -0
- data/app/helpers/delete_link_helper.rb +16 -0
- data/app/views/defra_ruby_features/feature_toggles/index.html.erb +48 -0
- data/app/views/defra_ruby_features/feature_toggles/new.html.erb +35 -0
- data/app/views/shared/_back.html.erb +1 -0
- data/config/locales/defra_ruby_features.en.yml +24 -0
- data/config/routes.rb +5 -0
- data/lib/defra_ruby_features.rb +26 -0
- data/lib/defra_ruby_features/configuration.rb +7 -0
- data/lib/defra_ruby_features/engine.rb +13 -0
- data/lib/defra_ruby_features/version.rb +5 -0
- data/lib/tasks/changelog.rake +8 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +6 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +6 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/test_controller.rb +7 -0
- data/spec/dummy/app/helpers/application_helper.rb +4 -0
- data/spec/dummy/app/javascript/packs/application.js +14 -0
- data/spec/dummy/app/jobs/application_job.rb +9 -0
- data/spec/dummy/app/models/ability.rb +9 -0
- data/spec/dummy/app/models/application_record.rb +5 -0
- data/spec/dummy/app/models/feature_toggle.rb +4 -0
- data/spec/dummy/app/models/user.rb +8 -0
- data/spec/dummy/app/views/layouts/application.html.erb +12 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +25 -0
- data/spec/dummy/config.ru +7 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/database.yml +21 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +50 -0
- data/spec/dummy/config/environments/test.rb +41 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +10 -0
- data/spec/dummy/config/initializers/assets.rb +14 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +30 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +7 -0
- data/spec/dummy/config/initializers/defra_ruby_features.rb +7 -0
- data/spec/dummy/config/initializers/devise.rb +311 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +18 -0
- data/spec/dummy/config/initializers/mime_types.rb +6 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
- data/spec/dummy/config/locales/devise.en.yml +65 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/routes.rb +7 -0
- data/spec/dummy/db/migrate/20200623125321_create_feature_toggles.rb +12 -0
- data/spec/dummy/db/migrate/20200624115316_devise_create_users.rb +44 -0
- data/spec/dummy/db/schema.rb +34 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +4579 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/development_secret.txt +1 -0
- data/spec/examples.txt +13 -0
- data/spec/factories/feature_toggle.rb +8 -0
- data/spec/factories/user.rb +11 -0
- data/spec/lib/defra_ruby_mocks/configuration_spec.rb +13 -0
- data/spec/rails_helper.rb +54 -0
- data/spec/requests/defra_ruby_features/feature_toggles_spec.rb +145 -0
- data/spec/spec_helper.rb +83 -0
- data/spec/support/database_cleaner.rb +19 -0
- data/spec/support/devise.rb +5 -0
- data/spec/support/factory_bot.rb +8 -0
- data/spec/support/migrations.rb +10 -0
- data/spec/support/pry.rb +7 -0
- data/spec/support/rails_controller_testing.rb +5 -0
- data/spec/support/simplecov.rb +17 -0
- metadata +364 -0
checksums.yaml
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# Defra Ruby Features
|
2
|
+
|
3
|
+
[](https://travis-ci.com/DEFRA/defra-ruby-features)
|
4
|
+
[](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-features)
|
5
|
+
[](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-features)
|
6
|
+
[](https://hakiri.io/github/DEFRA/defra-ruby-features/main)
|
7
|
+
[](https://badge.fury.io/rb/defra_ruby_features)
|
8
|
+
[](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.
|
data/Rakefile
ADDED
@@ -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,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,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"
|
data/config/routes.rb
ADDED
@@ -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
|
data/spec/dummy/Rakefile
ADDED
@@ -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
|