epi 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/epi +1 -1
- data/lib/epi/cli/command.rb +7 -0
- data/lib/epi/cli/commands/concerns/daemon.rb +33 -0
- data/lib/epi/cli/commands/config.rb +11 -3
- data/lib/epi/cli/commands/daemon.rb +14 -0
- data/lib/epi/cli/commands/help.rb +0 -0
- data/lib/epi/cli/commands/job.rb +1 -1
- data/lib/epi/cli/commands/restart.rb +16 -0
- data/lib/epi/cli/commands/start.rb +15 -0
- data/lib/epi/cli/commands/status.rb +6 -2
- data/lib/epi/cli/commands/stop.rb +16 -0
- data/lib/epi/cli.rb +1 -1
- data/lib/epi/connection.rb +7 -0
- data/lib/epi/core_ext/inflector.rb +1 -1
- data/lib/epi/daemon/receiver.rb +37 -0
- data/lib/epi/{server → daemon}/responder.rb +14 -5
- data/lib/epi/{server → daemon}/responders/config.rb +14 -2
- data/lib/epi/{server → daemon}/responders/job.rb +5 -6
- data/lib/epi/{server → daemon}/responders/shutdown.rb +1 -1
- data/lib/epi/daemon/responders/start.rb +19 -0
- data/lib/epi/{server → daemon}/responders/status.rb +4 -2
- data/lib/epi/daemon/responders/stop_all.rb +20 -0
- data/lib/epi/daemon/sender.rb +74 -0
- data/lib/epi/{server.rb → daemon.rb} +26 -27
- data/lib/epi/data.rb +4 -5
- data/lib/epi/job.rb +60 -2
- data/lib/epi/job_description.rb +6 -8
- data/lib/epi/jobs.rb +30 -2
- data/lib/epi/launch.rb +1 -1
- data/lib/epi/logging.rb +33 -0
- data/lib/epi/process_status.rb +1 -1
- data/lib/epi/running_process.rb +21 -3
- data/lib/epi/trigger.rb +53 -0
- data/lib/epi/triggers/concerns/comparison.rb +43 -0
- data/lib/epi/triggers/memory.rb +16 -0
- data/lib/epi/triggers/touch.rb +37 -0
- data/lib/epi/triggers/uptime.rb +12 -0
- data/lib/epi/version.rb +1 -1
- data/lib/epi.rb +4 -22
- metadata +26 -26
- data/lib/epi/cli/commands/server.rb +0 -38
- data/lib/epi/server/receiver.rb +0 -46
- data/lib/epi/server/responders/command.rb +0 -15
- data/lib/epi/server/sender.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff6e7a2df3b27a605072bbedff81ea4ebb5b7c1b
|
4
|
+
data.tar.gz: 4609bab09c0142f2206cb733d28fb22387806fa0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb50900af4312e6959956ec5a61af06f7dee51d8f509577e56eb55027c23a7a7c43e6dd2ea4ec03425e1bc0f880d9926e094667f55b0a7eb2d7b2c3ace8427bb
|
7
|
+
data.tar.gz: 491f3d33d0b2be9d558f159c0ac8f502a037cbea1536444c4a53ea9c77739f208ebe05507c0c895fc47e2e4b3be29401618abba1ff3ea17448b283e23c5e3608
|
data/bin/epi
CHANGED
data/lib/epi/cli/command.rb
CHANGED
@@ -19,8 +19,15 @@ module Epi
|
|
19
19
|
@args = args
|
20
20
|
end
|
21
21
|
|
22
|
+
def need_root!
|
23
|
+
process = Epi::Daemon.process
|
24
|
+
raise Exceptions::Fatal, 'You need root privileges to manage this daemon' if
|
25
|
+
process && process.was_alive? && process.root? && !Epi.root?
|
26
|
+
end
|
27
|
+
|
22
28
|
end
|
23
29
|
|
30
|
+
Dir[File.expand_path '../commands/concerns/*.rb', __FILE__].each { |f| require f }
|
24
31
|
Dir[File.expand_path '../commands/*.rb', __FILE__].each { |f| require f }
|
25
32
|
end
|
26
33
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Epi
|
2
|
+
module Cli
|
3
|
+
module Concerns
|
4
|
+
module Daemon
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def need_daemon!
|
9
|
+
raise Exceptions::Fatal, 'No daemon is running' unless Epi::Daemon.running?
|
10
|
+
end
|
11
|
+
|
12
|
+
def need_no_daemon!
|
13
|
+
raise Exceptions::Fatal, 'Daemon is already running' if Epi::Daemon.running?
|
14
|
+
end
|
15
|
+
|
16
|
+
def shutdown(&callback)
|
17
|
+
Epi::Daemon.send :shutdown, &callback
|
18
|
+
puts 'Shutting down ...'
|
19
|
+
end
|
20
|
+
|
21
|
+
def resume(&callback)
|
22
|
+
begin
|
23
|
+
Timeout::timeout(5) { sleep 0.05 while Epi::Daemon.socket_path.exist? }
|
24
|
+
rescue Timeout::Error
|
25
|
+
raise Exceptions::Fatal, 'Daemon failed to stop after 5 seconds'
|
26
|
+
end
|
27
|
+
Epi::Daemon.send :start, &callback
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -6,20 +6,28 @@ module Epi
|
|
6
6
|
def run
|
7
7
|
case args.shift
|
8
8
|
when 'add' then add
|
9
|
-
|
9
|
+
when 'remove' then remove
|
10
|
+
else raise Exceptions::Fatal, 'Unknown config command, use [ add | remove ]'
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
private
|
14
15
|
|
15
16
|
def add
|
17
|
+
Epi::Daemon.send config: {add_paths: paths}
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove
|
21
|
+
Epi::Daemon.send config: {remove_paths: paths}
|
22
|
+
end
|
23
|
+
|
24
|
+
def paths
|
16
25
|
raise Exceptions::Fatal, 'No path given' unless args.first
|
17
|
-
paths
|
26
|
+
@paths ||= args.map do |path|
|
18
27
|
path = Pathname(path)
|
19
28
|
path = Pathname('.').realpath.join(path) unless path.absolute?
|
20
29
|
path.to_s
|
21
30
|
end
|
22
|
-
Epi::Server.send config: {add_paths: paths}
|
23
31
|
end
|
24
32
|
|
25
33
|
end
|
File without changes
|
data/lib/epi/cli/commands/job.rb
CHANGED
@@ -10,7 +10,7 @@ module Epi
|
|
10
10
|
raise Exceptions::Fatal, 'No instruction given' if instruction.empty?
|
11
11
|
raise Exceptions::Fatal, 'Invalid instruction' unless
|
12
12
|
instruction =~ /^((\d+ )?(more|less)|\d+|pause|resume|reset|max|min|restart)$/
|
13
|
-
Epi::
|
13
|
+
Epi::Daemon.send job: {id: id, instruction: instruction}
|
14
14
|
end
|
15
15
|
|
16
16
|
end
|
data/lib/epi/cli.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative '../connection'
|
2
|
+
|
3
|
+
module Epi
|
4
|
+
module Daemon
|
5
|
+
class Receiver < Connection
|
6
|
+
|
7
|
+
def logger
|
8
|
+
Epi.logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def receive_object(data)
|
12
|
+
logger.debug "Received message of type '#{data[:type]}'"
|
13
|
+
begin
|
14
|
+
Responder.run(self, data.delete(:type).to_s, data) { |result| send_object result: result }
|
15
|
+
rescue Exceptions::Shutdown
|
16
|
+
send_object result: nil
|
17
|
+
Daemon.shutdown
|
18
|
+
rescue => error
|
19
|
+
send_object error: {
|
20
|
+
class: error.class.name,
|
21
|
+
message: error.message,
|
22
|
+
backtrace: error.backtrace
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def puts(text)
|
28
|
+
print "#{text}\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def print(text)
|
32
|
+
send_object print: text.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Epi
|
2
|
-
module
|
2
|
+
module Daemon
|
3
3
|
unless defined? Responder
|
4
4
|
class Responder
|
5
5
|
include Exceptions
|
@@ -9,13 +9,17 @@ module Epi
|
|
9
9
|
# @param receiver [Receiver] The receiver that is running the responder
|
10
10
|
# @param name [String] Name of the responder to invoke, e.g. 'command'
|
11
11
|
# @param data [Hash] Data included in the message, to be extracted onto the responder before it is run
|
12
|
-
def self.run(receiver, name, data)
|
12
|
+
def self.run(receiver, name, data, &callback)
|
13
13
|
klass_name = name.camelize.to_sym
|
14
14
|
klass = Responders.const_defined?(klass_name) && Responders.const_get(klass_name)
|
15
15
|
raise Fatal, 'Unknown message type' unless Class === klass && klass < Responder
|
16
|
-
responder = klass.new(receiver)
|
16
|
+
responder = klass.new(receiver, callback)
|
17
17
|
data.each { |key, value| responder.__send__ :"#{key}=", value }
|
18
|
-
responder.
|
18
|
+
if responder.respond_to? :run_async
|
19
|
+
responder.run_async
|
20
|
+
else
|
21
|
+
yield responder.run
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
attr_reader :receiver
|
@@ -24,8 +28,9 @@ module Epi
|
|
24
28
|
Epi.logger
|
25
29
|
end
|
26
30
|
|
27
|
-
def initialize(receiver)
|
31
|
+
def initialize(receiver, callback)
|
28
32
|
@receiver = receiver
|
33
|
+
@callback = callback
|
29
34
|
end
|
30
35
|
|
31
36
|
def run
|
@@ -36,6 +41,10 @@ module Epi
|
|
36
41
|
receiver.puts text
|
37
42
|
end
|
38
43
|
|
44
|
+
def done(result = nil)
|
45
|
+
@callback.call result
|
46
|
+
end
|
47
|
+
|
39
48
|
end
|
40
49
|
|
41
50
|
Dir[File.expand_path '../responders/*.rb', __FILE__].each { |f| require f }
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Epi
|
2
|
-
module
|
2
|
+
module Daemon
|
3
3
|
module Responders
|
4
4
|
class Config < Responder
|
5
5
|
|
6
|
-
attr_accessor :add_paths
|
6
|
+
attr_accessor :add_paths, :remove_paths
|
7
7
|
|
8
8
|
def run
|
9
9
|
result = []
|
@@ -19,6 +19,18 @@ module Epi
|
|
19
19
|
result << "Added config path: #{path}"
|
20
20
|
end
|
21
21
|
end if add_paths
|
22
|
+
remove_paths.each do |path|
|
23
|
+
path = path.to_s
|
24
|
+
if configs.include?(path)
|
25
|
+
logger.info "Removing config path: #{path}"
|
26
|
+
# TODO: clean up any junk the config file may have left
|
27
|
+
configs.delete path
|
28
|
+
result << "Removed config path: #{path}"
|
29
|
+
else
|
30
|
+
logger.warn "Tried to remove unknown config path: #{path}"
|
31
|
+
result << "Config path not loaded: #{path}"
|
32
|
+
end
|
33
|
+
end if remove_paths
|
22
34
|
Data.save
|
23
35
|
Jobs.beat!
|
24
36
|
result.join ' '
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Epi
|
2
|
-
module
|
2
|
+
module Daemon
|
3
3
|
module Responders
|
4
4
|
class Job < Responder
|
5
5
|
|
@@ -21,11 +21,11 @@ module Epi
|
|
21
21
|
@job ||= Jobs[id]
|
22
22
|
end
|
23
23
|
|
24
|
-
def set(count
|
24
|
+
def set(count)
|
25
25
|
allowed = job.allowed_processes
|
26
|
-
raise Exceptions::Fatal, "Requested count #{count} is outside allowed range #{allowed}" unless
|
26
|
+
raise Exceptions::Fatal, "Requested count #{count} is outside allowed range #{allowed}" unless allowed === count
|
27
27
|
original = job.expected_count
|
28
|
-
raise Exceptions::Fatal, "Already running #{count} process#{count != 1 ? 'es' : ''}" unless
|
28
|
+
raise Exceptions::Fatal, "Already running #{count} process#{count != 1 ? 'es' : ''}" unless original != count
|
29
29
|
job.expected_count = count
|
30
30
|
job.sync!
|
31
31
|
"#{count < original ? 'De' : 'In'}creasing '#{job.name}' processes by #{(original - count).abs} (from #{original} to #{count})"
|
@@ -59,8 +59,7 @@ module Epi
|
|
59
59
|
def restart
|
60
60
|
count = job.expected_count
|
61
61
|
raise Exceptions::Fatal, 'This job has no processes to restart' if count == 0
|
62
|
-
|
63
|
-
set count
|
62
|
+
job.restart!
|
64
63
|
"Replacing #{count} '#{job.name}' process#{count != 1 ? 'es' : ''}"
|
65
64
|
end
|
66
65
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Epi
|
2
|
+
module Daemon
|
3
|
+
module Responders
|
4
|
+
|
5
|
+
class Start < Responder
|
6
|
+
|
7
|
+
def run
|
8
|
+
count = Jobs.running_process_count
|
9
|
+
if count == 0
|
10
|
+
'Starting ...'
|
11
|
+
else
|
12
|
+
"Starting #{count} process#{count == 1 ? '' : 'es'} ..."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
1
3
|
module Epi
|
2
|
-
module
|
4
|
+
module Daemon
|
3
5
|
module Responders
|
4
6
|
# noinspection RubyStringKeysInHashInspection
|
5
7
|
class Status < Responder
|
@@ -14,7 +16,7 @@ module Epi
|
|
14
16
|
def stats
|
15
17
|
{
|
16
18
|
'Running as' => `whoami`.chomp,
|
17
|
-
'Since' =>
|
19
|
+
'Since' => Daemon.start_time.strftime('%c'),
|
18
20
|
'Jobs' => jobs
|
19
21
|
}
|
20
22
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Epi
|
2
|
+
module Daemon
|
3
|
+
module Responders
|
4
|
+
|
5
|
+
class StopAll < Responder
|
6
|
+
|
7
|
+
def run_async
|
8
|
+
count = Jobs.running_process_count
|
9
|
+
if count > 0
|
10
|
+
puts "Stopping #{count} process#{count == 1 ? '' : 'es'} ..."
|
11
|
+
Jobs.shutdown! { done }
|
12
|
+
else
|
13
|
+
done
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative '../connection'
|
2
|
+
|
3
|
+
module Epi
|
4
|
+
module Daemon
|
5
|
+
class Sender < Connection
|
6
|
+
include Exceptions
|
7
|
+
|
8
|
+
# Send a message to the Epi server
|
9
|
+
#
|
10
|
+
# @example Get Epi's status
|
11
|
+
# Sender.send :status
|
12
|
+
#
|
13
|
+
# @example Add a config file
|
14
|
+
# Sender.send config: {add_paths: ['config.epi']}
|
15
|
+
#
|
16
|
+
# @param what [Hash|Symbol] Either a symbol being the message type, or a hash
|
17
|
+
# with a single key (a symbol) being the message type, and value (a hash) being the message.
|
18
|
+
def self.send(what, &callback)
|
19
|
+
|
20
|
+
raise ArgumentError, 'Expected a hash with one key (a symbol) and value (a hash)' unless
|
21
|
+
Symbol === what ||
|
22
|
+
(Hash === what && what.count == 1 && Symbol === what.keys.first && Hash === what.values.first)
|
23
|
+
|
24
|
+
data = case what
|
25
|
+
when Symbol then {type: what}
|
26
|
+
when Hash then what.values.first.merge(type: what.keys.first)
|
27
|
+
else nil
|
28
|
+
end
|
29
|
+
|
30
|
+
EventMachine.connect Daemon.socket_path.to_s, Sender, data, callback
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(data, callback)
|
35
|
+
@callback = callback
|
36
|
+
send_object data
|
37
|
+
end
|
38
|
+
|
39
|
+
def receive_object(data)
|
40
|
+
if data[:print]
|
41
|
+
STDOUT << data[:print]
|
42
|
+
return
|
43
|
+
end
|
44
|
+
|
45
|
+
if data.key? :result
|
46
|
+
result = data[:result]
|
47
|
+
|
48
|
+
if @callback
|
49
|
+
@callback.call result
|
50
|
+
else
|
51
|
+
puts result unless result.nil?
|
52
|
+
EM.stop
|
53
|
+
end
|
54
|
+
|
55
|
+
elsif data[:error]
|
56
|
+
|
57
|
+
error = data[:error]
|
58
|
+
if error[:class] == Fatal.name
|
59
|
+
STDERR << error[:message]
|
60
|
+
STDERR << "\n"
|
61
|
+
else
|
62
|
+
STDERR << "#{error[:class]}: #{error[:message]}\n"
|
63
|
+
error[:backtrace].each { |x| STDERR << "\t#{x}\n" }
|
64
|
+
end
|
65
|
+
|
66
|
+
EM.stop
|
67
|
+
end
|
68
|
+
|
69
|
+
close_connection
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
5
|
-
require_relative '
|
3
|
+
require_relative 'daemon/sender'
|
4
|
+
require_relative 'daemon/receiver'
|
5
|
+
require_relative 'daemon/responder'
|
6
6
|
|
7
7
|
module Epi
|
8
|
-
module
|
8
|
+
module Daemon
|
9
9
|
|
10
10
|
class << self
|
11
11
|
|
@@ -19,7 +19,7 @@ module Epi
|
|
19
19
|
should_run_as_root = Data.root?
|
20
20
|
|
21
21
|
if running? && should_run_as_root && !process.root?
|
22
|
-
logger.info "
|
22
|
+
logger.info "Daemon needs to run as root, but is running as #{process.user}"
|
23
23
|
shutdown
|
24
24
|
end
|
25
25
|
|
@@ -29,15 +29,15 @@ module Epi
|
|
29
29
|
'or specify EPI_HOME as a directory other than /etc/epi'
|
30
30
|
end
|
31
31
|
|
32
|
-
logger.info 'Starting
|
33
|
-
Epi.launch [$0, '
|
34
|
-
|
35
|
-
|
32
|
+
logger.info 'Starting daemon'
|
33
|
+
Epi.launch [$0, 'daemon'],
|
34
|
+
stdout: Data.home + 'daemon.log',
|
35
|
+
stderr: Data.home + 'daemon_errors.log'
|
36
36
|
|
37
37
|
begin
|
38
38
|
Timeout::timeout(5) { sleep 0.05 until socket_path.exist? }
|
39
39
|
rescue Timeout::Error
|
40
|
-
raise Exceptions::Fatal, '
|
40
|
+
raise Exceptions::Fatal, 'Daemon not started after 5 seconds'
|
41
41
|
end unless socket_path.exist?
|
42
42
|
end
|
43
43
|
end
|
@@ -47,43 +47,42 @@ module Epi
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def run
|
50
|
-
raise Exceptions::Fatal, '
|
50
|
+
raise Exceptions::Fatal, 'Daemon already running' if running?
|
51
51
|
|
52
|
-
# Save the
|
53
|
-
Data.
|
52
|
+
# Save the daemon PID
|
53
|
+
Data.daemon_pid = Process.pid
|
54
54
|
|
55
55
|
# Run an initial beat
|
56
56
|
Jobs.beat!
|
57
57
|
|
58
|
-
# Start a
|
58
|
+
# Start a daemon
|
59
59
|
EventMachine.start_unix_domain_server socket_path.to_s, Receiver
|
60
60
|
logger.info "Listening on socket #{socket_path}"
|
61
61
|
|
62
|
-
# Make sure other users can connect to the
|
62
|
+
# Make sure other users can connect to the daemon
|
63
63
|
socket_path.chmod 0777 #TODO: make configurable
|
64
64
|
|
65
|
-
# Ensure the socket is destroyed when the
|
65
|
+
# Ensure the socket is destroyed when the daemon exits
|
66
66
|
EventMachine.add_shutdown_hook { socket_path.delete }
|
67
67
|
|
68
68
|
@start_time = Time.now
|
69
69
|
end
|
70
70
|
|
71
|
-
def send(*args)
|
71
|
+
def send(*args, &callback)
|
72
72
|
ensure_running
|
73
|
-
Sender.send *args
|
73
|
+
Sender.send *args, &callback
|
74
74
|
end
|
75
75
|
|
76
76
|
def shutdown(process = nil)
|
77
|
-
|
78
|
-
|
79
|
-
if process.pid == Process.pid
|
77
|
+
raise Exceptions::Fatal, 'Attempted to shut down daemon when no daemon is running' unless running?
|
78
|
+
if (process || self.process).pid == Process.pid
|
80
79
|
EventMachine.next_tick do
|
81
80
|
EventMachine.stop_event_loop
|
82
|
-
Data.
|
83
|
-
logger.info '
|
81
|
+
Data.daemon_pid = nil
|
82
|
+
logger.info 'Daemon has shut down'
|
84
83
|
end
|
85
84
|
else
|
86
|
-
logger.info '
|
85
|
+
logger.info 'Daemon will shut down'
|
87
86
|
send :shutdown
|
88
87
|
end
|
89
88
|
end
|
@@ -93,9 +92,9 @@ module Epi
|
|
93
92
|
end
|
94
93
|
|
95
94
|
def process
|
96
|
-
|
97
|
-
@process = nil if @process && @process.pid !=
|
98
|
-
@process ||=
|
95
|
+
daemon_pid = Data.daemon_pid
|
96
|
+
@process = nil if @process && @process.pid != daemon_pid
|
97
|
+
@process ||= daemon_pid && RunningProcess.new(daemon_pid)
|
99
98
|
end
|
100
99
|
|
101
100
|
end
|
data/lib/epi/data.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
require 'bson'
|
3
2
|
require 'forwardable'
|
4
3
|
|
5
4
|
module Epi
|
@@ -13,7 +12,7 @@ module Epi
|
|
13
12
|
|
14
13
|
delegate [:[], :[]=, :read, :write, :root?, :save, :reload, :home] => :default_instance
|
15
14
|
|
16
|
-
%w[
|
15
|
+
%w[daemon_pid].each do |property|
|
17
16
|
define_method(property) { read property }
|
18
17
|
define_method(property + '=') { |value| write property, value }
|
19
18
|
end
|
@@ -104,7 +103,7 @@ module Epi
|
|
104
103
|
end
|
105
104
|
|
106
105
|
def data_file
|
107
|
-
@data_file ||= home + 'data
|
106
|
+
@data_file ||= home + 'data'
|
108
107
|
end
|
109
108
|
|
110
109
|
# Force reload of data from disk
|
@@ -114,12 +113,12 @@ module Epi
|
|
114
113
|
|
115
114
|
# Save data to disk
|
116
115
|
def save
|
117
|
-
data_file.binwrite hash
|
116
|
+
data_file.binwrite Marshal.dump hash
|
118
117
|
data_file.chmod 0644
|
119
118
|
end
|
120
119
|
|
121
120
|
def hash
|
122
|
-
@hash ||= data_file.exist? ?
|
121
|
+
@hash ||= data_file.exist? ? Marshal.load(data_file.binread) : {}
|
123
122
|
end
|
124
123
|
|
125
124
|
# Returns true if using root data at /etc/epi, or false if using user data
|