alki 0.8.0 → 0.9.0
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/README.adoc +108 -6
- data/alki.gemspec +2 -2
- data/bin/alki +17 -0
- data/config/dsls.rb +2 -2
- data/doc/assembly_dsl.adoc +6 -6
- data/exe/alki +275 -0
- data/lib/alki/assembly/builder.rb +113 -0
- data/lib/alki/assembly/executor.rb +127 -0
- data/lib/alki/assembly/handler_base.rb +21 -0
- data/lib/alki/assembly/instance.rb +21 -0
- data/lib/alki/assembly/types/assembly.rb +92 -0
- data/lib/alki/assembly/types/factory.rb +29 -0
- data/lib/alki/assembly/types/func.rb +12 -0
- data/lib/alki/assembly/types/group.rb +39 -0
- data/lib/alki/assembly/types/override.rb +40 -0
- data/lib/alki/assembly/types/proc_value.rb +18 -0
- data/lib/alki/assembly/types/service.rb +27 -0
- data/lib/alki/assembly/types/value.rb +9 -0
- data/lib/alki/assembly.rb +21 -40
- data/lib/alki/dsls/assembly.rb +10 -12
- data/lib/alki/dsls/assembly_group.rb +92 -0
- data/lib/alki/dsls/assembly_type.rb +5 -15
- data/lib/alki/execution/cache_entry.rb +16 -0
- data/lib/alki/execution/context.rb +29 -0
- data/lib/alki/execution/context_class_builder.rb +36 -0
- data/lib/alki/execution/value_context.rb +14 -0
- data/lib/alki/overlay_delegator.rb +8 -20
- data/lib/alki/overlay_info.rb +3 -0
- data/lib/alki/override_builder.rb +6 -4
- data/lib/alki/service_delegator.rb +3 -3
- data/lib/alki/version.rb +1 -1
- data/lib/alki.rb +4 -4
- data/test/feature/alki_test.rb +1 -2
- data/test/feature/example_test.rb +2 -3
- data/test/feature/factories_test.rb +48 -0
- data/test/feature/overlays_test.rb +225 -0
- data/test/feature/overrides_test.rb +1 -2
- data/test/feature/pseudo_elements_test.rb +67 -0
- data/test/feature_test_helper.rb +1 -0
- data/test/fixtures/example/config/assembly.rb +11 -2
- data/test/fixtures/example/config/handlers.rb +2 -7
- data/test/fixtures/example/lib/log_overlay.rb +3 -3
- data/test/integration/dsls/assembly_test.rb +3 -8
- data/test/integration/dsls/assembly_type_test.rb +2 -2
- data/test/integration/dsls/service_dsl_test.rb +2 -2
- metadata +36 -18
- data/lib/alki/assembly_builder.rb +0 -109
- data/lib/alki/assembly_executor.rb +0 -129
- data/lib/alki/assembly_handler_base.rb +0 -19
- data/lib/alki/dsls/assembly_type_dsl.rb +0 -21
- data/lib/alki/dsls/assembly_types/assembly.rb +0 -101
- data/lib/alki/dsls/assembly_types/group.rb +0 -41
- data/lib/alki/dsls/assembly_types/load.rb +0 -31
- data/lib/alki/dsls/assembly_types/overlay.rb +0 -9
- data/lib/alki/dsls/assembly_types/value.rb +0 -100
- data/test/test_helper.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30b3e6d184beebdc00446b13b4930d47d32151df
|
4
|
+
data.tar.gz: 98249b15e58962ab44737b9e54199f8690938407
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df40acd78708b8a0371f5191f1b5160c4d09e9348e92c237791761528a22552f3212c7dd14759c555b20d5364def4cd1976eaa12ad90690d3d9b81a7c5e27670
|
7
|
+
data.tar.gz: 24bc23ef4c32c0b16a6e63a0386c3a767bcc9fc0a2393b8197c9d942e4f9ebad6bc97b394d1d142c060cc39371e42d6fc2edeb6d72a51035fd12dad836babc8c
|
data/README.adoc
CHANGED
@@ -1,12 +1,114 @@
|
|
1
1
|
# What is Alki?
|
2
|
+
:toc:
|
2
3
|
|
3
|
-
Alki is a framework for
|
4
|
+
Alki is a small framework for writing Ruby projects, providing a powerful dependency injection system with
|
5
|
+
helpers for testing, creating executables, and easy to use libraries.
|
4
6
|
|
5
|
-
It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on
|
7
|
+
It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on
|
8
|
+
implementing business logic. It can be used alongside other frameworks such as Ruby on Rails.
|
6
9
|
|
7
|
-
|
10
|
+
Alki tries to combine the time tested software engineering concepts such as
|
11
|
+
https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)[SOLID],
|
12
|
+
and https://en.wikipedia.org/wiki/Inversion_of_control[Inversion of Control], with
|
13
|
+
Ruby style ease of use, DSLs, and
|
14
|
+
https://en.wikipedia.org/wiki/Convention_over_configuration[Convention over Configuration].
|
8
15
|
|
9
|
-
|
10
|
-
|
16
|
+
Documentation can be found at https://github.com/alki-project/alki/blob/master/doc/index.adoc
|
17
|
+
|
18
|
+
## Getting Started
|
19
|
+
|
20
|
+
Install from rubygems
|
21
|
+
|
22
|
+
gem install alki
|
23
|
+
|
24
|
+
Setting up Alki in your project requires only created a couple of files, but the
|
25
|
+
`alki` command line tool can also be used to automate the process. Project names
|
26
|
+
should be given in lowercase using underscores and forward slashes as separators.
|
27
|
+
|
28
|
+
.Example project
|
29
|
+
```
|
30
|
+
$ cd <project dir>
|
31
|
+
$ alki init --console <project name>
|
32
|
+
```
|
33
|
+
## Example
|
34
|
+
|
35
|
+
To demonstrate the basic features of Alki, this will go through the creation of
|
36
|
+
a simple example project.
|
37
|
+
|
38
|
+
To get started, a new project should be created.
|
39
|
+
|
40
|
+
```
|
41
|
+
$ mkdir example
|
42
|
+
$ cd example
|
43
|
+
$ alki init --console example
|
44
|
+
```
|
45
|
+
|
46
|
+
### First Service
|
47
|
+
|
48
|
+
Open up `config/assembly.rb` in your favorite text editor and add a simple log
|
49
|
+
service as an example. The service definition should require the files it needs and
|
50
|
+
then construct an object.
|
51
|
+
|
52
|
+
.config/assembly.rb
|
53
|
+
```ruby
|
54
|
+
Alki do
|
55
|
+
mount :console, 'alki/console', name: 'example'
|
56
|
+
|
57
|
+
service :log do
|
58
|
+
require 'logger'
|
59
|
+
Logger.new STDERR
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
To test, we can open up the console and interact with our new service.
|
65
|
+
|
66
|
+
```bash
|
67
|
+
$ bin/console
|
68
|
+
example> log.info "test message"
|
69
|
+
I, [<timestamp>] INFO -- : test message
|
70
|
+
=> true
|
71
|
+
test> exit
|
72
|
+
```
|
73
|
+
|
74
|
+
### Adding Settings
|
75
|
+
|
76
|
+
Many times in a project there will end with a lot of configuration settings or magic
|
77
|
+
numbers sprinkled throughout your code, maybe pulled from `ENV` or just set directly
|
78
|
+
as constants. Alki provides a simple way to centralize them. By convention this is done
|
79
|
+
by creating a `config/settings.rb` and loading it into your Assembly.
|
80
|
+
|
81
|
+
To demonstrate we can pull the log io (`STDERR`) out of our main assembly and add it
|
82
|
+
to our settings.
|
83
|
+
|
84
|
+
.config/settings.rb
|
85
|
+
```ruby
|
86
|
+
Alki do
|
87
|
+
set :log_io, STDERR
|
88
|
+
end
|
89
|
+
```
|
90
|
+
Now we can load our settings file in our assembly config (`load :settings`) and use
|
91
|
+
the setting in our log service.
|
92
|
+
|
93
|
+
.config/assembly.rb
|
94
|
+
```ruby
|
95
|
+
Alki do
|
96
|
+
load :settings
|
97
|
+
mount :console, 'alki/console', name: 'example'
|
98
|
+
|
99
|
+
service :log do
|
100
|
+
require 'logger'
|
101
|
+
Logger.new settings.log_io
|
102
|
+
end
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
### Adding your own Classes
|
107
|
+
|
108
|
+
Generally, you're not just going to be using other people's classes. Alki doesn't have any requirements as to
|
109
|
+
where your classes need to go, but conventionally they can be placed under `lib/<project-name>/`.
|
110
|
+
|
111
|
+
The Alki style of writing a Class is to only `require` external libraries, or utility files in your class files.
|
112
|
+
All of the "glue" that ties your code together can instead be put in your assembly definition. This allows you
|
113
|
+
to modularize and loosely couple your programs.
|
11
114
|
|
12
|
-
Docs can be found at https://github.com/alki-project/alki/blob/master/doc/index.adoc
|
data/alki.gemspec
CHANGED
@@ -21,6 +21,6 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.6"
|
22
22
|
spec.add_development_dependency "rake", '~> 10.0'
|
23
23
|
spec.add_dependency "minitest", "~> 5.9", '>= 5.9.1'
|
24
|
-
spec.add_dependency "alki-dsl", "~> 0.3", '>= 0.3.
|
25
|
-
spec.add_dependency "alki-support", "~> 0.
|
24
|
+
spec.add_dependency "alki-dsl", "~> 0.3", '>= 0.3.3'
|
25
|
+
spec.add_dependency "alki-support", "~> 0.6"
|
26
26
|
end
|
data/bin/alki
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'alki' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("alki", "alki")
|
data/config/dsls.rb
CHANGED
data/doc/assembly_dsl.adoc
CHANGED
@@ -227,7 +227,7 @@ assembly = Alki.create_assembly do
|
|
227
227
|
Logger.new STDOUT
|
228
228
|
end
|
229
229
|
end
|
230
|
-
assembly.new.logger << "hello"
|
230
|
+
assembly.new.logger << "hello\n"
|
231
231
|
|
232
232
|
#output: hello
|
233
233
|
```
|
@@ -251,7 +251,7 @@ assembly = Alki.create_assembly do
|
|
251
251
|
logger STDOUT
|
252
252
|
end
|
253
253
|
end
|
254
|
-
assembly.new.main_logger << "hello"
|
254
|
+
assembly.new.main_logger << "hello\n"
|
255
255
|
|
256
256
|
#output: hello
|
257
257
|
```
|
@@ -265,9 +265,9 @@ Overlays are often most useful in groups where all services adhere to a common i
|
|
265
265
|
can be used to perform aspect oriented programming like logging, validation, or access controls.
|
266
266
|
|
267
267
|
|
268
|
-
## Assemblies (
|
268
|
+
## Mounting Assemblies (mount)
|
269
269
|
|
270
|
-
Other assemblies can be mounted into your Assembly using the `
|
270
|
+
Other assemblies can be mounted into your Assembly using the `mount` command.
|
271
271
|
|
272
272
|
The first argument is what the element should be named in the parent assembly. The optional second argument
|
273
273
|
is the name of the assembly to be mounted. This should be formatted like a require string (relative path but
|
@@ -296,7 +296,7 @@ end
|
|
296
296
|
Alki.create_assembly name: 'main_assembly' do
|
297
297
|
set :val2, "two"
|
298
298
|
# Mounts OtherAssembly as 'other'
|
299
|
-
|
299
|
+
mount :other, 'other_assembly'
|
300
300
|
end
|
301
301
|
instance = MainAssembly.new
|
302
302
|
puts instance.other.val
|
@@ -331,7 +331,7 @@ end
|
|
331
331
|
|
332
332
|
Alki.create_assembly name: 'main_assembly' do
|
333
333
|
set :val, "hello"
|
334
|
-
|
334
|
+
mount :other, 'other_assembly' do
|
335
335
|
set :msg do
|
336
336
|
val
|
337
337
|
end
|
data/exe/alki
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'fileutils'
|
3
|
+
require 'optparse'
|
4
|
+
require 'alki/support'
|
5
|
+
|
6
|
+
valid_addons = ["console"]
|
7
|
+
|
8
|
+
options = {
|
9
|
+
config_dir: nil,
|
10
|
+
primary_config: nil,
|
11
|
+
addons: [],
|
12
|
+
project_dir: Dir.pwd,
|
13
|
+
}
|
14
|
+
parser = OptionParser.new do |opts|
|
15
|
+
opts.banner = "Usage: alki init PROJECT_NAME [options]"
|
16
|
+
|
17
|
+
opts.on("-a", "--add[=ADDON]", "Add addon to project. Valid addons: #{valid_addons.join(', ')}") do |v|
|
18
|
+
unless valid_addons.include? v
|
19
|
+
puts "Invalid addon: #{v}"
|
20
|
+
exit 1
|
21
|
+
end
|
22
|
+
options[:addon] |= [v]
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-d','--directory=DIRECTORY', 'Project root, defaults to the current working directory') do |v|
|
26
|
+
options[:project_dir] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-c", "--config=DIECTORY", "Override config dir (default \"config\")") do |v|
|
30
|
+
options[:config_dir] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("-p", "--primary=PRIMARY_CONFIG", "Override primary config (default \"assembly\")") do |v|
|
34
|
+
options[:primary_config] = v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
parser.parse!
|
39
|
+
|
40
|
+
if ARGV.size != 2 || ARGV[0] != "init"
|
41
|
+
puts parser.banner
|
42
|
+
exit 1
|
43
|
+
end
|
44
|
+
|
45
|
+
unless ARGV[1] =~ %r{^[a-z0-9_]+(/[a-z0-9_]+)*$}
|
46
|
+
puts "Invalid project name. May only consist of lowercase letters, numbers, underscores, and forward slashes"
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
|
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
|
+
project_name = ARGV[1]
|
229
|
+
|
230
|
+
fw = AlkiFileWriter.new config[:project_dir]
|
231
|
+
|
232
|
+
config_dir = options[:config_dir] || 'config'
|
233
|
+
primary_config = options[:primary_config] || 'assembly'
|
234
|
+
primary_config_path = File.join(config_dir,primary_config+'.rb')
|
235
|
+
fw.opt_create primary_config_path, <<END
|
236
|
+
Alki do
|
237
|
+
# Assembly config goes here
|
238
|
+
end
|
239
|
+
END
|
240
|
+
|
241
|
+
opts = []
|
242
|
+
opts << "config_dir: '#{options[:config_dir]}'" if options[:config_dir]
|
243
|
+
opts << "primary_config: '#{options[:primary_config]}'" if options[:primary_config]
|
244
|
+
unless opts.empty?
|
245
|
+
opt_str = " #{opts.join(', ')}"
|
246
|
+
end
|
247
|
+
fw.create "lib/#{project_name}.rb", <<END
|
248
|
+
require 'alki'
|
249
|
+
Alki.project_assembly!#{opt_str}
|
250
|
+
END
|
251
|
+
|
252
|
+
fw.opt_create "Gemfile", <<END
|
253
|
+
source "https://rubygems.org"
|
254
|
+
END
|
255
|
+
|
256
|
+
fw.trigger 'Gemfile', 'bundle install'
|
257
|
+
|
258
|
+
fw.add_line "Gemfile", "gem 'alki'", match: /^gem ['"]alki['"]/
|
259
|
+
|
260
|
+
if options[:addons].include? "console"
|
261
|
+
fw.add_line "Gemfile", "gem 'alki-console'", match: /^gem ['"]alki-console['"]/
|
262
|
+
|
263
|
+
name = project_name.tr('/','-')
|
264
|
+
fw.add_line primary_config_path, " mount :console, 'alki/console', name: '#{name}'", match: /^\s*assembly :console/, after: 'Alki do'
|
265
|
+
|
266
|
+
fw.create_exec 'bin/console', <<END
|
267
|
+
#!/usr/bin/env ruby
|
268
|
+
require 'bundler/setup'
|
269
|
+
require 'alki/bin'
|
270
|
+
require '#{project_name}'
|
271
|
+
#{Alki::Support.classify(project_name)}.new.console.run
|
272
|
+
END
|
273
|
+
end
|
274
|
+
|
275
|
+
fw.write
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'alki/assembly'
|
2
|
+
require 'alki/class_builder'
|
3
|
+
require 'alki/dsl'
|
4
|
+
require 'alki/support'
|
5
|
+
|
6
|
+
module Alki
|
7
|
+
module Assembly
|
8
|
+
class Builder
|
9
|
+
def initialize
|
10
|
+
@config_dir = nil
|
11
|
+
@assembly_name = nil
|
12
|
+
@definition = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :config_dir, :assembly_name, :definition
|
16
|
+
|
17
|
+
def self.build(opts={},&blk)
|
18
|
+
new.build(opts,&blk)
|
19
|
+
end
|
20
|
+
|
21
|
+
def build(opts={},&blk)
|
22
|
+
build_assembly blk if blk
|
23
|
+
if opts[:config_dir]
|
24
|
+
context = if opts[:project_assembly]
|
25
|
+
File.dirname opts[:project_assembly]
|
26
|
+
else
|
27
|
+
Dir.pwd
|
28
|
+
end
|
29
|
+
@config_dir = File.expand_path opts[:config_dir], context
|
30
|
+
end
|
31
|
+
set_assembly_name opts[:name] if opts[:name]
|
32
|
+
setup_project_assembly opts[:project_assembly] if opts[:project_assembly]
|
33
|
+
register_config_directory if @config_dir
|
34
|
+
if blk
|
35
|
+
build_assembly blk
|
36
|
+
else
|
37
|
+
load_assembly_file opts[:primary_config]
|
38
|
+
end
|
39
|
+
build_empty_assembly unless definition
|
40
|
+
build_class
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup_project_assembly(path)
|
44
|
+
root = Alki::Support.find_root(path) do |dir|
|
45
|
+
File.exists?(File.join(dir,'config','assembly.rb')) ||
|
46
|
+
File.exists?(File.join(dir,'Gemfile')) ||
|
47
|
+
!Dir.glob(File.join(dir,'*.gemspec')).empty?
|
48
|
+
end
|
49
|
+
if root
|
50
|
+
unless @config_dir
|
51
|
+
config_dir = File.join(root,'config')
|
52
|
+
@config_dir = config_dir if File.exists? config_dir
|
53
|
+
end
|
54
|
+
|
55
|
+
unless @assembly_name
|
56
|
+
lib_dir = File.join(root,'lib')
|
57
|
+
name = Alki::Support.path_name path, lib_dir
|
58
|
+
unless name
|
59
|
+
raise "Can't auto-detect name of assembly"
|
60
|
+
end
|
61
|
+
set_assembly_name name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_assembly_name(name)
|
67
|
+
@assembly_name = name
|
68
|
+
end
|
69
|
+
|
70
|
+
def register_config_directory
|
71
|
+
opts = {config_dir: @config_dir}
|
72
|
+
opts[:prefix] = File.join(@assembly_name,'alki_config') if @assembly_name
|
73
|
+
Alki::Dsl.register_dir @config_dir, 'alki/dsls/assembly', opts
|
74
|
+
end
|
75
|
+
|
76
|
+
def load_assembly_file(name = nil)
|
77
|
+
name ||= 'assembly'
|
78
|
+
if @config_dir
|
79
|
+
assembly_config_path = File.join(@config_dir,"#{name}.rb")
|
80
|
+
if File.exists? assembly_config_path
|
81
|
+
@definition = Alki::Dsl.load(assembly_config_path)[:class]
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_empty_assembly
|
88
|
+
build_assembly ->{}
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_assembly(blk)
|
92
|
+
@definition = Alki::Dsl.build('alki/dsls/assembly', config_dir: @config_dir, &blk)[:class]
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_class
|
96
|
+
definition = @definition
|
97
|
+
Alki::ClassBuilder.build(
|
98
|
+
prefix: '',
|
99
|
+
name: @assembly_name,
|
100
|
+
class_modules: [Alki::Assembly],
|
101
|
+
type: :module,
|
102
|
+
class_methods: {
|
103
|
+
definition: {
|
104
|
+
body: ->{
|
105
|
+
definition
|
106
|
+
}
|
107
|
+
},
|
108
|
+
}
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|