micon 0.1.17 → 0.1.18
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.
- data/lib/micon.rb +8 -3
- data/lib/micon/config.rb +5 -5
- data/lib/micon/core.rb +76 -76
- data/lib/micon/helper.rb +6 -6
- data/lib/micon/metadata.rb +31 -31
- data/lib/micon/module.rb +12 -12
- data/lib/micon/spec.rb +4 -4
- data/lib/micon/support.rb +1 -1
- data/readme.md +72 -66
- data/spec/callbacks_spec.rb +18 -18
- data/spec/config_spec.rb +5 -5
- data/spec/constants_spec.rb +18 -18
- data/spec/custom_scope_spec.rb +14 -14
- data/spec/initialization_spec.rb +16 -16
- data/spec/managed_spec.rb +6 -6
- data/spec/miscellaneous_spec.rb +19 -19
- data/spec/nested_custom_scope_spec.rb +5 -5
- data/spec/spec_helper.rb +2 -2
- data/spec/static_scope_spec.rb +22 -22
- metadata +1 -12
- data/lib/micon/rad.rb +0 -8
- data/spec/example_spec.rb +0 -171
- data/spec/example_spec/lib/components/controller.rb +0 -8
- data/spec/example_spec/lib/components/logger.production.yml +0 -1
- data/spec/example_spec/lib/components/logger.rb +0 -8
- data/spec/example_spec/lib/components/logger.yml +0 -1
- data/spec/example_spec/lib/components/request.rb +0 -2
- data/spec/example_spec/lib/components/router.rb +0 -12
- data/spec/example_spec/lib/pages_controller.rb +0 -11
- data/spec/example_spec/lib/rack_adapter.rb +0 -25
- data/spec/example_spec/lib/request.rb +0 -8
data/lib/micon/module.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Module.class_eval do
|
2
|
-
#
|
2
|
+
#
|
3
3
|
# inject attribute: :session
|
4
|
-
#
|
4
|
+
#
|
5
5
|
def inject attributes
|
6
6
|
::MICON.raise_without_self "Invalid argument!" unless attributes.is_a? Hash
|
7
7
|
attributes.each do |name, specificator|
|
8
|
-
::MICON.raise_without_self "Attribute name should be a Symbol!" unless name.is_a? Symbol
|
9
|
-
|
8
|
+
::MICON.raise_without_self "Attribute name should be a Symbol!" unless name.is_a? Symbol
|
9
|
+
|
10
10
|
if [Class, Module].include? specificator.class
|
11
11
|
specificator = specificator.name
|
12
12
|
elsif specificator.is_a? Symbol
|
@@ -28,24 +28,24 @@ def #{name}?
|
|
28
28
|
::MICON.include? #{specificator}
|
29
29
|
end
|
30
30
|
RUBY
|
31
|
-
|
31
|
+
|
32
32
|
self.class_eval script, __FILE__, __LINE__
|
33
33
|
end
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
#
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
#
|
38
38
|
# Hook to use Constants as Components
|
39
|
-
#
|
39
|
+
#
|
40
40
|
# if defined? ::ClassLoader
|
41
41
|
# text = <<-TEXT
|
42
42
|
# It seems that ClassLoader already defined, but it supposed to be activated after the Micon, otherwise it can cause performance loss!
|
43
|
-
# Micon.const_missing extension should be included before than ClassLoader.const_missing otherwise the Micon.const_missing will be
|
43
|
+
# Micon.const_missing extension should be included before than ClassLoader.const_missing otherwise the Micon.const_missing will be
|
44
44
|
# called (and will ping file system) for every loaded class!
|
45
45
|
# TEXT
|
46
46
|
# warn text
|
47
47
|
# end
|
48
|
-
#
|
48
|
+
#
|
49
49
|
# alias_method :const_missing_without_micon, :const_missing
|
50
50
|
# protected :const_missing_without_micon
|
51
51
|
# def const_missing const
|
data/lib/micon/spec.rb
CHANGED
@@ -3,15 +3,15 @@ require 'rspec_ext'
|
|
3
3
|
rspec do
|
4
4
|
def self.with_micon options = {}
|
5
5
|
scope = options[:before] || :all
|
6
|
-
|
6
|
+
|
7
7
|
old, tmp = nil
|
8
|
-
|
9
|
-
before scope do
|
8
|
+
|
9
|
+
before scope do
|
10
10
|
old = MICON
|
11
11
|
tmp = old.clone
|
12
12
|
tmp.initialize!
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
after scope do
|
16
16
|
tmp.deinitialize!
|
17
17
|
old.initialize!
|
data/lib/micon/support.rb
CHANGED
data/readme.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Micon IoC assembles and manages Your Application
|
2
2
|
|
3
|
-
Micon is infrastructural component, invisible to user and it's main goal is to simplify development. It reduces complex monolithic application to set of simple low coupled components.
|
3
|
+
Micon is infrastructural component, invisible to user and it's main goal is to simplify development. It reduces complex monolithic application to set of simple low coupled components.
|
4
4
|
|
5
5
|
Concentrate on business logic and interfaces and Micon will provide automatic configuration, life cycle management and dependency resolving.
|
6
6
|
|
@@ -9,23 +9,20 @@ Technically it's [IoC][ioc] like framework with components, callbacks, scopes an
|
|
9
9
|
Is it usefull, is there any real-life application? - I'm using it as a heart of my [web framework][rad_core], this sites http://robotigra.ru, http://ruby-lang.info for example powered with it.
|
10
10
|
|
11
11
|
## Usage
|
12
|
-
|
12
|
+
|
13
13
|
Let's suppose you are building the Ruby on Rails clone, there are lots of modules let's try to deal with them
|
14
14
|
|
15
15
|
``` ruby
|
16
|
+
# Let's suppose that we want to build the Rails clone,
|
17
|
+
# there will be lot's of components - logger, controllers, router, ...
|
18
|
+
|
16
19
|
require 'micon'
|
17
20
|
|
18
|
-
#
|
21
|
+
# Handy shortcut to access the IoC API (this is optional and You can omit it).
|
22
|
+
def micon; MICON end
|
19
23
|
|
20
|
-
# Let's define
|
21
|
-
#
|
22
|
-
# so I leave this step to You.
|
23
|
-
class ::Object
|
24
|
-
def rad; MICON end
|
25
|
-
end
|
26
|
-
|
27
|
-
# let's define some components
|
28
|
-
# the :logger is one per application, it's a static component (like singleton)
|
24
|
+
# Let's define some components.
|
25
|
+
# The :logger is one per application, it's a static component (like singleton).
|
29
26
|
class Logger
|
30
27
|
register_as :logger
|
31
28
|
attr_accessor :log_file_path
|
@@ -36,29 +33,29 @@ end
|
|
36
33
|
|
37
34
|
# To demostrate basics of working with compnents let's configure our :logger
|
38
35
|
# explicitly (in the next example, it will be configured automatically).
|
39
|
-
|
36
|
+
micon.logger.log_file_path = '/tmp/web_framework.log'
|
40
37
|
|
41
|
-
# The :router requires complex initialization, so we use
|
38
|
+
# The :router requires complex initialization, so we use
|
42
39
|
# another form of component registration.
|
43
40
|
class Router
|
44
41
|
def initialize routes; @routes = routes end
|
45
|
-
def decode request;
|
46
|
-
class_name, method = @routes[request.url]
|
42
|
+
def decode request;
|
43
|
+
class_name, method = @routes[request.url]
|
47
44
|
return eval(class_name), method # returning actual class
|
48
45
|
end
|
49
46
|
end
|
50
|
-
|
47
|
+
micon.register :router do
|
51
48
|
Router.new '/index' => ['PagesController', :index]
|
52
49
|
end
|
53
50
|
|
54
|
-
# The :controller component should be created and destroyed dynamically,
|
55
|
-
# for each request, we specifying that component is dynamic
|
56
|
-
# by declaring it's :scope.
|
57
|
-
# And, we don't know beforehead what it actully will be, for different
|
58
|
-
# request there can be different controllers,
|
51
|
+
# The :controller component should be created and destroyed dynamically,
|
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,
|
59
56
|
# so, here we just declaring it without any initialization block, it
|
60
57
|
# will be created at runtime later.
|
61
|
-
|
58
|
+
micon.register :controller, scope: :request
|
62
59
|
|
63
60
|
# Let's define some of our controllers, the PagesController, note - we
|
64
61
|
# don't register it as component.
|
@@ -68,7 +65,7 @@ class PagesController
|
|
68
65
|
|
69
66
|
def index
|
70
67
|
# Here we can use injected component
|
71
|
-
logger.info "Application: processing #{request}"
|
68
|
+
logger.info "Application: processing #{request}"
|
72
69
|
end
|
73
70
|
end
|
74
71
|
|
@@ -76,38 +73,38 @@ end
|
|
76
73
|
# We also registering it without initialization, it will be
|
77
74
|
# created at runtime later.
|
78
75
|
class Request
|
79
|
-
attr_reader :url
|
80
|
-
def initialize url; @url = url end
|
76
|
+
attr_reader :url
|
77
|
+
def initialize url; @url = url end
|
81
78
|
def to_s; @url end
|
82
79
|
end
|
83
80
|
# Registering without initialization block.
|
84
|
-
|
81
|
+
micon.register :request, scope: :request
|
85
82
|
|
86
83
|
# We need to integrate our application with web server, for example with the Rack.
|
87
84
|
# When the server receive web request, it calls the :call method of our RackAdapter
|
88
85
|
class RackAdapter
|
89
86
|
# Injecting components
|
90
87
|
inject request: :request, controller: :controller
|
91
|
-
|
88
|
+
|
92
89
|
def call env
|
93
90
|
# We need to tell Micon that the :request scope is started, so it will know
|
94
|
-
# that some dynamic components should be created during this scope and
|
91
|
+
# that some dynamic components should be created during this scope and
|
95
92
|
# destroyed at the end of it.
|
96
|
-
|
93
|
+
micon.activate :request, {} do
|
97
94
|
# Here we manually creating the Request component
|
98
95
|
self.request = Request.new '/index'
|
99
|
-
|
96
|
+
|
100
97
|
# The :router also can be injected via :inject,
|
101
98
|
# but we can also use another way to access components,
|
102
|
-
# every component also availiable as
|
103
|
-
controller_class, method =
|
104
|
-
|
99
|
+
# every component also availiable as micon.<component_name>
|
100
|
+
controller_class, method = micon.router.decode request
|
101
|
+
|
105
102
|
# Let's create and call our controller
|
106
103
|
self.controller = controller_class.new
|
107
104
|
controller.send method
|
108
105
|
end
|
109
106
|
end
|
110
|
-
end
|
107
|
+
end
|
111
108
|
|
112
109
|
# Let's pretend that there's a Web Server and run our application,
|
113
110
|
# You should see something like this in the console:
|
@@ -120,69 +117,78 @@ In real-life scenario You probably will use it in a little different way, as wil
|
|
120
117
|
|
121
118
|
I would like to repeat it one more time - **auto-discovery and auto-configuration is extremelly important features** of the IoC, don't ignore them.
|
122
119
|
|
123
|
-
Below are the same example but done with utilizing these features, this is how the Micon IoC is supposed be used in the real-life scenario. As You can see it's almost empty, because all the components are auto-discovered, auto-loaded and auto-configured. Components are located in the [
|
120
|
+
Below are the same example but done with utilizing these features, this is how the Micon IoC is supposed be used in the real-life scenario. As You can see it's almost empty, because all the components are auto-discovered, auto-loaded and auto-configured. Components are located in the [examples/web_framework2/lib/components](https://github.com/alexeypetrushin/micon/blob/master/examples/web_framework2/lib/components) folder.
|
124
121
|
|
125
|
-
|
122
|
+
Note also, that this time logger convigured automatically, with the logger.yml configuration file.
|
126
123
|
|
127
124
|
``` ruby
|
125
|
+
# Please examine the 'web_framework1.rb' example before proceeding with this one.
|
126
|
+
|
127
|
+
# Let's suppose that we want to build the Rails clone,
|
128
|
+
# there will be lot's of components - logger, controllers, router, ...
|
129
|
+
|
130
|
+
# In this example we also need another tool that automatically find & load classes.
|
128
131
|
require 'micon'
|
129
132
|
require 'class_loader'
|
130
133
|
|
131
|
-
#
|
132
|
-
|
133
|
-
# Let's define shortcut to access the IoC API (optional
|
134
|
-
# but handy step). I don't know how You would like to call it,
|
135
|
-
# so I leave this step to You.
|
136
|
-
class ::Object
|
137
|
-
def rad; MICON end
|
138
|
-
end
|
134
|
+
# Handy shortcut to access the IoC API (this is optional and You can omit it).
|
135
|
+
def micon; MICON end
|
139
136
|
|
140
137
|
# Auto-discovering:
|
141
|
-
#
|
142
|
-
# All components (:logger, :router, :request, :controller) are
|
143
|
-
#
|
138
|
+
#
|
139
|
+
# All components (:logger, :router, :request, :controller) are defined in
|
140
|
+
# the web_framework2/lib/components folder.
|
144
141
|
# All classes (PagesController, RackAdapter) are
|
145
|
-
# located in
|
146
|
-
#
|
147
|
-
# Note that there
|
148
|
-
# classes
|
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
|
149
153
|
|
150
154
|
# Auto-configuring
|
151
|
-
#
|
152
|
-
# Remember our manual configuration of "logger.log_file_path" from
|
155
|
+
#
|
156
|
+
# Remember our manual configuration of "logger.log_file_path" from
|
153
157
|
# the previous example?
|
154
158
|
# This time it will be configured automatically, take a look at
|
155
|
-
# the
|
156
|
-
#
|
157
|
-
# Note, that there are also logger.production.yml,
|
158
|
-
# and
|
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:
|
159
163
|
# logger.yml <- logger.<env>.yml <- <runtime_path>/config/logger.yml
|
160
164
|
# (If you define :environment and :runtime_path variables).
|
161
|
-
|
165
|
+
|
162
166
|
# Let's pretend that there's a Web Server and run our application,
|
163
167
|
# You should see something like this in the console:
|
164
168
|
# Application: processing /index
|
165
169
|
RackAdapter.new.call({})
|
166
170
|
```
|
167
|
-
|
168
|
-
For the actual code please look at [
|
171
|
+
|
172
|
+
For the actual code please look at [examples](https://github.com/alexeypetrushin/micon/blob/master/examples)
|
173
|
+
|
174
|
+
If You are interested in more samples, please look at the [actual components][rad_core_components] used in the Rad Core Framework.
|
169
175
|
|
170
176
|
## Note
|
171
177
|
|
172
|
-
Current wersion isn't thread-safe,
|
173
|
-
|
174
|
-
|
175
|
-
|
178
|
+
Current wersion isn't thread-safe, and I did it intentially. Actually, the first version was implemented as thread-safe, but because there's no actual multithreading in Ruby, the only thing it does - adds complexity and performance losses, so I removed it.
|
179
|
+
But if you really need it for some reason - it can be easily done.
|
180
|
+
|
176
181
|
## Installation
|
177
182
|
|
178
183
|
``` bash
|
179
184
|
gem install micon
|
180
185
|
```
|
181
|
-
|
186
|
+
|
182
187
|
## License
|
183
188
|
|
184
189
|
Copyright (c) Alexey Petrushin http://petrush.in, released under the MIT license.
|
185
190
|
|
186
191
|
[ioc]: http://en.wikipedia.org/wiki/Inversion_of_control
|
187
192
|
[rad_core]: https://github.com/alexeypetrushin/rad_core
|
193
|
+
[rad_core_components]: https://github.com/alexeypetrushin/rad_core/tree/master/lib/components
|
188
194
|
[article]: http://ruby-lang.info/blog/you-underestimate-the-power-of-ioc-3fh
|
data/spec/callbacks_spec.rb
CHANGED
@@ -4,21 +4,21 @@ describe "Callbacks" do
|
|
4
4
|
before do
|
5
5
|
self.micon = Micon::Core.new
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
describe "components callbacs" do
|
9
9
|
it "basic" do
|
10
10
|
micon.register(:the_object){"The Object"}
|
11
|
-
|
11
|
+
|
12
12
|
check = mock
|
13
13
|
check.should_receive(:before)
|
14
14
|
micon.before :the_object do
|
15
15
|
check.before
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
check.should_receive(:after1).ordered
|
19
19
|
check.should_receive(:after2).ordered
|
20
20
|
obj = nil
|
21
|
-
micon.after :the_object do |o|
|
21
|
+
micon.after :the_object do |o|
|
22
22
|
check.after1
|
23
23
|
obj = o
|
24
24
|
end
|
@@ -26,10 +26,10 @@ describe "Callbacks" do
|
|
26
26
|
check.after2
|
27
27
|
obj.object_id.should == o.object_id
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
micon[:the_object].should == obj
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
it "should be able reference to the component itself inside of after filter (cycle reference)" do
|
34
34
|
micon.register(:the_object){"The Object"}
|
35
35
|
check = nil
|
@@ -40,7 +40,7 @@ describe "Callbacks" do
|
|
40
40
|
check.should == "The Object"
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
describe "custom scope callbacks" do
|
45
45
|
it "scope :before and :after callbacks" do
|
46
46
|
check = mock
|
@@ -54,9 +54,9 @@ describe "Callbacks" do
|
|
54
54
|
micon.after_scope(:custom){|container| check.after2 container}
|
55
55
|
|
56
56
|
micon.activate(:custom, {}){check.run}
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
60
|
describe "miscellaneous" do
|
61
61
|
it "should fire callbacks after assigning component" do
|
62
62
|
micon.register(:the_object)
|
@@ -67,32 +67,32 @@ describe "Callbacks" do
|
|
67
67
|
end
|
68
68
|
micon.the_object = 'the_object'
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
it "should raise error if callback defined after component already created" do
|
72
72
|
micon.register(:the_object){"the_object"}
|
73
73
|
micon[:the_object]
|
74
|
-
|
74
|
+
|
75
75
|
-> {micon.before(:the_object){}}.should raise_error(/already created/)
|
76
76
|
-> {micon.after(:the_object){}}.should raise_error(/already created/)
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
it "should raise error if callback defined after scope already started" do
|
80
80
|
micon.activate :custom, {} do
|
81
81
|
-> {micon.before_scope(:custom){}}.should raise_error(/already started/)
|
82
82
|
-> {micon.after_scope(:custom){}}.should raise_error(/already started/)
|
83
83
|
end
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
it ":after with bang: false should execute callback if component already started and also register it as :after callback" do
|
87
87
|
micon.register(:the_object){"the_object"}
|
88
88
|
micon[:the_object]
|
89
|
-
|
89
|
+
|
90
90
|
check = mock
|
91
|
-
check.should_receive(:first).twice
|
91
|
+
check.should_receive(:first).twice
|
92
92
|
micon.after(:the_object, bang: false){check.first}
|
93
|
-
|
93
|
+
|
94
94
|
micon.delete :the_object
|
95
95
|
micon[:the_object]
|
96
96
|
end
|
97
|
-
end
|
97
|
+
end
|
98
98
|
end
|
data/spec/config_spec.rb
CHANGED
@@ -2,17 +2,17 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Configuration" do
|
4
4
|
before{self.micon = Micon::Core.new}
|
5
|
-
|
5
|
+
|
6
6
|
it "should configure component if config provided" do
|
7
|
-
micon.register(:logger){::OpenStruct.new}
|
8
|
-
with_load_path "#{spec_dir}/basic/lib" do
|
7
|
+
micon.register(:logger){::OpenStruct.new}
|
8
|
+
with_load_path "#{spec_dir}/basic/lib" do
|
9
9
|
micon[:logger].level.should == :info
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it "should merge in order: conf <- conf.mode <- runtime <- runtime.mode" do
|
14
14
|
micon.register(:object){::OpenStruct.new}
|
15
|
-
with_load_path "#{spec_dir}/order/lib" do
|
15
|
+
with_load_path "#{spec_dir}/order/lib" do
|
16
16
|
micon.runtime_path = "#{spec_dir}/order/app"
|
17
17
|
micon.mode = :production
|
18
18
|
micon[:object].tap do |o|
|