broken_window 1.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/MIT-LICENSE +20 -0
- data/README.rdoc +45 -0
- data/Rakefile +21 -0
- data/app/assets/javascripts/broken_window/application.js +13 -0
- data/app/assets/stylesheets/broken_window/application.scss +25 -0
- data/app/assets/stylesheets/broken_window/layout.sass +39 -0
- data/app/assets/stylesheets/broken_window/metric.sass +16 -0
- data/app/assets/stylesheets/broken_window/metrics_list.sass +38 -0
- data/app/controllers/broken_window/application_controller.rb +4 -0
- data/app/controllers/broken_window/metrics_controller.rb +48 -0
- data/app/helpers/broken_window/application_helper.rb +4 -0
- data/app/models/broken_window/measured_metric.rb +54 -0
- data/app/models/broken_window/measurement.rb +7 -0
- data/app/models/broken_window/metric.rb +40 -0
- data/app/models/broken_window/metric_status.rb +37 -0
- data/app/services/broken_window/formatters/integer.rb +11 -0
- data/app/services/broken_window/formatters/percentage.rb +11 -0
- data/app/services/broken_window/measurer.rb +11 -0
- data/app/services/broken_window/metric_measurer.rb +26 -0
- data/app/services/broken_window/service.rb +23 -0
- data/app/services/broken_window/status_checker.rb +37 -0
- data/app/views/broken_window/metrics/_error_messages.html.erb +10 -0
- data/app/views/broken_window/metrics/_form.html.erb +35 -0
- data/app/views/broken_window/metrics/_list.html.erb +13 -0
- data/app/views/broken_window/metrics/edit.html.erb +5 -0
- data/app/views/broken_window/metrics/index.html.erb +4 -0
- data/app/views/broken_window/metrics/new.html.erb +5 -0
- data/app/views/broken_window/metrics/show.html.erb +17 -0
- data/app/views/layouts/broken_window/application.html.erb +16 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20160112005320_create_broken_window_metrics.rb +13 -0
- data/db/migrate/20160112010703_create_broken_window_measurements.rb +9 -0
- data/db/migrate/20160419161925_add_parent_id_to_metrics.rb +5 -0
- data/lib/broken_window/engine.rb +5 -0
- data/lib/broken_window/version.rb +3 -0
- data/lib/broken_window.rb +14 -0
- data/lib/tasks/broken_window_tasks.rake +7 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4f486568923c0240a670406f6f39d43106de773f
|
4
|
+
data.tar.gz: c8bc0696766613a6c62aca487087c928e3eb8855
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 62b0209d894eb0d4108689842ac8386673f4905f1f91037b476bb50f092be9b79255c18c4f15704f25df4dea5c87031e6532a24d084bd3143e2adc17533c60e5
|
7
|
+
data.tar.gz: 7a846fb8f52c16f81cb0ff83738bfd93d638af2de767e2ba50b2e1c6f1e95b9e85e050067763e4f93d0471ecadb0947589b33e767ef5935c9ea318b4bb5c6850
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
= BrokenWindow
|
2
|
+
|
3
|
+
This project rocks and uses MIT-LICENSE.
|
4
|
+
|
5
|
+
Installation
|
6
|
+
=============
|
7
|
+
|
8
|
+
Add:
|
9
|
+
|
10
|
+
gem 'broken_window', github: 'webpunch/broken_window'
|
11
|
+
|
12
|
+
to your Gemfile
|
13
|
+
|
14
|
+
Run: rake broken_window:install:migrations && rake db:migrate
|
15
|
+
|
16
|
+
Add to your routes file:
|
17
|
+
|
18
|
+
mount BrokenWindow::Engine => "/status"
|
19
|
+
|
20
|
+
Create config/initializers/broken_window.rb to register your calculators:
|
21
|
+
|
22
|
+
BrokenWindow.register_calculators [
|
23
|
+
BrokenWindow::Calculators::ExampleCalculator
|
24
|
+
]
|
25
|
+
|
26
|
+
Create your calculator:
|
27
|
+
|
28
|
+
|
29
|
+
module BrokenWindow
|
30
|
+
module Calculators
|
31
|
+
class ExampleCalculator
|
32
|
+
def initialize(options = {})
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
# Thing to be calculated
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'BrokenWindow'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any styles
|
10
|
+
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
11
|
+
* file per style scope.
|
12
|
+
*
|
13
|
+
*=
|
14
|
+
*/
|
15
|
+
|
16
|
+
@mixin clearfix {
|
17
|
+
&:after {
|
18
|
+
content: "";
|
19
|
+
display: table;
|
20
|
+
clear: both;
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
@import "./*.sass"
|
25
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
a.back-link
|
2
|
+
float: left
|
3
|
+
text-decoration: none
|
4
|
+
color: black
|
5
|
+
font-size: 42px
|
6
|
+
|
7
|
+
.page-actions
|
8
|
+
text-align: center
|
9
|
+
|
10
|
+
a
|
11
|
+
color: black
|
12
|
+
font-size: 42px
|
13
|
+
text-decoration: none
|
14
|
+
|
15
|
+
.error_explanation
|
16
|
+
color: red
|
17
|
+
|
18
|
+
body
|
19
|
+
color: black
|
20
|
+
padding: 10px
|
21
|
+
|
22
|
+
&.fail
|
23
|
+
background-color: red
|
24
|
+
|
25
|
+
&.pass
|
26
|
+
background-color: green
|
27
|
+
|
28
|
+
&.unknown
|
29
|
+
background-color: gray
|
30
|
+
|
31
|
+
&.background-metric
|
32
|
+
&.fail
|
33
|
+
background-color: darken(red, 10%)
|
34
|
+
|
35
|
+
&.pass
|
36
|
+
background-color: darken(green, 10%)
|
37
|
+
|
38
|
+
&.unknown
|
39
|
+
background-color: darken(gray, 10%)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
.metric
|
2
|
+
&.fail
|
3
|
+
background-color: red
|
4
|
+
|
5
|
+
&.pass
|
6
|
+
background-color: green
|
7
|
+
|
8
|
+
&.unknown
|
9
|
+
background-color: grey
|
10
|
+
|
11
|
+
.metrics-list
|
12
|
+
margin: 50px
|
13
|
+
|
14
|
+
a.metric
|
15
|
+
@include clearfix()
|
16
|
+
padding: 10px 20px
|
17
|
+
display: block
|
18
|
+
color: black
|
19
|
+
text-decoration: none
|
20
|
+
font-size: 20px
|
21
|
+
margin-bottom: 5px
|
22
|
+
|
23
|
+
.name
|
24
|
+
float: left
|
25
|
+
font-weight: bold
|
26
|
+
|
27
|
+
.value
|
28
|
+
float: right
|
29
|
+
|
30
|
+
.stale-time
|
31
|
+
background-color: #333
|
32
|
+
color: #CCCCCC
|
33
|
+
display: inline-block
|
34
|
+
padding: 0 5px
|
35
|
+
border-radius: 4px
|
36
|
+
font-weight: normal
|
37
|
+
float: left
|
38
|
+
margin-right: 10px
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class MetricsController < ApplicationController
|
3
|
+
layout 'broken_window/application'
|
4
|
+
|
5
|
+
def index
|
6
|
+
@metrics = Metric.roots.map {|metric| MeasuredMetric.new(metric)}
|
7
|
+
@status = MetricStatus.combine(@metrics.map(&:status))
|
8
|
+
@body_class = 'background-metric'
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
@metric = MeasuredMetric.find(params[:id])
|
13
|
+
@status = @metric.status
|
14
|
+
end
|
15
|
+
|
16
|
+
def new
|
17
|
+
@metric = Metric.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@metric = Metric.new(metric_params)
|
22
|
+
if @metric.save
|
23
|
+
redirect_to @metric
|
24
|
+
else
|
25
|
+
render action: :new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def edit
|
30
|
+
@metric = Metric.find(params[:id])
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
@metric = Metric.find(params[:id])
|
35
|
+
if @metric.update_attributes(metric_params)
|
36
|
+
redirect_to @metric
|
37
|
+
else
|
38
|
+
render action: :edit
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def metric_params
|
45
|
+
params[:metric].permit(:name, :threshold, :value_type, :calculator, :arguments, :threshold_type, :parent_id)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class MeasuredMetric
|
3
|
+
attr_reader :metric
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def find(param)
|
7
|
+
new(BrokenWindow::Metric.find(param))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(metric)
|
12
|
+
@metric = metric
|
13
|
+
end
|
14
|
+
|
15
|
+
def measurement
|
16
|
+
@measurement ||= @metric.latest_measurement unless container?
|
17
|
+
end
|
18
|
+
|
19
|
+
def status
|
20
|
+
@status ||= calculate_status
|
21
|
+
end
|
22
|
+
|
23
|
+
def old?
|
24
|
+
measurement && measurement.created_at < 3.days.ago
|
25
|
+
end
|
26
|
+
|
27
|
+
def children
|
28
|
+
@metric.children.map do |metric|
|
29
|
+
self.class.new(metric)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.model_name
|
34
|
+
Metric.model_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_model
|
38
|
+
metric
|
39
|
+
end
|
40
|
+
|
41
|
+
delegate :name, :to_param, :threshold, :value_type, :threshold_type, :container?, to: :metric
|
42
|
+
delegate :value, to: :measurement, allow_nil: true
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def calculate_status
|
47
|
+
if container?
|
48
|
+
MetricStatus.combine(children.map(&:status))
|
49
|
+
else
|
50
|
+
StatusChecker.check_status(@metric, measurement)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class Metric < ActiveRecord::Base
|
3
|
+
VALUE_TYPES = %w(percentage integer)
|
4
|
+
THRESHOLD_TYPES = %w(min max)
|
5
|
+
VALID_CLASS_NAME = /\A[A-Z][a-zA-Z_0-9\:]*\z/
|
6
|
+
|
7
|
+
has_many :measurements
|
8
|
+
|
9
|
+
validates :name, presence: true, length: {maximum: 255}
|
10
|
+
validates :calculator, format: {
|
11
|
+
with: VALID_CLASS_NAME, allow_blank: true, message: "must be a valid class name" }
|
12
|
+
validates :value_type, inclusion: {in: VALUE_TYPES}
|
13
|
+
validates :threshold, presence: true, numericality: {allow_blank: true}, unless: :container?
|
14
|
+
validates :threshold_type, inclusion: {in: THRESHOLD_TYPES}
|
15
|
+
|
16
|
+
acts_as_tree order: "name"
|
17
|
+
|
18
|
+
def latest_measurement
|
19
|
+
measurements.latest_first.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def arguments_hash
|
23
|
+
if arguments.present?
|
24
|
+
YAML::load(arguments)
|
25
|
+
else
|
26
|
+
{}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.format_value(value, value_type)
|
31
|
+
formatter = "BrokenWindow::Formatters::#{value_type.camelize}".constantize.new
|
32
|
+
formatter.call(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
def container?
|
36
|
+
calculator.blank?
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class MetricStatus
|
3
|
+
NAMES = [:fail, :unknown, :pass]
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def pass
|
7
|
+
new(:pass)
|
8
|
+
end
|
9
|
+
|
10
|
+
def unknown
|
11
|
+
new(:unknown)
|
12
|
+
end
|
13
|
+
|
14
|
+
def fail
|
15
|
+
new(:fail)
|
16
|
+
end
|
17
|
+
|
18
|
+
def combine(states)
|
19
|
+
states.min
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :value
|
24
|
+
|
25
|
+
def initialize(name)
|
26
|
+
@value = NAMES.index(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
NAMES[@value].to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def <=>(other)
|
34
|
+
value <=> other.value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class MetricMeasurer
|
3
|
+
include Service
|
4
|
+
|
5
|
+
def initialize(metric)
|
6
|
+
@metric = metric
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
value = calculator.call
|
11
|
+
@metric.measurements.create!(value: value)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def calculator
|
17
|
+
calculator_class.new(ActiveSupport::HashWithIndifferentAccess.new(@metric.arguments_hash))
|
18
|
+
end
|
19
|
+
|
20
|
+
def calculator_class
|
21
|
+
raise ArgumentError unless @metric.calculator
|
22
|
+
|
23
|
+
@metric.calculator.constantize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
module Service
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
|
6
|
+
# From http://brewhouse.io/blog/2014/04/30/gourmet-service-objects.html
|
7
|
+
# Possible responses
|
8
|
+
# 1: Fail loudly
|
9
|
+
# 2: Return ActiveRecord results
|
10
|
+
# 3: Response object
|
11
|
+
# Some services have several outcomes and complex error handling. They return a response object which responds to success? and error(s).
|
12
|
+
def call
|
13
|
+
raise NotImplementedError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def call(*args)
|
19
|
+
new(*args).call
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module BrokenWindow
|
2
|
+
class StatusChecker
|
3
|
+
class << self
|
4
|
+
def check_status(metric, measurement = nil)
|
5
|
+
measurement ||= metric.latest_measurement
|
6
|
+
if measurement
|
7
|
+
if value_passes_threshold?(measurement.value, metric.threshold, metric.threshold_type)
|
8
|
+
BrokenWindow::MetricStatus.pass
|
9
|
+
else
|
10
|
+
BrokenWindow::MetricStatus.fail
|
11
|
+
end
|
12
|
+
else
|
13
|
+
BrokenWindow::MetricStatus.unknown
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def value_passes_threshold?(value, threshold, threshold_type)
|
20
|
+
if value
|
21
|
+
value.send(comparison_method(threshold_type), threshold)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def comparison_method(threshold_type)
|
26
|
+
case threshold_type
|
27
|
+
when 'min'
|
28
|
+
return '>=';
|
29
|
+
when 'max'
|
30
|
+
return '<=';
|
31
|
+
else
|
32
|
+
raise "unknown threshold_type #{threshold_type.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<% if target.errors.any? %>
|
2
|
+
<div class="error_explanation">
|
3
|
+
<h2><%= pluralize(target.errors.count, "error") %> prevented this record from being saved:</h2>
|
4
|
+
<ul>
|
5
|
+
<% target.errors.full_messages.each do |msg| %>
|
6
|
+
<li><%= msg %></li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<%= form_for @metric do |f| %>
|
2
|
+
<%= render "error_messages", :target => @metric %>
|
3
|
+
|
4
|
+
<div class="form-group">
|
5
|
+
<%= f.label :name %>
|
6
|
+
<%= f.text_field :name, class: 'form-control' %>
|
7
|
+
</div>
|
8
|
+
<div class="form-group">
|
9
|
+
<%= f.label :calculator %>
|
10
|
+
<%= f.select :calculator, BrokenWindow.calculators.map(&:name), {include_blank: true}, class: 'form-control' %>
|
11
|
+
</div>
|
12
|
+
<div class="form-group">
|
13
|
+
<%= f.label :value_type %>
|
14
|
+
<%= f.select :value_type, BrokenWindow::Metric::VALUE_TYPES, class: 'form-control' %>
|
15
|
+
</div>
|
16
|
+
<div class="form-group">
|
17
|
+
<%= f.label :threshold %>
|
18
|
+
<%= f.text_field :threshold, class: 'form-control' %>
|
19
|
+
</div>
|
20
|
+
<div class="form-group">
|
21
|
+
<%= f.label :threshold_type %>
|
22
|
+
<%= f.select :threshold_type, BrokenWindow::Metric::THRESHOLD_TYPES, class: 'form-control' %>
|
23
|
+
</div>
|
24
|
+
<div class="form-group">
|
25
|
+
<%= f.label :arguments %>
|
26
|
+
<%= f.text_field :arguments, class: 'form-control' %>
|
27
|
+
</div>
|
28
|
+
<div class="form-group">
|
29
|
+
<%= f.label :parent_id %>
|
30
|
+
<%= f.text_field :parent_id, class: 'form-control' %>
|
31
|
+
</div>
|
32
|
+
<button type="submit" class="btn btn-default">Submit</button>
|
33
|
+
or
|
34
|
+
<%= link_to 'cancel', root_path %>
|
35
|
+
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="metrics-list">
|
2
|
+
<% metrics.each do |metric| %>
|
3
|
+
<%= link_to(metric, class: "metric #{metric.status}") do %>
|
4
|
+
<% if metric.old? %>
|
5
|
+
<span class="stale-time">
|
6
|
+
<%= time_ago_in_words(metric.measurement.created_at) %> old
|
7
|
+
</span>
|
8
|
+
<% end %>
|
9
|
+
<span class="name"><%= metric.name %></span>
|
10
|
+
<div class="value"><%= BrokenWindow::Metric.format_value metric.value, metric.value_type %></div>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
13
|
+
</div>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<%= link_to '', metrics_path, class: 'back-link fa fa-level-up' %>
|
2
|
+
|
3
|
+
<div class="show-metric">
|
4
|
+
<h1><%= @metric.name %></h1>
|
5
|
+
<%- unless @metric.container? %>
|
6
|
+
<div class="value"><%= BrokenWindow::Metric.format_value @metric.value, @metric.value_type %></div>
|
7
|
+
<div class="required">
|
8
|
+
<div class="minimum"><%= @metric.threshold_type %> <%= BrokenWindow::Metric.format_value @metric.threshold, @metric.value_type %></div>
|
9
|
+
</div>
|
10
|
+
<%- end %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<%= render partial: 'list', locals: {metrics: @metric.children} %>
|
14
|
+
|
15
|
+
<div class="page-actions">
|
16
|
+
<%= link_to '', edit_metric_path(@metric), class: 'fa fa-pencil-square-o' %>
|
17
|
+
</div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>BrokenWindow</title>
|
5
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
|
6
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
|
7
|
+
<%= stylesheet_link_tag "broken_window/application", media: "all" %>
|
8
|
+
<%= javascript_include_tag "broken_window/application" %>
|
9
|
+
<%= csrf_meta_tags %>
|
10
|
+
</head>
|
11
|
+
<body class="<%= [@status.to_s, @body_class].compact.join(' ') %>">
|
12
|
+
|
13
|
+
<%= yield %>
|
14
|
+
|
15
|
+
</body>
|
16
|
+
</html>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateBrokenWindowMetrics < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :broken_window_metrics do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :calculator
|
6
|
+
t.string :value_type
|
7
|
+
t.text :arguments
|
8
|
+
t.decimal :threshold
|
9
|
+
t.string :threshold_type
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: broken_window
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Craig Ambrose
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.1.12
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.1.12
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: acts_as_tree
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: combustion
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Calculate metrics and display a simple broken/not broken dashboard.
|
84
|
+
email:
|
85
|
+
- craig.ambrose@enspiral.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- MIT-LICENSE
|
91
|
+
- README.rdoc
|
92
|
+
- Rakefile
|
93
|
+
- app/assets/javascripts/broken_window/application.js
|
94
|
+
- app/assets/stylesheets/broken_window/application.scss
|
95
|
+
- app/assets/stylesheets/broken_window/layout.sass
|
96
|
+
- app/assets/stylesheets/broken_window/metric.sass
|
97
|
+
- app/assets/stylesheets/broken_window/metrics_list.sass
|
98
|
+
- app/controllers/broken_window/application_controller.rb
|
99
|
+
- app/controllers/broken_window/metrics_controller.rb
|
100
|
+
- app/helpers/broken_window/application_helper.rb
|
101
|
+
- app/models/broken_window/measured_metric.rb
|
102
|
+
- app/models/broken_window/measurement.rb
|
103
|
+
- app/models/broken_window/metric.rb
|
104
|
+
- app/models/broken_window/metric_status.rb
|
105
|
+
- app/services/broken_window/formatters/integer.rb
|
106
|
+
- app/services/broken_window/formatters/percentage.rb
|
107
|
+
- app/services/broken_window/measurer.rb
|
108
|
+
- app/services/broken_window/metric_measurer.rb
|
109
|
+
- app/services/broken_window/service.rb
|
110
|
+
- app/services/broken_window/status_checker.rb
|
111
|
+
- app/views/broken_window/metrics/_error_messages.html.erb
|
112
|
+
- app/views/broken_window/metrics/_form.html.erb
|
113
|
+
- app/views/broken_window/metrics/_list.html.erb
|
114
|
+
- app/views/broken_window/metrics/edit.html.erb
|
115
|
+
- app/views/broken_window/metrics/index.html.erb
|
116
|
+
- app/views/broken_window/metrics/new.html.erb
|
117
|
+
- app/views/broken_window/metrics/show.html.erb
|
118
|
+
- app/views/layouts/broken_window/application.html.erb
|
119
|
+
- config/routes.rb
|
120
|
+
- db/migrate/20160112005320_create_broken_window_metrics.rb
|
121
|
+
- db/migrate/20160112010703_create_broken_window_measurements.rb
|
122
|
+
- db/migrate/20160419161925_add_parent_id_to_metrics.rb
|
123
|
+
- lib/broken_window.rb
|
124
|
+
- lib/broken_window/engine.rb
|
125
|
+
- lib/broken_window/version.rb
|
126
|
+
- lib/tasks/broken_window_tasks.rake
|
127
|
+
homepage: http://app.webpunch12.com
|
128
|
+
licenses:
|
129
|
+
- MIT
|
130
|
+
metadata: {}
|
131
|
+
post_install_message:
|
132
|
+
rdoc_options: []
|
133
|
+
require_paths:
|
134
|
+
- lib
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
requirements: []
|
146
|
+
rubyforge_project:
|
147
|
+
rubygems_version: 2.2.2
|
148
|
+
signing_key:
|
149
|
+
specification_version: 4
|
150
|
+
summary: Show me whether the app is broken or not
|
151
|
+
test_files: []
|