liprug 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []