alki 0.6.1 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bfc0e6ec1368d9f0df28e72d8978aef6afe37ecf
4
- data.tar.gz: a251bd8975e08e982b0cf18710bb5abbea957cb0
3
+ metadata.gz: 31a32ce28977b35ce72bab1992d4512c7ea33f44
4
+ data.tar.gz: c6ab121484e99b437aaae6390db983d83a011cc7
5
5
  SHA512:
6
- metadata.gz: 6203b1f5a0914be306e711733aabe161f11346e286a4a2011eeea8d97cff7a2ebc8e5ed6ea41df0506917062b54dab07175b6061a6ccae32bf1cb29294009b91
7
- data.tar.gz: 3a8d1da8bc5a4396fb5124c7e6b823747942f38b56e046e5933b77d806656b8567276f3e5d129caf1998f415ace2287e722adaf42958d951aa05a7083485467c
6
+ metadata.gz: bd1da1b284b8caa1d75a32ac5e199f5f64e504213de1abeedb694e6f6fca517633992b8f2572129b71d329ed0069de2d698b89c0dd2f395a72d49243dbe2032c
7
+ data.tar.gz: fd7e999ffc561b91e6452bd859f8f8783fd0a90f097a703febd7746d837227717fa2bfafdaac310903eecef08090d1b2fd834444682b49a38ccdb37dac404a10
data/.gitignore CHANGED
@@ -7,7 +7,6 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
- doc/
11
10
  lib/bundler/man
12
11
  pkg
13
12
  rdoc
data/README.ad ADDED
@@ -0,0 +1,12 @@
1
+ # What is Alki?
2
+
3
+ Alki is a framework for creating projects that are modular, testable, and well organized.
4
+
5
+ It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on implementing business logic.
6
+
7
+ # Synopsis
8
+
9
+ Best place to start would be to check out some examples:
10
+
11
+ * https://github.com/alki-project/alki-example
12
+ * https://github.com/alki-project/alki/tree/master/test/fixtures/example
data/doc/assemblies.ad ADDED
@@ -0,0 +1,138 @@
1
+ Assemblies
2
+ ==========
3
+
4
+ Project Assemblies
5
+ ------------------
6
+
7
+ Most of the time, a project will have a single assembly. Alki makes doing this easy.
8
+
9
+ First. in your project's `lib` directory create a ruby file for your assembly. If your assembly is
10
+ to be called `MyAssembly`, create `lib/my_assembly.rb`. If it's namespaced but it in a subdirectory
11
+ as usual, `MyModule::MyAssembly` would go in `lib/my_module/my_assembly.rb`
12
+
13
+ Your assembly file just needs two lines:
14
+
15
+ ```ruby
16
+ require 'alki'
17
+ Alki.project_assembly!
18
+ ```
19
+
20
+ It will detect the project root and what class name it should create automatically.
21
+
22
+ Second, a `config` directory must be created in the project root, and in that directory an `assembly.rb`
23
+ file should be created. It should be a normal Alki loader block.
24
+
25
+ ```ruby
26
+ Alki do
27
+ # Assembly DSL ...
28
+ end
29
+ ```
30
+
31
+ `Alki.project_assembly!` defaults can be overridden by passing in the following keyword arguments:
32
+
33
+ [horizontal]
34
+ name:: Set the name of the assembly. Should be formatted how you would put it in a `require` call
35
+ (e.g. if `MyModule::MyAssembly` is desired, use `'my_module/my_assembly'`). Default is
36
+ determined from the filename of the caller.
37
+
38
+ config_dir:: Set directory where assembly config files are found. Default is `<project root>/config`.
39
+
40
+ primary_config:: Sets the name of the main config file to load. Defaults to `assembly.rb`.
41
+
42
+ Manually Creating Assemblies
43
+ ----------------------------
44
+
45
+ In addition to Project Assemblies, you can also create assemblies directly using `Alki.create_assembly`.
46
+ It should be called with a block that contains the assembly DSL. It will return a Module object that can
47
+ be used directly or assigned to a constant.
48
+
49
+ ```ruby
50
+ assembly = Alki.create_assembly do
51
+ set :msg, "hello world"
52
+ func :run do
53
+ puts msg
54
+ end
55
+ end
56
+
57
+ assembly.new.run
58
+
59
+ # output: hello world
60
+ ```
61
+
62
+ Using Assemblies
63
+ ----------------
64
+
65
+ Assemblies have a `new` method used to create new assembly instances (like a normal class). Once an
66
+ instance is created, anything in the assembly is accessible by drilling down into groups.
67
+
68
+ Given an assembly like this:
69
+
70
+ ```ruby
71
+ require 'alki'
72
+ assembly = Alki.create_assembly do
73
+ set :log_io, STDERR
74
+ group :util do
75
+ service :logger do
76
+ require 'logger'
77
+ Logger.new log_io
78
+ end
79
+ end
80
+ end
81
+ ```
82
+
83
+ Once can use the logger service like so:
84
+
85
+ ```ruby
86
+ instance = assembly.new
87
+ instance.util.logger.info "test"
88
+
89
+ # output: I, [<timestamp>] INFO -- : test
90
+ ```
91
+
92
+ ### Overrides
93
+
94
+ Assembly overrides provide a way to do configure or customize an Assembly when
95
+ constructing an instance.
96
+
97
+ For example, using the assembly created above, one might want to change the IO object logged to.
98
+
99
+ The simplist way to do this is to provide a hash of values which will set values (as if the `set`
100
+ command was called in the DSL):
101
+
102
+ ```ruby
103
+ require 'stringio'
104
+ io = StringIO.new
105
+ instance = assembly.new log_io: io
106
+ instance.util.logger.info "test"
107
+ puts(io.string.match(/INFO -- : test/) != nil)
108
+
109
+ # output: true
110
+ ```
111
+
112
+ The limitiation of this is that it can only override basic values. To override more complex elements
113
+ a block can be given to `new` allowing the full assembly DSL.
114
+
115
+ ```ruby
116
+ class MyLogger
117
+ def initialize(io)
118
+ @io = io
119
+ end
120
+ def info(msg)
121
+ @io.puts "INFO #{msg}"
122
+ end
123
+ end
124
+
125
+ instance = assembly.new do
126
+ group :util do
127
+ service :logger do
128
+ MyLogger.new original.log_io
129
+ end
130
+ end
131
+ end
132
+ instance.util.logger.info "test"
133
+
134
+ # output: INFO test
135
+ ```
136
+
137
+ One thing of note is that elements from the assembly are accessibly via the `original` method.
138
+ This can also be used to access the original versions of elements that have been overriden.
@@ -0,0 +1,242 @@
1
+ Assembly DSL
2
+ ===========
3
+
4
+ Building Assemblies is done via a DSL that makes putting together the pieces of your project easy.
5
+
6
+ Groups
7
+ ------
8
+
9
+ Groups are the basic way of organizing the elements of your Assembly. Creating elements at the top
10
+ level of an Assembly will place them in a root group, but subgroups are easily created via the `group`
11
+ command.
12
+
13
+ ```ruby
14
+ require 'alki'
15
+ assembly = Alki.create_assembly do
16
+ group :sub_group do
17
+ set :val, "hello world"
18
+ end
19
+ end
20
+ puts assembly.new.sub_group.val
21
+ ```
22
+
23
+ Scoping is also done by group, so that an element will be found by moving up parent groups until
24
+ it is found.
25
+
26
+ ```ruby
27
+ require 'alki'
28
+ assembly = Alki.create_assembly do
29
+ set :val, "one"
30
+ group :g1 do
31
+ set :val, "two"
32
+ group :g2 do
33
+ func :print do
34
+ puts val
35
+ end
36
+ end
37
+ end
38
+ end
39
+ assembly.new.g1.g2.print
40
+
41
+ #output: two
42
+ ```
43
+
44
+ === Loading groups
45
+
46
+ Groups can also be loaded from other config files via the `load` command.
47
+
48
+ .config/settings.rb
49
+ ```ruby
50
+ Alki do
51
+ set :val, "hello"
52
+ end
53
+ ```
54
+
55
+ ```ruby
56
+ require 'alki'
57
+ assembly = Alki.create_assembly config_dir: 'config' do
58
+ load :settings
59
+ func :print do
60
+ puts settings.val
61
+ end
62
+ end
63
+ assembly.new.print
64
+
65
+ # output: hello
66
+ ```
67
+
68
+
69
+ Values
70
+ ------
71
+
72
+ There are four types of elements loosely categorized as "values".
73
+
74
+ ### Basic Values (set)
75
+
76
+ The simplest type of values are ones created via the `set` command. There are two forms of `set`.
77
+
78
+ ```ruby
79
+ require 'alki'
80
+ assembly = Alki.create_assembly do
81
+ # This form takes the value as the second argument
82
+ set :val1, "hello"
83
+
84
+ # INVALID! Value may not be a reference to another element
85
+ # set :val2, val1
86
+
87
+ # This form takes the value as a block.
88
+ # Block is run once and result cached.
89
+ # Allows referencing other elements
90
+ set :val2 do
91
+ val1
92
+ end
93
+ end
94
+ puts assembly.new.val2
95
+
96
+ #output: hello
97
+ ```
98
+
99
+ ### Functions (func)
100
+
101
+ Simple callable values can be created with the `func` command. These can take arguments, are run
102
+ each time they are referenced, and can access other elements.
103
+
104
+ ```ruby
105
+ require 'alki'
106
+ assembly = Alki.create_assembly do
107
+ set :greeting, "Hello %s!"
108
+
109
+ func :greet do |name|
110
+ puts(greeting % [name])
111
+ end
112
+ end
113
+ puts assembly.new.greet "Matt"
114
+
115
+ #output: Hello Matt!
116
+ ```
117
+
118
+ ### Services (service)
119
+
120
+ Services are the key element Assemblies are typically made up of. Like the block form of `set`,
121
+ `service` takes a name and block, which will be evaluated once on-demand and the result cached.
122
+ One difference between the block form of `set` and `service` is that services are affected
123
+ by overlays, whereas basic values are not.
124
+
125
+ Commonly a service will require the file that defines a class, and then constructs an instance of
126
+ that class.
127
+
128
+ ```ruby
129
+ require 'alki'
130
+ assembly = Alki.create_assembly do
131
+ service :logger do
132
+ require 'logger'
133
+ Logger.new STDOUT
134
+ end
135
+ end
136
+ assembly.new.logger << "hello"
137
+
138
+ #output: hello
139
+ ```
140
+
141
+ ### Factories (factory)
142
+
143
+ Factories are a mix between services and funcs. Like services, they take a block which is evaluated
144
+ once. Unlike services though, that block must return a callable object (like a Proc). This object
145
+ is then called directly when the factory is referenced. This allows you to require files or construct
146
+ a factory object once but still run code on every reference.
147
+
148
+ ```ruby
149
+ require 'alki'
150
+ assembly = Alki.create_assembly do
151
+ factory :logger do
152
+ require 'logger'
153
+ -> (io) { Logger.new io }
154
+ end
155
+
156
+ service :main_logger do
157
+ logger STDOUT
158
+ end
159
+ end
160
+ assembly.new.main_logger << "hello"
161
+
162
+ #output: hello
163
+ ```
164
+
165
+ ### Overlays (overlay)
166
+
167
+ Overlays are a way to intercept and transform calls made to all services in a given group or it's
168
+ sub-groups.
169
+
170
+ Overlays are often most useful in groups where all services adhere to a common interface, and overlays
171
+ can be used to perform aspect oriented programming like logging, validation, or access controls.
172
+
173
+
174
+ ### Assemblies (assembly)
175
+
176
+ Other assemblies can be mounted into your Assembly using the `assembly` command.
177
+
178
+ The first argument is what it should be named in your assembly. The optional second argument
179
+ is the name of the assembly. This should be formatted like a require string (relative path but
180
+ no `.rb`) and will default to the value of the first argument. If a classified version of that name
181
+ can't be found, it will attempt to `require` it, and then try again.
182
+
183
+ ```ruby
184
+ require 'alki'
185
+ Alki.create_assembly name: 'other_assembly' do
186
+ set :val, "one"
187
+
188
+ set :invalid_val2 do
189
+ val2
190
+ end
191
+
192
+ set :root_val2 do
193
+ root.val2
194
+ end
195
+ end
196
+
197
+ Alki.create_assembly name: 'main_assembly' do
198
+ set :val2, "two"
199
+ assembly :other, 'other_assembly'
200
+ end
201
+ instance = MainAssembly.new
202
+ puts instance.other.val
203
+ #output: one
204
+
205
+ # Can't access val2 in main assembly
206
+ begin
207
+ puts instance.other.invalid_val2
208
+ rescue => e
209
+ puts e
210
+ end
211
+ # output: undefined local variable or method 'val2'
212
+
213
+ # This works, because root returns the root assembly
214
+ puts instance.other.root_val2
215
+ #output: two
216
+ ```
217
+
218
+ In addition, `assembly` takes an optional third hash argument or a block which can be used to set
219
+ overrides in the same way `::new` does for assemblies. Elements from the parent assembly are
220
+ automatically in scope for overrides.
221
+
222
+ ```ruby
223
+ require 'alki'
224
+ Alki.create_assembly name: 'other_assembly' do
225
+ set :msg, nil
226
+ func :print do
227
+ puts msg
228
+ end
229
+ end
230
+
231
+ Alki.create_assembly name: 'main_assembly' do
232
+ set :val, "hello"
233
+ assembly :other, 'other_assembly' do
234
+ set :msg do
235
+ val
236
+ end
237
+ end
238
+ end
239
+ MainAssembly.new.other.print
240
+
241
+ #output: hello
242
+ ```
data/doc/index.ad ADDED
@@ -0,0 +1,15 @@
1
+ Alki
2
+ ====
3
+
4
+ Alki is a framework for creating projects that are modular, testable, and well organized.
5
+
6
+ It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on implementing business logic.
7
+
8
+ :leveloffset: 1
9
+ include::projects.ad[]
10
+
11
+ :leveloffset: 1
12
+ include::assemblies.ad[]
13
+
14
+ :leveloffset: 1
15
+ include::assembly_dsl.ad[]
data/doc/projects.ad ADDED
@@ -0,0 +1,43 @@
1
+ Setting up Alki Projects
2
+ ========================
3
+
4
+ Generally, projects that use alki work the same as normal Ruby projects but there are a couple of
5
+ guidelines to make life easier.
6
+
7
+ Alki projects should always use Bundler and have a `Gemfile` in the project root. It is also optional
8
+ that the project itself be a gem, with a `<project name>.gemspec` file in the project root and a `gemspec`
9
+ line in the `Gemfile`.
10
+
11
+
12
+ Using Alki without a gemspec
13
+ ----------------------------
14
+
15
+ If your project isn't a gem, and doesn't have a gemspec, executables should go in `bin` and should include these two
16
+ lines before `require`-ing any other files.
17
+ ```ruby
18
+ require 'bundler/setup'
19
+ require 'alki/bin`
20
+ ```
21
+
22
+ After that you can require your library files and run them.
23
+
24
+ Using Alki with a gemspec
25
+ -------------------------
26
+
27
+ If your project *is* a gem, you'll do things a bit differently.
28
+
29
+ First, in your gemspec, set the `bindir` option to `'exe'` (not `'bin'`). Also if automatically setting
30
+ the `executables` option, make sure it is looking for files in `exe`, not `bin`.
31
+
32
+ As noted, executables should be placed in `exe`, not `bin`.
33
+
34
+ Finally, once your gemspec is setup, run `bundle binstubs <gem name>` where `<gem name>` is whatever
35
+ you set `spec.name` to in your gemspec.
36
+
37
+ Once done, you should have binstubs in `bin` for each executable you have in `exe`. These are what you
38
+ should run for testing/development. If you add new executables to `exe`, just run `bundle install` to
39
+ generate new binstubs for them.
40
+
41
+ Second, executables in `exe` shouldn't require any extra files other then the project files they need to
42
+ run (no `bundle/setup` or `alki/bin`).
43
+
data/lib/alki/assembly.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'alki/assembly_executor'
2
+ require 'alki/override_builder'
2
3
  require 'alki/dsls/assembly'
3
4
 
4
5
  module Alki
5
6
  module Assembly
6
- def new(overrides={})
7
- Alki::Assembly::Instance.new create_assembly(overrides), self.assembly_options
7
+ def new(overrides={},&blk)
8
+ Alki::Assembly::Instance.new create_assembly(overrides,&blk), self.assembly_options
8
9
  end
9
10
 
10
11
  def root
@@ -13,36 +14,14 @@ module Alki
13
14
 
14
15
  private
15
16
 
16
- def create_assembly(overrides={})
17
+ def create_assembly(overrides={},&blk)
17
18
  config_dir = if assembly_options[:load_path]
18
- build_type :value, assembly_options[:load_path]
19
+ Alki::Support.load_class("alki/assembly_types/value").new assembly_options[:load_path]
19
20
  else
20
21
  nil
21
22
  end
22
23
 
23
- build_type :assembly, root, config_dir, create_override_group(overrides)
24
- end
25
-
26
-
27
- def create_override_group(overrides)
28
- unless overrides.empty?
29
- root = build_type(:group)
30
- overrides.each do |path,value|
31
- set_override root, *path.to_s.split('.'), value
32
- end
33
- root
34
- end
35
- end
36
-
37
- def set_override(root,*parent_keys,key,value)
38
- parent = parent_keys.inject(root) do |parent,key|
39
- parent.children[key.to_sym] ||= build_type(:group)
40
- end
41
- parent.children[key.to_sym] = build_type(:value, value)
42
- end
43
-
44
- def build_type(type,*args)
45
- Alki::Support.load_class("alki/assembly_types/#{type}").new *args
24
+ Alki::Support.load_class("alki/assembly_types/assembly").new root, config_dir, OverrideBuilder.build(overrides,&blk)
46
25
  end
47
26
 
48
27
  class Instance
@@ -19,7 +19,14 @@ module Alki
19
19
 
20
20
  def build(opts={},&blk)
21
21
  build_assembly blk if blk
22
- set_config_directory opts[:config_dir] if opts[:config_dir]
22
+ if opts[:config_dir]
23
+ context = if opts[:project_assembly]
24
+ File.dirname opts[:project_assembly]
25
+ else
26
+ Dir.pwd
27
+ end
28
+ set_config_directory File.expand_path opts[:config_dir], context
29
+ end
23
30
  set_assembly_name opts[:name] if opts[:name]
24
31
  setup_project_assembly opts[:project_assembly] if opts[:project_assembly]
25
32
  load_assembly_file opts[:primary_config] unless definition
@@ -65,42 +65,35 @@ module Alki
65
65
  proc = -> (name,*args,&blk) {
66
66
  call res.pkg, res.cache, res.elem[:scope][name], *args, &blk
67
67
  }
68
- group = GroupContext.new(proc,res.elem[:scope].keys)
68
+ group = create_context(GroupContext,res)
69
69
  -> { group }
70
70
  end
71
71
 
72
72
  def with_scope_context(res,blk = nil)
73
- proc = -> (name,*args,&blk) {
74
- call res.pkg, res.cache, res.elem[:scope][name], *args, &blk
73
+ methods = {
74
+ __call__: { body: (blk || res.elem[:block])}
75
75
  }
76
+ yield create_context(ValueContext,res,methods)
77
+ end
76
78
 
77
- context_class = Alki::ClassBuilder.build(
78
- super_class: ValueContext,
79
- instance_methods: {
80
- __call__: { body: (blk || res.elem[:block])}
79
+ def create_context(super_class,res,methods={})
80
+ executor = self
81
+
82
+ res.elem[:scope].keys.each do |meth|
83
+ methods[meth] = {
84
+ body: ->(*args,&blk) {
85
+ executor.call res.pkg, res.cache, res.elem[:scope][meth], *args, &blk
86
+ }
81
87
  }
88
+ end
89
+ context_class = Alki::ClassBuilder.build(
90
+ super_class: super_class,
91
+ instance_methods: methods
82
92
  )
83
-
84
- yield context_class.new(proc, res.elem[:scope].keys)
93
+ context_class.new
85
94
  end
86
95
 
87
96
  class Context
88
- def initialize(executor,scope)
89
- @executor = executor
90
- @scope = scope
91
- end
92
-
93
- def respond_to_missing?(name,include_all)
94
- @scope.include? name
95
- end
96
-
97
- def method_missing(name,*args,&blk)
98
- if @scope.include? name
99
- @executor.call name, *args, &blk
100
- else
101
- super
102
- end
103
- end
104
97
  end
105
98
 
106
99
  class ValueContext < Context
@@ -118,7 +111,7 @@ module Alki
118
111
  unless path.is_a?(String) or path.is_a?(Symbol)
119
112
  raise ArgumentError.new("lazy can only take Strings or Symbols")
120
113
  end
121
- Alki::ServiceDelegator.new pkg, path
114
+ Alki::ServiceDelegator.new assembly, path
122
115
  end
123
116
  end
124
117
 
data/lib/alki/bin.rb ADDED
@@ -0,0 +1,5 @@
1
+ lib_dir = File.join(Bundler.root,'lib')
2
+
3
+ unless $LOAD_PATH.include? lib_dir
4
+ $LOAD_PATH.unshift lib_dir
5
+ end
@@ -12,7 +12,7 @@ Alki do
12
12
  end
13
13
 
14
14
  finish do
15
- root = build_group(ctx[:elems], ctx[:overlays])
15
+ ctx[:root] = root = build_group(ctx.delete(:elems), ctx.delete(:overlays))
16
16
  add_class_method :root do
17
17
  root
18
18
  end
@@ -1,16 +1,15 @@
1
+ require 'alki/override_builder'
1
2
  require 'alki/support'
2
3
 
3
4
  Alki do
4
5
  require_dsl 'alki/dsls/assembly_types/group'
5
6
  require_dsl 'alki/dsls/assembly_types/value'
6
7
 
7
- dsl_method :assembly do |name,pkg=name.to_s,&blk|
8
+ dsl_method :assembly do |name,pkg=name.to_s,**overrides,&blk|
8
9
  klass = Alki::Support.load_class pkg
9
10
  config_dir = klass.assembly_options[:load_path]
10
11
  config_dir = build_value config_dir if config_dir
11
- overrides = if blk
12
- build_group_dsl(blk)
13
- end
12
+ overrides = Alki::OverrideBuilder.build overrides, &blk
14
13
 
15
14
  add_assembly name, klass.root, config_dir, overrides
16
15
  end
@@ -24,11 +23,13 @@ Alki do
24
23
  if key == :config_dir
25
24
  data.merge! main_data
26
25
  config_dir
26
+ elsif key == :original
27
+ root
27
28
  else
28
29
  if overrides
29
30
  data.replace(
30
31
  main: data.merge(main_data),
31
- override: data.dup,
32
+ override: override_data,
32
33
  )
33
34
  override.index data, key
34
35
  else
@@ -40,6 +41,7 @@ Alki do
40
41
  output do
41
42
  scope = root.output(data)[:scope]
42
43
  scope[:config_dir] = (data[:prefix]||[]) + [:config_dir]
44
+ scope[:original] = (data[:prefix]||[]) + [:original]
43
45
  scope.merge! overrides.output(data)[:scope] if overrides
44
46
  {
45
47
  type: :group,
@@ -47,9 +49,16 @@ Alki do
47
49
  }
48
50
  end
49
51
 
52
+ def override_data
53
+ od = data.dup
54
+ od[:scope] ||= {}
55
+ od[:scope].merge! original: ((data[:prefix]||[]) + [:original])
56
+ od
57
+ end
58
+
50
59
  def main_data
51
- pkg = data[:prefix] ? data[:prefix].dup : []
52
- {scope: {pkg: pkg, root: []}, overlays: []}
60
+ assembly_path = data[:prefix] ? data[:prefix].dup : []
61
+ {scope: {assembly: assembly_path, root: []}, overlays: []}
53
62
  end
54
63
 
55
64
  def override
@@ -2,12 +2,7 @@ require 'alki/dsls/assembly'
2
2
 
3
3
  Alki do
4
4
  dsl_method :group do |name,&blk|
5
- add name, build_group_dsl(blk)
6
- end
7
-
8
- helper :build_group_dsl do |blk|
9
- data = Alki::Dsls::Assembly.build &blk
10
- build_group data[:elems], data[:overlays]
5
+ add name, Alki::Dsls::Assembly.build(&blk)[:root]
11
6
  end
12
7
 
13
8
  element_type :group do
@@ -0,0 +1,35 @@
1
+ require 'alki/dsls/assembly'
2
+
3
+ module Alki
4
+ module OverrideBuilder
5
+ def self.build(override_hash=nil,&blk)
6
+ if blk
7
+ Alki::Dsls::Assembly.build(&blk)[:root]
8
+ elsif override_hash && !override_hash.empty?
9
+ create_override_group override_hash
10
+ end
11
+ end
12
+
13
+ def self.create_override_group(overrides)
14
+ unless overrides.empty?
15
+ root = build_type(:group)
16
+ overrides.each do |path,value|
17
+ set_override root, *path.to_s.split('.'), value
18
+ end
19
+ root
20
+ end
21
+ end
22
+
23
+ def self.set_override(root,*parent_keys,key,value)
24
+ parent = parent_keys.inject(root) do |parent,key|
25
+ parent.children[key.to_sym] ||= build_type(:group)
26
+ end
27
+ parent.children[key.to_sym] = build_type(:value, value)
28
+ end
29
+
30
+ def self.build_type(type,*args)
31
+ Alki::Support.load_class("alki/assembly_types/#{type}").new *args
32
+ end
33
+
34
+ end
35
+ end
data/lib/alki/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alki
2
- VERSION = "0.6.1"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -1,18 +1,50 @@
1
1
  require_relative '../test_helper'
2
-
3
- $LOAD_PATH.unshift Alki::Test.fixture_path('tlogger','lib')
4
- require 'tlogger'
2
+ require 'logger'
3
+ require 'alki'
5
4
  require 'stringio'
6
5
 
7
6
  describe 'Overrides' do
7
+ before do
8
+ @assembly = Alki.create_assembly do
9
+ set :log_io do
10
+ raise "Must set log_io"
11
+ end
12
+ group :util do
13
+ service :logger do
14
+ require 'logger'
15
+ Logger.new log_io
16
+ end
17
+ end
18
+ end
19
+ end
20
+
8
21
  it 'should be possibly to override assembly values on initialize' do
9
22
  assert_raises RuntimeError do
10
- Tlogger.new.log << "test"
23
+ @assembly.new.util.logger << "test"
11
24
  end
12
25
  io = StringIO.new
13
- logger = Tlogger.new(io: io)
14
- logger.log << "test"
15
- logger.log << "test"
26
+ logger = @assembly.new(log_io: io)
27
+ logger.util.logger << "test"
28
+ logger.util.logger << "test"
16
29
  io.string.must_equal "testtest"
17
30
  end
31
+
32
+ it 'should allow overriding via block' do
33
+ logger_class = Class.new(Logger) do
34
+ def info(msg)
35
+ self << "INFO #{msg}"
36
+ end
37
+ end
38
+ io = StringIO.new
39
+ instance = @assembly.new do
40
+ set :log_io, io
41
+ group :util do
42
+ service :logger do
43
+ logger_class.new original.log_io
44
+ end
45
+ end
46
+ end
47
+ instance.util.logger.info "test"
48
+ io.string.must_equal "INFO test"
49
+ end
18
50
  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.6.1
4
+ version: 0.7.0
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-03 00:00:00.000000000 Z
11
+ date: 2016-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -103,15 +103,20 @@ files:
103
103
  - ".gitignore"
104
104
  - Gemfile
105
105
  - LICENSE.txt
106
- - README.md
106
+ - README.ad
107
107
  - Rakefile
108
108
  - alki.gemspec
109
109
  - config/dsls.rb
110
+ - doc/assemblies.ad
111
+ - doc/assembly_dsl.ad
112
+ - doc/index.ad
113
+ - doc/projects.ad
110
114
  - lib/alki.rb
111
115
  - lib/alki/assembly.rb
112
116
  - lib/alki/assembly_builder.rb
113
117
  - lib/alki/assembly_executor.rb
114
118
  - lib/alki/assembly_handler_base.rb
119
+ - lib/alki/bin.rb
115
120
  - lib/alki/dsls/assembly.rb
116
121
  - lib/alki/dsls/assembly_type.rb
117
122
  - lib/alki/dsls/assembly_type_dsl.rb
@@ -122,6 +127,7 @@ files:
122
127
  - lib/alki/dsls/assembly_types/value.rb
123
128
  - lib/alki/dsls/service.rb
124
129
  - lib/alki/overlay_delegator.rb
130
+ - lib/alki/override_builder.rb
125
131
  - lib/alki/service_delegator.rb
126
132
  - lib/alki/test.rb
127
133
  - lib/alki/version.rb
data/README.md DELETED
@@ -1,55 +0,0 @@
1
- # What is Alki?
2
-
3
- Alki is a framework for creating projects that are modular, testable, and well organized.
4
-
5
- It's goal is to remove uncertainty and friction when building Ruby projects, allowing developers to focus on implementing business logic.
6
-
7
- # Synopsis
8
-
9
- Best place to start would be to check out some examples:
10
-
11
- * https://github.com/alki-project/alki-example
12
- * https://github.com/alki-project/alki/tree/master/test/fixtures/example
13
-
14
- # The Alki Assembly
15
-
16
- If a set of classes are the raw materials of your product, an Assembly is the finished product, ready to ship.
17
-
18
- To get there, you provide Alki with your assembly definition, which acts as the instructions for how to piece together your classes and objects.
19
-
20
- Assembly definitions are written in a simple DSL and are transformed into classes.
21
-
22
- ```ruby
23
- require 'alki'
24
-
25
- class Printer
26
- def print(msg)
27
- puts msg
28
- end
29
- end
30
-
31
- MyAssembly = Alki.create_assembly do
32
- service :printer do
33
- Printer.new
34
- end
35
- end
36
-
37
- MyAssembly.new.printer.print "hello world"
38
- ```
39
-
40
- ## Project Assemblies
41
-
42
- While Assemblies can be created directly as in the previous example, most
43
- of the time an entire project will contain instructions for a single Assembly.
44
-
45
- To ease this use case, Alki supports a simple standard project layout.
46
-
47
- * `config` and `lib` directories in your project root.
48
- * A file called `config/assembly.rb` with your assembly definition inside an `Alki do ... end` block.
49
- * A file under lib that has the name of your project. For example if your project was called `MyProject`, create a file called `lib/my_project.rb`.
50
- Inside the file put the following two lines:
51
-
52
- ```ruby
53
- require 'alki'
54
- Alki.project_assembly!
55
- ```