wireless 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +4 -4
- data/lib/wireless.rb +20 -1
- data/lib/wireless/fetch.rb +6 -2
- data/lib/wireless/fetcher.rb +0 -5
- data/lib/wireless/registry.rb +18 -22
- data/lib/wireless/resolver.rb +5 -5
- data/lib/wireless/synchronized_store.rb +56 -10
- data/lib/wireless/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15c44fc9bf9e10af9c1a9d8d0241e2a808a7dfabdadd895581271f6fa076fdbc
|
|
4
|
+
data.tar.gz: 3159342e5f335e4a9b92fd6c09556c4fbb39af17bc5fada60737e0ad3150ac0f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ded107266b0b9cf0d6a279a5609e102603b9e14e9a09a8b72684d5239b3bae6b61e623bf20297dba65b92d93e93f5c3a66ef5b7197b5d1030778c213eafec93
|
|
7
|
+
data.tar.gz: 22276968eb991723ced37a404b4e8aa0a805ccf3ca5b69746431712323bd3290235fd91b7535e9c59180ea82c40badfc59df8bec29ac2e93d736f2809fbf2d0d
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -109,7 +109,7 @@ which has the following features:
|
|
|
109
109
|
|
|
110
110
|
## Why Wireless?
|
|
111
111
|
|
|
112
|
-
I wanted a simple service locator like [
|
|
112
|
+
I wanted a simple service locator like [DiFtw](https://github.com/jhollinger/ruby-diftw),
|
|
113
113
|
with cycle detection and control over the [visibility of getters](https://github.com/jhollinger/ruby-diftw/issues/1).
|
|
114
114
|
|
|
115
115
|
## Why Service Locators?
|
|
@@ -127,17 +127,17 @@ Examples include:
|
|
|
127
127
|
Rather than wiring these dependencies together manually, service locators allow them to be
|
|
128
128
|
registered and retrieved in a declarative way. This is similar to the difference between
|
|
129
129
|
imperative build tools like Ant or Gulp and declarative build tools like Make or Rake, which
|
|
130
|
-
allow prerequisites to be acquired on-demand, without micromanaging their
|
|
130
|
+
allow prerequisites to be acquired on-demand, without micromanaging their construction or connections.
|
|
131
131
|
|
|
132
132
|
# VERSION
|
|
133
133
|
|
|
134
|
-
0.0.
|
|
134
|
+
0.0.2
|
|
135
135
|
|
|
136
136
|
# SEE ALSO
|
|
137
137
|
|
|
138
138
|
## Gems
|
|
139
139
|
|
|
140
|
-
- [Canister](https://github.com/mlibrary/canister) -
|
|
140
|
+
- [Canister](https://github.com/mlibrary/canister) - a simple service-locator inspired by Jim Weirich's [article](https://archive.li/shxeA) on Dependency Injection
|
|
141
141
|
- [DiFtw](https://github.com/jhollinger/ruby-diftw) - the original inspiration for this module: a similar API with a focus on testing/mocking
|
|
142
142
|
|
|
143
143
|
## Articles
|
data/lib/wireless.rb
CHANGED
|
@@ -6,7 +6,26 @@
|
|
|
6
6
|
module Wireless
|
|
7
7
|
class Error < StandardError; end
|
|
8
8
|
class CycleError < Error; end
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
# Raised when an attempt is made to:
|
|
11
|
+
#
|
|
12
|
+
# - retrieve a value from a key-indexed store when the key doesn't exist
|
|
13
|
+
# - write a value when the key exists and the store doesn't allow replacements
|
|
14
|
+
#
|
|
15
|
+
# Can be passed a message, the receiver the lookup failed on and the key. All
|
|
16
|
+
# are optional and default to nil.
|
|
17
|
+
#
|
|
18
|
+
# XXX eventually (i.e. in ruby 2.6), this can be a subclass of (or replaced by)
|
|
19
|
+
# the core KeyError class: https://bugs.ruby-lang.org/issues/14313
|
|
20
|
+
class KeyError < Wireless::Error
|
|
21
|
+
def initialize(message = nil, receiver: nil, key: nil)
|
|
22
|
+
super(message)
|
|
23
|
+
@receiver = receiver
|
|
24
|
+
@key = key
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
attr_reader :key, :receiver
|
|
28
|
+
end
|
|
10
29
|
|
|
11
30
|
# a shortcut which allows:
|
|
12
31
|
#
|
data/lib/wireless/fetch.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Wireless
|
|
|
10
10
|
# @seen is an immutable Set of symbols, which is used to detect dependency cycles.
|
|
11
11
|
module Fetch
|
|
12
12
|
# Fetches the dependency with the specified name. Creates the dependency if
|
|
13
|
-
# it doesn't exist. Raises a Wireless::
|
|
13
|
+
# it doesn't exist. Raises a Wireless::KeyError if the dependency is not
|
|
14
14
|
# defined or a Wireless::CycleError if resolving the dependency results in
|
|
15
15
|
# a cycle.
|
|
16
16
|
def fetch(name)
|
|
@@ -22,7 +22,11 @@ module Wireless
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
unless (resolver = @registry[name])
|
|
25
|
-
raise Wireless::
|
|
25
|
+
raise Wireless::KeyError.new(
|
|
26
|
+
"dependency not found: #{name}",
|
|
27
|
+
key: name,
|
|
28
|
+
receiver: self
|
|
29
|
+
)
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
fetcher = lambda do
|
data/lib/wireless/fetcher.rb
CHANGED
data/lib/wireless/registry.rb
CHANGED
|
@@ -4,12 +4,12 @@ require 'set'
|
|
|
4
4
|
require_relative 'synchronized_store'
|
|
5
5
|
|
|
6
6
|
module Wireless
|
|
7
|
-
# The
|
|
8
|
-
#
|
|
9
|
-
# which resolve the dependency
|
|
7
|
+
# The public API of the dependency provider (AKA service locator). A hash-like
|
|
8
|
+
# object which maps names (symbols) to dependencies (objects) via blocks
|
|
9
|
+
# which either resolve the dependency every time (factory) or once (singleton).
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
# which calls +new+ on the class e.g.:
|
|
11
|
+
# A class can be supplied instead of the block, in which case it is equivalent to
|
|
12
|
+
# a block which calls +new+ on the class e.g.:
|
|
13
13
|
#
|
|
14
14
|
# WL = Wireless.new do
|
|
15
15
|
# on(:foo, Foo)
|
|
@@ -26,17 +26,17 @@ module Wireless
|
|
|
26
26
|
|
|
27
27
|
include Fetch
|
|
28
28
|
|
|
29
|
-
def initialize(
|
|
30
|
-
@default_visibility =
|
|
31
|
-
@module_cache =
|
|
32
|
-
@registry = SynchronizedStore.new
|
|
29
|
+
def initialize(default_visibility = DEFAULT_VISIBILITY, &block)
|
|
30
|
+
@default_visibility = default_visibility
|
|
31
|
+
@module_cache = SynchronizedStore.new(type: :module)
|
|
32
|
+
@registry = SynchronizedStore.new(type: :resolver)
|
|
33
33
|
@seen = Set.new
|
|
34
34
|
instance_eval(&block) if block
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# Registers a dependency which is resolved every time its value is fetched.
|
|
38
38
|
def factory(name, klass = nil, &block)
|
|
39
|
-
|
|
39
|
+
@registry[name.to_sym] = Resolver::Factory.new(block || klass)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Returns true if a service with the specified name has been registered, false
|
|
@@ -48,7 +48,7 @@ module Wireless
|
|
|
48
48
|
# Registers a dependency which is only resolved the first time its value is
|
|
49
49
|
# fetched. On subsequent fetches, the cached value is returned.
|
|
50
50
|
def singleton(name, klass = nil, &block)
|
|
51
|
-
|
|
51
|
+
@registry[name.to_sym] = Resolver::Singleton.new(block || klass)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Takes an array or hash specifying the dependencies to export, and returns
|
|
@@ -99,22 +99,24 @@ module Wireless
|
|
|
99
99
|
#
|
|
100
100
|
# { :foo => :foo, :bar => :baz, :quux => :quux }
|
|
101
101
|
|
|
102
|
-
# XXX transform_values isn't available
|
|
103
|
-
# pull in ActiveSupport for
|
|
102
|
+
# XXX transform_values isn't available in ruby 2.3 and we don't want to
|
|
103
|
+
# pull in ActiveSupport just for one method (on this occasion)
|
|
104
104
|
#
|
|
105
105
|
# args = DEFAULT_EXPORTS.merge(args).transform_values do |exports|
|
|
106
|
-
#
|
|
106
|
+
# exports = [exports] unless exports.is_a?(Array)
|
|
107
|
+
# exports.reduce({}) do |a, b|
|
|
107
108
|
# a.merge(b.is_a?(Hash) ? b : { b => b })
|
|
108
109
|
# end
|
|
109
110
|
# end
|
|
110
111
|
|
|
111
112
|
args = DEFAULT_EXPORTS.merge(args).each_with_object({}) do |(key, exports), merged|
|
|
112
|
-
|
|
113
|
+
exports = [exports] unless exports.is_a?(Array)
|
|
114
|
+
merged[key] = exports.reduce({}) do |a, b|
|
|
113
115
|
a.merge(b.is_a?(Hash) ? b : { b => b })
|
|
114
116
|
end
|
|
115
117
|
end
|
|
116
118
|
|
|
117
|
-
@module_cache
|
|
119
|
+
@module_cache.get!(args) { module_for(args) }
|
|
118
120
|
end
|
|
119
121
|
|
|
120
122
|
alias on factory
|
|
@@ -147,11 +149,5 @@ module Wireless
|
|
|
147
149
|
|
|
148
150
|
mod
|
|
149
151
|
end
|
|
150
|
-
|
|
151
|
-
# Assign a locator for the specified dependency name
|
|
152
|
-
def register(locator, name, block)
|
|
153
|
-
name = name.to_sym
|
|
154
|
-
@registry[name] = locator.new(block)
|
|
155
|
-
end
|
|
156
152
|
end
|
|
157
153
|
end
|
data/lib/wireless/resolver.rb
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
module Wireless
|
|
4
4
|
# The registry is a key/value store (Hash) whose keys are symbols and whose values
|
|
5
5
|
# are instances of this class. Resolvers are responsible for returning their
|
|
6
|
-
# dependencies, which they do by calling their corresponding
|
|
6
|
+
# dependencies, which they do by calling their corresponding blocks. They can wrap
|
|
7
7
|
# additional behaviors around this call e.g. singletons (Wireless::Resolver::Singleton)
|
|
8
8
|
# cache the result so that the block is only called once.
|
|
9
9
|
class Resolver
|
|
10
10
|
def initialize(block = nil)
|
|
11
|
-
if block.
|
|
11
|
+
if block.respond_to?(:call)
|
|
12
|
+
@block = block
|
|
13
|
+
elsif block.is_a?(Class)
|
|
12
14
|
@block = proc { block.new }
|
|
13
|
-
elsif !block.respond_to?(:call)
|
|
14
|
-
raise ArgumentError, "invalid argument: expected a class or a block, got: #{block.class}"
|
|
15
15
|
else
|
|
16
|
-
|
|
16
|
+
raise ArgumentError, "invalid argument: expected a block or a class, got: #{block.class}"
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -1,32 +1,78 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wireless
|
|
4
|
-
# A Hash wrapper
|
|
5
|
-
# Implemented as a wrapper rather than a subclass so we don't have to worry about
|
|
6
|
-
# every possible mutator.
|
|
4
|
+
# A Hash wrapper with synchronized get, set and check methods.
|
|
7
5
|
class SynchronizedStore
|
|
8
|
-
def initialize
|
|
9
|
-
@
|
|
6
|
+
def initialize(store = {}, replace: false, type: :key)
|
|
7
|
+
@type = type
|
|
8
|
+
@replace = replace
|
|
9
|
+
@store = store
|
|
10
10
|
@lock = Mutex.new
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
# Retrieve a
|
|
13
|
+
# Retrieve a value from the store
|
|
14
|
+
#
|
|
15
|
+
# A synchronized version of:
|
|
16
|
+
#
|
|
17
|
+
# store[key]
|
|
18
|
+
#
|
|
14
19
|
def [](key)
|
|
15
20
|
@lock.synchronize { @store[key] }
|
|
16
21
|
end
|
|
17
22
|
|
|
18
|
-
#
|
|
23
|
+
# Add a key/value to the store
|
|
24
|
+
#
|
|
25
|
+
# A synchronized version of:
|
|
26
|
+
#
|
|
27
|
+
# store[key] = value
|
|
28
|
+
#
|
|
19
29
|
def []=(key, value)
|
|
20
30
|
@lock.synchronize do
|
|
21
|
-
if @store.include?(key)
|
|
22
|
-
|
|
31
|
+
if !@replace && @store.include?(key)
|
|
32
|
+
# XXX don't expose the receiver as this class is an internal
|
|
33
|
+
# implementation detail
|
|
34
|
+
raise Wireless::KeyError.new(
|
|
35
|
+
"#{@type} already exists: #{key}",
|
|
36
|
+
key: key
|
|
37
|
+
)
|
|
23
38
|
end
|
|
24
39
|
|
|
25
40
|
@store[key] = value
|
|
26
41
|
end
|
|
27
42
|
end
|
|
28
43
|
|
|
29
|
-
#
|
|
44
|
+
# Retrieve a value from the store. If it doesn't exist and a block is
|
|
45
|
+
# supplied, create and return it; otherwise, raise a KeyError.
|
|
46
|
+
#
|
|
47
|
+
# A synchronized version of:
|
|
48
|
+
#
|
|
49
|
+
# store[key] ||= value
|
|
50
|
+
#
|
|
51
|
+
def get_or_create(key)
|
|
52
|
+
@lock.synchronize do
|
|
53
|
+
if @store.include?(key)
|
|
54
|
+
@store[key]
|
|
55
|
+
elsif block_given?
|
|
56
|
+
@store[key] = yield
|
|
57
|
+
else
|
|
58
|
+
# XXX don't expose the receiver as this class is an internal
|
|
59
|
+
# implementation detail
|
|
60
|
+
raise Wireless::KeyError.new(
|
|
61
|
+
"#{@type} not found: #{key}",
|
|
62
|
+
key: key
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
alias get! get_or_create
|
|
69
|
+
|
|
70
|
+
# Returns true if the store contains the key, false otherwise
|
|
71
|
+
#
|
|
72
|
+
# A synchronized version of:
|
|
73
|
+
#
|
|
74
|
+
# store.include?(key)
|
|
75
|
+
#
|
|
30
76
|
def include?(key)
|
|
31
77
|
@lock.synchronize { @store.include?(key) }
|
|
32
78
|
end
|
data/lib/wireless/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wireless
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- chocolateboy
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-06-
|
|
11
|
+
date: 2018-06-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|