alki 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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
- ```