mindi 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: aca8e938147f48488f4e0f0c39a0cf2b2dc73c6d
4
+ data.tar.gz: 45deba41f62f63149767a778bdb634b54c0d2a4c
5
+ SHA512:
6
+ metadata.gz: 90b98a6bface5c02dc56a7c12642f612a6e3ce2ba8ed3210c62e9909012277b0f7cfb05c5469a2900c0c6f6ad886d2d3c915b9285ef695142ee586cedecf605c
7
+ data.tar.gz: b0500a9adb11e764ea026658222e2b9d502e8947601a6567a69bb1973a85269b42357112f1faf44efe513b65c516be8688e14500d56a69f24d89df5f6bf0ff19
data/COPYING ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2004-2014, Joel VanderWerf, vjoel@users.sourceforge.net
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ MinDI
2
+ =====
3
+
4
+ MinDI is Minimalist Dependency Injection for Ruby. It is inspired by Jamis Buck's Needle (http://needle.rubyforge.org) and Jim Weirich's article on DI in Ruby (http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc).
5
+
6
+ MinDI is minimalist in that it attempts to map concepts of DI into basic ruby
7
+ constructs, rather than into a layer of specialized constructs. In particular, classes and modules function as containers and registries, and methods and method definitions function as service points and services. There are some inherent advantages and disadvantages to this approach, discussed below.
8
+
9
+ MinDI builds on this minimal DI container by adding the InjectableContainer concept, which is a kind of DI available only in dynamic languages: through the magic of <tt>method_missing</tt>, a service may invoke other services without having explicit setter or constructor references to those services.
10
+
11
+ Synopsis
12
+ --------
13
+
14
+ Using the BasicContainer module for constructor injection:
15
+
16
+ require 'mindi'
17
+
18
+ class SimpleContainer
19
+ include MinDI::BasicContainer
20
+
21
+ greeting { "Hello, world\n" }
22
+
23
+ point_at { |x,y| [x,y] }
24
+
25
+ stuff { [greeting, point_at(100,200)] }
26
+ end
27
+
28
+ cont = SimpleContainer.new
29
+
30
+ p cont.stuff # ==> ["Hello, world\n", [100, 200]]
31
+
32
+ Using the InjectableContainer module for "dynamic" or "fallback" injection, using `method_missing`:
33
+
34
+ require 'mindi'
35
+
36
+ class Transformer
37
+ def transform string
38
+ string.gsub(pattern, &replacement)
39
+ end
40
+ end
41
+
42
+ class TransformerContainer
43
+ include MinDI::InjectableContainer
44
+
45
+ pattern { /foo/ }
46
+ replacement { proc {|match| match.upcase } }
47
+ transformer { Transformer.new }
48
+ transform { |str| transformer.transform(str) }
49
+ end
50
+
51
+ cont = TransformerContainer.new
52
+ s1 = cont.transform("fo foo fee")
53
+ s2 = cont.transform("fo foo fee")
54
+ p s1 # ==> "fo FOO fee"
55
+ p s1.equal?(s2) # ==> true
56
+
57
+
58
+ Note that the Transformer class is written without explicitly linking up
59
+ to services (either in initialize or in setters). It just assumes that
60
+ the services will be defined in the container.
61
+
62
+ Note also that the #transform service is a multiton service, and (like
63
+ singleton services) it caches its value for each argument.
64
+
65
+
66
+ Advantages
67
+ ----------
68
+
69
+ - Compact implementation (the essentials are about 100 lines).
70
+
71
+ - Compact syntax.
72
+
73
+ - Familiar constructs and idioms, like subclassing, module inclusion, nested
74
+ classes, protected and private, all apply.
75
+
76
+ - Use of classes and methods as containers and services means you can apply a
77
+ standard AOP or debugging lib.
78
+
79
+ - Services can take arguments, and this permits multiton services. Like singleton services, multiton services cache their results.
80
+
81
+ - Dynamic service registration is easy, since ruby's class system is itself
82
+ so dynamic. See examples/dynamic.rb.
83
+
84
+ Disadvantages
85
+ -------------
86
+
87
+ - A container's services live in the same namespace as the methods inherited
88
+ from Kernel and Object, so a service called "dup", for example, will
89
+ prevent calling Object#dup on the container (except in the implementation
90
+ of the dup service, which can use super to invoke Object#dup). The MinDI
91
+ framework itself adds a few methods that could conflict with services
92
+ (#singleton, #generic, etc.). Also, the "shortcut" service definition
93
+ (using method_missing) will not let you define services like "dup"--you
94
+ would have to use an explicit definer, like #singleton.
95
+
96
+ - No built-in AOP, logging, debugging, or reflection interface, as in Needle.
97
+
98
+ Notes
99
+ -----
100
+
101
+ - Supports threaded, deferred, singleton, and multiton service models (though
102
+ these are not yet independent choices). Additional service models can be
103
+ easily added in modules which include Container. The "generic" model can
104
+ be used like "prototype" in Needle, or for manual service management.
105
+
106
+ - Use mixins to build apps out of groups of services that need to coexist in
107
+ one name space.
108
+
109
+ - Use a nested class for a group of services when you want them to live in
110
+ their own namespace. (See the ColorNamespace example.)
111
+
112
+ - The Injectable module can be used without MinDI containers as a kind of
113
+ delegation:
114
+
115
+ require 'mindi'
116
+ x = [1,2,3]
117
+ y = {}
118
+ x.extend MinDI::Injectable
119
+ x.inject_into y
120
+ p y.reverse # ==> [3, 2, 1]
121
+
122
+ - MinDI can be used as a Rake-like task scheduler:
123
+
124
+ require 'mindi'
125
+
126
+ class Tasks
127
+ include MinDI::BasicContainer
128
+
129
+ a { print "a" }
130
+ b { a; print "b" }
131
+ c { a; print "c" }
132
+ d { b; c; print "d" }
133
+ end
134
+
135
+ Tasks.new.d # ==> abcd
136
+
137
+ Bugs
138
+ ----
139
+
140
+ - Private and protected services must be declared explicitly:
141
+
142
+ private :some_service
143
+
144
+ rather than by putting them in the private section of the class def.
145
+
146
+ - Because of how ruby defines Proc#arity, a service defined like
147
+
148
+ sname { do_something }
149
+
150
+ with no argument list will be treated as a multikey_multiton rather than
151
+ as a singleton. The behavior will be the same, though.
152
+
153
+ - Running ruby with the -w option will warn about 'instance variable @foo not
154
+ initialized'.
155
+
156
+ Todo
157
+ ----
158
+
159
+ - MinDI had some introspection methods ("services_by_model(m)"), but I took
160
+ them out to keep the lib minimal. Maybe they will be in a mixin later.
161
+
162
+ - Use args passed to service point declarations to specify aspects of the
163
+ service model (e.g., threaded and deferred could be specified this way).
164
+
165
+ - DRb services and distributed containers. Use Rinda for service discovery.
166
+
167
+ - Thread safety issues.
168
+
169
+ Legal and Contact Information
170
+ -----------------------------
171
+
172
+ Copyright (C) 2004-2014 Joel VanderWerf, mailto:vjoel@users.sourceforge.net.
173
+
174
+ License is BSD. See [COPYING](COPYING).
data/RELEASE-NOTES ADDED
@@ -0,0 +1,26 @@
1
+ mindi 0.5
2
+
3
+ - Repackaged and released as gem.
4
+
5
+ mindi 0.4
6
+
7
+ - The internal names for instance variables used to store values associated
8
+ with services are now more obscure. Thanks to Greg Fodor for the suggestion.
9
+
10
+ mindi 0.3
11
+
12
+ - Allow +nil+ and +false+ as service values. This allows limited rake-like functionality--see examples/rake-alike.rb.
13
+
14
+ - Can now include MinDI::BasicContainer instead of extend MinDI::Container, for convenience.
15
+
16
+ - Lots of examples.
17
+
18
+ mindi 0.2
19
+
20
+ - Injectable containers. See examples/inject.rb. Note that including the MinDI::Injectable module not only provides this new functionality but also extends with MinDI::Container
21
+
22
+ - Because of the above change, including MinDI::Injectable is now the recommended way of using MinDI.
23
+
24
+ mindi 0.1
25
+
26
+ - first release
data/Rakefile ADDED
@@ -0,0 +1,64 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ PRJ = "mindi"
5
+
6
+ def version
7
+ @version ||= begin
8
+ require 'mindi'
9
+ warn "MinDI::VERSION not a string" unless MinDI::VERSION.kind_of? String
10
+ MinDI::VERSION
11
+ end
12
+ end
13
+
14
+ def tag
15
+ @tag ||= "#{PRJ}-#{version}"
16
+ end
17
+
18
+ desc "Run tests"
19
+ Rake::TestTask.new :test do |t|
20
+ t.libs << "lib"
21
+ t.libs << "ext"
22
+ t.test_files = FileList["test/**/*.rb"]
23
+ end
24
+
25
+ desc "Commit, tag, and push repo; build and push gem"
26
+ task :release => "release:is_new_version" do
27
+ require 'tempfile'
28
+
29
+ sh "gem build #{PRJ}.gemspec"
30
+
31
+ file = Tempfile.new "template"
32
+ begin
33
+ file.puts "release #{version}"
34
+ file.close
35
+ sh "git commit --allow-empty -a -v -t #{file.path}"
36
+ ensure
37
+ file.close unless file.closed?
38
+ file.unlink
39
+ end
40
+
41
+ sh "git tag #{tag}"
42
+ sh "git push"
43
+ sh "git push --tags"
44
+
45
+ sh "gem push #{tag}.gem"
46
+ end
47
+
48
+ namespace :release do
49
+ desc "Diff to latest release"
50
+ task :diff do
51
+ latest = `git describe --abbrev=0 --tags --match '#{PRJ}-*'`.chomp
52
+ sh "git diff #{latest}"
53
+ end
54
+
55
+ desc "Log to latest release"
56
+ task :log do
57
+ latest = `git describe --abbrev=0 --tags --match '#{PRJ}-*'`.chomp
58
+ sh "git log #{latest}.."
59
+ end
60
+
61
+ task :is_new_version do
62
+ abort "#{tag} exists; update version!" unless `git tag -l #{tag}`.empty?
63
+ end
64
+ end
@@ -0,0 +1,36 @@
1
+ require 'mindi'
2
+
3
+ # Jim Weirich's example, in MinDI. From Jim Weirich's article at
4
+ # http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc).
5
+
6
+ # Note that this code does not run because it depends on undefined classes.
7
+
8
+ # For the "injected" version, we assume that classes like WebApp, StockQuotes,
9
+ # and so on are written to refer directly to "error_handler", "logger", etc.
10
+ # The effect of injection is that these references will resolve to the
11
+ # error_handler, logger, etc. that belong to the same container.
12
+ #
13
+ # In the case of DBI and Logger, which are pre-existing classes, we use the
14
+ # more traditional approach of using argument lists to pass in references to
15
+ # the services that they need from the container.
16
+
17
+ class JWApplicationContainer
18
+ include MinDI::InjectableContainer
19
+
20
+ logfilename { "logfile.log" }
21
+ db_user { "jim" }
22
+ db_password { "secret" }
23
+ dbi_string { "DBI:Pg:example_data" }
24
+
25
+ app { WebApp.new }
26
+ quotes { StockQuotes.new }
27
+ authenticator { Authenticator.new }
28
+ database { DBI.connect(dbi_string, db_user, db_password) }
29
+
30
+ logger { Logger.new }
31
+ error_handler { ErrorHandler.new }
32
+ end
33
+
34
+ def create_application
35
+ JWApplicationContainer.new.app
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'mindi'
2
+
3
+ # Jim Weirich's example, in MinDI. From Jim Weirich's article at
4
+ # http://onestepback.org/index.cgi/Tech/Ruby/DependencyInjectionInRuby.rdoc).
5
+
6
+ # Note that this code does not run because it depends on undefined classes.
7
+
8
+ class JWApplicationContainer
9
+ include MinDI::BasicContainer
10
+
11
+ logfilename { "logfile.log" }
12
+ db_user { "jim" }
13
+ db_password { "secret" }
14
+ dbi_string { "DBI:Pg:example_data" }
15
+
16
+ app {
17
+ app = WebApp.new(quotes, authenticator, database)
18
+ app.logger = logger
19
+ app.set_error_handler error_handler
20
+ app
21
+ }
22
+
23
+ quotes { StockQuotes.new(error_handler, logger) }
24
+ authenticator { Authenticator.new(database, logger, error_handler) }
25
+ database { DBI.connect(dbi_string, db_user, db_password) }
26
+
27
+ logger { Logger.new(logfilename) }
28
+ error_handler {
29
+ errh = ErrorHandler.new
30
+ errh.logger = logger
31
+ errh
32
+ }
33
+ end
34
+
35
+ def create_application
36
+ JWApplicationContainer.new.app
37
+ end
@@ -0,0 +1,77 @@
1
+ # Coffee machine example taken from Jim Weirich's OSCON 2005 slides
2
+ # and rewritten in MinDI (following Christian Neukirchen).
3
+
4
+ require 'mindi'
5
+
6
+ class PotSensor
7
+ def initialize(port)
8
+ @port = port
9
+ end
10
+
11
+ def coffee_present?
12
+ #...
13
+ end
14
+ end
15
+
16
+ class MockSensor < PotSensor; end
17
+
18
+ class Heater
19
+ def initialize(port)
20
+ @port = port
21
+ end
22
+
23
+ def on
24
+ #...
25
+ end
26
+ def off
27
+ #...
28
+ end
29
+ end
30
+
31
+ class MockHeater < Heater; end
32
+
33
+ class Warmer
34
+ # Use attr_reader instead of "inject"--advantages: Warmer is
35
+ # not dependent on DI framework, and the configuration of
36
+ # the warmer is explicit in the container definition, below,
37
+ # rather than using implicit, based on method names.
38
+ attr_reader :pot_sensor, :heater
39
+
40
+ def initialize(h)
41
+ @pot_sensor = h[:pot_sensor]
42
+ @heater = h[:heater]
43
+ end
44
+
45
+ def trigger
46
+ if pot_sensor.coffee_present?
47
+ heater.on
48
+ else
49
+ heater.off
50
+ end
51
+ end
52
+ end
53
+
54
+
55
+ class MarkIVConfiguration
56
+ include MinDI::InjectableContainer
57
+
58
+ uninjected # avoid warnings that "class Fixnum cannot be injected into"
59
+ pot_sensor_io_port {0x08F0}
60
+ heater_io_port {0x08F1}
61
+ injected
62
+
63
+ pot_sensor {PotSensor.new pot_sensor_io_port}
64
+ heater {Heater.new heater_io_port}
65
+ warmer {Warmer.new :heater => heater, :pot_sensor => pot_sensor}
66
+ # IMO, it's better to keep this information in the container def.,
67
+ # rather than "hide" it in the inject declarations in Warmer. Isn't
68
+ # that more the spirit of DI?
69
+ end
70
+
71
+ class MarkIVTestConfig < MarkIVConfiguration
72
+ heater {MockHeater.new}
73
+ pot_sensor {MockSensor.new}
74
+ end
75
+
76
+ mkiv = MarkIVConfiguration.new
77
+ p mkiv.warmer