gdk-toogle 1.0.1 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b7fb4c9b37ce9f5712cc5635ff39bea2b3745179ea554dbe4b941f2a9472cab
4
- data.tar.gz: 57a6c432b93d5dc0e88c52f46c653ca26338638f99f866c57e56dc5540a21692
3
+ metadata.gz: 31e11a727853a15792bcdb3963066222752c19b30234d0dc63db07a360a44472
4
+ data.tar.gz: ddef44896dfda4944158d2e4204900b3df49d1c0c582973dc0efadc104122f57
5
5
  SHA512:
6
- metadata.gz: da4f86f837651e78dc12c7c61e2563cb8e099ff5a6a454e00226728906427243649190743cc36b46bcac34967551542afe8da58f5f5a35e5c6fd834412d434e4
7
- data.tar.gz: 807b565d41e6f131a0811fb7e4d980f1c5a0a5ad5a61c892e81f3c9e6d4c2c614fe269bfadf8087465467deca63d3297080366ad1827b4abd302a09c5fb30e21
6
+ metadata.gz: abcefb8b6457c45c10d5adb906deb4f25bc01f5202e0ac1cf17acc61da1d6d6c0ef4b32abd4aa8fbcb7e1fc00a6488d5ba0b429028d06e9649f24cbf4ce36052
7
+ data.tar.gz: d13585fdd22505d237f1166cc7d07b9c0233e66d1e6047ab7d945d68aa43dc08b09a67e8613b33298667e90c7049e5435d40e11d754756cd4ef92c4625e08cf5
data/README.md CHANGED
@@ -10,7 +10,7 @@ GitLab's `Feature` classes, so mounting it in any other Rails app won't work
10
10
 
11
11
  ### Usage within the GitLab Development Kit
12
12
 
13
- The UI is available at http://gdk.test:3000/rails/features.
13
+ The UI is available at http://gdk.test:4000/rails/features.
14
14
 
15
15
  ## Contributing
16
16
 
@@ -29,11 +29,12 @@ The dummy app used for testing includes fake in-memory versions of GitLab's
29
29
  develop the engine without a GDK:
30
30
 
31
31
  ```sh
32
+ bundle install
32
33
  cd spec/dummy
33
34
  bin/rails server
34
35
  ```
35
36
 
36
- Then open http://localhost:3000/toogle. Toggled state lives in memory and
37
+ Then open http://localhost:4000/toogle. Toggled state lives in memory and
37
38
  resets on restart.
38
39
 
39
40
  To test your changes inside a real GDK, point GitLab's `Gemfile` at your
@@ -80,7 +81,8 @@ Keep Ruby and gem versions at or below what GitLab itself runs (see
80
81
  One design goal of this little Rails engine was to keep it free of any dependencies, that GitLab uses today.
81
82
  Because, tomorrow we might decide to ditch one or the other tech we use today, and Toogle shouldn't break just because GitLab removes `sassc`, as an example (that actually happened during development).
82
83
 
83
- That's why Toogle only depends on the bare basics: `rails`, `sprockets` and `haml`.
84
+ That's why Toogle only depends on the bare basics: `rails` and `haml`.
85
+ It doesn't even use the host's asset pipeline: the engine serves its own CSS and JS files.
84
86
 
85
87
  ### Javascript
86
88
 
@@ -33,25 +33,20 @@ document.addEventListener("alpine:init", () => {
33
33
  definitionsUrl,
34
34
 
35
35
  init() {
36
- const featuresLoaded = this.fetchFeatures();
37
-
38
- const definitionsLoaded = fetch(this.definitionsUrl, {
39
- method: "GET",
40
- headers: {
41
- Accept: "application/json",
42
- },
43
- })
44
- .then((response) => response.json())
45
- .then((data) => {
46
- this.definitions = data;
47
- })
48
- .catch(showFetchError);
36
+ const loading = this.reload();
49
37
 
50
38
  if (shareName) {
51
- Promise.all([featuresLoaded, definitionsLoaded]).then(() =>
39
+ loading.then(() =>
52
40
  this.openDetails(shareName)
53
41
  );
54
42
  }
43
+
44
+ this.onReload = () => this.reload();
45
+ window.addEventListener("reload", this.onReload);
46
+ },
47
+
48
+ destroy() {
49
+ window.removeEventListener("reload", this.onReload);
55
50
  },
56
51
 
57
52
  openDetails(name) {
@@ -119,6 +114,27 @@ document.addEventListener("alpine:init", () => {
119
114
  .catch(showFetchError);
120
115
  },
121
116
 
117
+ fetchDefinitions() {
118
+ return fetch(this.definitionsUrl, {
119
+ method: "GET",
120
+ headers: {
121
+ Accept: "application/json",
122
+ },
123
+ })
124
+ .then((response) => response.json())
125
+ .then((data) => {
126
+ this.definitions = data;
127
+ })
128
+ .catch(showFetchError);
129
+ },
130
+
131
+ reload() {
132
+ const featuresLoaded = this.fetchFeatures();
133
+ const definitionsLoaded = this.fetchDefinitions();
134
+
135
+ return Promise.all([featuresLoaded, definitionsLoaded])
136
+ },
137
+
122
138
  // Animates the layout change, e.g. a row moving between the sections.
123
139
  // Only the moving row gets a view-transition-name (see the view), so the
124
140
  // browser snapshots one row, not all ~700 — keeping toggles fast no
@@ -238,15 +254,39 @@ document.addEventListener("alpine:init", () => {
238
254
  ["@click"]() {
239
255
  this.isDark = !this.isDark;
240
256
  document.cookie = `toogle-dark=${this.isDark}; SameSite=Strict`;
241
- if (this.isDark) {
242
- document.documentElement.classList.remove("light-mode");
243
- } else {
244
- document.documentElement.classList.add("light-mode");
245
- }
257
+ document.documentElement.classList.toggle("dark-mode", this.isDark);
258
+ document.documentElement.classList.toggle("light-mode", !this.isDark);
246
259
  },
247
260
  ["x-bind:title"]() {
248
261
  return `Switch to ${this.isDark ? "Light Mode" : "Dark Mode"}`;
249
262
  },
250
263
  },
251
264
  }));
265
+
266
+ Alpine.data("gitBranchWatcher", (branch, branchUrl) => ({
267
+ branch,
268
+ branchUrl,
269
+
270
+ init() {
271
+ this.$watch("branch", () => {
272
+ this.$dispatch("reload")
273
+ });
274
+
275
+ window.addEventListener("focus", () => this.fetchCurrentBranch())
276
+ },
277
+
278
+ fetchCurrentBranch() {
279
+ fetch(this.branchUrl, {
280
+ method: "GET",
281
+ headers: {
282
+ Accept: "application/json",
283
+ },
284
+ })
285
+ .then((response) => response.json())
286
+ .then((data) => {
287
+ this.branch = data;
288
+ })
289
+ .catch(showFetchError);
290
+ },
291
+ }))
252
292
  });
@@ -1,10 +1,10 @@
1
- @import "variables";
1
+ @import "variables.css";
2
2
 
3
- @import "elements";
4
- @import "layout";
5
- @import "utilities";
3
+ @import "elements.css";
4
+ @import "layout.css";
5
+ @import "utilities.css";
6
6
 
7
- @import "components/toggle";
7
+ @import "components/toggle.css";
8
8
 
9
9
  [x-cloak] {
10
10
  display: none !important;
@@ -49,8 +49,11 @@
49
49
  }
50
50
  }
51
51
 
52
+ input:checked + .handle {
53
+ background-color: var(--color-on);
54
+ }
55
+
52
56
  input:checked + .handle:before {
53
- background-color: var(--color-primary);
54
57
  transform: translateX(26px);
55
58
  }
56
59
 
@@ -15,6 +15,10 @@ body {
15
15
  position: sticky;
16
16
  top: 0;
17
17
  transition: 0.2s;
18
+
19
+ code {
20
+ color: var(--color-text-muted);
21
+ }
18
22
  }
19
23
  }
20
24
 
@@ -2,6 +2,7 @@ html {
2
2
  color-scheme: light;
3
3
 
4
4
  --hue-primary: 35;
5
+ --hue-on: 140;
5
6
  --saturation-bg: 5%;
6
7
  --lightness-bg0: 92%; /* page */
7
8
  --lightness-bg2: 97%; /* raised surfaces: inputs, badges */
@@ -11,8 +12,11 @@ html {
11
12
  --lightness-fg: 10%;
12
13
  --lightness-muted: 42%;
13
14
  --lightness-shadow: 65%;
15
+ --saturation-on: 60%; /* toggle track when on */
16
+ --lightness-on: 38%;
14
17
 
15
18
  --color-primary: hsl(var(--hue-primary), 90%, 50%);
19
+ --color-on: hsl(var(--hue-on), var(--saturation-on), var(--lightness-on));
16
20
  --color-text: hsl(var(--hue-primary), 0%, var(--lightness-fg));
17
21
  --color-text-muted: hsl(var(--hue-primary), 5%, var(--lightness-muted));
18
22
  --color-shadow: hsl(0, var(--saturation-bg), var(--lightness-shadow));
@@ -43,6 +47,7 @@ html {
43
47
  );
44
48
  }
45
49
 
50
+ /* Keep in sync with the block below */
46
51
  @media (prefers-color-scheme: dark) {
47
52
  html:not(.light-mode) {
48
53
  color-scheme: dark;
@@ -55,5 +60,23 @@ html {
55
60
  --lightness-fg: 90%;
56
61
  --lightness-muted: 60%;
57
62
  --lightness-shadow: 3%;
63
+ --saturation-on: 45%;
64
+ --lightness-on: 48%;
58
65
  }
59
66
  }
67
+
68
+ /* Keep in sync with the block above */
69
+ html.dark-mode {
70
+ color-scheme: dark;
71
+
72
+ --lightness-bg0: 8%;
73
+ --lightness-bg2: 16%;
74
+ --lightness-track: 22%;
75
+ --lightness-thumb: 70%;
76
+ --lightness-disabled: 12%;
77
+ --lightness-fg: 90%;
78
+ --lightness-muted: 60%;
79
+ --lightness-shadow: 3%;
80
+ --saturation-on: 45%;
81
+ --lightness-on: 48%;
82
+ }
@@ -0,0 +1,29 @@
1
+ module Toogle
2
+ # Serves our own CSS and JS instead of going through the host's asset
3
+ # pipeline: hosts that only serve precompiled assets, or whose web server
4
+ # answers /assets/* itself, never deliver them otherwise.
5
+ class AssetsController < ApplicationController
6
+ ROOT = Engine.root.join("app/assets")
7
+
8
+ # Only these files are served, so no path from the URL ever reaches the
9
+ # filesystem.
10
+ FILES = Dir[ROOT.join("{stylesheets,javascript}/**/*.{css,js}")]
11
+ .index_by { Pathname(_1).relative_path_from(ROOT).to_s }
12
+
13
+ # Static public files: nothing to protect, and Rails' cross-origin check
14
+ # would otherwise refuse to serve JavaScript to a <script> tag.
15
+ skip_forgery_protection
16
+
17
+ def show
18
+ file = FILES[params[:path]]
19
+ return head :not_found unless file
20
+
21
+ # Send the content, not the file: GitLab enables X-Sendfile, which hands
22
+ # file bodies to Workhorse, and Workhorse may run on another host (as in
23
+ # Caproni) where this path doesn't exist.
24
+ send_data File.read(file),
25
+ type: Mime::Type.lookup_by_extension(File.extname(file).delete(".")),
26
+ disposition: :inline
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Toogle
2
+ class BranchesController < ApplicationController
3
+ def show
4
+ render json: GitRepo.current_branch.to_json
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Toogle
2
+ class GitRepo
3
+ def self.current_branch
4
+ `git rev-parse --abbrev-ref HEAD`.chomp
5
+ end
6
+ end
7
+ end
@@ -1,5 +1,6 @@
1
1
  !!!
2
- %html{ class: ("light-mode" if request.cookies["toogle-dark"] == "false") }
2
+ - theme_class = {"true" => "dark-mode", "false" => "light-mode"}[request.cookies["toogle-dark"]]
3
+ %html{ class: theme_class }
3
4
  %head
4
5
  %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
5
6
  %meta(name="viewport" content="width=device-width, initial-scale=1.0")
@@ -10,15 +11,17 @@
10
11
  %title Feature flags
11
12
  = csrf_meta_tags
12
13
  = csp_meta_tag
13
- = stylesheet_link_tag "toogle/application", media: "all"
14
- = javascript_include_tag 'toogle/application'
14
+ %link(rel="stylesheet" media="all" href="#{asset_file_path("stylesheets/toogle/application.css", v: Toogle::VERSION)}")
15
+ %script(src="#{asset_file_path("javascript/toogle/application.js", v: Toogle::VERSION)}")
15
16
  %script(defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js")
16
17
 
17
18
  %body
18
19
  %header
19
20
  %nav.d-flex.gap
20
- = link_to main_app.root_path do
21
- = render partial: "shared/logo", formats: :svg
21
+ %div(x-data="gitBranchWatcher(#{Toogle::GitRepo.current_branch.to_json}, '#{branch_url}')")
22
+ = link_to main_app.root_path do
23
+ = render partial: "shared/logo", formats: :svg
24
+ %code(x-text="branch" title="Current git branch")
22
25
 
23
26
  %button(x-data="darkModeSwitcher" x-bind="button")
24
27
  %svg(x-show="isDark" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round")
data/config/routes.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  Toogle::Engine.routes.draw do
2
+ get "assets/*path", to: "assets#show", format: false, as: :asset_file
3
+
4
+ resource :branch, only: %i[show]
5
+
2
6
  resources :definitions, only: %i[index]
3
7
 
4
8
  # Keep this line last as it matches any URL
@@ -1,3 +1,3 @@
1
1
  module Toogle
2
- VERSION = "1.0.1"
2
+ VERSION = "1.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gdk-toogle
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Hutterer
@@ -90,7 +90,6 @@ files:
90
90
  - MIT-LICENSE
91
91
  - README.md
92
92
  - Rakefile
93
- - app/assets/config/toogle_manifest.js
94
93
  - app/assets/javascript/toogle/application.js
95
94
  - app/assets/stylesheets/toogle/application.css
96
95
  - app/assets/stylesheets/toogle/components/toggle.css
@@ -99,10 +98,13 @@ files:
99
98
  - app/assets/stylesheets/toogle/utilities.css
100
99
  - app/assets/stylesheets/toogle/variables.css
101
100
  - app/controllers/toogle/application_controller.rb
101
+ - app/controllers/toogle/assets_controller.rb
102
+ - app/controllers/toogle/branches_controller.rb
102
103
  - app/controllers/toogle/definitions_controller.rb
103
104
  - app/controllers/toogle/features_controller.rb
104
105
  - app/models/toogle/definition.rb
105
106
  - app/models/toogle/feature.rb
107
+ - app/services/toogle/git_repo.rb
106
108
  - app/views/layouts/toogle/application.html.haml
107
109
  - app/views/toogle/features/_details_button.html.haml
108
110
  - app/views/toogle/features/index.html.haml
@@ -132,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
134
  - !ruby/object:Gem::Version
133
135
  version: '0'
134
136
  requirements: []
135
- rubygems_version: 4.0.12
137
+ rubygems_version: 4.0.22
136
138
  specification_version: 4
137
139
  summary: Toogle toggles feature flags
138
140
  test_files: []
@@ -1,2 +0,0 @@
1
- //= link toogle/application.css
2
- //= link toogle/application.js