stoplight-admin 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 720f024afd31b7bd10e275d8e308adcb4bfe3a5a
4
+ data.tar.gz: 6198427cb003abba4b76d4f26ff8646fb3320402
5
+ SHA512:
6
+ metadata.gz: c5bb750a5a81f710ee638062625fc571303ab847259f5fa512a56eb56d554cedcea56e27bee7dbdd43f3af72a7fca4359e03c039c258985ac522cb162301f08a
7
+ data.tar.gz: ead92213a7d26b7366ce5631cb66821065d1ce3a386cfb8011c985e7c7fb948591ff718da278fa7017f379ce297395367bb2c8154df2be99c91925a295e062f4
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stoplight-admin.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Cameron Desautels
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # [Stoplight Admin][1]
2
+
3
+ A simple administration interface for [stoplight][2]. Monitor the
4
+ status, failures, and invocations of your stoplights. Change
5
+ stoplight colors, or lock them in either red or green state.
6
+
7
+ ## Configuration
8
+
9
+ This project is packaged as a Ruby gem so that you can easily embed it
10
+ in your own code containing the configuration details for your
11
+ [stoplight][2] data store.
12
+
13
+ First you'll need a `Gemfile`:
14
+
15
+ ``` rb
16
+ source 'https://rubygems.org'
17
+
18
+ gem 'stoplight-admin', '~> 0.1.0'
19
+ ```
20
+
21
+ Run [Bundler][3] to install the dependencies:
22
+
23
+ ``` sh
24
+ $ bundle install
25
+ ```
26
+
27
+ Lastly we need to make our (tiny) application. Here's a typical
28
+ example using a local Redis data store:
29
+
30
+ ``` rb
31
+ # app.rb
32
+
33
+ require 'sinatra'
34
+ require 'sinatra/stoplight_admin'
35
+
36
+ REDIS_URL = 'redis://localhost:6379'
37
+ set :data_store, Stoplight::DataStore::Redis.new(url: REDIS_URL)
38
+
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ``` sh
44
+ $ bundle exec ruby app.rb
45
+ ```
46
+
47
+ ## Credits
48
+
49
+ Stoplight is brought to you by [@camdez][4] and [@tfausak][5] from
50
+ [@OrgSync][6].
51
+
52
+ [1]: https://github.com/orgsync/stoplight-admin
53
+ [2]: https://github.com/orgsync/stoplight
54
+ [3]: http://bundler.io
55
+ [4]: https://github.com/camdez
56
+ [5]: https://github.com/tfausak
57
+ [6]: https://github.com/OrgSync
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,145 @@
1
+ # coding: utf-8
2
+
3
+ require 'haml'
4
+ require 'sinatra/base'
5
+ require 'stoplight'
6
+
7
+ module Sinatra
8
+ module StoplightAdmin
9
+ module Helpers
10
+ def data_store
11
+ return @data_store if defined?(@data_store)
12
+
13
+ Stoplight.data_store settings.data_store
14
+ @data_store = Stoplight.data_store
15
+ end
16
+
17
+ def lights
18
+ data_store
19
+ .names
20
+ .map { |name| light_info(name) }
21
+ .sort_by { |light| light_sort_key(light) }
22
+ end
23
+
24
+ def light_info(light)
25
+ green = Stoplight.green?(light)
26
+ attempts = green ? 0 : data_store.attempts(light)
27
+ failures = green ? [] : data_store.failures(light).map do |f|
28
+ JSON.parse(f)
29
+ end
30
+
31
+ {
32
+ name: light,
33
+ green: green,
34
+ failures: failures,
35
+ attempts: attempts,
36
+ locked: locked?(light)
37
+ }
38
+ end
39
+
40
+ def light_sort_key(light)
41
+ [light[:green] ? 1 : 0,
42
+ light[:attempts] * -1,
43
+ light[:name]]
44
+ end
45
+
46
+ def locked?(light_name)
47
+ [Stoplight::DataStore::STATE_LOCKED_GREEN,
48
+ Stoplight::DataStore::STATE_LOCKED_RED]
49
+ .include?(data_store.state(light_name))
50
+ end
51
+
52
+ def stat_params(ls)
53
+ total_count = ls.size
54
+ success_count = ls.count { |l| l[:green] }
55
+ failure_count = total_count - success_count
56
+
57
+ if total_count > 0
58
+ failure_percentage = (100.0 * failure_count / total_count).ceil
59
+ success_percentage = 100 - failure_percentage
60
+ else
61
+ failure_percentage = success_percentage = 0
62
+ end
63
+
64
+ {
65
+ success_count: success_count,
66
+ failure_count: failure_count,
67
+ success_percentage: success_percentage,
68
+ failure_percentage: failure_percentage
69
+ }
70
+ end
71
+
72
+ def lock(light)
73
+ new_state =
74
+ if Stoplight.green?(light)
75
+ Stoplight::DataStore::STATE_LOCKED_GREEN
76
+ else
77
+ Stoplight::DataStore::STATE_LOCKED_RED
78
+ end
79
+
80
+ data_store.set_state(light, new_state)
81
+ end
82
+
83
+ def unlock(light)
84
+ data_store.set_state(light, Stoplight::DataStore::STATE_UNLOCKED)
85
+ end
86
+
87
+ def green(light)
88
+ if data_store.state(light) == Stoplight::DataStore::STATE_LOCKED_RED
89
+ new_state = Stoplight::DataStore::STATE_LOCKED_GREEN
90
+ data_store.set_state(light, new_state)
91
+ end
92
+
93
+ data_store.clear_attempts(light)
94
+ data_store.clear_failures(light)
95
+ end
96
+
97
+ def red(light)
98
+ data_store.set_state(light, Stoplight::DataStore::STATE_LOCKED_RED)
99
+ end
100
+ end
101
+
102
+ def self.registered(app)
103
+ app.helpers StoplightAdmin::Helpers
104
+
105
+ app.set :data_store, nil
106
+ app.set :views, File.join(File.dirname(__FILE__), 'views')
107
+
108
+ app.get '/' do
109
+ ls = lights
110
+ stats = stat_params(ls)
111
+
112
+ haml :index, locals: stats.merge(lights: ls)
113
+ end
114
+
115
+ app.post '/lock' do
116
+ [*params[:names]].each { |l| lock(l) }
117
+ redirect to('/')
118
+ end
119
+
120
+ app.post '/unlock' do
121
+ [*params[:names]].each { |l| unlock(l) }
122
+ redirect to('/')
123
+ end
124
+
125
+ app.post '/green' do
126
+ [*params[:names]].each { |l| green(l) }
127
+ redirect to('/')
128
+ end
129
+
130
+ app.post '/red' do
131
+ [*params[:names]].each { |l| red(l) }
132
+ redirect to('/')
133
+ end
134
+
135
+ app.post '/green_all' do
136
+ data_store.names
137
+ .reject { |l| Stoplight.green?(l) }
138
+ .each { |l| green(l) }
139
+ redirect to('/')
140
+ end
141
+ end
142
+ end
143
+
144
+ register StoplightAdmin
145
+ end
@@ -0,0 +1,70 @@
1
+ .jumbotron
2
+ %h1 🚦 All of the Lights
3
+ %p Cop lights, flashlights, spotlights, strobe lights…
4
+
5
+ .main
6
+ - if lights.any?
7
+ .btn-group
8
+ %form{method: 'post', action: '/green_all'}
9
+ %button.btn-lg.btn.btn-success{type: 'submit', disabled: failure_count > 0 ? nil : 'disabled'} Greenify All
10
+
11
+ .progress
12
+ .progress-bar.progress-bar-danger{style: "width: #{failure_percentage}%"}
13
+ .span= failure_count
14
+ .progress-bar.progress-bar-success{style: "width: #{success_percentage}%"}
15
+ .span= success_count
16
+
17
+ .lights
18
+ %table.table.table-hover
19
+ %thead
20
+ %tr
21
+ %th Status
22
+ %th
23
+ %abbr{title: 'Locked lights will not change color automatically.'} Locked?
24
+ %th
25
+ %abbr{title: 'Exceptions which caused the light to turn red.'} Failures
26
+ %th
27
+ %abbr{title: 'Number of times the light was invoked while red.'} Attempts
28
+ %th Name
29
+ %tbody
30
+ - lights.each do |l|
31
+ %tr
32
+ %td.indicator
33
+ - if l[:green]
34
+ %form{method: 'post', action: '/red'}
35
+ %input{type: 'hidden', name: 'names', value: URI.escape(l[:name])}
36
+ %button{type: 'submit', class: 'btn btn-success'} GREEN
37
+ - else
38
+ %form{method: 'post', action: '/green'}
39
+ %input{type: 'hidden', name: 'names', value: URI.escape(l[:name])}
40
+ %button{type: 'submit', class: 'btn btn-danger'} RED
41
+ %td.locked
42
+ - if l[:locked]
43
+ %form{method: 'post', action: '/unlock'}
44
+ %input{type: 'hidden', name: 'names', value: URI.escape(l[:name])}
45
+ %button{type: 'submit', class: 'btn btn-link'}
46
+ %span{class: 'glyphicon glyphicon-lock'}
47
+ - else
48
+ %form{method: 'post', action: '/lock'}
49
+ %input{type: 'hidden', name: 'names', value: URI.escape(l[:name])}
50
+ %button{type: 'submit', class: 'btn btn-link'}
51
+ %span{class: 'glyphicon glyphicon-minus'}
52
+ %td.failures
53
+ - if l[:failures]
54
+ %ul
55
+ - l[:failures].each do |failure|
56
+ %li
57
+ %span.error
58
+ &= failure['error']
59
+ @
60
+ %span.timestamp
61
+ &= failure['time']
62
+ %td.attempts
63
+ - if l[:attempts] > 0
64
+ = l[:attempts]
65
+ %td.name
66
+ = l[:name]
67
+ - else
68
+ .alert.alert-warning{role: 'alert'}
69
+ %strong No lights found.
70
+ Ensure that your Stoplight data store is properly configured and that your Stoplight blocks have been run.
@@ -0,0 +1,52 @@
1
+ !!! 5
2
+ %html{lang: 'en'}
3
+ %head
4
+ %meta{charset: 'utf-8'}
5
+ %meta{viewport: 'width=device-width, initial-scale=1'}
6
+
7
+ %title Stoplight Admin
8
+
9
+ %link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css'}
10
+ %link{rel: 'stylesheet', href: '//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css'}
11
+
12
+ :css
13
+ body {
14
+ padding-top: 70px;
15
+ padding-bottom: 30px;
16
+ }
17
+
18
+ .progress {
19
+ margin-top: 30px
20
+ }
21
+
22
+ td.indicator {
23
+ font-size: 200%;
24
+ }
25
+
26
+ td.name {
27
+ font-weight: bold;
28
+ }
29
+
30
+ td.locked button {
31
+ font-size: 200%;
32
+ }
33
+
34
+ td.failures span.error {
35
+ font-weight: bold;
36
+ }
37
+
38
+ td.failures ul {
39
+ padding: 0;
40
+ list-style-type: none;
41
+ }
42
+
43
+ %body
44
+ .navbar.navbar-inverse.navbar-fixed-top{role: 'navigation'}
45
+ .container
46
+ .navbar-header
47
+ %a{class: 'navbar-brand', href: '/'} Stoplight Admin
48
+
49
+ .container{role: 'main'}
50
+ = yield
51
+
52
+ %script{src: '//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js'}
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'stoplight-admin'
5
+ spec.version = '0.1.0'
6
+ spec.authors = ['Cameron Desautels', 'Taylor Fausak']
7
+ spec.email = %w(camdez@gmail.com taylor@fausak.me)
8
+ spec.summary = %q{A simple administration interface for the stoplight gem.}
9
+ spec.description = spec.summary
10
+ spec.homepage = 'https://github.com/orgsync/stoplight-admin'
11
+ spec.license = 'MIT'
12
+
13
+ spec.files = `git ls-files`.split($/)
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.add_development_dependency 'bundler', '~> 1.3'
19
+ spec.add_development_dependency 'rake'
20
+
21
+ spec.add_dependency 'haml', '~> 4.0.4'
22
+ spec.add_dependency 'redis', '~> 3.1.0'
23
+ spec.add_dependency 'sinatra', '~> 1.4.5'
24
+ spec.add_dependency 'stoplight', '~> 0.1.0'
25
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stoplight-admin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Cameron Desautels
8
+ - Taylor Fausak
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-08-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: haml
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: 4.0.4
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 4.0.4
56
+ - !ruby/object:Gem::Dependency
57
+ name: redis
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: 3.1.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 3.1.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: sinatra
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.4.5
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: 1.4.5
84
+ - !ruby/object:Gem::Dependency
85
+ name: stoplight
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 0.1.0
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: 0.1.0
98
+ description: A simple administration interface for the stoplight gem.
99
+ email:
100
+ - camdez@gmail.com
101
+ - taylor@fausak.me
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - .gitignore
107
+ - Gemfile
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - lib/sinatra/stoplight_admin.rb
112
+ - lib/sinatra/views/index.haml
113
+ - lib/sinatra/views/layout.haml
114
+ - stoplight-admin.gemspec
115
+ homepage: https://github.com/orgsync/stoplight-admin
116
+ licenses:
117
+ - MIT
118
+ metadata: {}
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.1.11
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: A simple administration interface for the stoplight gem.
139
+ test_files: []