alki 0.9.0 → 0.9.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 +4 -4
- data/exe/alki +19 -186
- data/lib/alki/assembly/builder.rb +31 -6
- data/lib/alki/assembly/executor.rb +21 -16
- data/lib/alki/assembly/instance.rb +26 -9
- data/lib/alki/assembly/types/service.rb +1 -1
- data/lib/alki/assembly.rb +20 -6
- data/lib/alki/dsls/assembly.rb +1 -0
- data/lib/alki/execution/value_context.rb +9 -0
- data/lib/alki/generator.rb +177 -0
- data/lib/alki/reloadable_delegator.rb +17 -0
- data/lib/alki/version.rb +1 -1
- data/lib/alki.rb +1 -0
- data/test/feature/overlays_test.rb +6 -4
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc1b79ca08e965916ecbd78625d9635f9b11868c
|
4
|
+
data.tar.gz: 7d847de049a43edefa043d07ef4d64160c625407
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05ffaa08ff5d51121f127ee2db0cc01238031b1b0ff6f7e0c1a95eedab5cd551e6733c991ea54a7058cc308468843b97591e40fdbac5c19b005317fbaddd5614
|
7
|
+
data.tar.gz: a1db4bc72926268f1d1ccb789215c903bfd83a0e165816a98777fa15bf5ab70f230a2a35854f94ebf62df8cb0810f9f7b3a2c1fc09bc1cd3cac0d468964b148b
|
data/exe/alki
CHANGED
@@ -2,8 +2,9 @@
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'optparse'
|
4
4
|
require 'alki/support'
|
5
|
+
require 'alki/generator'
|
5
6
|
|
6
|
-
valid_addons = ["console"]
|
7
|
+
valid_addons = ["console","reload"]
|
7
8
|
|
8
9
|
options = {
|
9
10
|
config_dir: nil,
|
@@ -14,12 +15,14 @@ options = {
|
|
14
15
|
parser = OptionParser.new do |opts|
|
15
16
|
opts.banner = "Usage: alki init PROJECT_NAME [options]"
|
16
17
|
|
17
|
-
opts.on("-a", "--
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
opts.on("-a", "--addons=ADDONS", Array, "Add addon to project. Valid addons: #{valid_addons.join(', ')}") do |vs|
|
19
|
+
vs.each do |v|
|
20
|
+
unless valid_addons.include? v
|
21
|
+
puts "Invalid addon: #{v}"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
options[:addons] |= [v]
|
21
25
|
end
|
22
|
-
options[:addon] |= [v]
|
23
26
|
end
|
24
27
|
|
25
28
|
opts.on('-d','--directory=DIRECTORY', 'Project root, defaults to the current working directory') do |v|
|
@@ -47,187 +50,9 @@ unless ARGV[1] =~ %r{^[a-z0-9_]+(/[a-z0-9_]+)*$}
|
|
47
50
|
exit 1
|
48
51
|
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
class AlkiFileWriter
|
53
|
-
def initialize(root_dir)
|
54
|
-
@root_dir = root_dir
|
55
|
-
@changes = []
|
56
|
-
@triggers = []
|
57
|
-
end
|
58
|
-
|
59
|
-
def create(file,contents)
|
60
|
-
@changes << [:create,file: file,contents: contents,opts: {}]
|
61
|
-
end
|
62
|
-
|
63
|
-
def create_exec(file,contents)
|
64
|
-
@changes << [:create,file: file,contents: contents,opts: {exec: true}]
|
65
|
-
end
|
66
|
-
|
67
|
-
def check_create(file:, contents:, opts:)
|
68
|
-
path = abs_path( file)
|
69
|
-
if File.exists? path
|
70
|
-
if File.read(path) == contents
|
71
|
-
:skip
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def desc_create(file:, contents:, opts: {})
|
77
|
-
path = abs_path( file)
|
78
|
-
if opts[:exec]
|
79
|
-
adj = "executable "
|
80
|
-
end
|
81
|
-
if File.exists? path
|
82
|
-
"Overwriting #{adj}#{file}!"
|
83
|
-
else
|
84
|
-
"Create #{adj}#{file}"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def do_create(file:, contents:, opts: {})
|
89
|
-
path = abs_path( file)
|
90
|
-
FileUtils.mkdir_p File.dirname(path)
|
91
|
-
File.write path, contents
|
92
|
-
FileUtils.chmod '+x', path if opts[:exec]
|
93
|
-
end
|
94
|
-
|
95
|
-
def opt_create(file,contents)
|
96
|
-
@changes << [:opt_create,file: file,contents: contents]
|
97
|
-
end
|
98
|
-
|
99
|
-
def check_opt_create(file:, contents:)
|
100
|
-
if File.exists? abs_path( file)
|
101
|
-
:skip
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def desc_opt_create(opts)
|
106
|
-
desc_create opts
|
107
|
-
end
|
108
|
-
|
109
|
-
def do_opt_create(opts)
|
110
|
-
do_create opts
|
111
|
-
end
|
112
|
-
|
113
|
-
def add_line(file,line,opts={})
|
114
|
-
@changes << [:add_line,file: file,line: line,opts: opts]
|
115
|
-
end
|
116
|
-
|
117
|
-
def check_add_line(file:,line:,opts:)
|
118
|
-
if File.exists? abs_path(file)
|
119
|
-
File.open(file) do |f|
|
120
|
-
if opts[:after]
|
121
|
-
found = until f.eof?
|
122
|
-
break true if f.readline.chomp == opts[:after]
|
123
|
-
end
|
124
|
-
unless found
|
125
|
-
puts "File \"#{file}\" doesn't contain required line #{opts[:after]}"
|
126
|
-
return :abort
|
127
|
-
end
|
128
|
-
end
|
129
|
-
until f.eof?
|
130
|
-
l = f.readline
|
131
|
-
if opts[:match] ? l.chomp.match(opts[:match]) : (l.chomp == line)
|
132
|
-
return :skip
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
else
|
137
|
-
unless @changes.find {|c| [:create,:opt_create].include?(c[0]) && c[1][:file] == file}
|
138
|
-
puts "File \"#{file}\" doesn't exist!"
|
139
|
-
return :abort
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def desc_add_line(file:,line:,opts:)
|
145
|
-
"Add line \"#{line}\" to #{file}"
|
146
|
-
end
|
147
|
-
|
148
|
-
def do_add_line(file:,line:,opts:)
|
149
|
-
if opts[:after]
|
150
|
-
File.write file, File.read(file).sub(/^#{Regexp.quote(opts[:after])}\n/){|m| m + line + "\n"}
|
151
|
-
else
|
152
|
-
File.open(file,'a') do |f|
|
153
|
-
f.puts line
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def trigger(file,cmd)
|
159
|
-
@triggers << [file, cmd]
|
160
|
-
end
|
161
|
-
|
162
|
-
def check_trigger(changes,file)
|
163
|
-
changes.find {|c| c[1][:file] == file}
|
164
|
-
end
|
165
|
-
|
166
|
-
def desc_trigger(cmd:)
|
167
|
-
"Run \"#{cmd}\""
|
168
|
-
end
|
169
|
-
|
170
|
-
def do_trigger(cmd:)
|
171
|
-
system cmd
|
172
|
-
end
|
173
|
-
|
174
|
-
def check_changes
|
175
|
-
puts "Checking preconditions..."
|
176
|
-
abort = false
|
177
|
-
unless Dir.exists?(@root_dir)
|
178
|
-
puts "Root dir doesn't exist"
|
179
|
-
exit 1
|
180
|
-
end
|
181
|
-
do_changes = []
|
182
|
-
@changes.each do |(type,args)|
|
183
|
-
res = send "check_#{type}", args
|
184
|
-
abort = true if res == :abort
|
185
|
-
do_changes << [type,args] unless res == :skip
|
186
|
-
end
|
187
|
-
@triggers.each do |(file,cmd)|
|
188
|
-
if check_trigger do_changes, file
|
189
|
-
do_changes << [:trigger,cmd: cmd]
|
190
|
-
end
|
191
|
-
end
|
192
|
-
exit 1 if abort
|
193
|
-
do_changes
|
194
|
-
end
|
195
|
-
|
196
|
-
def print_overview(changes)
|
197
|
-
puts "Overview of changes to be made:"
|
198
|
-
|
199
|
-
changes.each do |(type,args)|
|
200
|
-
desc = send "desc_#{type}", args
|
201
|
-
puts " #{desc}" if desc
|
202
|
-
end
|
203
|
-
print "Proceed? "
|
204
|
-
resp = STDIN.gets.chomp
|
205
|
-
unless resp =~ /^y(es?)?/i
|
206
|
-
puts "Aborting"
|
207
|
-
exit
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
def write
|
212
|
-
puts "Using project root: #{@root_dir}\n"
|
213
|
-
do_changes = check_changes
|
214
|
-
print_overview do_changes
|
215
|
-
|
216
|
-
puts "Writing changes..."
|
217
|
-
do_changes.each do |(type,args)|
|
218
|
-
send "do_#{type}", args
|
219
|
-
end
|
220
|
-
puts "Done"
|
221
|
-
end
|
222
|
-
|
223
|
-
def abs_path(p)
|
224
|
-
File.expand_path(p,@root_dir)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
53
|
project_name = ARGV[1]
|
229
54
|
|
230
|
-
fw =
|
55
|
+
fw = Alki::Generator.new options[:project_dir]
|
231
56
|
|
232
57
|
config_dir = options[:config_dir] || 'config'
|
233
58
|
primary_config = options[:primary_config] || 'assembly'
|
@@ -261,7 +86,7 @@ if options[:addons].include? "console"
|
|
261
86
|
fw.add_line "Gemfile", "gem 'alki-console'", match: /^gem ['"]alki-console['"]/
|
262
87
|
|
263
88
|
name = project_name.tr('/','-')
|
264
|
-
fw.add_line primary_config_path, " mount :console, 'alki/console', name: '#{name}'", match: /^\s*
|
89
|
+
fw.add_line primary_config_path, " mount :console, 'alki/console', name: '#{name}'", match: /^\s*mount\( *:console/, after: 'Alki do'
|
265
90
|
|
266
91
|
fw.create_exec 'bin/console', <<END
|
267
92
|
#!/usr/bin/env ruby
|
@@ -272,4 +97,12 @@ require '#{project_name}'
|
|
272
97
|
END
|
273
98
|
end
|
274
99
|
|
100
|
+
if options[:addons].include? "reload"
|
101
|
+
fw.add_line "Gemfile", "gem 'alki-reload'", match: /^gem ['"]alki-reload['"]/
|
102
|
+
|
103
|
+
env_name = project_name.tr('a-z/','A-Z_')+"_ENV"
|
104
|
+
fw.add_line primary_config_path, " mount(:reloader, 'alki/reload'){ set(:watch) { development? } }", match: /^\s*mount\(? *:reloader/, after: 'Alki do'
|
105
|
+
fw.add_line primary_config_path, " set(:development?){ ENV['#{env_name}'] == 'development' }", match: /^\s*set :development?/, after: 'Alki do'
|
106
|
+
end
|
107
|
+
|
275
108
|
fw.write
|
@@ -10,6 +10,7 @@ module Alki
|
|
10
10
|
@config_dir = nil
|
11
11
|
@assembly_name = nil
|
12
12
|
@definition = nil
|
13
|
+
@load_mode = :direct
|
13
14
|
end
|
14
15
|
|
15
16
|
attr_reader :config_dir, :assembly_name, :definition
|
@@ -19,6 +20,7 @@ module Alki
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def build(opts={},&blk)
|
23
|
+
@load_mode = opts[:load_mode] if opts[:load_mode]
|
22
24
|
build_assembly blk if blk
|
23
25
|
if opts[:config_dir]
|
24
26
|
context = if opts[:project_assembly]
|
@@ -68,9 +70,7 @@ module Alki
|
|
68
70
|
end
|
69
71
|
|
70
72
|
def register_config_directory
|
71
|
-
|
72
|
-
opts[:prefix] = File.join(@assembly_name,'alki_config') if @assembly_name
|
73
|
-
Alki::Dsl.register_dir @config_dir, 'alki/dsls/assembly', opts
|
73
|
+
Alki::Dsl.register_dir @config_dir, 'alki/dsls/assembly', dsl_opts
|
74
74
|
end
|
75
75
|
|
76
76
|
def load_assembly_file(name = nil)
|
@@ -78,7 +78,7 @@ module Alki
|
|
78
78
|
if @config_dir
|
79
79
|
assembly_config_path = File.join(@config_dir,"#{name}.rb")
|
80
80
|
if File.exists? assembly_config_path
|
81
|
-
@definition =
|
81
|
+
@definition = assembly_config_path
|
82
82
|
true
|
83
83
|
end
|
84
84
|
end
|
@@ -89,22 +89,47 @@ module Alki
|
|
89
89
|
end
|
90
90
|
|
91
91
|
def build_assembly(blk)
|
92
|
-
@definition = Alki::Dsl.build('alki/dsls/assembly',
|
92
|
+
@definition = Alki::Dsl.build('alki/dsls/assembly', dsl_opts, &blk)[:class]
|
93
|
+
end
|
94
|
+
|
95
|
+
def dsl_opts
|
96
|
+
opts = {config_dir: @config_dir}
|
97
|
+
if @assembly_name
|
98
|
+
opts[:prefix] = File.join(@assembly_name,'alki_config')
|
99
|
+
opts[:assembly_name] = @assembly_name
|
100
|
+
end
|
101
|
+
opts
|
93
102
|
end
|
94
103
|
|
95
104
|
def build_class
|
96
105
|
definition = @definition
|
106
|
+
name = @assembly_name
|
107
|
+
load_class = if @load_mode == :require
|
108
|
+
->{ name }
|
109
|
+
else
|
110
|
+
->{ self }
|
111
|
+
end
|
97
112
|
Alki::ClassBuilder.build(
|
98
113
|
prefix: '',
|
99
114
|
name: @assembly_name,
|
100
115
|
class_modules: [Alki::Assembly],
|
101
116
|
type: :module,
|
102
117
|
class_methods: {
|
118
|
+
assembly_name: {
|
119
|
+
body: ->{
|
120
|
+
name
|
121
|
+
}
|
122
|
+
},
|
103
123
|
definition: {
|
104
124
|
body: ->{
|
105
|
-
definition
|
125
|
+
definition.is_a?(String) ?
|
126
|
+
Alki::Dsl.load(definition)[:class] :
|
127
|
+
definition
|
106
128
|
}
|
107
129
|
},
|
130
|
+
load_class: {
|
131
|
+
body: load_class
|
132
|
+
}
|
108
133
|
}
|
109
134
|
)
|
110
135
|
end
|
@@ -11,16 +11,18 @@ module Alki
|
|
11
11
|
@overlays = overlays
|
12
12
|
@data = {}
|
13
13
|
@semaphore = Monitor.new
|
14
|
-
clear
|
15
|
-
end
|
16
|
-
|
17
|
-
def clear
|
18
14
|
@lookup_cache = {}
|
19
15
|
@call_cache = {}
|
20
16
|
@context_cache = {}
|
21
17
|
@processed_overlays = false
|
22
18
|
end
|
23
19
|
|
20
|
+
def synchronize
|
21
|
+
@semaphore.synchronize do
|
22
|
+
yield
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
24
26
|
def call(path,*args,&blk)
|
25
27
|
execute({},path,args,blk)
|
26
28
|
end
|
@@ -29,7 +31,7 @@ module Alki
|
|
29
31
|
cache_entry = @call_cache[path]
|
30
32
|
if cache_entry
|
31
33
|
if cache_entry.status == :building
|
32
|
-
raise "Circular element reference found"
|
34
|
+
raise "Circular element reference found: #{path.join(".")}"
|
33
35
|
end
|
34
36
|
else
|
35
37
|
@semaphore.synchronize do
|
@@ -49,6 +51,15 @@ module Alki
|
|
49
51
|
call_value(cache_entry.type,cache_entry.value,meta,args,blk)
|
50
52
|
end
|
51
53
|
|
54
|
+
def canonical_path(from,path)
|
55
|
+
from_elem = lookup(from)
|
56
|
+
scope = from_elem[:full_scope] || from_elem[:scope]
|
57
|
+
path.inject(nil) do |p,elem|
|
58
|
+
scope = lookup(p)[:scope] if p
|
59
|
+
scope[elem]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
52
63
|
private
|
53
64
|
|
54
65
|
def process_overlays
|
@@ -58,8 +69,11 @@ module Alki
|
|
58
69
|
@overlays.each do |(from,info)|
|
59
70
|
target = canonical_path(from,info.target) or
|
60
71
|
raise InvalidPathError.new("Invalid overlay target #{info.target.join('.')}")
|
61
|
-
overlay =
|
62
|
-
|
72
|
+
overlay = info.overlay
|
73
|
+
if overlay.is_a?(Array)
|
74
|
+
overlay = canonical_path(from,info.overlay) or
|
75
|
+
raise InvalidPathError.new("Invalid overlay path #{info.overlay.join('.')}")
|
76
|
+
end
|
63
77
|
(@data[:overlays][target]||=[]) << [overlay,info.args]
|
64
78
|
end
|
65
79
|
end
|
@@ -84,15 +98,6 @@ module Alki
|
|
84
98
|
elem.output data
|
85
99
|
end
|
86
100
|
|
87
|
-
|
88
|
-
def canonical_path(from,path)
|
89
|
-
scope = lookup(from)[:full_scope]
|
90
|
-
path.inject(nil) do |p,elem|
|
91
|
-
scope = lookup(p)[:scope] if p
|
92
|
-
scope[elem]
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
101
|
def process_action(action)
|
97
102
|
if action.key?(:value)
|
98
103
|
[:value,action[:value]]
|
@@ -1,20 +1,37 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'alki/support'
|
3
|
+
|
1
4
|
module Alki
|
2
5
|
module Assembly
|
3
|
-
class Instance
|
4
|
-
def initialize(
|
5
|
-
@
|
6
|
+
class Instance < Delegator
|
7
|
+
def initialize(assembly_module,args)
|
8
|
+
@assembly_module = assembly_module
|
9
|
+
@args = args
|
6
10
|
end
|
7
11
|
|
8
|
-
def
|
9
|
-
|
12
|
+
def __reload__
|
13
|
+
if @obj.respond_to? :__reload__
|
14
|
+
did_something = @obj.__reload__
|
15
|
+
end
|
16
|
+
if did_something != false && @obj
|
17
|
+
@obj = nil
|
18
|
+
did_something = true
|
19
|
+
end
|
20
|
+
if did_something
|
21
|
+
GC.start
|
22
|
+
end
|
23
|
+
!!did_something
|
10
24
|
end
|
11
25
|
|
12
|
-
def
|
13
|
-
|
26
|
+
def __setobj__(obj)
|
27
|
+
@obj = obj
|
14
28
|
end
|
15
29
|
|
16
|
-
def
|
17
|
-
|
30
|
+
def __getobj__
|
31
|
+
unless @obj
|
32
|
+
Alki::Support.load_class(@assembly_module).raw_instance self, *@args
|
33
|
+
end
|
34
|
+
@obj
|
18
35
|
end
|
19
36
|
end
|
20
37
|
end
|
@@ -12,7 +12,7 @@ Alki do
|
|
12
12
|
},
|
13
13
|
proc: -> (elem) {
|
14
14
|
elem[:value] = overlays.inject(__build__) do |val,(overlay,args)|
|
15
|
-
overlay = root.lookup(overlay)
|
15
|
+
overlay = root.lookup(overlay) if overlay.is_a?(Array)
|
16
16
|
if !overlay.respond_to?(:call) && overlay.respond_to?(:new)
|
17
17
|
overlay = overlay.method(:new)
|
18
18
|
end
|
data/lib/alki/assembly.rb
CHANGED
@@ -3,26 +3,34 @@ require 'alki/assembly/types/assembly'
|
|
3
3
|
require 'alki/assembly/types/group'
|
4
4
|
require 'alki/assembly/instance'
|
5
5
|
require 'alki/assembly/executor'
|
6
|
+
require 'alki/overlay_info'
|
6
7
|
|
7
8
|
module Alki
|
8
9
|
module Assembly
|
9
10
|
def new(overrides={},&blk)
|
11
|
+
Instance.new load_class, [overrides, blk]
|
12
|
+
end
|
13
|
+
|
14
|
+
def raw_instance(instance,overrides,blk)
|
10
15
|
overrides_info = OverrideBuilder.build(overrides,&blk)
|
11
16
|
override_root = overrides_info[:root] || build(:group)
|
12
17
|
|
13
18
|
assembly = build :assembly, root, override_root
|
14
|
-
|
19
|
+
update_instance_overlay = [[],OverlayInfo.new(
|
20
|
+
[:assembly_instance],
|
21
|
+
->obj{instance.__setobj__ obj; instance},
|
22
|
+
[]
|
23
|
+
)]
|
24
|
+
all_overlays = overlays+overrides_info[:overlays]+[update_instance_overlay]
|
25
|
+
executor = Executor.new(assembly, all_overlays)
|
15
26
|
|
16
27
|
override_root.children[:assembly_instance] = build(:service,->{
|
17
|
-
|
28
|
+
root
|
18
29
|
})
|
30
|
+
override_root.children[:assembly_executor] = build(:value,executor)
|
19
31
|
executor.call [:assembly_instance]
|
20
32
|
end
|
21
33
|
|
22
|
-
def build(type,*args)
|
23
|
-
Alki::Support.load_class("alki/assembly/types/#{type}").new *args
|
24
|
-
end
|
25
|
-
|
26
34
|
def root
|
27
35
|
self.definition.root
|
28
36
|
end
|
@@ -30,5 +38,11 @@ module Alki
|
|
30
38
|
def overlays
|
31
39
|
self.definition.overlays
|
32
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def build(type,*args)
|
45
|
+
Alki::Support.load_class("alki/assembly/types/#{type}").new *args
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
data/lib/alki/dsls/assembly.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'alki/execution/context'
|
2
2
|
require 'alki/overlay_delegator'
|
3
|
+
require 'alki/reloadable_delegator'
|
3
4
|
|
4
5
|
module Alki
|
5
6
|
module Execution
|
@@ -9,6 +10,14 @@ module Alki
|
|
9
10
|
def delegate_overlay(obj,overlay,**args)
|
10
11
|
Alki::OverlayDelegator.new(obj,overlay,args)
|
11
12
|
end
|
13
|
+
|
14
|
+
def entrypoint(klass,*args, reloadable: false)
|
15
|
+
klass.new *args.map {|a| reloadable ? self.reloadable(a) : lookup(a) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def reloadable(path)
|
19
|
+
Alki::ReloadableDelegator.new(root.assembly_instance,meta[:building],path)
|
20
|
+
end
|
12
21
|
end
|
13
22
|
end
|
14
23
|
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module Alki
|
2
|
+
class Generator
|
3
|
+
def initialize(root_dir)
|
4
|
+
@root_dir = root_dir
|
5
|
+
@changes = []
|
6
|
+
@triggers = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(file,contents)
|
10
|
+
@changes << [:create,file: file,contents: contents,opts: {}]
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_exec(file,contents)
|
14
|
+
@changes << [:create,file: file,contents: contents,opts: {exec: true}]
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_create(file:, contents:, opts:)
|
18
|
+
path = abs_path( file)
|
19
|
+
if File.exists? path
|
20
|
+
if File.read(path) == contents
|
21
|
+
:skip
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def desc_create(file:, contents:, opts: {})
|
27
|
+
path = abs_path( file)
|
28
|
+
if opts[:exec]
|
29
|
+
adj = "executable "
|
30
|
+
end
|
31
|
+
if File.exists? path
|
32
|
+
"Overwriting #{adj}#{file}!"
|
33
|
+
else
|
34
|
+
"Create #{adj}#{file}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def do_create(file:, contents:, opts: {})
|
39
|
+
path = abs_path( file)
|
40
|
+
FileUtils.mkdir_p File.dirname(path)
|
41
|
+
File.write path, contents
|
42
|
+
FileUtils.chmod '+x', path if opts[:exec]
|
43
|
+
end
|
44
|
+
|
45
|
+
def opt_create(file,contents)
|
46
|
+
@changes << [:opt_create,file: file,contents: contents]
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_opt_create(file:, contents:)
|
50
|
+
if File.exists? abs_path( file)
|
51
|
+
:skip
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def desc_opt_create(opts)
|
56
|
+
desc_create opts
|
57
|
+
end
|
58
|
+
|
59
|
+
def do_opt_create(opts)
|
60
|
+
do_create opts
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_line(file,line,opts={})
|
64
|
+
@changes << [:add_line,file: file,line: line,opts: opts]
|
65
|
+
end
|
66
|
+
|
67
|
+
def check_add_line(file:,line:,opts:)
|
68
|
+
if File.exists? abs_path(file)
|
69
|
+
File.open(file) do |f|
|
70
|
+
if opts[:after]
|
71
|
+
found = until f.eof?
|
72
|
+
break true if f.readline.chomp == opts[:after]
|
73
|
+
end
|
74
|
+
unless found
|
75
|
+
puts "File \"#{file}\" doesn't contain required line #{opts[:after]}"
|
76
|
+
return :abort
|
77
|
+
end
|
78
|
+
end
|
79
|
+
until f.eof?
|
80
|
+
l = f.readline
|
81
|
+
if opts[:match] ? l.chomp.match(opts[:match]) : (l.chomp == line)
|
82
|
+
return :skip
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
else
|
87
|
+
unless @changes.find {|c| [:create,:opt_create].include?(c[0]) && c[1][:file] == file}
|
88
|
+
puts "File \"#{file}\" doesn't exist!"
|
89
|
+
return :abort
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def desc_add_line(file:,line:,opts:)
|
95
|
+
"Add line \"#{line}\" to #{file}"
|
96
|
+
end
|
97
|
+
|
98
|
+
def do_add_line(file:,line:,opts:)
|
99
|
+
if opts[:after]
|
100
|
+
File.write file, File.read(file).sub(/^#{Regexp.quote(opts[:after])}\n/){|m| m + line + "\n"}
|
101
|
+
else
|
102
|
+
File.open(file,'a') do |f|
|
103
|
+
f.puts line
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def trigger(file,cmd)
|
109
|
+
@triggers << [file, cmd]
|
110
|
+
end
|
111
|
+
|
112
|
+
def check_trigger(changes,file)
|
113
|
+
changes.find {|c| c[1][:file] == file}
|
114
|
+
end
|
115
|
+
|
116
|
+
def desc_trigger(cmd:)
|
117
|
+
"Run \"#{cmd}\""
|
118
|
+
end
|
119
|
+
|
120
|
+
def do_trigger(cmd:)
|
121
|
+
system cmd
|
122
|
+
end
|
123
|
+
|
124
|
+
def check_changes
|
125
|
+
puts "Checking preconditions..."
|
126
|
+
abort = false
|
127
|
+
unless Dir.exists?(@root_dir)
|
128
|
+
puts "Root dir doesn't exist"
|
129
|
+
exit 1
|
130
|
+
end
|
131
|
+
do_changes = []
|
132
|
+
@changes.each do |(type,args)|
|
133
|
+
res = send "check_#{type}", args
|
134
|
+
abort = true if res == :abort
|
135
|
+
do_changes << [type,args] unless res == :skip
|
136
|
+
end
|
137
|
+
@triggers.each do |(file,cmd)|
|
138
|
+
if check_trigger do_changes, file
|
139
|
+
do_changes << [:trigger,cmd: cmd]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
exit 1 if abort
|
143
|
+
do_changes
|
144
|
+
end
|
145
|
+
|
146
|
+
def print_overview(changes)
|
147
|
+
puts "Overview of changes to be made:"
|
148
|
+
|
149
|
+
changes.each do |(type,args)|
|
150
|
+
desc = send "desc_#{type}", args
|
151
|
+
puts " #{desc}" if desc
|
152
|
+
end
|
153
|
+
print "Proceed? "
|
154
|
+
resp = STDIN.gets.chomp
|
155
|
+
unless resp =~ /^y(es?)?/i
|
156
|
+
puts "Aborting"
|
157
|
+
exit
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def write
|
162
|
+
puts "Using project root: #{@root_dir}\n"
|
163
|
+
do_changes = check_changes
|
164
|
+
print_overview do_changes
|
165
|
+
|
166
|
+
puts "Writing changes..."
|
167
|
+
do_changes.each do |(type,args)|
|
168
|
+
send "do_#{type}", args
|
169
|
+
end
|
170
|
+
puts "Done"
|
171
|
+
end
|
172
|
+
|
173
|
+
def abs_path(p)
|
174
|
+
File.expand_path(p,@root_dir)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Alki
|
4
|
+
class ReloadableDelegator < Delegator
|
5
|
+
def initialize(instance,from,path)
|
6
|
+
@instance = instance
|
7
|
+
@from = from.to_s.split('.').map(&:to_sym)
|
8
|
+
@path = path.to_s.split('.').map(&:to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
def __getobj__
|
12
|
+
@instance.assembly_executor.call(
|
13
|
+
@instance.assembly_executor.canonical_path(@from,@path)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/alki/version.rb
CHANGED
data/lib/alki.rb
CHANGED
@@ -210,16 +210,18 @@ describe 'Overlays' do
|
|
210
210
|
|
211
211
|
it 'should allow setting overlays on assembly_instance' do
|
212
212
|
values = []
|
213
|
+
mock = Minitest::Mock.new
|
213
214
|
assembly = Alki.create_assembly do
|
214
215
|
overlay :assembly_instance, :test_overlay
|
215
|
-
|
216
|
+
set :val, 1
|
216
217
|
set :test_overlay, ->(value) {
|
217
218
|
values << value
|
218
|
-
|
219
|
+
mock
|
219
220
|
}
|
220
221
|
end
|
221
|
-
|
222
|
+
mock.expect :val, 2
|
223
|
+
assembly.new.val.must_equal 2
|
222
224
|
values.size.must_equal 1
|
223
|
-
values[0].
|
225
|
+
values[0].val.must_equal 1
|
224
226
|
end
|
225
227
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alki
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Edlefsen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -138,9 +138,11 @@ files:
|
|
138
138
|
- lib/alki/execution/context_class_builder.rb
|
139
139
|
- lib/alki/execution/value_context.rb
|
140
140
|
- lib/alki/feature_test.rb
|
141
|
+
- lib/alki/generator.rb
|
141
142
|
- lib/alki/overlay_delegator.rb
|
142
143
|
- lib/alki/overlay_info.rb
|
143
144
|
- lib/alki/override_builder.rb
|
145
|
+
- lib/alki/reloadable_delegator.rb
|
144
146
|
- lib/alki/service_delegator.rb
|
145
147
|
- lib/alki/test.rb
|
146
148
|
- lib/alki/version.rb
|
@@ -186,7 +188,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
188
|
version: '0'
|
187
189
|
requirements: []
|
188
190
|
rubyforge_project:
|
189
|
-
rubygems_version: 2.5.
|
191
|
+
rubygems_version: 2.5.2
|
190
192
|
signing_key:
|
191
193
|
specification_version: 4
|
192
194
|
summary: Base library for building applications.
|