perus 0.1.2 → 0.1.3

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
  SHA1:
3
- metadata.gz: 426902fcecc63ec81acf9352f4bafba9fa1988d4
4
- data.tar.gz: 36838e6272731efce01b25f8c8773d4ed8622cfe
3
+ metadata.gz: bae9eee51f945b6f2ba014af73f5cdfa8fa6ab03
4
+ data.tar.gz: 3253602c339568d245a4a448abbd3990cf89479f
5
5
  SHA512:
6
- metadata.gz: a4e06d529a4d1d00f2beba82bd5c118b662fedd4b5b70856d39fadbd9a28781c3a9c2243ab9670dac271c69ace4ad6e8961c6988590b296536816a941995d146
7
- data.tar.gz: 2bb970e51ed715718b85078c46c34218d25f4596994136215cf99d313e32a78822ebcba13120848e4b38d542caee7726a4f7a75fc15dd17fc8dd0550339abe02
6
+ metadata.gz: 4a88068ed77a22b0e7fa210394931994053dcca0d0e8b025f01e1638623f462024ebabf83a9e67c8a4f442879bdaa6f5dbda86485698bc86d57af327c253bd36
7
+ data.tar.gz: e7611359ef127aef4bbad47f0ed74baf32cf59fd808e7484fd658e108d2b0f93008655d41b672b022143ddea8cac3358629a963c274e187f13e4955bfe0e720c
data/exe/perus-pinger CHANGED
@@ -4,10 +4,10 @@ require 'optparse'
4
4
 
5
5
  options_path = Perus::Pinger::DEFAULT_PINGER_OPTIONS_PATH
6
6
 
7
- ARGV.options do |opts|
7
+ OptionParser.new do |opts|
8
8
  opts.banner = "Usage: perus-pinger [options]"
9
9
 
10
- opts.on('-c', '--config', String, "Path to config file (default: #{Perus::Pinger::DEFAULT_PINGER_OPTIONS_PATH})") do |c|
10
+ opts.on('-c', '--config PATH', "Path to config file (default: #{Perus::Pinger::DEFAULT_PINGER_OPTIONS_PATH})") do |c|
11
11
  options_path = c
12
12
  end
13
13
 
@@ -15,9 +15,7 @@ ARGV.options do |opts|
15
15
  puts opts
16
16
  exit
17
17
  end
18
-
19
- opts.parse!
20
- end
18
+ end.parse!
21
19
 
22
20
  pinger = Perus::Pinger::Pinger.new(options_path)
23
21
  pinger.run
data/exe/perus-server CHANGED
@@ -3,21 +3,29 @@ require 'perus'
3
3
  require 'optparse'
4
4
 
5
5
  options_path = Perus::Server::DEFAULT_SERVER_OPTIONS_PATH
6
+ environment = 'development'
6
7
 
7
- ARGV.options do |opts|
8
+ OptionParser.new do |opts|
8
9
  opts.banner = "Usage: perus-server [options]"
9
10
 
10
- opts.on('-c', '--config', String, "Path to config file (default: #{Perus::Server::DEFAULT_SERVER_OPTIONS_PATH})") do |c|
11
+ opts.on('-c', '--config PATH', "Path to config file (default: #{Perus::Server::DEFAULT_SERVER_OPTIONS_PATH})") do |c|
11
12
  options_path = c
12
13
  end
13
14
 
15
+ opts.on('-e', '--env ENV', "Environment (values: development (default), production)") do |e|
16
+ environment = e
17
+
18
+ unless %w{development production}.include?(e)
19
+ puts 'Environment must be "development" or "production"'
20
+ exit
21
+ end
22
+ end
23
+
14
24
  opts.on('-h', '--help', 'Prints this help') do
15
25
  puts opts
16
26
  exit
17
27
  end
28
+ end.parse!
18
29
 
19
- opts.parse!
20
- end
21
-
22
- server = Perus::Server::Server.new(options_path)
30
+ server = Perus::Server::Server.new(options_path, environment)
23
31
  server.run
@@ -1,4 +1,5 @@
1
1
  require 'ostruct'
2
+ require 'open3'
2
3
 
3
4
  module Perus::Pinger
4
5
  class Option
@@ -119,5 +120,19 @@ module Perus::Pinger
119
120
  # called after sending data to server. remove temporary
120
121
  # files etc.
121
122
  end
123
+
124
+ # helper function to run shell commands and return early if anything
125
+ # is written to stderr or the command didn't run successfully
126
+ class ShellCommandError < StandardError; end
127
+ def shell(command)
128
+ out, err, status = Open3.capture3(command)
129
+ raise ShellCommandError.new(err.strip) unless err.empty?
130
+ raise ShellCommandError.new(out.strip) if status.exitstatus > 0
131
+ out
132
+ end
133
+
134
+ def darwin?
135
+ shell('uname -s').strip == 'Darwin'
136
+ end
122
137
  end
123
138
  end
@@ -17,6 +17,9 @@ module Perus::Pinger
17
17
  else
18
18
  result = false
19
19
  end
20
+
21
+ # clean up any memory used by the executed command
22
+ send_command('{"id":2,"method":"Runtime.releaseObjectGroup","params":{"objectGroup":"perus"}}')
20
23
  end
21
24
  end
22
25
 
@@ -6,8 +6,8 @@ module Perus::Pinger
6
6
  option :signal, default: 'KILL'
7
7
 
8
8
  def run
9
- result = `killall -#{option.signal} #{option.process_name}`
10
- result.include?('no process found') ? result : true
9
+ result = shell("killall -#{option.signal} #{option.process_name}")
10
+ true # shell will capture any errors
11
11
  end
12
12
  end
13
13
  end
@@ -5,9 +5,9 @@ module Perus::Pinger
5
5
 
6
6
  def run
7
7
  if options.sudo
8
- result = `sudo gem upgrade perus`
8
+ result = shell('sudo gem upgrade perus')
9
9
  else
10
- result = `gem upgrade perus`
10
+ result = shell('gem upgrade perus')
11
11
  end
12
12
 
13
13
  result.include?('ERROR') ? result : true
@@ -5,10 +5,10 @@ module Perus::Pinger
5
5
  metric!
6
6
 
7
7
  def run
8
- if `uname -s`.strip == 'Darwin'
9
- percent = 100 - `iostat -n 0`.split("\n")[2].split[2].to_i
8
+ if darwin?
9
+ percent = 100 - shell('iostat dxxvdfs -n 0').split("\n")[2].split[2].to_i
10
10
  else
11
- percent = `grep 'cpu ' /proc/stat | awk '{print (1 - ($5 / ($2+$3+$4+$5+$6+$7+$8)))*100}'`
11
+ percent = shell("grep 'cpu ' /proc/stat | awk '{print (1 - ($5 / ($2+$3+$4+$5+$6+$7+$8)))*100}'")
12
12
  end
13
13
 
14
14
  {cpu_all: percent.to_f}
@@ -7,7 +7,7 @@ module Perus::Pinger
7
7
 
8
8
  def run
9
9
  regex = "/^#{options.drive.gsub("/", "\\/")}/"
10
- percent = `df -h / | awk '#{regex} {print $5}'`
10
+ percent = shell("df -h / | awk '#{regex} {print $5}'")
11
11
  {hd_used: percent.to_i}
12
12
  end
13
13
  end
@@ -4,7 +4,7 @@ module Perus::Pinger
4
4
  metric!
5
5
 
6
6
  def run
7
- percent = `cat /proc/meminfo | awk '{if ($1=="MemTotal:") total = $2; if ($1 == "MemFree:") free = $2;} END {print (1 - (free / total))*100}'`
7
+ percent = shell(%q[cat /proc/meminfo | awk '{if ($1=="MemTotal:") total = $2; if ($1 == "MemFree:") free = $2;} END {print (1 - (free / total))*100}'])
8
8
  {mem_all: percent.to_f}
9
9
  end
10
10
  end
@@ -10,12 +10,13 @@ module Perus::Pinger
10
10
  metric!
11
11
 
12
12
  def run
13
- path = options.process_path.gsub("/", "\\\\\\/")
14
- cpu, mem = `ps aux | awk '/#{path}/ {cpu += $3; mem += $4} END {print cpu, mem;}'`.split
15
- if `uname -s`.strip == 'Darwin'
16
- core_count = `sysctl -n hw.ncpu`
13
+ path = options.process_path.gsub("/", "\\/")
14
+ cpu, mem = shell("ps aux | awk '/#{path}/ {cpu += $3; mem += $4} END {print cpu, mem;}'").split
15
+
16
+ if darwin?
17
+ core_count = shell('sysctl -n hw.ncpu')
17
18
  else
18
- core_count = `cat /proc/cpuinfo | grep processor | awk '{count += 1} END {print count}'`
19
+ core_count = shell("cat /proc/cpuinfo | grep processor | awk '{count += 1} END {print count}'")
19
20
  end
20
21
 
21
22
  {
@@ -9,19 +9,22 @@ module Perus::Pinger
9
9
  metric!
10
10
 
11
11
  def run
12
- if `uname -s`.strip == 'Darwin'
12
+ if darwin?
13
13
  if options.resize[-1] != '%'
14
14
  raise 'Non percent resize option unsupported by OS X currently'
15
15
  else
16
16
  percent = options.resize.to_f / 100
17
17
  end
18
18
 
19
- `screencapture -m -t jpg -x #{options.path}`
20
- width = `sips -g pixelWidth #{options.path}`.match(/pixelWidth: (\d+)/)[1]
21
- height = `sips -g pixelHeight #{options.path}`.match(/pixelHeight: (\d+)/)[1]
19
+ shell("screencapture -m -t jpg -x #{options.path}")
20
+ width = shell("sips -g pixelWidth #{options.path}").match(/pixelWidth: (\d+)/)[1]
21
+ height = shell("sips -g pixelHeight #{options.path}").match(/pixelHeight: (\d+)/)[1]
22
+
23
+ # sips helpfully prints data to stderr, so it's run with
24
+ # backticks to avoid thowing an exception on success
22
25
  `sips -z #{height.to_i * percent} #{width.to_i * percent} #{options.path}`
23
26
  else
24
- `export DISPLAY=:0; import -window root -resize #{options.resize} #{options.path}`
27
+ shell("export DISPLAY=:0; import -window root -resize #{options.resize} #{options.path}")
25
28
  end
26
29
 
27
30
  @screenshot_file = File.new(options.path)
@@ -29,7 +32,10 @@ module Perus::Pinger
29
32
  end
30
33
 
31
34
  def cleanup
32
- @screenshot_file.close unless @screenshot_file.closed?
35
+ unless @screenshot_file.nil? || @screenshot_file.closed?
36
+ @screenshot_file.close
37
+ end
38
+
33
39
  File.delete(options.path)
34
40
  end
35
41
  end
@@ -6,10 +6,10 @@ module Perus::Pinger
6
6
  metric!
7
7
 
8
8
  def run
9
- if `uname -s`.strip == 'Darwin'
10
- degrees = `istats cpu temp`.split[2].match(/([0-9\.]+)/)[0]
9
+ if darwin?
10
+ degrees = shell('istats cpu temp').split[2].match(/([0-9\.]+)/)[0]
11
11
  else
12
- degrees = `sensors | grep "#{options.device}:"`.match(/#{options.device}:\s+(\S+)/)[1]
12
+ degrees = shell(%q[sensors | grep "#{options.device}:"]).match(/#{options.device}:\s+(\S+)/)[1]
13
13
  end
14
14
 
15
15
  {temp: degrees.to_f}
@@ -4,7 +4,7 @@ module Perus::Pinger
4
4
  metric!
5
5
 
6
6
  def run
7
- {uptime: `uptime`.strip}
7
+ {uptime: shell('uptime').strip}
8
8
  end
9
9
  end
10
10
  end
@@ -10,7 +10,8 @@ module Perus::Pinger
10
10
  metric!
11
11
 
12
12
  def run
13
- line = `cat #{options.path} | grep #{options.grep}`
13
+ grep = optipns.grep.gsub('"', '\\"')
14
+ line = shell(%q[cat #{options.path} | egrep "#{grep}"])
14
15
  value = line.match(Regexp.compile(options.match))[1]
15
16
  {options.name => value}
16
17
  end
@@ -51,8 +51,8 @@ module Perus::Pinger
51
51
 
52
52
  # cache urls on initialisation since the urls depend on values known
53
53
  # at startup and that won't change over the object lifetime
54
- config_path = URI("/systems/#{Pinger.options.system_id}/config")
55
- pinger_path = URI("/systems/#{Pinger.options.system_id}/ping")
54
+ config_path = URI("systems/#{Pinger.options.system_id}/config")
55
+ pinger_path = URI("systems/#{Pinger.options.system_id}/ping")
56
56
  server_uri = URI(Pinger.options.server)
57
57
 
58
58
  @config_url = (server_uri + config_path).to_s
@@ -92,10 +92,10 @@ module Perus::Pinger
92
92
  @metric_errors[metric.name] ||= []
93
93
  @metrics << metric.new(config['options'])
94
94
  else
95
- @metric_errors[config['type']] = e.inspect
95
+ @metric_errors[config['type']] = format_exception(e)
96
96
  end
97
97
  rescue => e
98
- @metric_errors[metric.name] << e.inspect
98
+ @metric_errors[metric.name] << format_exception(e)
99
99
  end
100
100
  end
101
101
 
@@ -105,7 +105,7 @@ module Perus::Pinger
105
105
  @actions << command.new(config['options'], config['id'])
106
106
  rescue => e
107
107
  if config['id']
108
- @action_results[config['id']] = e.inspect
108
+ @action_results[config['id']] = format_exception(e)
109
109
  else
110
110
  puts 'Error - action does not have an associated id'
111
111
  p config
@@ -117,13 +117,21 @@ module Perus::Pinger
117
117
  #----------------------
118
118
  # run
119
119
  #----------------------
120
+ def format_exception(e)
121
+ if e.backtrace.empty?
122
+ e.inspect
123
+ else
124
+ "#{e.inspect}\n#{e.backtrace.first}"
125
+ end
126
+ end
127
+
120
128
  def run_metrics
121
129
  @metrics.each do |metric|
122
130
  begin
123
131
  result = metric.run
124
132
  @metric_results.merge!(result)
125
133
  rescue => e
126
- @metric_errors[metric.class.name] << e.inspect
134
+ @metric_errors[metric.class.name] << format_exception(e)
127
135
  end
128
136
  end
129
137
  end
@@ -141,7 +149,7 @@ module Perus::Pinger
141
149
  @action_results[action.id] = result
142
150
 
143
151
  rescue => e
144
- @action_results[action.id] = e.inspect
152
+ @action_results[action.id] = format_exception(e)
145
153
  end
146
154
  end
147
155
  end
@@ -179,7 +187,7 @@ module Perus::Pinger
179
187
  RestClient.post(@pinger_url, payload)
180
188
  rescue => e
181
189
  puts 'Ping failed with exception'
182
- puts e.inspect
190
+ puts format_exception(e)
183
191
  end
184
192
  end
185
193
 
@@ -192,7 +200,7 @@ module Perus::Pinger
192
200
  metric.cleanup
193
201
  rescue => e
194
202
  puts 'Error running metric cleanup'
195
- puts e.inspect
203
+ puts format_exception(e)
196
204
  end
197
205
  end
198
206
 
@@ -201,7 +209,7 @@ module Perus::Pinger
201
209
  action.cleanup
202
210
  rescue => e
203
211
  puts 'Error running action cleanup'
204
- puts e.inspect
212
+ puts format_exception(e)
205
213
  end
206
214
  end
207
215
 
@@ -210,7 +218,7 @@ module Perus::Pinger
210
218
  code.call
211
219
  rescue => e
212
220
  puts 'Error running late action'
213
- puts e.inspect
221
+ puts format_exception(e)
214
222
  end
215
223
  end
216
224
  end
@@ -11,6 +11,7 @@ module Perus::Server
11
11
  helpers Helpers
12
12
 
13
13
  before do
14
+ @singular = '#{singular}'
14
15
  @plural = '#{plural}'
15
16
  @title = '#{title}'
16
17
  load_site_information
@@ -1,5 +1,4 @@
1
1
  require 'sinatra/base'
2
- require 'sinatra/synchrony'
3
2
  require 'sinatra/reloader'
4
3
  require 'sequel'
5
4
  require 'json'
@@ -10,7 +9,6 @@ module Perus::Server
10
9
  #----------------------
11
10
  # config
12
11
  #----------------------
13
- register Sinatra::Synchrony
14
12
  helpers Helpers
15
13
 
16
14
  configure do
@@ -1,9 +1,15 @@
1
1
  require 'sequel'
2
2
  require 'sequel/plugins/serialization'
3
+ require 'concurrent'
3
4
 
4
5
  module Perus::Server
5
6
  module DB
7
+ def self.db
8
+ @db
9
+ end
10
+
6
11
  def self.start
12
+ puts 'Loading database'
7
13
  Sequel.extension :migration
8
14
  Sequel.extension :inflector
9
15
 
@@ -24,6 +30,62 @@ module Perus::Server
24
30
  require File.join(__dir__, 'models', 'command_config')
25
31
  require File.join(__dir__, 'models', 'script_command')
26
32
  require File.join(__dir__, 'models', 'config_metric')
33
+
34
+ # attempt to run vacuum twice a day. this is done to increase
35
+ # performance rather than reclaim unused space. as old values and
36
+ # metrics are deleted the data become very fragmented. vacuuming
37
+ # restructures the db so system records in the values index should
38
+ # be sequentially stored
39
+ vacuum_task = Concurrent::TimerTask.new do
40
+ @db.execute('vacuum')
41
+ end
42
+
43
+ # fire every 12 hours
44
+ vacuum_task.execution_interval = 60 * 60 * 12
45
+ vacuum_task.execute
46
+
47
+ # a fixed number of hours of data are kept in the database. once an
48
+ # hour, old values and files are removed. if all values of a metric
49
+ # are removed from a system, the accompanying metric record is also
50
+ # removed.
51
+ cleanup_task = Concurrent::TimerTask.new do
52
+ Perus::Server::DB.cleanup
53
+ end
54
+
55
+ # fire every hour
56
+ cleanup_task.execution_interval = 60 * 60
57
+ cleanup_task.execute
58
+ end
59
+
60
+ def self.cleanup
61
+ puts 'Cleaning old values and metrics'
62
+ keep_hours = Server.options.keep_hours
63
+
64
+ # remove old values
65
+ min_timestamp = Time.now.to_i - (keep_hours * 60)
66
+ values = Value.where("timestamp < #{min_timestamp}")
67
+ puts "Deleting #{values.count} values"
68
+ values.each(&:destroy)
69
+
70
+ # remove metrics from systems if they no longer have any values
71
+ empty_deleted = 0
72
+ file_deleted = 0
73
+
74
+ Metric.each do |metric|
75
+ if metric.file
76
+ path = metric.path
77
+ if !File.exists?(path) || File.mtime(path).to_i < min_timestamp
78
+ metric.destroy
79
+ file_deleted += 1
80
+ end
81
+ elsif metric.values_dataset.empty?
82
+ metric.destroy
83
+ empty_deleted += 1
84
+ end
85
+ end
86
+
87
+ puts "#{empty_deleted} metrics were deleted as they had no values"
88
+ puts "#{file_deleted} metrics were deleted as they had old files"
27
89
  end
28
90
  end
29
91
  end
@@ -1,5 +1,7 @@
1
1
  module Perus::Server
2
2
  class Form
3
+ include Helpers
4
+
3
5
  def initialize(record)
4
6
  @record = record
5
7
  end
@@ -33,7 +35,8 @@ module Perus::Server
33
35
  end
34
36
 
35
37
  def input(field, options)
36
- "<input type=\"text\" name=\"record[#{field}]\" value=\"#{@record.send(field)}\">"
38
+ value = escape_quotes(@record.send(field))
39
+ "<input type=\"text\" name=\"record[#{field}]\" value=\"#{value}\">"
37
40
  end
38
41
 
39
42
  def textarea(field, options)
@@ -56,7 +59,7 @@ module Perus::Server
56
59
  existing = @record.send(field)
57
60
  option_rows = options.collect do |(value, name)|
58
61
  selected = existing == value ? 'selected' : ''
59
- "<option value=\"#{value}\" #{selected}>#{name || value}</option>"
62
+ "<option value=\"#{escape_quotes(value)}\" #{selected}>#{name || value}</option>"
60
63
  end
61
64
 
62
65
  "<select name=\"record[#{field}]\">#{option_rows.join("\n")}</select>"
@@ -39,6 +39,10 @@ module Perus::Server
39
39
  text.gsub('<', '&lt;').gsub('>', '&gt;')
40
40
  end
41
41
 
42
+ def escape_quotes(text)
43
+ text.to_s.gsub('"', '&quot;')
44
+ end
45
+
42
46
  def url_prefix
43
47
  Server.options.url_prefix
44
48
  end
@@ -16,6 +16,10 @@ module Perus::Server
16
16
  end
17
17
  end
18
18
 
19
+ def can_delete?
20
+ systems_dataset.empty?
21
+ end
22
+
19
23
  def validate
20
24
  super
21
25
  validates_presence :name
@@ -8,5 +8,17 @@ module Perus::Server
8
8
  validates_presence :name
9
9
  validates_unique :name
10
10
  end
11
+
12
+ def after_destroy
13
+ super
14
+
15
+ # rather than deleting all systems in a group, each system in the
16
+ # group is just removed from the group instead. this is better than
17
+ # accidentally removing a group and all related system data.
18
+ systems.each do |system|
19
+ system.group_id = nil
20
+ system.save
21
+ end
22
+ end
11
23
  end
12
24
  end
@@ -24,6 +24,19 @@ module Perus::Server
24
24
  (prefix + path).to_s
25
25
  end
26
26
 
27
+ def path
28
+ File.join(system.uploads_dir, file['filename'])
29
+ end
30
+
31
+ def values_dataset
32
+ system.values_dataset.where(metric: name)
33
+ end
34
+
35
+ def after_destroy
36
+ super
37
+ File.unlink(path) if file && File.exists?(path)
38
+ end
39
+
27
40
  def self.add(name, system_id, type, file_data = nil)
28
41
  existing = Metric.where(system_id: system_id, name: name, type: type).first
29
42
 
@@ -2,6 +2,7 @@ module Perus::Server
2
2
  class Script < Sequel::Model
3
3
  plugin :validation_helpers
4
4
  one_to_many :script_commands, order: 'name asc'
5
+ one_to_many :actions
5
6
 
6
7
  def code_name
7
8
  name.gsub(' ', '_').camelize
@@ -25,6 +26,10 @@ module Perus::Server
25
26
  end
26
27
  end
27
28
 
29
+ def can_delete?
30
+ actions_dataset.empty?
31
+ end
32
+
28
33
  def validate
29
34
  super
30
35
  validates_presence :name
@@ -18,6 +18,19 @@ module Perus::Server
18
18
  validates_unique :name
19
19
  end
20
20
 
21
+ def after_destroy
22
+ super
23
+
24
+ # remove dependent records
25
+ metrics.each(&:destroy)
26
+ values.each(&:destroy)
27
+ actions.each(&:destroy)
28
+ collection_errors.each(&:destroy)
29
+
30
+ # remove any uploaded files
31
+ FileUtils.rm_rf([uploads_dir], secure: true)
32
+ end
33
+
21
34
  def pending_actions
22
35
  actions_dataset.where(timestamp: nil).all
23
36
  end
@@ -252,14 +252,21 @@ main {
252
252
  #meta img {
253
253
  display: inline-block;
254
254
  vertical-align: top;
255
- width: 450px;
256
255
  margin-right: 30px;
257
256
  }
258
257
 
258
+ #meta.portrait img, #meta.other img {
259
+ width: 250px;
260
+ }
261
+
262
+ #meta.landscape img {
263
+ width: 450px;
264
+ }
265
+
259
266
  #meta dl {
260
267
  display: inline-block;
261
268
  vertical-align: top;
262
- width: 300px;
269
+ width: 450px;
263
270
  }
264
271
 
265
272
  #meta dt, #meta dd {
@@ -275,7 +282,7 @@ main {
275
282
  }
276
283
 
277
284
  #meta dd {
278
- width: 190px;
285
+ width: 390px;
279
286
  line-height: 16px;
280
287
  }
281
288
 
@@ -8,14 +8,16 @@ DEFAULT_SERVER_OPTIONS = {
8
8
  'listen' => '0.0.0.0',
9
9
  'port' => 3000,
10
10
  'site_name' => 'Perus',
11
- 'url_prefix' => '/'
11
+ 'url_prefix' => '/',
12
+ 'keep_hours' => 24
12
13
  }
13
14
  }
14
15
 
15
16
  module Perus::Server
16
17
  class Server
17
- def initialize(options_path = DEFAULT_SERVER_OPTIONS_PATH)
18
+ def initialize(options_path = DEFAULT_SERVER_OPTIONS_PATH, environment = 'development')
18
19
  self.class.options.load(options_path, DEFAULT_SERVER_OPTIONS)
20
+ ENV['RACK_ENV'] = environment
19
21
  DB.start
20
22
  end
21
23
 
@@ -5,8 +5,17 @@
5
5
  <table>
6
6
  <% @records.each do |record| %>
7
7
  <tr>
8
- <td><a href="<%= url_prefix %>admin/<%= @plural %>/<%= record.id %>"><%= record.name %></a></td>
9
- <td><a href="<%= url_prefix %>admin/<%= @plural %>/<%= record.id %>">Delete</a></td>
8
+ <td>
9
+ <a href="<%= url_prefix %>admin/<%= @plural %>/<%= record.id %>"><%= record.name %></a>
10
+ </td>
11
+ <td>
12
+ <% if !record.respond_to?(:can_delete?) || record.can_delete? %>
13
+ <form class="delete-row" action="<%= url_prefix %>admin/<%= @plural %>/<%= record.id %>" method="POST">
14
+ <input type="hidden" name="_method" value="DELETE">
15
+ <input type="submit" value="Delete">
16
+ </form>
17
+ <% end %>
18
+ </td>
10
19
  </tr>
11
20
  <% end %>
12
21
  </table>
@@ -16,3 +25,10 @@
16
25
  </section>
17
26
 
18
27
  <p class="actions"><a href="<%= url_prefix %>admin/<%= @plural %>/new">New <%= @title.downcase %></a></p>
28
+
29
+ <script>
30
+ $('form.delete-row').submit(function(event) {
31
+ if (!confirm('Are you sure you want to delete this <%= @singular %>?'))
32
+ event.preventDefault();
33
+ });
34
+ </script>
@@ -19,7 +19,7 @@
19
19
  <% end %>
20
20
  </select>
21
21
  <% else %>
22
- <input type="text" name="options[<%= option.name %>]" placeholder="<%= option.default %>" value="<%= command_options[option.name.to_s] if defined?(command_options) %>">
22
+ <input type="text" name="options[<%= option.name %>]" placeholder="<%= option.default %>" value="<%= escape_quotes(command_options[option.name.to_s]) if defined?(command_options) %>">
23
23
  <% end %>
24
24
  </span>
25
25
  </p>
@@ -1,6 +1,6 @@
1
1
  <% errors.each do |error| %>
2
2
  <li>
3
3
  <h1><%= error.module %> - <%= Time.at(error.timestamp).ctime %></h1>
4
- <p><%= clean_arrows(error.description) %></p>
4
+ <p><%= clean_arrows(error.description).gsub("\n", "<br>") %></p>
5
5
  </li>
6
6
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <h1>System: <%= @system.name %></h1>
2
2
 
3
- <section id="meta">
3
+ <section id="meta" class="<%= @system.orientation.downcase %>">
4
4
  <img src="<%= @uploads['screenshot'] %>">
5
5
  <dl>
6
6
  <% unless @system.logical_name.empty? %>
data/lib/perus/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Perus
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  end
data/lib/perus.rb CHANGED
@@ -7,6 +7,21 @@ end
7
7
 
8
8
  if ARGV[0] == 'server'
9
9
  Perus::Server::Server.new.run
10
+
10
11
  elsif ARGV[0] == 'pinger'
11
12
  Perus::Pinger::Pinger.new.run
13
+
14
+ elsif ARGV[0] == 'console'
15
+ require 'irb'
16
+
17
+ # start in the Server namespace
18
+ include Perus::Server
19
+
20
+ # console is used to access the database. initialise a server to load
21
+ # default settings and start the database connection.
22
+ Server.new
23
+
24
+ # remove the arg otherwise irb will try to load a file named 'console'
25
+ ARGV.shift
26
+ IRB.start
12
27
  end
data/perus.gemspec CHANGED
@@ -26,8 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'faye-websocket', '~> 0.9'
27
27
  spec.add_dependency 'rest-client', '~> 1.8'
28
28
  spec.add_dependency 'sinatra', '~> 1.4'
29
- spec.add_dependency 'sinatra-synchrony', '~> 0.4'
30
- spec.add_dependency 'sinatra-websocket', '~> 0.3'
31
29
  spec.add_dependency 'sinatra-contrib', '~> 1.4'
32
30
  spec.add_dependency 'sqlite3', '~> 1.3'
33
31
  spec.add_dependency 'sequel', '~> 4.23'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Cannings
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-07-10 00:00:00.000000000 Z
11
+ date: 2015-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,34 +108,6 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '1.4'
111
- - !ruby/object:Gem::Dependency
112
- name: sinatra-synchrony
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - "~>"
116
- - !ruby/object:Gem::Version
117
- version: '0.4'
118
- type: :runtime
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - "~>"
123
- - !ruby/object:Gem::Version
124
- version: '0.4'
125
- - !ruby/object:Gem::Dependency
126
- name: sinatra-websocket
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: '0.3'
132
- type: :runtime
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - "~>"
137
- - !ruby/object:Gem::Version
138
- version: '0.3'
139
111
  - !ruby/object:Gem::Dependency
140
112
  name: sinatra-contrib
141
113
  requirement: !ruby/object:Gem::Requirement
@@ -352,4 +324,3 @@ signing_key:
352
324
  specification_version: 4
353
325
  summary: Simple system overview server
354
326
  test_files: []
355
- has_rdoc: