coral_core 0.1.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.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +674 -0
- data/README.rdoc +33 -0
- data/Rakefile +77 -0
- data/VERSION +1 -0
- data/lib/coral_core/command.rb +236 -0
- data/lib/coral_core/core.rb +200 -0
- data/lib/coral_core/event/regexp_event.rb +55 -0
- data/lib/coral_core/event.rb +168 -0
- data/lib/coral_core/interface.rb +179 -0
- data/lib/coral_core/memory.rb +222 -0
- data/lib/coral_core/repository.rb +156 -0
- data/lib/coral_core/util/data.rb +59 -0
- data/lib/coral_core/util/disk.rb +82 -0
- data/lib/coral_core/util/git/base.rb +58 -0
- data/lib/coral_core/util/git/lib.rb +82 -0
- data/lib/coral_core/util/git/remote.rb +12 -0
- data/lib/coral_core/util/git.rb +15 -0
- data/lib/coral_core/util/shell.rb +178 -0
- data/lib/coral_core.rb +49 -0
- data/spec/coral_core/interface_spec.rb +489 -0
- data/spec/coral_mock_input.rb +29 -0
- data/spec/coral_test_kernel.rb +22 -0
- data/spec/spec_helper.rb +15 -0
- metadata +251 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
module Coral
|
3
|
+
module Util
|
4
|
+
class Data < Core
|
5
|
+
|
6
|
+
#-----------------------------------------------------------------------------
|
7
|
+
# Utilities
|
8
|
+
|
9
|
+
def self.merge(objects, merge_arrays = false)
|
10
|
+
value = nil
|
11
|
+
return value unless objects
|
12
|
+
|
13
|
+
unless objects.is_a?(Array)
|
14
|
+
objects = [ objects ]
|
15
|
+
end
|
16
|
+
|
17
|
+
objects.each do |object|
|
18
|
+
value = recursive_merge(value, object, merge_arrays)
|
19
|
+
end
|
20
|
+
return value
|
21
|
+
end
|
22
|
+
|
23
|
+
#---
|
24
|
+
|
25
|
+
def self.recursive_merge(overrides, data, merge_arrays = false)
|
26
|
+
return data unless overrides
|
27
|
+
return overrides unless data
|
28
|
+
|
29
|
+
if overrides.is_a?(Hash)
|
30
|
+
overrides.each do |name, override|
|
31
|
+
if data.is_a?(Hash)
|
32
|
+
if data[name]
|
33
|
+
data[name] = recursive_merge(override, data[name])
|
34
|
+
else
|
35
|
+
begin
|
36
|
+
item = override.dup
|
37
|
+
rescue TypeError
|
38
|
+
item = override
|
39
|
+
end
|
40
|
+
data[name] = recursive_merge(override, item)
|
41
|
+
end
|
42
|
+
else
|
43
|
+
data = overrides
|
44
|
+
end
|
45
|
+
end
|
46
|
+
elsif merge_arrays && overrides.is_a?(Array)
|
47
|
+
if data.is_a?(Array)
|
48
|
+
data = data | overrides
|
49
|
+
else
|
50
|
+
data = overrides
|
51
|
+
end
|
52
|
+
else
|
53
|
+
data = overrides
|
54
|
+
end
|
55
|
+
return data
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
module Coral
|
3
|
+
module Util
|
4
|
+
class Disk < Core
|
5
|
+
|
6
|
+
#-----------------------------------------------------------------------------
|
7
|
+
# Properties
|
8
|
+
|
9
|
+
@@files = {}
|
10
|
+
|
11
|
+
#-----------------------------------------------------------------------------
|
12
|
+
# Utilities
|
13
|
+
|
14
|
+
def self.open(file_name, options = {}, reset = false)
|
15
|
+
mode = string(options[:mode])
|
16
|
+
separator = ( options[:separator] ? options[:separator] : false )
|
17
|
+
description = ( options[:description] ? options[:description] : '' )
|
18
|
+
|
19
|
+
if @@files.has_key?(file_name) && ! reset
|
20
|
+
reset = true if ! mode.empty? && mode != @@files[file_name][:mode]
|
21
|
+
end
|
22
|
+
|
23
|
+
if ! @@files.has_key?(file_name) || ! @@files[file_name][:file] || reset
|
24
|
+
@@files[file_name][:file].close if @@files[file_name][:file]
|
25
|
+
unless mode.empty?
|
26
|
+
@@files[file_name] = {
|
27
|
+
:file => File.open(file_name, mode),
|
28
|
+
:mode => mode,
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
return @@files[file_name][:file]
|
33
|
+
end
|
34
|
+
|
35
|
+
#---
|
36
|
+
|
37
|
+
def self.read(file_name, options = {})
|
38
|
+
options[:mode] = ( options[:mode] ? options[:mode] : 'r' )
|
39
|
+
file = open(file_name, options)
|
40
|
+
|
41
|
+
if file
|
42
|
+
return file.read
|
43
|
+
end
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
|
47
|
+
#---
|
48
|
+
|
49
|
+
def self.write(file_name, data, options = {})
|
50
|
+
options[:mode] = ( options[:mode] ? options[:mode] : 'w' )
|
51
|
+
file = open(file_name, options)
|
52
|
+
|
53
|
+
if file
|
54
|
+
return file.write(data)
|
55
|
+
end
|
56
|
+
return nil
|
57
|
+
end
|
58
|
+
|
59
|
+
#---
|
60
|
+
|
61
|
+
def self.log(data, options = {})
|
62
|
+
reset = ( options[:file_name] || options[:mode] )
|
63
|
+
file = open(( options[:file_name] ? options[:file_name] : 'log.txt' ), options, reset)
|
64
|
+
if file
|
65
|
+
file.write("--------------------------------------\n") if separator
|
66
|
+
file.write("#{description}\n") if description
|
67
|
+
file.write("#{data}\n")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
#---
|
72
|
+
|
73
|
+
def self.close(file_names = [])
|
74
|
+
file_names = @@files.keys unless file_names && ! file_names.empty?
|
75
|
+
array(file_names).each do |file_name|
|
76
|
+
@@files[file_name][:file].close if @@files[file_name][:file]
|
77
|
+
@@files.delete(file_name)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
module Git
|
3
|
+
|
4
|
+
#*******************************************************************************
|
5
|
+
# Errors
|
6
|
+
|
7
|
+
class GitDirectoryError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
#*******************************************************************************
|
11
|
+
# Base Git definition
|
12
|
+
|
13
|
+
class Base
|
14
|
+
|
15
|
+
#-----------------------------------------------------------------------------
|
16
|
+
# Constructor / Destructor
|
17
|
+
|
18
|
+
def initialize(options = {})
|
19
|
+
if working_dir = options[:working_directory]
|
20
|
+
options[:repository] ||= File.join(working_dir, '.git')
|
21
|
+
|
22
|
+
if File.file?(options[:repository])
|
23
|
+
File.read(options[:repository]).each_line do |line|
|
24
|
+
matches = line.match(/^\s*gitdir:\s*(.+)\s*/)
|
25
|
+
if matches.length && matches[1]
|
26
|
+
options[:repository] = matches[1]
|
27
|
+
break
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
if File.directory?(options[:repository])
|
33
|
+
options[:index] ||= File.join(options[:repository], 'index')
|
34
|
+
else
|
35
|
+
raise GitDirectoryError.new("Git repository directory #{options[:repository]} not found for #{working_dir}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
if options[:log]
|
40
|
+
@logger = options[:log]
|
41
|
+
@logger.info("Starting Git")
|
42
|
+
else
|
43
|
+
@logger = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
@working_directory = options[:working_directory] ? Git::WorkingDirectory.new(options[:working_directory]) : nil
|
47
|
+
@repository = options[:repository] ? Git::Repository.new(options[:repository]) : nil
|
48
|
+
@index = options[:index] ? Git::Index.new(options[:index], false) : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
#-----------------------------------------------------------------------------
|
52
|
+
# Commit extensions
|
53
|
+
|
54
|
+
def add(path = '.', opts = {})
|
55
|
+
self.lib.add(path, opts)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
|
2
|
+
module Git
|
3
|
+
class Lib
|
4
|
+
|
5
|
+
#-----------------------------------------------------------------------------
|
6
|
+
# Commit extensions
|
7
|
+
|
8
|
+
def add(path = '.', opts = {})
|
9
|
+
arr_opts = []
|
10
|
+
arr_opts << '-u' if opts[:update]
|
11
|
+
if path.is_a?(Array)
|
12
|
+
arr_opts += path
|
13
|
+
else
|
14
|
+
arr_opts << path
|
15
|
+
end
|
16
|
+
command('add', arr_opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
#---
|
20
|
+
|
21
|
+
def commit(message, opts = {})
|
22
|
+
arr_opts = ['-m', message]
|
23
|
+
arr_opts << "--author=\'#{opts[:author]}\'" unless opts[:author] && opts[:author].empty?
|
24
|
+
arr_opts << '-a' if opts[:add_all]
|
25
|
+
arr_opts << '--allow-empty' if opts[:allow_empty]
|
26
|
+
command('commit', arr_opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
#-----------------------------------------------------------------------------
|
30
|
+
# Remote extensions
|
31
|
+
|
32
|
+
def remote_add(name, url, opts = {})
|
33
|
+
arr_opts = ['add']
|
34
|
+
arr_opts << '-f' if opts[:with_fetch]
|
35
|
+
arr_opts << name
|
36
|
+
arr_opts << url
|
37
|
+
|
38
|
+
command('remote', arr_opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
#---
|
42
|
+
|
43
|
+
def remote_set_url(name, url, opts = {})
|
44
|
+
arr_opts = ['set-url']
|
45
|
+
|
46
|
+
if opts[:add]
|
47
|
+
arr_opts << '--add' if opts[:add]
|
48
|
+
end
|
49
|
+
|
50
|
+
if opts[:delete]
|
51
|
+
arr_opts << '--delete' if opts[:delete]
|
52
|
+
end
|
53
|
+
|
54
|
+
if opts[:push]
|
55
|
+
arr_opts << '--push' if opts[:push]
|
56
|
+
end
|
57
|
+
|
58
|
+
arr_opts << name
|
59
|
+
arr_opts << url
|
60
|
+
|
61
|
+
command('remote', arr_opts)
|
62
|
+
end
|
63
|
+
|
64
|
+
#---
|
65
|
+
|
66
|
+
def remote_remove(name)
|
67
|
+
command('remote', ['rm', name])
|
68
|
+
end
|
69
|
+
|
70
|
+
#-----------------------------------------------------------------------------
|
71
|
+
# Utilities
|
72
|
+
|
73
|
+
def escape(s)
|
74
|
+
escaped = s.to_s.gsub('"', '\'')
|
75
|
+
if escaped =~ /^\-+/
|
76
|
+
escaped
|
77
|
+
else
|
78
|
+
%Q{"#{escaped}"}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
require 'git'
|
3
|
+
|
4
|
+
module Git
|
5
|
+
|
6
|
+
#-----------------------------------------------------------------------------
|
7
|
+
# Utilities
|
8
|
+
|
9
|
+
def self.url(host, repo, options = {})
|
10
|
+
options[:user] = ( options[:user] ? options[:user] : 'git' )
|
11
|
+
options[:auth] = ( options[:auth] ? options[:auth] : true )
|
12
|
+
|
13
|
+
return options[:user] + ( options[:auth] ? '@' : '://' ) + host + ( options[:auth] ? ':' : '/' ) + repo
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
|
2
|
+
module Coral
|
3
|
+
module Util
|
4
|
+
class Shell < Core
|
5
|
+
|
6
|
+
#-----------------------------------------------------------------------------
|
7
|
+
# Utilities
|
8
|
+
|
9
|
+
def self.exec!(command, options = {})
|
10
|
+
min = ( options[:min] ? options[:min].to_i : 1 )
|
11
|
+
tries = ( options[:tries] ? options[:tries].to_i : min )
|
12
|
+
tries = ( min > tries ? min : tries )
|
13
|
+
|
14
|
+
info_prefix = ( options[:info_prefix] ? options[:info_prefix] : '' )
|
15
|
+
info_suffix = ( options[:info_suffix] ? options[:info_suffix] : '' )
|
16
|
+
error_prefix = ( options[:error_prefix] ? options[:error_prefix] : '' )
|
17
|
+
error_suffix = ( options[:error_suffix] ? options[:error_suffix] : '' )
|
18
|
+
|
19
|
+
conditions = Coral::Event.instance(options[:exit], true)
|
20
|
+
|
21
|
+
$stdout.sync = true
|
22
|
+
$stderr.sync = true
|
23
|
+
|
24
|
+
for i in tries.downto(1)
|
25
|
+
ui.info(">> running: #{command}")
|
26
|
+
|
27
|
+
begin
|
28
|
+
t1, output_new, output_orig = pipe_exec_stream!($stdout, conditions, {
|
29
|
+
:prefix => info_prefix,
|
30
|
+
:suffix => info_suffix,
|
31
|
+
}) do |line|
|
32
|
+
block_given? ? yield(line) : true
|
33
|
+
end
|
34
|
+
|
35
|
+
t2, error_new, error_orig = pipe_exec_stream!($stderr, conditions, {
|
36
|
+
:prefix => error_prefix,
|
37
|
+
:suffix => error_suffix,
|
38
|
+
}) do |line|
|
39
|
+
block_given? ? yield(line) : true
|
40
|
+
end
|
41
|
+
|
42
|
+
system_success = system(command)
|
43
|
+
|
44
|
+
ensure
|
45
|
+
output_success = close_exec_pipe(t1, $stdout, output_orig, output_new)
|
46
|
+
error_success = close_exec_pipe(t2, $stderr, error_orig, error_new)
|
47
|
+
end
|
48
|
+
ui.info('')
|
49
|
+
|
50
|
+
success = ( system_success && output_success && error_success )
|
51
|
+
|
52
|
+
min -= 1
|
53
|
+
break if success && min <= 0 && conditions.empty?
|
54
|
+
end
|
55
|
+
unless conditions.empty?
|
56
|
+
success = false
|
57
|
+
end
|
58
|
+
|
59
|
+
return success
|
60
|
+
end
|
61
|
+
|
62
|
+
#---
|
63
|
+
|
64
|
+
def self.exec(command, options = {})
|
65
|
+
return exec!(command, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
#---
|
69
|
+
|
70
|
+
def self.pipe_exec_stream!(output, conditions, options)
|
71
|
+
original = output.dup
|
72
|
+
read, write = IO.pipe
|
73
|
+
|
74
|
+
match_prefix = ( options[:match_prefix] ? options[:match_prefix] : 'EXIT' )
|
75
|
+
|
76
|
+
thread = process_stream!(read, original, options) do |line|
|
77
|
+
check_conditions!(line, conditions, match_prefix) do
|
78
|
+
block_given? ? yield(line) : true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
thread.abort_on_exception = false
|
83
|
+
|
84
|
+
output.reopen(write)
|
85
|
+
return thread, write, original
|
86
|
+
end
|
87
|
+
|
88
|
+
#---
|
89
|
+
|
90
|
+
def self.close_exec_pipe(thread, output, original, write)
|
91
|
+
output.reopen(original)
|
92
|
+
|
93
|
+
write.close
|
94
|
+
success = thread.value
|
95
|
+
Thread.kill(thread) # Die bastard, die!
|
96
|
+
|
97
|
+
original.close
|
98
|
+
return success
|
99
|
+
end
|
100
|
+
|
101
|
+
#---
|
102
|
+
|
103
|
+
def self.check_conditions!(line, conditions, match_prefix = '')
|
104
|
+
prefix = ''
|
105
|
+
|
106
|
+
unless ! conditions || conditions.empty?
|
107
|
+
conditions.each do |key, event|
|
108
|
+
if event.check(line)
|
109
|
+
prefix = match_prefix
|
110
|
+
conditions.delete(key)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
result = true
|
116
|
+
if block_given?
|
117
|
+
result = yield
|
118
|
+
|
119
|
+
unless prefix.empty?
|
120
|
+
case result
|
121
|
+
when Hash
|
122
|
+
result[:prefix] = prefix
|
123
|
+
else
|
124
|
+
result = { :success => result, :prefix => prefix }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
return result
|
129
|
+
end
|
130
|
+
|
131
|
+
#---
|
132
|
+
|
133
|
+
def self.process_stream!(input, output, options)
|
134
|
+
return Thread.new do
|
135
|
+
success = true
|
136
|
+
default_prefix = ( options[:prefix] ? options[:prefix] : '' )
|
137
|
+
default_suffix = ( options[:suffix] ? options[:suffix] : '' )
|
138
|
+
|
139
|
+
begin
|
140
|
+
while ( data = input.readpartial(1024) )
|
141
|
+
message = data.strip
|
142
|
+
newline = ( data[-1,1].match(/\n/) ? true : false )
|
143
|
+
|
144
|
+
unless message.empty?
|
145
|
+
lines = message.split(/\n/)
|
146
|
+
lines.each_with_index do |line, index|
|
147
|
+
prefix = default_prefix
|
148
|
+
suffix = default_suffix
|
149
|
+
|
150
|
+
if block_given?
|
151
|
+
result = yield(line)
|
152
|
+
|
153
|
+
if result && result.is_a?(Hash)
|
154
|
+
prefix = result[:prefix]
|
155
|
+
suffix = result[:suffix]
|
156
|
+
result = result[:success]
|
157
|
+
end
|
158
|
+
success = result if success
|
159
|
+
end
|
160
|
+
|
161
|
+
prefix = ( prefix && ! prefix.empty? ? "#{prefix}: " : '' )
|
162
|
+
suffix = ( suffix && ! suffix.empty? ? suffix : '' )
|
163
|
+
eol = ( index < lines.length - 1 || newline ? "\n" : ' ' )
|
164
|
+
|
165
|
+
output.write(prefix.lstrip + line + suffix.rstrip + eol)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
rescue EOFError
|
170
|
+
end
|
171
|
+
|
172
|
+
input.close
|
173
|
+
success
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/coral_core.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
home = File.dirname(__FILE__)
|
3
|
+
|
4
|
+
$:.unshift(home) unless
|
5
|
+
$:.include?(home) || $:.include?(File.expand_path(home))
|
6
|
+
|
7
|
+
#-------------------------------------------------------------------------------
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
|
11
|
+
#---
|
12
|
+
|
13
|
+
# Include core
|
14
|
+
[ :interface, :core ].each do |name|
|
15
|
+
require File.join('coral_core', name.to_s + ".rb")
|
16
|
+
end
|
17
|
+
|
18
|
+
# Include utilities
|
19
|
+
[ :git, :data, :disk, :shell ].each do |name|
|
20
|
+
require File.join('coral_core', 'util', name.to_s + ".rb")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Include Git overrides
|
24
|
+
Dir.glob(File.join(home, 'coral_core', 'util', 'git', '*.rb')).each do |file|
|
25
|
+
require file
|
26
|
+
end
|
27
|
+
|
28
|
+
# Include data model
|
29
|
+
[ :event, :command, :repository, :memory ].each do |name|
|
30
|
+
require File.join('coral_core', name.to_s + ".rb")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Include specialized events
|
34
|
+
Dir.glob(File.join(home, 'coral_core', 'event', '*.rb')).each do |file|
|
35
|
+
require file
|
36
|
+
end
|
37
|
+
|
38
|
+
#*******************************************************************************
|
39
|
+
# Coral Core Library
|
40
|
+
#
|
41
|
+
# This provides core data elements and utilities used in the Coral gems.
|
42
|
+
#
|
43
|
+
# Author:: Adrian Webb (mailto:adrian.webb@coraltech.net)
|
44
|
+
# License:: GPLv3
|
45
|
+
module Coral
|
46
|
+
|
47
|
+
VERSION = File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
|
48
|
+
|
49
|
+
end
|