diftw 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 621429df0a88505d46bff336d29bb4cf4196726d
4
+ data.tar.gz: 52fbcdcc3013037ac707f08053fd8465a90f8bc6
5
+ SHA512:
6
+ metadata.gz: e0ca2d926827c97501bbad7a38fc0ba2c7f0626b25a5e82a52161febe84ce02f759e3a04e84f61812ade709fcb6677de98802ff378ee3b4776fcb262227508e4
7
+ data.tar.gz: 6cd4cf04d662607d0ecfce83396501dbe44c34096ad7329d70cc537d8e09cc0c4bbbb71d07c9c7c250bf3df6d57f1549d5cebadd7df76cb2ab0cd180d0eebe86
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2016 Jordan Hollinger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,86 @@
1
+ # DiFtw
2
+
3
+ Dependency Injection For The Win! A small gem for simple, yet flexible, dependency injection in Ruby.
4
+
5
+ Some say you don't need DI in Ruby. Perhaps. Others say you don't need a DI *library* in Ruby. Probably true, but only in the pedantic sense that you don't need a DI library for *any* language. But I'll take a nice, idiomatic DI library over "just manually pass in every dependency to all your constructors!" any day. I couldn't find one, so I wrote this.
6
+
7
+ ## Basic Use
8
+
9
+ # Create your injector
10
+ Injector = DiFtw::Injector.new do
11
+ # Register some dependencies
12
+ register :foo do
13
+ OpenStruct.new(message: "Foo")
14
+ end
15
+ end
16
+
17
+ # Or register them like this
18
+ Injector.register :bar do
19
+ OpenStruct.new(message: "Bar")
20
+ end
21
+
22
+ # Or with a lambda
23
+ Injector[:baz] = -> { OpenStruct.new(message: "Baz") }
24
+
25
+ # Inject some dependencies into your class
26
+ class Widget
27
+ include Injector.inject(:foo, :bar)
28
+
29
+ def initialize(random_arg)
30
+ # Unlike most DI, it doesn't hijack your initializer! Because Ruby!!1!
31
+ @random_arg = random_arg
32
+ end
33
+ end
34
+
35
+ # Now they're instance methods!
36
+ widget = Widget.new
37
+ puts widget.bar.message
38
+ => "Bar"
39
+
40
+ ## Singletons By Default
41
+
42
+ By default the injector injects singletons. Observe:
43
+
44
+ widget1, widget2 = Widget.new, Widget.new
45
+ widget1.bar.object_id == widget1.bar.object_id
46
+ => true
47
+ widget1.bar.object_id == widget2.bar.object_id
48
+ => true
49
+
50
+ If you *don't* want your injector to return singletons (i.e. get a new copy each time you inject), initialize your injector like this:
51
+
52
+ Injector = DiFtw::Injector.new(singleton: false)
53
+ Injector[:bar] = -> { OpenStruct.new(message: 'Bar') }
54
+ ...
55
+ widget1, widget2 = Widget.new, Widget.new
56
+ widget1.bar.object_id == widget1.bar.object_id
57
+ => true
58
+ widget1.bar.object_id == widget2.bar.object_id
59
+ => false
60
+
61
+ Accessing injected singletons **is thread safe**. However, registering them is not.
62
+
63
+ ## Lazy, Nested Dependencies
64
+
65
+ # Define your class and tell it to inject :ab into instances
66
+ class Spline
67
+ include Injector.inject(:ab)
68
+ end
69
+
70
+ # Init a Spline instance. Doesn't matter that :ab isn't registered; just don't call spline.ab yet.
71
+ spline = Spline.new
72
+
73
+ # Register :ab, which uses :a and :b. The order you register them in doesn't matter.
74
+ Injector[:ab] = -> { Injector[:a] + Injector[:b] }
75
+ Injector[:a] = -> { 'A' }
76
+ Injector[:b] = -> { 'B' }
77
+
78
+ # The :ab dependency isn't actually injected until .ab is called!
79
+ puts spline.ab
80
+ => 'AB'
81
+
82
+ ## DI in tests
83
+
84
+ # Presumably your injector is already initialized.
85
+ # Simply re-register the dependencies you need for your tests.
86
+ Injector[:foo] = -> { OpenStruct.new(message: 'Test Foo') }
@@ -0,0 +1,2 @@
1
+ require 'diftw/injector'
2
+ require 'diftw/version'
@@ -0,0 +1,145 @@
1
+ #
2
+ # Dependency Injection For The Win!
3
+ #
4
+ module DiFtw
5
+ #
6
+ # The Injector class. Create an instance to start registering and injecting dependencies.
7
+ #
8
+ # DI = DiFtw::Injector.new
9
+ #
10
+ # # You can call register on the Injector object
11
+ # DI.register :bar do
12
+ # Bar.new
13
+ # end
14
+ #
15
+ # # Or you can assign a Proc to the Injector object like a Hash
16
+ # DI[:baz] = -> { Baz.new }
17
+ #
18
+ # Alternatively, you can pass a block to the initializer and register your depencies right inside it:
19
+ #
20
+ # DI = DiFtw::Injector.new do
21
+ # register :foo do
22
+ # Foo.new
23
+ # end
24
+ #
25
+ # register(:bar) { Bar }
26
+ # end
27
+ #
28
+ class Injector
29
+ # @return [Boolean] If true, the Injector injects singleton objects
30
+ attr_reader :singleton
31
+ # @return [Hash] A Hash containing all registered depencies (as procs) keyed by Symbols
32
+ attr_reader :registry
33
+ # @return [Hash] A Hash containing a Mutex for each dependency (only if singleton == true)
34
+ attr_reader :mutexes
35
+
36
+ private :registry, :mutexes
37
+
38
+ #
39
+ # Instantiate a new Injector.
40
+ #
41
+ # DI = DiFtw::Injector.new
42
+ #
43
+ # @param singleton [Boolean] If true, each dependency will only be initialized once. When false, it will be initialized each time it's injected.
44
+ #
45
+ def initialize(singleton: true, &registrator)
46
+ @registry, @mutexes = {}, {}
47
+ @singleton = singleton
48
+ instance_eval &registrator if registrator
49
+ end
50
+
51
+ #
52
+ # Register a new dependency by passing a Proc or a block.
53
+ #
54
+ # DI.register :foo do
55
+ # Foo
56
+ # end
57
+ #
58
+ # DI.register :bar, -> { Bar }
59
+ #
60
+ # @param name [Symbol] name of the dependency
61
+ # @param y [Proc] the dependency wrapped in a Proc or block
62
+ # @return [Injector] returns the Injector object
63
+ #
64
+ def register(name, y = nil, &block)
65
+ registry[name] = y || block
66
+ mutexes[name] = Mutex.new if singleton
67
+ self
68
+ end
69
+
70
+ #
71
+ # Register a new dependency by passing a Proc or a block.
72
+ #
73
+ # DI[:foo] = -> { Foo }
74
+ #
75
+ # @param name [Symbol] name of the dependency
76
+ # @param y [Proc] the dependency wrapped in a Proc
77
+ #
78
+ def []=(name, y)
79
+ register name, y
80
+ end
81
+
82
+ #
83
+ # Fetches a dependency by name (calls the Proc/block). If this is a Singleton injector,
84
+ # the same object will be returned each time. Otherwise a new one will be returned each time.
85
+ #
86
+ # An application will probably never want to call this directly.
87
+ #
88
+ # @return whatever the Proc/block returns
89
+ #
90
+ def [](name)
91
+ if singleton
92
+ var = "@_singleton_#{name}"
93
+ instance_variable_get(var) || mutexes[name].synchronize {
94
+ instance_variable_get(var) || instance_variable_set(var, registry.fetch(name).call)
95
+ }
96
+ else
97
+ registry.fetch(name).call
98
+ end
99
+ end
100
+
101
+ #
102
+ # Creates and returns a new Module which contains instance methods for all the dependencies you specified.
103
+ # Simply include this module in your class, and all it's instances will have their dependencies injected.
104
+ #
105
+ # class Widget
106
+ # include DI.inject(:foo, :bar)
107
+ # end
108
+ #
109
+ # @param dependencies [Symbol] All dependency names you want to inject.
110
+ #
111
+ def inject(*dependencies)
112
+ injector_module.tap do |mod|
113
+ mod._diftw_injector = self
114
+ mod._diftw_dependencies = dependencies
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ #
121
+ # Builds a new module that, when included in a class, defines instance methods for each dependecy.
122
+ #
123
+ # @return a new module
124
+ #
125
+ def injector_module
126
+ Module.new do
127
+ class << self
128
+ attr_accessor :_diftw_injector, :_diftw_dependencies
129
+ end
130
+
131
+ def self.included(base)
132
+ di_mod = self
133
+ base.class_eval do
134
+ di_mod._diftw_dependencies.each do |dep|
135
+ define_method dep do
136
+ var = "@_diftw_#{dep}"
137
+ instance_variable_get(var) || instance_variable_set(var, di_mod._diftw_injector[dep])
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,4 @@
1
+ module DiFtw
2
+ # Library version
3
+ VERSION = '0.0.1'.freeze
4
+ end
metadata ADDED
@@ -0,0 +1,48 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diftw
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jordan Hollinger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A small dependency injection library for Ruby
14
+ email: jordan.hollinger@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - LICENSE
20
+ - README.md
21
+ - lib/diftw.rb
22
+ - lib/diftw/injector.rb
23
+ - lib/diftw/version.rb
24
+ homepage: https://github.com/jhollinger/ruby-diftw
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.0.0
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.4.5.1
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Dependency Injection For The Win!
48
+ test_files: []