rservicebus2 0.0.1
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/LICENSE +64 -0
- data/bin/return_messages_to_source_queue +114 -0
- data/bin/rsb_ctl +38 -0
- data/bin/rservicebus2 +14 -0
- data/bin/rservicebus2-create +107 -0
- data/bin/rservicebus2-init +104 -0
- data/bin/rservicebus2-transport +16 -0
- data/bin/send_empty_message +15 -0
- data/lib/rservicebus.rb +59 -0
- data/lib/rservicebus/agent.rb +54 -0
- data/lib/rservicebus/appresource.rb +65 -0
- data/lib/rservicebus/appresource/dir.rb +29 -0
- data/lib/rservicebus/appresource/file.rb +8 -0
- data/lib/rservicebus/appresource/fluiddb.rb +24 -0
- data/lib/rservicebus/appresource_configure.rb +33 -0
- data/lib/rservicebus/audit.rb +28 -0
- data/lib/rservicebus/circuitbreaker.rb +79 -0
- data/lib/rservicebus/config.rb +168 -0
- data/lib/rservicebus/cron_manager.rb +76 -0
- data/lib/rservicebus/endpointmapping.rb +72 -0
- data/lib/rservicebus/errormessage.rb +14 -0
- data/lib/rservicebus/handler_loader.rb +162 -0
- data/lib/rservicebus/handler_manager.rb +131 -0
- data/lib/rservicebus/helper_functions.rb +85 -0
- data/lib/rservicebus/host.rb +487 -0
- data/lib/rservicebus/message.rb +78 -0
- data/lib/rservicebus/message/statisticoutput.rb +7 -0
- data/lib/rservicebus/message/subscription.rb +10 -0
- data/lib/rservicebus/message/verboseoutput.rb +7 -0
- data/lib/rservicebus/monitor.rb +61 -0
- data/lib/rservicebus/monitor/csvdir.rb +52 -0
- data/lib/rservicebus/monitor/dir.rb +139 -0
- data/lib/rservicebus/monitor/dirnotifier.rb +101 -0
- data/lib/rservicebus/monitor/message.rb +11 -0
- data/lib/rservicebus/monitor/xmldir.rb +11 -0
- data/lib/rservicebus/monitor_configure.rb +71 -0
- data/lib/rservicebus/mq.rb +98 -0
- data/lib/rservicebus/mq/beanstalk.rb +72 -0
- data/lib/rservicebus/resource_manager.rb +69 -0
- data/lib/rservicebus/saga/base.rb +17 -0
- data/lib/rservicebus/saga/data.rb +20 -0
- data/lib/rservicebus/saga/manager.rb +128 -0
- data/lib/rservicebus/saga_loader.rb +118 -0
- data/lib/rservicebus/saga_storage.rb +18 -0
- data/lib/rservicebus/saga_storage/dir.rb +87 -0
- data/lib/rservicebus/saga_storage/inmemory.rb +37 -0
- data/lib/rservicebus/sendat_manager.rb +33 -0
- data/lib/rservicebus/sendat_storage.rb +20 -0
- data/lib/rservicebus/sendat_storage/file.rb +37 -0
- data/lib/rservicebus/sendat_storage/inmemory.rb +20 -0
- data/lib/rservicebus/state_manager.rb +30 -0
- data/lib/rservicebus/state_storage.rb +18 -0
- data/lib/rservicebus/state_storage/dir.rb +66 -0
- data/lib/rservicebus/state_storage/inmemory.rb +25 -0
- data/lib/rservicebus/statistic_manager.rb +86 -0
- data/lib/rservicebus/stats.rb +68 -0
- data/lib/rservicebus/subscription_manager.rb +31 -0
- data/lib/rservicebus/subscription_storage.rb +39 -0
- data/lib/rservicebus/subscription_storage/file.rb +42 -0
- data/lib/rservicebus/subscription_storage/redis.rb +69 -0
- data/lib/rservicebus/subscription_storage_configure.rb +19 -0
- data/lib/rservicebus/test.rb +2 -0
- data/lib/rservicebus/test/bus.rb +32 -0
- data/lib/rservicebus/transporter.rb +142 -0
- data/lib/rservicebus/usermessage/withpayload.rb +10 -0
- metadata +184 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# Given a directory, this class is responsible loading Sagas
|
3
|
+
class SagaLoader
|
4
|
+
attr_reader :saga_list
|
5
|
+
|
6
|
+
# Constructor
|
7
|
+
# @param [RServiceBus::Host] host instance
|
8
|
+
# @param [Hash] appResources As hash[k,v] where k is the name of a
|
9
|
+
# resource, and v is the resource
|
10
|
+
def initialize(host, saga_manager)
|
11
|
+
@host = host
|
12
|
+
@saga_manager = saga_manager
|
13
|
+
@list_of_loaded_paths = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Cleans the given path to ensure it can be used for as a parameter for the
|
17
|
+
# require statement.
|
18
|
+
# @param [String] file_path the path to be cleaned
|
19
|
+
def get_require_path(filePath)
|
20
|
+
file_path = './' + file_path unless file_path.start_with?('/')
|
21
|
+
|
22
|
+
return file_path.sub('.rb', '') if File.exist?(file_path)
|
23
|
+
|
24
|
+
abort('Filepath, ' + filePath + ", given for Saga require doesn't exist")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Instantiate the saga named in sagaName from the file name in filePath
|
28
|
+
# Exceptions will be raised if encountered when loading sagas. This is a
|
29
|
+
# load time activity, so sagas should load correctly. As much information
|
30
|
+
# as possible is returned to enable the saga to be fixed, or configuration
|
31
|
+
# corrected.
|
32
|
+
# @param [String] sagaName name of the saga to instantiate
|
33
|
+
# @param [String] filePath the path to the file to be loaded
|
34
|
+
# @return [RServiceBus::Saga] the loader
|
35
|
+
def load_saga_from_file(saga_name, file_path)
|
36
|
+
require_path = get_require_path(file_path)
|
37
|
+
|
38
|
+
require require_path
|
39
|
+
begin
|
40
|
+
saga = Object.const_get(saga_name)
|
41
|
+
rescue StandardError => e
|
42
|
+
puts 'Expected class name: ' + saga_name + ', not found after require:
|
43
|
+
' + require_path
|
44
|
+
puts '**** Check in ' + file_path + ' that the class is named : ' +
|
45
|
+
saga_name
|
46
|
+
puts '( In case its not that )'
|
47
|
+
raise e
|
48
|
+
end
|
49
|
+
saga
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wrapper function
|
53
|
+
# @param [String] filePath
|
54
|
+
# @param [String] sagaName
|
55
|
+
# @returns [RServiceBus::Saga] saga
|
56
|
+
def load_saga(file_path, saga_name)
|
57
|
+
if @list_of_loaded_paths.key?(file_path)
|
58
|
+
RServiceBus.log "Not reloading, #{file_path}"
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
RServiceBus.rlog 'file_path: ' + file_path
|
64
|
+
RServiceBus.rlog 'saga_name: ' + saga_name
|
65
|
+
|
66
|
+
saga = load_saga_from_file(saga_name, file_path)
|
67
|
+
RServiceBus.log 'Loaded Saga: ' + saga_name
|
68
|
+
|
69
|
+
@saga_manager.register_saga(saga)
|
70
|
+
|
71
|
+
@list_of_loaded_paths[file_path] = 1
|
72
|
+
rescue StandardError => e
|
73
|
+
puts 'Exception loading saga from file: ' + file_path
|
74
|
+
puts e.message
|
75
|
+
puts e.backtrace[0]
|
76
|
+
abort
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# This method is overloaded for unit tests
|
81
|
+
# @param [String] path directory to check
|
82
|
+
# @return [Array] a list of paths to files found in the given path
|
83
|
+
def get_list_of_files_for_dir(path)
|
84
|
+
list = Dir[path + '/*']
|
85
|
+
|
86
|
+
RServiceBus.rlog "SagaLoader.getListOfFilesForDir. path: #{path},
|
87
|
+
list: #{list}"
|
88
|
+
|
89
|
+
list
|
90
|
+
end
|
91
|
+
|
92
|
+
# Extract the top level dir or file name as it is the msg name
|
93
|
+
# @param [String] file_path path to check - this can be a directory or file
|
94
|
+
def get_saga_name(file_path)
|
95
|
+
base_name = File.basename(file_path)
|
96
|
+
ext_name = File.extname(base_name)
|
97
|
+
|
98
|
+
saga_name = base_name.sub(ext_name, '')
|
99
|
+
|
100
|
+
"Saga_#{saga_name}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Entry point for loading Sagas
|
104
|
+
# @param [String] base_dir directory to check - should not have trailing slash
|
105
|
+
def load_sagas_from_path(base_dir)
|
106
|
+
RServiceBus.rlog "SagaLoader.loadSagasFromPath. base_dir: #{base_dir}"
|
107
|
+
|
108
|
+
get_list_of_files_for_dir(base_dir).each do |file_path|
|
109
|
+
unless filePath.end_with?('.')
|
110
|
+
saga_name = get_saga_name(file_path)
|
111
|
+
load_saga(file_path, saga_name)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
self
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# Saga Storage
|
3
|
+
class SagaStorage
|
4
|
+
def self.get(uri)
|
5
|
+
case uri.scheme
|
6
|
+
when 'dir'
|
7
|
+
require 'rservicebus/saga_storage/dir'
|
8
|
+
return SagaStorageDir.new(uri)
|
9
|
+
when 'inmem'
|
10
|
+
require 'rservicebus/saga_storage/inmemory'
|
11
|
+
return SagaStorageInMemory.new(uri)
|
12
|
+
else
|
13
|
+
abort("Scheme, #{uri.scheme}, not recognised when configuring
|
14
|
+
SagaStorage, #{uri}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
|
3
|
+
# Saga Storage Dir
|
4
|
+
class SagaStorageDir
|
5
|
+
def initialize(uri)
|
6
|
+
@saga_dir = uri.path
|
7
|
+
|
8
|
+
input_dir = Dir.new(@saga_dir)
|
9
|
+
unless File.writable?(@saga_dir)
|
10
|
+
puts "***** Directory is not writable, #{@saga_dir}."
|
11
|
+
puts "***** Make the directory, #{@saga_dir}, writable and try again."
|
12
|
+
puts '***** Or, set the Saga Directory explicitly by,
|
13
|
+
SAGA_URI=<dir://path/to/saga>'
|
14
|
+
abort
|
15
|
+
end
|
16
|
+
rescue Errno::ENOENT
|
17
|
+
puts "***** Directory does not exist, #{@saga_dir}."
|
18
|
+
puts "***** Create the directory, #{@saga_dir}, and try again."
|
19
|
+
puts "***** eg, mkdir #{@saga_dir}"
|
20
|
+
puts '***** Or, set the Saga Directory explicitly by,
|
21
|
+
SAGA_URI=<dir://path/to/saga>'
|
22
|
+
abort
|
23
|
+
rescue Errno::ENOTDIR
|
24
|
+
puts "***** The specified path does not point to a directory,
|
25
|
+
#{@saga_dir}."
|
26
|
+
puts "***** Either repoint path to a directory, or remove,
|
27
|
+
#{@saga_dir}, and create it as a directory."
|
28
|
+
puts "***** eg, rm #{@saga_dir} && mkdir #{@saga_dir}"
|
29
|
+
puts '***** Or, set the Saga Directory explicitly by,
|
30
|
+
SAGA_URI=<dir://path/to/saga>'
|
31
|
+
abort
|
32
|
+
end
|
33
|
+
|
34
|
+
# Start
|
35
|
+
def begin
|
36
|
+
@list = []
|
37
|
+
@deleted = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set
|
41
|
+
def set(data)
|
42
|
+
path = get_path(data.correlation_id)
|
43
|
+
@list << Hash['path', path, 'data', data]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get
|
47
|
+
def get(correlation_id)
|
48
|
+
path = get_path(correlation_id)
|
49
|
+
data = load(path)
|
50
|
+
@list << Hash['path', path, 'data', data]
|
51
|
+
|
52
|
+
data
|
53
|
+
end
|
54
|
+
|
55
|
+
# Finish
|
56
|
+
def commit
|
57
|
+
@list.each do |e|
|
58
|
+
File.open(e['path'], 'w') { |f| f.write(YAML.dump(e['data'])) }
|
59
|
+
end
|
60
|
+
@deleted.each do |correlation_id|
|
61
|
+
File.unlink(get_path(correlation_id))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def rollback
|
66
|
+
end
|
67
|
+
|
68
|
+
def delete(correlation_id)
|
69
|
+
@deleted << correlation_id
|
70
|
+
end
|
71
|
+
|
72
|
+
# Detail Functions
|
73
|
+
def get_path(correlation_id)
|
74
|
+
"#{@saga_dir}/saga-#{correlation_id}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def load(path)
|
78
|
+
return {} unless File.exist?(path)
|
79
|
+
|
80
|
+
content = IO.read(path)
|
81
|
+
|
82
|
+
return {} if content == ''
|
83
|
+
|
84
|
+
YAML.load(content)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# Saga Storage In Memory
|
3
|
+
class SagaStorageInMemory
|
4
|
+
def initialize(_uri)
|
5
|
+
end
|
6
|
+
|
7
|
+
# Start
|
8
|
+
def begin
|
9
|
+
@hash = {}
|
10
|
+
@deleted = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set
|
14
|
+
def set(data)
|
15
|
+
@hash[data.correlation_id] = data
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get
|
19
|
+
def get(correlation_id)
|
20
|
+
@hash[correlation_id]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finish
|
24
|
+
def commit
|
25
|
+
@deleted.each do |correlation_id|
|
26
|
+
@hash.delete(correlation_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(correlation_id)
|
31
|
+
@deleted << correlation_id
|
32
|
+
end
|
33
|
+
|
34
|
+
def rollback
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rservicebus/sendat_storage'
|
2
|
+
|
3
|
+
module RServiceBus
|
4
|
+
# Send At Manager
|
5
|
+
class SendAtManager
|
6
|
+
def initialize(bus)
|
7
|
+
# Check if the SendAt Dir has been specified
|
8
|
+
# If it has, make sure it exists, and is writable
|
9
|
+
|
10
|
+
string = RServiceBus.get_value('SENDAT_URI')
|
11
|
+
string = 'file:///tmp/rservicebus-sendat' if string.nil?
|
12
|
+
|
13
|
+
uri = URI.parse(string)
|
14
|
+
@sendat_storage = SendAtStorage.get(uri)
|
15
|
+
@bus = bus
|
16
|
+
end
|
17
|
+
|
18
|
+
def process
|
19
|
+
now = DateTime.now
|
20
|
+
@sendat_storage.get_all.each_with_index do |row, idx|
|
21
|
+
next if row['timestamp'] <= now
|
22
|
+
|
23
|
+
@bus._send_needs_wrapping(row['msg'], row['queue_name'],
|
24
|
+
row['correlation_id'])
|
25
|
+
@sendat_storage.delete(idx)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(row)
|
30
|
+
@sendat_storage.add(row)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module RServiceBus
|
3
|
+
# Send At Storage
|
4
|
+
class SendAtStorage
|
5
|
+
def self.get(uri)
|
6
|
+
case uri.scheme
|
7
|
+
when 'file'
|
8
|
+
require 'rservicebus/sendat_storage/file'
|
9
|
+
return SendAtStorageFile.new(uri)
|
10
|
+
when 'inmem'
|
11
|
+
require 'rservicebus/sendat_storage/inmemory'
|
12
|
+
return SendAtStorageInMemory.new(uri)
|
13
|
+
else
|
14
|
+
abort("Scheme, #{uri.scheme}, not recognised when configuring
|
15
|
+
SendAtStorage, #{uri}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# Send at storage file
|
3
|
+
class SendAtStorageFile
|
4
|
+
def initialize(uri)
|
5
|
+
@list = load(uri.path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def load(path)
|
9
|
+
return [] unless File.exist?(path)
|
10
|
+
|
11
|
+
content = IO.read(path)
|
12
|
+
|
13
|
+
return [] if content == ''
|
14
|
+
|
15
|
+
YAML.load(content)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(msg)
|
19
|
+
@list << msg
|
20
|
+
save
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_all
|
24
|
+
@list
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete(idx)
|
28
|
+
@list.delete_at(idx)
|
29
|
+
save
|
30
|
+
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
content = YAML.dump(@list)
|
34
|
+
File.open(@uri.path, 'w') { |f| f.write(YAML.dump(content)) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# Send at storage in memory
|
3
|
+
class SendAtStorageInMemory
|
4
|
+
def initialize(_uri)
|
5
|
+
@list = []
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(msg)
|
9
|
+
@list << msg
|
10
|
+
end
|
11
|
+
|
12
|
+
def all
|
13
|
+
@list
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete(idx)
|
17
|
+
@list.delete_at(idx)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rservicebus/state_storage'
|
2
|
+
|
3
|
+
module RServiceBus
|
4
|
+
|
5
|
+
# State Manager
|
6
|
+
class StateManager
|
7
|
+
def required
|
8
|
+
# Check if the State Dir has been specified
|
9
|
+
# If it has, make sure it exists, and is writable
|
10
|
+
|
11
|
+
string = RServiceBus.get_value('STATE_URI')
|
12
|
+
string = 'dir:///tmp' if string.nil?
|
13
|
+
|
14
|
+
uri = URI.parse(string)
|
15
|
+
@state_storage = StateStorage.get(uri)
|
16
|
+
end
|
17
|
+
|
18
|
+
def begin
|
19
|
+
@state_storage.begin unless @state_storage.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(handler)
|
23
|
+
@state_storage.get(handler) unless @state_storage.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def commit
|
27
|
+
@state_storage.commit unless @state_storage.nil?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# State Storage
|
3
|
+
class StateStorage
|
4
|
+
def self.get(uri)
|
5
|
+
case uri.scheme
|
6
|
+
when 'dir'
|
7
|
+
require 'rservicebus/state_storage/dir.rb'
|
8
|
+
return StateStorageDir.new(uri)
|
9
|
+
when 'inmem'
|
10
|
+
require 'rservicebus/state_storage/inmemory.rb'
|
11
|
+
return StateStorageInMemory.new(uri)
|
12
|
+
else
|
13
|
+
abort("Scheme, #{uri.scheme}, not recognised when configuring
|
14
|
+
StateStorage, #{uri}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module RServiceBus
|
2
|
+
# State Storage on the file system
|
3
|
+
class StateStorageDir
|
4
|
+
def initialize(uri)
|
5
|
+
@state_dir = uri.path
|
6
|
+
|
7
|
+
inputdir = Dir.new(@state_dir)
|
8
|
+
unless File.writable?(@state_dir)
|
9
|
+
puts "***** Directory is not writable, #{@state_dir}."
|
10
|
+
puts "***** Make the directory, #{@state_dir}, writable and try
|
11
|
+
again."
|
12
|
+
puts '***** Or, set the State Directory explicitly by,
|
13
|
+
STATE_URI=<dir://path/to/state>'
|
14
|
+
abort
|
15
|
+
end
|
16
|
+
rescue Errno::ENOENT
|
17
|
+
puts "***** Directory does not exist, #{@state_dir}."
|
18
|
+
puts "***** Create the directory, #{@state_dir}, and try again."
|
19
|
+
puts "***** eg, mkdir #{@state_dir}"
|
20
|
+
puts '***** Or, set the State Directory explicitly by,
|
21
|
+
STATE_URI=<dir://path/to/state>'
|
22
|
+
abort
|
23
|
+
rescue Errno::ENOTDIR
|
24
|
+
puts "***** The specified path does not point to a directory,
|
25
|
+
#{@state_dir}."
|
26
|
+
puts "***** Either repoint path to a directory, or remove,
|
27
|
+
#{@state_dir}, and create it as a directory."
|
28
|
+
puts "***** eg, rm #{@state_dir} && mkdir #{@state_dir}"
|
29
|
+
puts '***** Or, set the State Directory explicitly by,
|
30
|
+
STATE_URI=<dir://path/to/state>'
|
31
|
+
abort
|
32
|
+
end
|
33
|
+
|
34
|
+
def begin
|
35
|
+
@list = []
|
36
|
+
end
|
37
|
+
|
38
|
+
def get(handler)
|
39
|
+
path = get_path(handler)
|
40
|
+
hash = load(path)
|
41
|
+
@list << Hash['path', path, 'hash', hash]
|
42
|
+
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
|
46
|
+
def commit
|
47
|
+
@list.each do |e|
|
48
|
+
File.open(e['path'], 'w') { |f| f.write(YAML.dump(e['hash'])) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_path(handler)
|
53
|
+
"#{@state_dir}/#{handler.class.name}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def load(path)
|
57
|
+
return {} unless File.exist?(path)
|
58
|
+
|
59
|
+
content = IO.read(path)
|
60
|
+
|
61
|
+
return {} if content == ''
|
62
|
+
|
63
|
+
YAML.load(content)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|