liprug 0.0.2

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: 3c2a0efd0372fc1dcc5d21a8d65d5911fed62e53
4
+ data.tar.gz: da31cd1f5c7c1a649d5d5bc4e5ea3eeb09eb3844
5
+ SHA512:
6
+ metadata.gz: d59576b841a9f2b596fd75428baae63413c03011c3a5f8b3281fcd2155394653e6f4771ef20c6c25feed6f9ca4716b7c7142e6a79923721b6f6b373b86020d87
7
+ data.tar.gz: 86cac38dc47ea2c6c38e148aed5a28cc7361817c22e255b37264e62f602dd7e841a4690b19f5e4c4830bb0e3e9c9ee57d2fe6c4c3c7f88705a969d45dd3bbc89
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in liprug.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ liprug (0.0.1)
5
+ rdiscount
6
+ redis
7
+ sinatra
8
+ slim
9
+ unicorn
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ kgio (2.8.0)
15
+ rack (1.5.2)
16
+ rack-protection (1.5.0)
17
+ rack
18
+ raindrops (0.12.0)
19
+ rake (10.1.0)
20
+ rdiscount (2.1.6)
21
+ redis (3.0.4)
22
+ sinatra (1.4.3)
23
+ rack (~> 1.4)
24
+ rack-protection (~> 1.4)
25
+ tilt (~> 1.3, >= 1.3.4)
26
+ slim (2.0.1)
27
+ temple (~> 0.6.6)
28
+ tilt (>= 1.3.3, < 2.1)
29
+ temple (0.6.6)
30
+ tilt (1.4.1)
31
+ unicorn (4.6.3)
32
+ kgio (~> 2.6)
33
+ rack
34
+ raindrops (~> 0.7)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ bundler (~> 1.3)
41
+ liprug!
42
+ rake
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Exoscale
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,42 @@
1
+ liprug: simple status board
2
+ ----------------------------
3
+
4
+ ![liprug](http://i.imgur.com/nI9l58i.png)
5
+
6
+ The idea behind liprug is to give you a simple tool
7
+ to communicate your operational status to clients.
8
+
9
+ The main objectives behind liprug were:
10
+
11
+ * Simple deployment compatible with heroku and AWS
12
+ * All-in-one solution
13
+ * Simple administration
14
+
15
+ ### Deployment
16
+
17
+ You can deploy to heroku or any host provider. It is advised to set up liprug to operate
18
+ behind SSL only.
19
+
20
+ ### Configuration
21
+
22
+ Configuration is done through environment variables, here are the relevant ones:
23
+
24
+ * `REDIS_URL` or `REDISTOGO_URL`: url of redis host for storage
25
+ * `LIPRUG_BRAND_HEADER`: text you want shown on the header bar
26
+ * `LIPRUG_BRAND_CONTACT`: if present, a "report issue" button will show up in the bar, with this value as href
27
+ * `LIPRUG_BRAND_SCRIPT`: if present, a script to be inserted in the page, to track analytics for instance
28
+ * `LIPRUG_CREDENTIALS`: colon separated user and password
29
+
30
+ ### Usage
31
+
32
+ The main URL shows current service status, the `/admin` URL allows input and modifications
33
+
34
+ ### Roadmap
35
+
36
+ * Additional authentication methods to admin interface
37
+ * Per-service history
38
+ * Typed service data to allow graphs
39
+
40
+ ### Authors
41
+
42
+ liprug is a production of [exoscale](https://exoscale.ch)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/config.ru ADDED
@@ -0,0 +1,6 @@
1
+ $stdout.sync = true
2
+ $:.unshift File.dirname(__FILE__) + '/lib'
3
+
4
+ require 'liprug/service'
5
+
6
+ run Rack::URLMap.new('/' => Liprug::Service)
@@ -0,0 +1,7 @@
1
+ module Liprug
2
+ class Config
3
+ def self.[] index
4
+ ENV[index]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ require 'redis'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'liprug/config'
5
+
6
+ module Liprug
7
+ class Model
8
+ def self.connect!
9
+ url = Liprug::Config['REDISTOGO_URL']
10
+ url ||= Liprug::Config['REDIS_URL']
11
+ url ||= "redis://127.0.0.1"
12
+
13
+ uri = URI.parse(url)
14
+ options = {:host => uri.host}
15
+ options[:port] = uri.port if uri.port
16
+ options[:password] = uri.password if uri.password
17
+
18
+ Redis.new(options)
19
+ end
20
+
21
+ def self.fetch
22
+ redis = connect!
23
+ services = redis.smembers 'services'
24
+ {
25
+ :brand => {
26
+ :script => Liprug::Config['LIPRUG_BRAND_SCRIPT'] || '',
27
+ :name => Liprug::Config['LIPRUG_BRAND_HEADER'] || 'status board',
28
+ :contact => Liprug::Config['LIPRUG_BRAND_CONTACT']
29
+ },
30
+ :services => services.map do |service|
31
+ {
32
+ service => JSON.parse(redis.get("service:#{service}"),
33
+ :symbolize_names => true)
34
+ }
35
+ end.reduce({}) do |e1,e2|
36
+ e1.merge e2
37
+ end,
38
+ :past => redis.lrange('events_past', 0, -1).map do |event|
39
+ JSON.parse event, :symbolize_names => true
40
+ end,
41
+ :upcoming => redis.lrange('events_upcoming', 0, -1).map do |event|
42
+ JSON.parse event, :symbolize_names => true
43
+ end
44
+ }
45
+ end
46
+
47
+ def self.add_status service, status
48
+ redis = connect!
49
+ redis.sadd "services", service
50
+ redis.set "service:#{service}", status.to_json
51
+ end
52
+
53
+ def self.delete_service service
54
+ redis = connect!
55
+ redis.srem "services", service
56
+ redis.del "service:#{service}"
57
+ end
58
+
59
+ def self.add_upcoming_event event
60
+ redis = connect!
61
+ event[:date] = Time.new.asctime
62
+ event = event.to_json
63
+ redis.lpush "events_upcoming", event
64
+ end
65
+
66
+ def self.add_past_event event
67
+ redis = connect!
68
+ event[:date] = Time.new.asctime
69
+ event = event.to_json
70
+ redis.lpush "events_past", event
71
+ end
72
+
73
+ def self.delete_upcoming_event index
74
+ redis = connect!
75
+ redis.lset "events_upcoming", index, "XXXXX"
76
+ redis.lrem "events_upcoming", 1, "XXXXX"
77
+ end
78
+
79
+ def self.delete_past_event index
80
+ redis = connect!
81
+ redis.lset "events_past", index, "XXXXX"
82
+ redis.lrem "events_past", 1, "XXXXX"
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,33 @@
1
+ $(document).ready(function () {
2
+ $("td.delete-service").each(function(index, el) {
3
+ var b = $(el).find("button");
4
+ var id = b.attr("id");
5
+ b.click(function (ev) {
6
+ ev.preventDefault();
7
+ $.ajax({url: '/api/services/' + id,
8
+ type: 'DELETE'});
9
+ });
10
+ });
11
+
12
+ $("td.delete-past-event").each(function(index, el) {
13
+
14
+ var b = $(el).find("button");
15
+ var id = b.attr("id");
16
+ b.click(function (ev) {
17
+ ev.preventDefault();
18
+ $.ajax({url: '/api/past/' + id,
19
+ type: 'DELETE'});
20
+ });
21
+ });
22
+
23
+ $("td.delete-upcoming-event").each(function(index, el) {
24
+
25
+ var b = $(el).find("button");
26
+ var id = b.attr("id");
27
+ b.click(function (ev) {
28
+ ev.preventDefault();
29
+ $.ajax({url: '/api/upcoming/' + id,
30
+ type: 'DELETE'});
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,21 @@
1
+ require 'sinatra'
2
+ require 'liprug/config'
3
+
4
+ module Liprug
5
+ class Service < Sinatra::Base
6
+ helpers do
7
+
8
+ def authorized?
9
+ creds = (Liprug::Config['LIPRUG_CREDENTIALS'] || ":").split ":"
10
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
11
+ @auth.provided? and @auth.basic? and @auth.credentials and @auth.credentials == creds
12
+ end
13
+
14
+ def protected!
15
+ return if authorized?
16
+ headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
17
+ halt 401, "Not authorized"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,67 @@
1
+ require 'sinatra'
2
+ require 'rdiscount'
3
+ require 'slim'
4
+ require 'liprug/model'
5
+ require 'liprug/service/helpers'
6
+
7
+ module Liprug
8
+ class Service < Sinatra::Base
9
+
10
+ get '/' do
11
+ slim :overview, :locals => Liprug::Model.fetch
12
+ end
13
+
14
+ get '/admin' do
15
+ protected!
16
+ slim :admin, :locals => Liprug::Model.fetch
17
+ end
18
+
19
+ post '/api/services' do
20
+ protected!
21
+ Liprug::Model.add_status(params[:name],
22
+ {
23
+ :class => params[:class],
24
+ :message => params[:message]
25
+ })
26
+ redirect "/admin"
27
+ end
28
+
29
+ delete '/api/services/:service' do
30
+ protected!
31
+ Liprug::Model.delete_service params[:service]
32
+ redirect "/admin"
33
+ end
34
+
35
+ post '/api/past' do
36
+ protected!
37
+ Liprug::Model.add_past_event({
38
+ :class => params[:class],
39
+ :message => params[:message]
40
+ })
41
+ redirect "/admin"
42
+ end
43
+
44
+ post '/api/upcoming' do
45
+ protected!
46
+ Liprug::Model.add_upcoming_event({
47
+ :class => params[:class],
48
+ :message => params[:message]
49
+ })
50
+ redirect "/admin"
51
+ end
52
+
53
+ delete '/api/past/:event' do
54
+ protected!
55
+ Liprug::Model.delete_past_event params[:event].to_i
56
+
57
+ redirect "/admin"
58
+ end
59
+
60
+ delete '/api/upcoming/:event' do
61
+ protected!
62
+ Liprug::Model.delete_upcoming_event params[:event].to_i
63
+
64
+ redirect "/admin"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,9 @@
1
+ require 'sinatra'
2
+ module Liprug
3
+ class Service < Sinatra::Base
4
+
5
+ require 'liprug/model'
6
+ require 'liprug/service/helpers'
7
+ require 'liprug/service/routes'
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Liprug
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,118 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Status Board
5
+ meta name="viewport" content="width=device-width, initial-scale=1.0"
6
+ link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"
7
+ body
8
+
9
+ nav.navbar.navbar-default role="navigation"
10
+ div.navbar-header
11
+ a.navbar-brand href="/" status admin
12
+
13
+ div.container
14
+ div.row
15
+
16
+ div.col-md-8.col-md-offset-2
17
+
18
+ h2 Services
19
+
20
+ form.form-horizontal role="form" method="POST" action="/api/services"
21
+ div.form-group
22
+ label.col-lg-2.control-label for="name" Service Name
23
+ div.col-lg-6
24
+ input.form-control type="text" id="name" name="name"
25
+ div.form-group
26
+ label.col-lg-2.control-label for="class" Status class
27
+ div.col-lg-6
28
+ div
29
+ input type="radio" id="class" name="class" value="success" success
30
+ div
31
+ input type="radio" id="class" name="class" value="danger" danger
32
+ div
33
+ input type="radio" id="class" name="class" value="warning" warning
34
+ div.form-group
35
+ label.col-lg-2.control-label for="message" Message
36
+ div.col-lg-6
37
+ input.form-control type="text" id="message" name="message"
38
+ div.form-group
39
+ div.col-lg-offset-2.col-lg-6
40
+ button.btn.btn-default type="submit" update
41
+
42
+ table.table.table-bordered.table-striped
43
+ tbody
44
+ - for name,status in services do
45
+ tr
46
+ td #{name}
47
+ td.delete-service
48
+ button.btn.btn-primary.pull-right id="#{name}"
49
+ span.glyphicon.glyphicon-trash
50
+ | delete
51
+
52
+ h2 Upcoming Events
53
+
54
+ form.form-horizontal role="form" method="POST" action="/api/upcoming"
55
+ div.form-group
56
+ label.col-lg-2.control-label for="class" Status class
57
+ div.col-lg-6
58
+ div
59
+ input type="radio" id="event-class" name="class" value="info" info
60
+ div
61
+ input type="radio" id="event-class" name="class" value="success" success
62
+ div
63
+ input type="radio" id="event-class" name="class" value="danger" danger
64
+ div
65
+ input type="radio" id="event-class" name="class" value="warning" warning
66
+ div.form-group
67
+ label.col-lg-2.control-label for="message" Message
68
+ div.col-lg-6
69
+ textarea.form-control id="event-message" name="message"
70
+ div.form-group
71
+ div.col-lg-offset-2.col-lg-6
72
+ button.btn.btn-default type="submit" update
73
+
74
+ table.table.table-bordered.table-striped
75
+ tbody
76
+ - for event, index in upcoming.each_with_index do
77
+ tr
78
+ td #{event[:message]}
79
+ td.delete-upcoming-event
80
+ button.btn.btn-primary.pull-right id="#{index}"
81
+ span.glyphicon.glyphicon-trash
82
+ | delete
83
+
84
+ h2 Past Events
85
+
86
+ form.form-horizontal role="form" method="POST" action="/api/past"
87
+ div.form-group
88
+ label.col-lg-2.control-label for="class" Status class
89
+ div.col-lg-6
90
+ div
91
+ input type="radio" id="event-class" name="class" value="info" info
92
+ div
93
+ input type="radio" id="event-class" name="class" value="success" success
94
+ div
95
+ input type="radio" id="event-class" name="class" value="danger" danger
96
+ div
97
+ input type="radio" id="event-class" name="class" value="warning" warning
98
+ div.form-group
99
+ label.col-lg-2.control-label for="message" Message
100
+ div.col-lg-6
101
+ textarea.form-control id="event-message" name="message"
102
+ div.form-group
103
+ div.col-lg-offset-2.col-lg-6
104
+ button.btn.btn-default type="submit" update
105
+
106
+ table.table.table-bordered.table-striped
107
+ tbody
108
+ - for event, index in past.each_with_index do
109
+ tr
110
+ td #{event[:message]}
111
+ td.delete-past-event
112
+ button.btn.btn-primary.pull-right id="#{index}"
113
+ span.glyphicon.glyphicon-trash
114
+ | delete
115
+
116
+
117
+ script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"
118
+ script type="text/javascript" src="/js/app.js"
@@ -0,0 +1,73 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title Status Board
5
+ meta name="viewport" content="width=device-width, initial-scale=1.0"
6
+ link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"
7
+ body
8
+
9
+ style type="text/css"
10
+ | .bs-callout {
11
+ | margin: 20px 0;
12
+ | padding: 15px 30px 15px 15px;
13
+ | border-left: 5px solid #eee;
14
+ | }
15
+ | .bs-callout h4 { margin-top: 0; }
16
+ | .bs-callout p:last-child { margin-bottom: 0; }
17
+ | .bs-callout code, .bs-callout .highlight {background-color: #fff;}
18
+ | .bs-callout-danger {background-color: #fcf2f2; border-color: #dFb5b4;}
19
+ | .bs-callout-warning {background-color: #fefbed; border-color: #f1e7bc;}
20
+ | .bs-callout-info {background-color: #f0f7fd; border-color: #d0e3f0;}
21
+ | .bs-callout-success {background-color: #dff0d8; border-color: #d6e9c6;}
22
+
23
+ == brand[:script]
24
+
25
+ nav.navbar.navbar-default role="navigation"
26
+ div.navbar-header
27
+ button.navbar-toggle data-toggle="collapse" data-target=".navbar-main-collapse"
28
+ span.sr-only Toggle Navigation
29
+ span.icon-bar
30
+ span.icon-bar
31
+ span.icon-bar
32
+ a.navbar-brand href="/" #{brand[:name]}
33
+
34
+ - if brand[:contact]
35
+ nav.collapse.navbar-collapse.navbar-main-collapse role="navigation"
36
+ a.navbar-btn.navbar-right.btn.btn-info.btn-sm href="#{brand[:contact]}" Report issue
37
+
38
+ div.container
39
+ div.row
40
+ div.col-md-8.col-md-offset-2
41
+ h2 Services
42
+
43
+ table.table.table-bordered.table-striped
44
+ thead
45
+ tr
46
+ th Service
47
+ th Status
48
+ tbody
49
+ - for name in services.keys.sort do
50
+ tr
51
+ td #{name}
52
+ td class="#{services[name][:class]}"
53
+ | #{services[name][:message]}
54
+
55
+ h2 Upcoming Events
56
+ - for event in upcoming do
57
+ div class="bs-callout bs-callout-#{event[:class]}"
58
+ p
59
+ small
60
+ i On #{event[:date]}
61
+ div
62
+ == RDiscount.new(event[:message]).to_html
63
+ h2 Latest Events
64
+ - for event in past do
65
+ div class="bs-callout bs-callout-#{event[:class]}"
66
+ p
67
+ small
68
+ i On #{event[:date]}
69
+ div
70
+ == RDiscount.new(event[:message]).to_html
71
+ script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"
72
+ script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"
73
+
data/lib/liprug.rb ADDED
@@ -0,0 +1,6 @@
1
+ require "liprug/version"
2
+ require "liprug/service"
3
+
4
+ module Liprug
5
+ # Your code goes here...
6
+ end
data/liprug.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'liprug/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "liprug"
8
+ spec.version = Liprug::VERSION
9
+ spec.authors = ["Pierre-Yves Ritschard"]
10
+ spec.email = ["pyr@spootnik.org"]
11
+ spec.description = %q{simple operational dashboard}
12
+ spec.summary = %q{operational dashboard}
13
+ spec.homepage = "https://github.com/exoscale/liprug"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "sinatra"
22
+ spec.add_dependency "slim"
23
+ spec.add_dependency "redis"
24
+ spec.add_dependency "rdiscount"
25
+ spec.add_dependency "unicorn"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liprug
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Pierre-Yves Ritschard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slim
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: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: rdiscount
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
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: unicorn
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: simple operational dashboard
112
+ email:
113
+ - pyr@spootnik.org
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - .gitignore
119
+ - Gemfile
120
+ - Gemfile.lock
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - config.ru
125
+ - lib/liprug.rb
126
+ - lib/liprug/config.rb
127
+ - lib/liprug/model.rb
128
+ - lib/liprug/public/js/app.js
129
+ - lib/liprug/service.rb
130
+ - lib/liprug/service/helpers.rb
131
+ - lib/liprug/service/routes.rb
132
+ - lib/liprug/version.rb
133
+ - lib/liprug/views/admin.slim
134
+ - lib/liprug/views/overview.slim
135
+ - liprug.gemspec
136
+ homepage: https://github.com/exoscale/liprug
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - '>='
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.0.0
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: operational dashboard
160
+ test_files: []