stoplight-admin 0.3.6 → 0.4.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: ea092c1ab6285d75d91b6b7582c0c521304c6aa9a417067793224a4b31cbc83f
4
- data.tar.gz: d31a8c40d3adf0cf517194b3ffdb96f4c1d425d7b1655fe8d2d7717051d08104
3
+ metadata.gz: c61065004c1c254f44f10415c852818e962b82ce7d2b7e9d451bd2b998487144
4
+ data.tar.gz: 3162abbe1ad6f0e0aad93fbade2b35fae6e7df7ddfa2b61a161bc7d339da9c9e
5
5
  SHA512:
6
- metadata.gz: 3bf410421247546b33d42ba22eb4180b92027713806a8bdd55c6f93adcdf07bf5771d7a10c7ca0706e4cf55828d160fcae5da1326352ff5078b115742a28108e
7
- data.tar.gz: e8e8a55b481d9ac832206596afc412ff2f72cb127befdb6ffc0b776393c805cab25ac96fdf41f2d4df1c8ce6ceacd265cc5d98762df845c6e22cb616c1745b2a
6
+ metadata.gz: b53b0093a1ac59c7d21352a6ea0ecd7ac3de163d000ab701cf92eb19250f9fdf803cccb1df30b6bf55d721ff12a9ca25d28239ce16c223ded9e9e42f99cf72b3
7
+ data.tar.gz: e709637df523e89529e507159bff0bffc63b364539ae1e2fe7873b5525f2aded28833d9d45ee6bfc26199b58f8115c43edf0b3fcb283b68487cf444d3becbe50
data/README.md CHANGED
@@ -6,6 +6,9 @@ A simple administration interface for [stoplight][2]. Monitor the
6
6
  status, failures, and invocations of your stoplights. Change
7
7
  stoplight colors, or lock them in either red or green state.
8
8
 
9
+ The stoplight-admin version 0.4 and above is only compatible with Stoplight 4.1. If you have Stoplight version
10
+ below 4.0, use [stoplight-admin 0.3.6].
11
+
9
12
  ## Configuration
10
13
 
11
14
  This project is packaged as a Ruby gem so that you can easily embed it
@@ -32,9 +35,19 @@ example using a local Redis data store:
32
35
  ``` rb
33
36
  # app.rb
34
37
 
38
+ require 'bundler/inline'
39
+
40
+ gemfile do
41
+ source 'https://rubygems.org'
42
+
43
+ gem 'redis'
44
+ gem 'stoplight-admin'
45
+ gem 'webrick'
46
+ end
47
+
35
48
  require 'redis'
36
49
  require 'sinatra'
37
- require 'sinatra/stoplight_admin'
50
+ require 'stoplight_admin/application'
38
51
 
39
52
  redis = Redis.new(url: 'redis://localhost:6379')
40
53
  set :data_store, Stoplight::DataStore::Redis.new(redis)
@@ -65,10 +78,10 @@ Add something like this to your `config/routes.rb`:
65
78
 
66
79
  ``` rb
67
80
  require 'redis'
68
- require 'sinatra/stoplight_admin'
81
+ require 'stoplight_admin/application'
69
82
 
70
- class StoplightAdmin < Sinatra::Base
71
- register Sinatra::StoplightAdmin
83
+ class StoplightAdminApp < Sinatra::Base
84
+ register StoplightAdmin::Application
72
85
 
73
86
  redis = Redis.new # Uses REDIS_URL environment variable.
74
87
  data_store = Stoplight::DataStore::Redis.new(redis)
@@ -76,7 +89,7 @@ class StoplightAdmin < Sinatra::Base
76
89
  end
77
90
 
78
91
  Rails.application.routes.draw do
79
- mount StoplightAdmin => '/stoplights'
92
+ mount StoplightAdminApp => '/stoplights'
80
93
  end
81
94
  ```
82
95
 
@@ -93,3 +106,4 @@ Stoplight is brought to you by [@camdez][4] and [@tfausak][5] from
93
106
  [6]: https://github.com/OrgSync
94
107
  [7]: https://badge.fury.io/rb/stoplight-admin.svg
95
108
  [8]: https://rubygems.org/gems/stoplight-admin
109
+ [stoplight-admin 0.3.6]: https://github.com/bolshakov/stoplight-admin/releases/tag/v0.3.6
@@ -1,161 +1,4 @@
1
- # coding: utf-8
1
+ require "stoplight_admin/application"
2
2
 
3
- require 'haml'
4
- require 'sinatra/base'
5
- require 'sinatra/json'
6
- require 'stoplight'
7
-
8
- module Sinatra
9
- module StoplightAdmin
10
- module Helpers
11
- COLORS = [
12
- GREEN = Stoplight::Color::GREEN,
13
- YELLOW = Stoplight::Color::YELLOW,
14
- RED = Stoplight::Color::RED
15
- ].freeze
16
-
17
- def data_store
18
- return @data_store if defined?(@data_store)
19
- @data_store = Stoplight::Light.default_data_store = settings.data_store
20
- end
21
-
22
- def lights
23
- data_store
24
- .names
25
- .map { |name| light_info(name) }
26
- .sort_by { |light| light_sort_key(light) }
27
- end
28
-
29
- def light_info(light)
30
- l = Stoplight::Light.new(light) {}
31
- color = l.color
32
- failures, state = l.data_store.get_all(l)
33
-
34
- {
35
- name: light,
36
- color: color,
37
- failures: failures,
38
- locked: locked?(state)
39
- }
40
- end
41
-
42
- def light_sort_key(light)
43
- [-COLORS.index(light[:color]),
44
- light[:name]]
45
- end
46
-
47
- def locked?(state)
48
- [Stoplight::State::LOCKED_GREEN,
49
- Stoplight::State::LOCKED_RED]
50
- .include?(state)
51
- end
52
-
53
- def stat_params(ls)
54
- h = {
55
- count_red: 0, count_yellow: 0, count_green: 0,
56
- percent_red: 0, percent_yellow: 0, percent_green: 0
57
- }
58
- return h if (size = ls.size).zero?
59
-
60
- h[:count_red] = ls.count { |l| l[:color] == RED }
61
- h[:count_yellow] = ls.count { |l| l[:color] == YELLOW }
62
- h[:count_green] = size - h[:count_red] - h[:count_yellow]
63
-
64
- h[:percent_red] = (100.0 * h[:count_red] / size).ceil
65
- h[:percent_yellow] = (100.0 * h[:count_yellow] / size).ceil
66
- h[:percent_green] = 100.0 - h[:percent_red] - h[:percent_yellow]
67
-
68
- h
69
- end
70
-
71
- def lock(light)
72
- l = Stoplight::Light.new(light) {}
73
- new_state =
74
- case l.color
75
- when Stoplight::Color::GREEN
76
- Stoplight::State::LOCKED_GREEN
77
- else
78
- Stoplight::State::LOCKED_RED
79
- end
80
-
81
- data_store.set_state(l, new_state)
82
- end
83
-
84
- def unlock(light)
85
- l = Stoplight::Light.new(light) {}
86
- data_store.set_state(l, Stoplight::State::UNLOCKED)
87
- end
88
-
89
- def green(light)
90
- l = Stoplight::Light.new(light) {}
91
- if data_store.get_state(l) == Stoplight::State::LOCKED_RED
92
- new_state = Stoplight::State::LOCKED_GREEN
93
- data_store.set_state(l, new_state)
94
- end
95
-
96
- data_store.clear_failures(l)
97
- end
98
-
99
- def red(light)
100
- l = Stoplight::Light.new(light) {}
101
- data_store.set_state(l, Stoplight::State::LOCKED_RED)
102
- end
103
-
104
- def with_lights
105
- [*params[:names]]
106
- .map { |l| CGI.unescape(l) }
107
- .each { |l| yield(l) }
108
- end
109
- end
110
-
111
- def self.registered(app)
112
- app.helpers StoplightAdmin::Helpers
113
-
114
- app.set :data_store, nil
115
- app.set :views, File.join(File.dirname(__FILE__), 'views')
116
-
117
- app.get '/' do
118
- ls = lights
119
- stats = stat_params(ls)
120
-
121
- haml :index, locals: stats.merge(lights: ls)
122
- end
123
-
124
- app.get '/stats' do
125
- ls = lights
126
- stats = stat_params(ls)
127
-
128
- json({stats: stats, lights: ls})
129
- end
130
-
131
- app.post '/lock' do
132
- with_lights { |l| lock(l) }
133
- redirect to('/')
134
- end
135
-
136
- app.post '/unlock' do
137
- with_lights { |l| unlock(l) }
138
- redirect to('/')
139
- end
140
-
141
- app.post '/green' do
142
- with_lights { |l| green(l) }
143
- redirect to('/')
144
- end
145
-
146
- app.post '/red' do
147
- with_lights { |l| red(l) }
148
- redirect to('/')
149
- end
150
-
151
- app.post '/green_all' do
152
- data_store.names
153
- .reject { |l| Stoplight::Light.new(l) {}.color == Stoplight::Color::GREEN }
154
- .each { |l| green(l) }
155
- redirect to('/')
156
- end
157
- end
158
- end
159
-
160
- register StoplightAdmin
161
- end
3
+ warn "[DEPRECATED] requiring `sinatra/stoplight_admin` is deprecated. " \
4
+ "Please require `stoplight_admin/application` instead."
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # @abstract
6
+ class Action
7
+ # @!attribute lights_repository
8
+ # @return [StoplightAdmin::LightsRepository]
9
+ attr_reader :lights_repository
10
+ private :lights_repository
11
+
12
+ # @return lights_repository [StoplightAdmin::LightsRepository]
13
+ def initialize(lights_repository:)
14
+ @lights_repository = lights_repository
15
+ end
16
+
17
+ def call(params)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ class Home < Action
6
+ # @!attribute lights_stats
7
+ # @return [Class<StoplightAdmin::LightsStats>]
8
+ attr_reader :lights_stats
9
+ private :lights_stats
10
+
11
+ # @param lights_stats [Class<StoplightAdmin::LightsStats>]
12
+ def initialize(lights_stats:, **deps)
13
+ super(**deps)
14
+ @lights_stats = lights_stats
15
+ end
16
+
17
+ # @return [(StoplightAdmin::LightsRepository::Light)]
18
+ def call(*)
19
+ lights = lights_repository.all
20
+ stats = lights_stats.call(lights)
21
+ [lights, stats]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # This action locks light
6
+ class Lock < Action
7
+ # @param params [Hash] query parameters
8
+ # @return [void]
9
+ def call(params)
10
+ light_names(params).each do |name|
11
+ lights_repository.lock(name)
12
+ end
13
+ end
14
+
15
+ private def light_names(params)
16
+ Array(params[:names])
17
+ .map { |name| CGI.unescape(name) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # This action locks all lights green
6
+ class LockAllGreen < Action
7
+ # @return [void]
8
+ def call(*)
9
+ lights_repository
10
+ .with_color(RED, YELLOW)
11
+ .map(&:name)
12
+ .each { |name| lights_repository.lock(name, GREEN) }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # This action locks light with the specific name green
6
+ class LockGreen < Action
7
+ # @param params [Hash] query parameters
8
+ # @return [void]
9
+ def call(params)
10
+ light_names(params).each do |name|
11
+ lights_repository.lock(name, GREEN)
12
+ end
13
+ end
14
+
15
+ private def light_names(params)
16
+ Array(params[:names])
17
+ .map { |name| CGI.unescape(name) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # This action locks light with the specific name red
6
+ class LockRed < Action
7
+ # @param params [Hash] query parameters
8
+ # @return [void]
9
+ def call(params)
10
+ light_names(params).each do |name|
11
+ lights_repository.lock(name, RED)
12
+ end
13
+ end
14
+
15
+ private def light_names(params)
16
+ Array(params[:names])
17
+ .map { |name| CGI.unescape(name) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ class Stats < Home
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Actions
5
+ # This action unlocks light
6
+ class Unlock < Action
7
+ # @param params [Hash] query parameters
8
+ # @return [void]
9
+ def call(params)
10
+ light_names(params).each do |name|
11
+ lights_repository.unlock(name)
12
+ end
13
+ end
14
+
15
+ private def light_names(params)
16
+ Array(params[:names])
17
+ .map { |name| CGI.unescape(name) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ module Application
5
+ module Helpers
6
+ COLORS = [
7
+ GREEN = Stoplight::Color::GREEN,
8
+ YELLOW = Stoplight::Color::YELLOW,
9
+ RED = Stoplight::Color::RED
10
+ ].freeze
11
+
12
+ def dependencies
13
+ StoplightAdmin::Dependencies.new(data_store: settings.data_store)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sinatra"
4
+ require "sinatra/base"
5
+ require "sinatra/json"
6
+ require "stoplight_admin"
7
+
8
+ module StoplightAdmin
9
+ module Application
10
+ def self.registered(app)
11
+ app.helpers Application::Helpers
12
+
13
+ app.set :data_store, nil
14
+ app.set :views, File.join(File.dirname(__FILE__), "views")
15
+
16
+ app.get "/" do
17
+ lights, stats = dependencies.home_action.call
18
+
19
+ erb :index, locals: stats.merge(lights: lights)
20
+ end
21
+
22
+ app.get "/stats" do
23
+ lights, stats = dependencies.stats_action.call
24
+
25
+ json({stats: stats, lights: lights.map(&:as_json)})
26
+ end
27
+
28
+ app.post "/lock" do
29
+ dependencies.lock_action.call(params)
30
+
31
+ redirect to("/")
32
+ end
33
+
34
+ app.post "/unlock" do
35
+ dependencies.unlock_action.call(params)
36
+
37
+ redirect to("/")
38
+ end
39
+
40
+ app.post "/green" do
41
+ dependencies.green_action.call(params)
42
+
43
+ redirect to("/")
44
+ end
45
+
46
+ app.post "/red" do
47
+ dependencies.red_action.call(params)
48
+
49
+ redirect to("/")
50
+ end
51
+
52
+ app.post "/green_all" do
53
+ dependencies.green_all_action.call
54
+
55
+ redirect to("/")
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ StoplightAdmin.loader!.eager_load
62
+
63
+ register StoplightAdmin::Application
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ class Dependencies
5
+ # @!attribute data_store
6
+ # @return [Stoplight::DataStore::Base]
7
+ attr_reader :data_store
8
+ private :data_store
9
+
10
+ # @param data_store [Stoplight::DataStore::Base]
11
+ def initialize(data_store:)
12
+ @data_store = data_store
13
+ end
14
+
15
+ # @return [StoplightAdmin::LightsRepository]
16
+ def lights_repository
17
+ StoplightAdmin::LightsRepository.new(data_store: data_store)
18
+ end
19
+
20
+ # @return [StoplightAdmin::Actions::Home]
21
+ def home_action
22
+ StoplightAdmin::Actions::Home.new(
23
+ lights_repository: lights_repository,
24
+ lights_stats: StoplightAdmin::LightsStats
25
+ )
26
+ end
27
+
28
+ # @return [StoplightAdmin::Actions::Stats]
29
+ def stats_action
30
+ StoplightAdmin::Actions::Stats.new(
31
+ lights_repository: lights_repository,
32
+ lights_stats: StoplightAdmin::LightsStats
33
+ )
34
+ end
35
+
36
+ # @return [StoplightAdmin::Actions::Lock]
37
+ def lock_action
38
+ StoplightAdmin::Actions::Lock.new(lights_repository: lights_repository)
39
+ end
40
+
41
+ # @return [StoplightAdmin::Actions::Unlock]
42
+ def unlock_action
43
+ StoplightAdmin::Actions::Unlock.new(lights_repository: lights_repository)
44
+ end
45
+
46
+ # @return [StoplightAdmin::Actions::LockGreen]
47
+ def green_action
48
+ StoplightAdmin::Actions::LockGreen.new(lights_repository: lights_repository)
49
+ end
50
+
51
+ # @return [StoplightAdmin::Actions::LockRed]
52
+ def red_action
53
+ StoplightAdmin::Actions::LockRed.new(lights_repository: lights_repository)
54
+ end
55
+
56
+ # @return [StoplightAdmin::Actions::LockAllGreen]
57
+ def green_all_action
58
+ StoplightAdmin::Actions::LockAllGreen.new(lights_repository: lights_repository)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ class LightsRepository
5
+ class Light
6
+ COLORS = [
7
+ GREEN = Stoplight::Color::GREEN,
8
+ YELLOW = Stoplight::Color::YELLOW,
9
+ RED = Stoplight::Color::RED
10
+ ].freeze
11
+
12
+ # @!attribute name
13
+ # @return [String]
14
+ attr_reader :name
15
+
16
+ # @!attribute color
17
+ # @return [String]
18
+ attr_reader :color
19
+
20
+ # @!attribute state
21
+ # @return [String]
22
+ attr_reader :state
23
+
24
+ # @!attribute failures
25
+ # @return [<Stoplight::Failure>]
26
+ attr_reader :failures
27
+
28
+ # @param name [String]
29
+ # @param color [String]
30
+ # @param state [String]
31
+ # @param failures [<Stoplight::Failure>]
32
+ def initialize(name:, color:, state:, failures:)
33
+ @name = name
34
+ @color = color
35
+ @state = state
36
+ @failures = failures
37
+ end
38
+
39
+ # @return [Boolean]
40
+ def locked?
41
+ !unlocked?
42
+ end
43
+
44
+ # @return [Boolean]
45
+ def unlocked?
46
+ state == Stoplight::State::UNLOCKED
47
+ end
48
+
49
+ def as_json
50
+ {
51
+ name: name,
52
+ color: color,
53
+ failures: failures,
54
+ locked: locked?
55
+ }
56
+ end
57
+
58
+ # @return [Array]
59
+ def default_sort_key
60
+ [-COLORS.index(color), name]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ class LightsRepository
5
+ # @!attribute data_store
6
+ # @return [Stoplight::DataStore::Base]
7
+ attr_reader :data_store
8
+ private :data_store
9
+
10
+ # @param data_store [Stoplight::DataStore::Base]
11
+ def initialize(data_store:)
12
+ @data_store = data_store
13
+ end
14
+
15
+ # @return [<StoplightAdmin::LightsRepository::Light>]
16
+ def all
17
+ data_store
18
+ .names
19
+ .map { |name| load_light(name) }
20
+ .sort_by(&:default_sort_key)
21
+ end
22
+
23
+ # @param colors <String>] colors name
24
+ # @return [<StoplightAdmin::LightsRepository::Light>] lights with the requested colors
25
+ #
26
+ def with_color(*colors)
27
+ requested_colors = Array(colors)
28
+
29
+ all.select do |light|
30
+ requested_colors.include?(light.color)
31
+ end
32
+ end
33
+
34
+ # @param name [String] locks light by its name
35
+ # @param color [String, nil] locks to this color. When nil is given, locks to the current
36
+ # color
37
+ # @return [void]
38
+ def lock(name, color = nil)
39
+ light = build_light(name)
40
+
41
+ case color || light.color
42
+ when Stoplight::Color::GREEN
43
+ light.lock(Stoplight::Color::GREEN)
44
+ else
45
+ light.lock(Stoplight::Color::RED)
46
+ end
47
+ end
48
+
49
+ # @param name [String] unlocks light by its name
50
+ # @return [void]
51
+ def unlock(name)
52
+ build_light(name).unlock
53
+ end
54
+
55
+ private def load_light(name)
56
+ light = build_light(name)
57
+ failures, state = data_store.get_all(light)
58
+
59
+ Light.new(
60
+ name: name,
61
+ color: light.color,
62
+ state: state,
63
+ failures: failures
64
+ )
65
+ end
66
+
67
+ private def build_light(name)
68
+ Stoplight(name).with_data_store(data_store).build
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoplightAdmin
4
+ class LightsStats
5
+ EMPTY_STATS = {
6
+ count_red: 0, count_yellow: 0, count_green: 0,
7
+ percent_red: 0, percent_yellow: 0, percent_green: 0
8
+ }.freeze
9
+
10
+ # @!attribute lights
11
+ # @return [<StoplightAdmin::LightsRepository::Light>]
12
+ attr_reader :lights
13
+ private :lights
14
+
15
+ class << self
16
+ def call(lights)
17
+ new(lights).call
18
+ end
19
+ end
20
+
21
+ # @param lights [<StoplightAdmin::LightsRepository::Light>]
22
+ def initialize(lights)
23
+ @lights = lights
24
+ end
25
+
26
+ def call
27
+ return EMPTY_STATS if size.zero?
28
+
29
+ EMPTY_STATS.merge(
30
+ count_red: count_red,
31
+ count_yellow: count_yellow,
32
+ count_green: count_green,
33
+ percent_red: percent_red,
34
+ percent_yellow: percent_yellow,
35
+ percent_green: percent_green
36
+ )
37
+ end
38
+
39
+ private def count_red
40
+ count_lights(RED)
41
+ end
42
+
43
+ private def percent_red
44
+ percent_lights(RED)
45
+ end
46
+
47
+ private def count_green
48
+ count_lights(GREEN)
49
+ end
50
+
51
+ private def percent_green
52
+ percent_lights(GREEN)
53
+ end
54
+
55
+ private def count_yellow
56
+ count_lights(YELLOW)
57
+ end
58
+
59
+ private def percent_yellow
60
+ percent_lights(YELLOW)
61
+ end
62
+
63
+ private def count_lights(color)
64
+ lights.count { |l| l.color == color }
65
+ end
66
+
67
+ private def percent_lights(color)
68
+ (100 * count_lights(color).fdiv(size)).ceil
69
+ end
70
+
71
+ private def size
72
+ lights.size
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,120 @@
1
+ <div class="jumbotron hidden-xs hidden-sm">
2
+ <h1>&#x1f6a6; All of the Lights</h1>
3
+ <p>Cop lights, flashlights, spotlights, strobe lights&hellip;</p>
4
+ </div>
5
+
6
+ <div class="main">
7
+ <% if lights.empty? %>
8
+ <div class="alert alert-warning" role="alert">
9
+ <strong>No lights found.</strong>
10
+ Ensure that your Stoplight data store is properly configured and that your Stoplight blocks have been run.
11
+ </div>
12
+ <% else %>
13
+ <div class="well">
14
+ <form method="post" action="<%= url('/green_all') %>">
15
+ <button class="btn-lg btn btn-success"
16
+ type="submit"
17
+ <%= count_red + count_yellow > 0 ? nil : "disabled" %>>
18
+ Greenify All
19
+ </button>
20
+ </form>
21
+ </div>
22
+
23
+ <div class="progress">
24
+ <div class="progress-bar progress-bar-danger" style="width: <%= percent_red %>%">
25
+ <span><%= count_red %></span>
26
+ </div>
27
+ <div class="progress-bar progress-bar-warning" style="width: <%= percent_yellow %>%">
28
+ <span><%= count_yellow %></span>
29
+ </div>
30
+ <div class="progress-bar progress-bar-success" style="width: <%= percent_green %>%">
31
+ <span><%= count_green %></span>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="lights">
36
+ <table class="table table-hover">
37
+ <thead class="hidden-xs">
38
+ <tr>
39
+ <th>Status</th>
40
+ <th>
41
+ <abbr title="Locked lights will not change color automatically.">Locked?</abbr>
42
+ </th>
43
+ <th>Name</th>
44
+ <th>
45
+ <abbr title="Exceptions which caused the light to turn red.">Failures</abbr>
46
+ </th>
47
+ </tr>
48
+ </thead>
49
+ <tbody>
50
+ <% lights.each do |l| %>
51
+ <tr>
52
+ <td class="indicator">
53
+ <% if l.color == GREEN %>
54
+ <form method="post" action="<%= url('/red') %>">
55
+ <input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
56
+ <button type="submit" class="btn btn-success">
57
+ <span>G</span><span class="hidden-xs">REEN</span>
58
+ </button>
59
+ </form>
60
+ <% elsif l.color == YELLOW %>
61
+ <form method="post" action="<%= url('/green') %>">
62
+ <input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
63
+ <button type="submit" class="btn btn-warning">
64
+ <span>Y</span><span class="hidden-xs">ELLOW</span>
65
+ </button>
66
+ </form>
67
+ <% else %>
68
+ <form method="post" action="<%= url('/green') %>">
69
+ <input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
70
+ <button type="submit" class="btn btn-danger">
71
+ <span>R</span><span class="hidden-xs">ED</span>
72
+ </button>
73
+ </form>
74
+ <% end %>
75
+ </td>
76
+ <td class="locked">
77
+ <% if l.locked? %>
78
+ <form method="post" action="<%= url('/unlock') %>">
79
+ <input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
80
+ <button type="submit" class="btn btn-link">
81
+ <span class="glyphicon glyphicon-lock"></span>
82
+ </button>
83
+ </form>
84
+ <% else %>
85
+ <form method="post" action="<%= url('/lock') %>">
86
+ <input type="hidden" name="names" value="<%= ERB::Util.html_escape(l.name) %>">
87
+ <button type="submit" class="btn btn-link">
88
+ <span class="glyphicon glyphicon-minus"></span>
89
+ </button>
90
+ </form>
91
+ <% end %>
92
+ </td>
93
+ <td class="name">
94
+ <%= l.name %>
95
+ </td>
96
+ <td class="failures">
97
+ <% if l.failures %>
98
+ <ul>
99
+ <% l.failures.each do |failure| %>
100
+ <li>
101
+ <span class="error">
102
+ <%= ERB::Util.html_escape(failure.error_class) %>
103
+ \:
104
+ <%= ERB::Util.html_escape(failure.error_message) %>
105
+ </span>
106
+ <span class="timestamp">
107
+ <%= ERB::Util.html_escape(failure.time) %>
108
+ </span>
109
+ </li>
110
+ <% end %>
111
+ </ul>
112
+ <% end %>
113
+ </td>
114
+ </tr>
115
+ <% end %>
116
+ </tbody>
117
+ </table>
118
+ </div>
119
+ <% end %>
120
+ </div>
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+
7
+ <title>Stoplight Admin</title>
8
+
9
+ <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
10
+ <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css">
11
+ <link rel="shortcut icon" href="//i.imgur.com/qvfoNuh.png">
12
+
13
+ <style>
14
+ td.indicator {
15
+ font-size: 200%;
16
+ }
17
+
18
+ td.name {
19
+ font-weight: bold;
20
+ }
21
+
22
+ td.locked button {
23
+ font-size: 200%;
24
+ }
25
+
26
+ td.failures span.error {
27
+ font-weight: bold;
28
+ }
29
+
30
+ td.failures ul {
31
+ padding: 0;
32
+ list-style-type: none;
33
+ }
34
+
35
+ .btn-group form {
36
+ display: inline-block;
37
+ }
38
+ </style>
39
+ </head>
40
+ <body>
41
+ <div class="navbar navbar-inverse navbar-static-top" role="navigation">
42
+ <div class="container">
43
+ <div class="navbar-header">
44
+ <a class="navbar-brand" href="<%= url('/') %>">Stoplight Admin</a>
45
+ </div>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="container" role="main">
50
+ <%= yield %>
51
+ </div>
52
+
53
+ <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
54
+ </body>
55
+ </html>
@@ -0,0 +1,21 @@
1
+ require "stoplight"
2
+ require "zeitwerk"
3
+
4
+ module StoplightAdmin
5
+ COLORS = [
6
+ GREEN = Stoplight::Color::GREEN,
7
+ YELLOW = Stoplight::Color::YELLOW,
8
+ RED = Stoplight::Color::RED
9
+ ].freeze
10
+
11
+ class << self
12
+ def loader!
13
+ @loader ||= Zeitwerk::Loader.for_gem.tap do |loader|
14
+ loader.ignore("#{__dir__}/sinatra")
15
+ loader.setup
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ StoplightAdmin.loader!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stoplight-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Desautels
@@ -9,36 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-27 00:00:00.000000000 Z
12
+ date: 2023-08-23 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: haml
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '5.0'
21
- type: :runtime
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - "~>"
26
- - !ruby/object:Gem::Version
27
- version: '5.0'
28
- - !ruby/object:Gem::Dependency
29
- name: sinatra-contrib
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - "~>"
33
- - !ruby/object:Gem::Version
34
- version: 2.2.3
35
- type: :runtime
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "~>"
40
- - !ruby/object:Gem::Version
41
- version: 2.2.3
42
14
  - !ruby/object:Gem::Dependency
43
15
  name: redis
44
16
  requirement: !ruby/object:Gem::Requirement
@@ -54,75 +26,53 @@ dependencies:
54
26
  - !ruby/object:Gem::Version
55
27
  version: '3.2'
56
28
  - !ruby/object:Gem::Dependency
57
- name: stoplight
29
+ name: zeitwerk
58
30
  requirement: !ruby/object:Gem::Requirement
59
31
  requirements:
60
32
  - - ">="
61
33
  - !ruby/object:Gem::Version
62
- version: '1.4'
34
+ version: '0'
63
35
  type: :runtime
64
36
  prerelease: false
65
37
  version_requirements: !ruby/object:Gem::Requirement
66
38
  requirements:
67
39
  - - ">="
68
40
  - !ruby/object:Gem::Version
69
- version: '1.4'
41
+ version: '0'
70
42
  - !ruby/object:Gem::Dependency
71
- name: bundler
43
+ name: sinatra-contrib
72
44
  requirement: !ruby/object:Gem::Requirement
73
45
  requirements:
74
- - - "~>"
75
- - !ruby/object:Gem::Version
76
- version: '2.4'
77
- type: :development
78
- prerelease: false
79
- version_requirements: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - "~>"
46
+ - - ">="
82
47
  - !ruby/object:Gem::Version
83
- version: '2.4'
84
- - !ruby/object:Gem::Dependency
85
- name: pry-byebug
86
- requirement: !ruby/object:Gem::Requirement
87
- requirements:
88
- - - "~>"
48
+ version: 2.2.3
49
+ - - "<"
89
50
  - !ruby/object:Gem::Version
90
- version: 3.10.0
91
- type: :development
51
+ version: 3.2.0
52
+ type: :runtime
92
53
  prerelease: false
93
54
  version_requirements: !ruby/object:Gem::Requirement
94
55
  requirements:
95
- - - "~>"
96
- - !ruby/object:Gem::Version
97
- version: 3.10.0
98
- - !ruby/object:Gem::Dependency
99
- name: rake
100
- requirement: !ruby/object:Gem::Requirement
101
- requirements:
102
- - - "~>"
56
+ - - ">="
103
57
  - !ruby/object:Gem::Version
104
- version: '13.0'
105
- type: :development
106
- prerelease: false
107
- version_requirements: !ruby/object:Gem::Requirement
108
- requirements:
109
- - - "~>"
58
+ version: 2.2.3
59
+ - - "<"
110
60
  - !ruby/object:Gem::Version
111
- version: '13.0'
61
+ version: 3.2.0
112
62
  - !ruby/object:Gem::Dependency
113
- name: rspec
63
+ name: stoplight
114
64
  requirement: !ruby/object:Gem::Requirement
115
65
  requirements:
116
- - - "~>"
66
+ - - ">="
117
67
  - !ruby/object:Gem::Version
118
- version: 3.12.0
119
- type: :development
68
+ version: '4.1'
69
+ type: :runtime
120
70
  prerelease: false
121
71
  version_requirements: !ruby/object:Gem::Requirement
122
72
  requirements:
123
- - - "~>"
73
+ - - ">="
124
74
  - !ruby/object:Gem::Version
125
- version: 3.12.0
75
+ version: '4.1'
126
76
  description: A simple administration interface for Stoplight.
127
77
  email:
128
78
  - camdez@gmail.com
@@ -134,8 +84,23 @@ files:
134
84
  - LICENSE.md
135
85
  - README.md
136
86
  - lib/sinatra/stoplight_admin.rb
137
- - lib/sinatra/views/index.haml
138
- - lib/sinatra/views/layout.haml
87
+ - lib/stoplight_admin.rb
88
+ - lib/stoplight_admin/actions/action.rb
89
+ - lib/stoplight_admin/actions/home.rb
90
+ - lib/stoplight_admin/actions/lock.rb
91
+ - lib/stoplight_admin/actions/lock_all_green.rb
92
+ - lib/stoplight_admin/actions/lock_green.rb
93
+ - lib/stoplight_admin/actions/lock_red.rb
94
+ - lib/stoplight_admin/actions/stats.rb
95
+ - lib/stoplight_admin/actions/unlock.rb
96
+ - lib/stoplight_admin/application.rb
97
+ - lib/stoplight_admin/application/helpers.rb
98
+ - lib/stoplight_admin/dependencies.rb
99
+ - lib/stoplight_admin/lights_repository.rb
100
+ - lib/stoplight_admin/lights_repository/light.rb
101
+ - lib/stoplight_admin/lights_stats.rb
102
+ - lib/stoplight_admin/views/index.erb
103
+ - lib/stoplight_admin/views/layout.erb
139
104
  homepage: https://github.com/bolshakov/stoplight-admin
140
105
  licenses:
141
106
  - MIT
@@ -148,14 +113,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
113
  requirements:
149
114
  - - ">="
150
115
  - !ruby/object:Gem::Version
151
- version: 2.5.0
116
+ version: 3.0.0
152
117
  required_rubygems_version: !ruby/object:Gem::Requirement
153
118
  requirements:
154
119
  - - ">="
155
120
  - !ruby/object:Gem::Version
156
121
  version: '0'
157
122
  requirements: []
158
- rubygems_version: 3.3.7
123
+ rubygems_version: 3.4.18
159
124
  signing_key:
160
125
  specification_version: 4
161
126
  summary: A simple administration interface for Stoplight.
@@ -1,79 +0,0 @@
1
- .jumbotron.hidden-xs.hidden-sm
2
- %h1 &#x1f6a6; All of the Lights
3
- %p Cop lights, flashlights, spotlights, strobe lights&hellip;
4
-
5
- .main
6
- - if lights.empty?
7
- .alert.alert-warning{role: 'alert'}
8
- %strong No lights found.
9
- Ensure that your Stoplight data store is properly configured and that your Stoplight blocks have been run.
10
- - else
11
- .well
12
- %form{method: 'post', action: url('/green_all')}
13
- %button.btn-lg.btn.btn-success{type: 'submit', disabled: count_red + count_yellow > 0 ? nil : 'disabled'} Greenify All
14
-
15
- .progress
16
- .progress-bar.progress-bar-danger{style: "width: #{percent_red}%"}
17
- .span= count_red
18
- .progress-bar.progress-bar-warning{style: "width: #{percent_yellow}%"}
19
- .span= count_yellow
20
- .progress-bar.progress-bar-success{style: "width: #{percent_green}%"}
21
- .span= count_green
22
-
23
- .lights
24
- %table.table.table-hover
25
- %thead.hidden-xs
26
- %tr
27
- %th Status
28
- %th
29
- %abbr{title: 'Locked lights will not change color automatically.'} Locked?
30
- %th Name
31
- %th
32
- %abbr{title: 'Exceptions which caused the light to turn red.'} Failures
33
- %tbody
34
- - lights.each do |l|
35
- %tr
36
- %td.indicator
37
- - if l[:color] == GREEN
38
- %form{method: 'post', action: url('/red')}
39
- %input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
40
- %button{type: 'submit', class: 'btn btn-success'}
41
- G
42
- %span.hidden-xs> REEN
43
- - elsif l[:color] == YELLOW
44
- %form{method: 'post', action: url('/green')}
45
- %input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
46
- %button{type: 'submit', class: 'btn btn-warning'}
47
- Y
48
- %span.hidden-xs> ELLOW
49
- - else
50
- %form{method: 'post', action: url('/green')}
51
- %input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
52
- %button{type: 'submit', class: 'btn btn-danger'}
53
- R
54
- %span.hidden-xs> ED
55
- %td.locked
56
- - if l[:locked]
57
- %form{method: 'post', action: url('/unlock')}
58
- %input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
59
- %button{type: 'submit', class: 'btn btn-link'}
60
- %span{class: 'glyphicon glyphicon-lock'}
61
- - else
62
- %form{method: 'post', action: url('/lock')}
63
- %input{type: 'hidden', name: 'names', value: CGI.escape(l[:name])}
64
- %button{type: 'submit', class: 'btn btn-link'}
65
- %span{class: 'glyphicon glyphicon-minus'}
66
- %td.name
67
- = l[:name]
68
- %td.failures
69
- - if l[:failures]
70
- %ul
71
- - l[:failures].each do |failure|
72
- %li
73
- %span.error
74
- &= failure.error_class
75
- \:
76
- &= failure.error_message
77
- @
78
- %span.timestamp
79
- &= failure.time
@@ -1,48 +0,0 @@
1
- !!! 5
2
- %html{lang: 'en'}
3
- %head
4
- %meta{charset: 'utf-8'}
5
- %meta{name: 'viewport', content: 'width=device-width, initial-scale=1'}
6
-
7
- %title Stoplight Admin
8
-
9
- %link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css'}
10
- %link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css'}
11
- %link{rel: 'shortcut icon', href: '//i.imgur.com/qvfoNuh.png'}
12
-
13
- :css
14
- td.indicator {
15
- font-size: 200%;
16
- }
17
-
18
- td.name {
19
- font-weight: bold;
20
- }
21
-
22
- td.locked button {
23
- font-size: 200%;
24
- }
25
-
26
- td.failures span.error {
27
- font-weight: bold;
28
- }
29
-
30
- td.failures ul {
31
- padding: 0;
32
- list-style-type: none;
33
- }
34
-
35
- .btn-group form {
36
- display: inline-block;
37
- }
38
-
39
- %body
40
- .navbar.navbar-inverse.navbar-static-top{role: 'navigation'}
41
- .container
42
- .navbar-header
43
- %a{class: 'navbar-brand', href: url('/')} Stoplight Admin
44
-
45
- .container{role: 'main'}
46
- = yield
47
-
48
- %script{src: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js'}