micon 0.1.19 → 0.1.20
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/micon.rb +3 -1
- data/readme.md +116 -140
- metadata +3 -3
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rake_ext'
|
|
3
3
|
project(
|
4
4
|
name: "micon",
|
5
5
|
gem: true,
|
6
|
-
summary: "
|
6
|
+
summary: "Silent and invisible Killer of dependencies and configs",
|
7
7
|
|
8
8
|
author: "Alexey Petrushin",
|
9
9
|
homepage: "http://github.com/alexeypetrushin/micon"
|
data/lib/micon.rb
CHANGED
data/readme.md
CHANGED
@@ -1,177 +1,154 @@
|
|
1
|
-
# Micon
|
1
|
+
# Micon - silent and invisible Killer of dependencies and configs
|
2
2
|
|
3
|
-
Micon
|
3
|
+
Micon allows You easilly and transparently eliminate dependencies and configs in Your Application. Usually, when You are building complex system there are following tasks should be solved:
|
4
4
|
|
5
|
-
|
5
|
+
- where the component's code is located
|
6
|
+
- in what order should it be loaded
|
7
|
+
- what configs does the component needs to be properly initialized
|
8
|
+
- where those configs are stored
|
9
|
+
- how to change configs in different environments
|
10
|
+
- where are dependencies for component and how they should be initialized
|
11
|
+
- how to replace some components with custom implementation
|
12
|
+
- how to assembly parts of application for specs/tests
|
13
|
+
- how to restore state after each spec/test (isolate it from each other)
|
14
|
+
- how to control life-cycle of dynamically created components
|
15
|
+
- connecting components to assemble an application
|
6
16
|
|
7
|
-
|
17
|
+
*By component I mean any parts of code logically grouped together.*
|
8
18
|
|
9
|
-
|
19
|
+
Micon **solves all these tasks automatically**, and has the following **price** - You has to:
|
10
20
|
|
11
|
-
|
21
|
+
- use the *register component_name, &initialization_block* method for component initialization
|
22
|
+
- use the *inject component_name* to whire components toghether
|
23
|
+
- place component definition to the lib/components folder
|
12
24
|
|
13
|
-
|
25
|
+
That's all the price, not a big one, compared to the value, eh?
|
26
|
+
That all You need to know to use 95% of it, there are also 2-3 more specific methods, but they are needed very rarelly.
|
14
27
|
|
15
|
-
|
16
|
-
|
17
|
-
|
28
|
+
Techincally Micon is sort of Dependency Injector, but because of it's simplicity and invisibility it looks like an alien compared to it's complex and bloated IoC / DI cousins.
|
29
|
+
|
30
|
+
## Basic example
|
18
31
|
|
32
|
+
``` ruby
|
19
33
|
require 'micon'
|
34
|
+
# standard ruby logger
|
35
|
+
require 'logger'
|
20
36
|
|
21
|
-
|
22
|
-
def micon; MICON end
|
37
|
+
micon.register(:logger){Logger.new}
|
23
38
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def info msg
|
30
|
-
puts "#{msg} (writen to #{log_file_path})" unless defined?(RSpec)
|
39
|
+
class Application
|
40
|
+
inject logger: :logger
|
41
|
+
|
42
|
+
def run
|
43
|
+
logger.info 'running ...'
|
31
44
|
end
|
32
45
|
end
|
33
46
|
|
34
|
-
#
|
35
|
-
|
36
|
-
micon.logger.log_file_path = '/tmp/web_framework.log'
|
37
|
-
|
38
|
-
# The :router requires complex initialization, so we use
|
39
|
-
# another form of component registration.
|
40
|
-
class Router
|
41
|
-
def initialize routes; @routes = routes end
|
42
|
-
def decode request;
|
43
|
-
class_name, method = @routes[request.url]
|
44
|
-
return eval(class_name), method # returning actual class
|
45
|
-
end
|
46
|
-
end
|
47
|
-
micon.register :router do
|
48
|
-
Router.new '/index' => ['PagesController', :index]
|
49
|
-
end
|
47
|
+
Application.new.run # => running ...
|
48
|
+
```
|
50
49
|
|
51
|
-
|
52
|
-
# for each request, we specifying that component is dynamic
|
53
|
-
# by declaring it's :scope.
|
54
|
-
# And, we don't know beforehead what it actully will be, for different
|
55
|
-
# request there can be different controllers,
|
56
|
-
# so, here we just declaring it without any initialization block, it
|
57
|
-
# will be created at runtime later.
|
58
|
-
micon.register :controller, scope: :request
|
59
|
-
|
60
|
-
# Let's define some of our controllers, the PagesController, note - we
|
61
|
-
# don't register it as component.
|
62
|
-
class PagesController
|
63
|
-
# We need access to :logger and :request, let's inject them
|
64
|
-
inject logger: :logger, request: :request
|
65
|
-
|
66
|
-
def index
|
67
|
-
# Here we can use injected component
|
68
|
-
logger.info "Application: processing #{request}"
|
69
|
-
end
|
70
|
-
end
|
50
|
+
Code in examples/basics.rb
|
71
51
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def to_s; @url end
|
79
|
-
end
|
80
|
-
# Registering without initialization block.
|
81
|
-
micon.register :request, scope: :request
|
82
|
-
|
83
|
-
# We need to integrate our application with web server, for example with the Rack.
|
84
|
-
# When the server receive web request, it calls the :call method of our RackAdapter
|
85
|
-
class RackAdapter
|
86
|
-
# Injecting components
|
87
|
-
inject request: :request, controller: :controller
|
88
|
-
|
89
|
-
def call env
|
90
|
-
# We need to tell Micon that the :request scope is started, so it will know
|
91
|
-
# that some dynamic components should be created during this scope and
|
92
|
-
# destroyed at the end of it.
|
93
|
-
micon.activate :request, {} do
|
94
|
-
# Here we manually creating the Request component
|
95
|
-
self.request = Request.new '/index'
|
52
|
+
## Advanced example: let's build the Ultima - an Ultimate Web Framework
|
53
|
+
|
54
|
+
This example is more complicated and requires about 3-7 minutes.
|
55
|
+
|
56
|
+
Let's pretend that we are building an Ultimate Framework, the RoR Killer. There will be lot's of modules and dependencies, let's see how Micon can eliminate them.
|
57
|
+
There will be two steps, at the first we'll build it as usual, and at the second refactor it using Micon.
|
96
58
|
|
97
|
-
|
98
|
-
# but we can also use another way to access components,
|
99
|
-
# every component also availiable as micon.<component_name>
|
100
|
-
controller_class, method = micon.router.decode request
|
59
|
+
There will be following components: router, request.
|
101
60
|
|
102
|
-
|
103
|
-
|
61
|
+
``` ruby
|
62
|
+
# Assembling Ultima Framework
|
63
|
+
module Ultima
|
64
|
+
class << self
|
65
|
+
attr_accessor :router, :config
|
66
|
+
|
67
|
+
def run url
|
68
|
+
request = Request.new url
|
69
|
+
|
70
|
+
controller_class, method = router.decode url
|
71
|
+
|
72
|
+
controller = controller_class.new
|
73
|
+
controller.request = request
|
104
74
|
controller.send method
|
105
75
|
end
|
106
76
|
end
|
107
77
|
end
|
108
78
|
|
109
|
-
|
79
|
+
require 'yaml'
|
80
|
+
Ultima.config = YAML.load_file "#{dir}/config/config.yml"
|
81
|
+
|
82
|
+
require 'router'
|
83
|
+
router = Router.new
|
84
|
+
router.url_root = Ultima.config['url_root']
|
85
|
+
Ultima.router = router
|
86
|
+
|
87
|
+
require 'request'
|
88
|
+
require 'controller'
|
89
|
+
|
90
|
+
|
91
|
+
# Assemblilng Application
|
92
|
+
require 'pages_controller'
|
93
|
+
|
94
|
+
Ultima.router.add_route '/index', PagesController, :index
|
95
|
+
|
96
|
+
|
97
|
+
# Let's run it
|
98
|
+
Ultima.run '/index'
|
110
99
|
# You should see something like this in the console:
|
111
|
-
#
|
112
|
-
RackAdapter.new.call({})
|
100
|
+
# PagesController: displaying the :index page.
|
113
101
|
```
|
114
102
|
|
115
|
-
|
116
|
-
|
103
|
+
Code in examples/ultima1/run.rb
|
104
|
+
|
105
|
+
Below are the same example but done with Micon. As You can see there's no any assembling or configuration code, because all the components are auto-discovered, auto-loaded and auto-configured.
|
106
|
+
|
107
|
+
``` ruby
|
108
|
+
# Assembling Ultima Framework
|
109
|
+
# All components: router, request, controller will be automatically loaded & configured.
|
110
|
+
class Ultima
|
111
|
+
inject router: :router
|
112
|
+
|
113
|
+
def run url
|
114
|
+
# we need to tell Micon about the :request scope, so the Request will be
|
115
|
+
# created & destroyed during this scope automatically.
|
116
|
+
micon.activate :request, {} do
|
117
|
+
request = Request.new url
|
118
|
+
|
119
|
+
controller_class, method = router.decode url
|
120
|
+
|
121
|
+
controller = controller_class.new
|
122
|
+
# no need to explicitly set request, it will be automatically injected
|
123
|
+
controller.send method
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
micon.register(:ultima){Ultima.new}
|
128
|
+
|
129
|
+
# No need for 'requre', all classes will be discowered & laoded automatically
|
117
130
|
|
118
|
-
|
131
|
+
# No need for config, Micon will automatically discower config/router.yml
|
119
132
|
|
120
|
-
|
133
|
+
# No need for manual router configuring, router.yml config will be applied automatically
|
121
134
|
|
122
|
-
Note also, that this time logger convigured automatically, with the logger.yml configuration file.
|
123
135
|
|
124
|
-
|
125
|
-
#
|
136
|
+
# Assemblilng Application
|
137
|
+
# Controller will be loaded automatically
|
138
|
+
micon.router.add_route '/index', PagesController, :index
|
126
139
|
|
127
|
-
# Let's suppose that we want to build the Rails clone,
|
128
|
-
# there will be lot's of components - logger, controllers, router, ...
|
129
140
|
|
130
|
-
#
|
131
|
-
|
132
|
-
require 'class_loader'
|
133
|
-
|
134
|
-
# Handy shortcut to access the IoC API (this is optional and You can omit it).
|
135
|
-
def micon; MICON end
|
136
|
-
|
137
|
-
# Auto-discovering:
|
138
|
-
#
|
139
|
-
# All components (:logger, :router, :request, :controller) are defined in
|
140
|
-
# the web_framework2/lib/components folder.
|
141
|
-
# All classes (PagesController, RackAdapter) are
|
142
|
-
# located in web_framework2/lib folder.
|
143
|
-
#
|
144
|
-
# Note that there will be no any "require 'xxx'" clause, all components and
|
145
|
-
# classes will be loaded and dependecies be resolved automatically.
|
146
|
-
|
147
|
-
# Adding libraries to load path (in order to load components automatically).
|
148
|
-
lib_dir = "#{File.dirname(__FILE__)}/web_framework2/lib"
|
149
|
-
$LOAD_PATH << lib_dir
|
150
|
-
|
151
|
-
# Adding libraries to autoload path (in order to load classes automatically).
|
152
|
-
autoload_path lib_dir
|
153
|
-
|
154
|
-
# Auto-configuring
|
155
|
-
#
|
156
|
-
# Remember our manual configuration of "logger.log_file_path" from
|
157
|
-
# the previous example?
|
158
|
-
# This time it will be configured automatically, take a look at
|
159
|
-
# the web_framework2/lib/components/logger.yml file.
|
160
|
-
#
|
161
|
-
# Note, that there are also logger.production.yml, Micon is smart
|
162
|
-
# and will merge configs in the following order:
|
163
|
-
# logger.yml <- logger.<env>.yml <- <runtime_path>/config/logger.yml
|
164
|
-
# (If you define :environment and :runtime_path variables).
|
165
|
-
|
166
|
-
# Let's pretend that there's a Web Server and run our application,
|
141
|
+
# Let's run it
|
142
|
+
micon.ultima.run '/index'
|
167
143
|
# You should see something like this in the console:
|
168
|
-
#
|
169
|
-
RackAdapter.new.call({})
|
144
|
+
# PagesController: displaying the :index page.
|
170
145
|
```
|
171
146
|
|
172
|
-
|
147
|
+
Code in examples/ultima2/run.rb
|
148
|
+
|
149
|
+
## More samples
|
173
150
|
|
174
|
-
If You are interested in more samples, please look at the [actual components][rad_core_components] used in the Rad Core Framework.
|
151
|
+
If You are interested in more samples, please take a look at the [actual components][rad_core_components] used in the Rad Core Web Framework.
|
175
152
|
|
176
153
|
## Note
|
177
154
|
|
@@ -190,5 +167,4 @@ Copyright (c) Alexey Petrushin http://petrush.in, released under the MIT license
|
|
190
167
|
|
191
168
|
[ioc]: http://en.wikipedia.org/wiki/Inversion_of_control
|
192
169
|
[rad_core]: https://github.com/alexeypetrushin/rad_core
|
193
|
-
[rad_core_components]: https://github.com/alexeypetrushin/rad_core/tree/master/lib/components
|
194
|
-
[article]: http://ruby-lang.info/blog/you-underestimate-the-power-of-ioc-3fh
|
170
|
+
[rad_core_components]: https://github.com/alexeypetrushin/rad_core/tree/master/lib/components
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: micon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.20
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-08-
|
12
|
+
date: 2011-08-16 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email:
|
@@ -70,5 +70,5 @@ rubyforge_project:
|
|
70
70
|
rubygems_version: 1.8.6
|
71
71
|
signing_key:
|
72
72
|
specification_version: 3
|
73
|
-
summary:
|
73
|
+
summary: Silent and invisible Killer of dependencies and configs
|
74
74
|
test_files: []
|