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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 875d0a0704d8e905a826e1a171d887877990c1c90e92b73026305efd419337d2
4
- data.tar.gz: 0ea0ce2e1f08e7b923361514eb2529a57ca6787beb182285bd3be8298ae62d92
3
+ metadata.gz: 15c44fc9bf9e10af9c1a9d8d0241e2a808a7dfabdadd895581271f6fa076fdbc
4
+ data.tar.gz: 3159342e5f335e4a9b92fd6c09556c4fbb39af17bc5fada60737e0ad3150ac0f
5
5
  SHA512:
6
- metadata.gz: 330cfceecbc844ecf795d8f6b102ba00da16d5a512f3125c061d364b19e7d9d2635e1bd06c4872c8337fad46de99586acb997a35ca1f9bee5a966493ad3a773c
7
- data.tar.gz: 94390e87a8b56cd0d31ce37bde2f47ce1162a84639ef345464a9eb7e6e20644ffcf415b306b3fff407f2e6778eebc2f674624d4ba3bae83c6e54a8f22b9b7782
6
+ metadata.gz: 0ded107266b0b9cf0d6a279a5609e102603b9e14e9a09a8b72684d5239b3bae6b61e623bf20297dba65b92d93e93f5c3a66ef5b7197b5d1030778c213eafec93
7
+ data.tar.gz: 22276968eb991723ced37a404b4e8aa0a805ccf3ca5b69746431712323bd3290235fd91b7535e9c59180ea82c40badfc59df8bec29ac2e93d736f2809fbf2d0d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.0.2 - 2018-06-17
2
+
3
+ * fix import aliasing
4
+
1
5
  ## 0.0.1 - 2018-06-16
2
6
 
3
7
  * initial release
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 [DiFTW](https://github.com/jhollinger/ruby-diftw),
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 creation and connections.
130
+ allow prerequisites to be acquired on-demand, without micromanaging their construction or connections.
131
131
 
132
132
  # VERSION
133
133
 
134
- 0.0.1
134
+ 0.0.2
135
135
 
136
136
  # SEE ALSO
137
137
 
138
138
  ## Gems
139
139
 
140
- - [Canister](https://github.com/mlibrary/canister) - a simple Service Locator for Ruby inspired by Jim Weirich's [article](https://archive.li/shxeA) on Dependency Injection
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
- class NameError < Error; end
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
  #
@@ -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::NameError if the dependency is not
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::NameError, "dependency not found: #{name}"
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
@@ -11,10 +11,5 @@ module Wireless
11
11
  @registry = registry
12
12
  @seen = seen
13
13
  end
14
-
15
- def to_s
16
- hash = { registry: @registry.keys, seen: @seen.to_a }
17
- "#<#{self.class}: #{hash.inspect}>"
18
- end
19
14
  end
20
15
  end
@@ -4,12 +4,12 @@ require 'set'
4
4
  require_relative 'synchronized_store'
5
5
 
6
6
  module Wireless
7
- # The hash-like object which is the public API of the dependency provider
8
- # (AKA service locator). It maps names (symbols) to dependencies (objects) via blocks
9
- # which resolve the dependency either every time (factory) or once (singleton).
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
- # The block can be supplied as a class, in which case it is equivalent to a block
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(args = DEFAULT_VISIBILITY, &block)
30
- @default_visibility = args
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
- register(Resolver::Factory, name, block || klass)
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
- register(Resolver::Singleton, name, block || klass)
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 on ruby 2.3 and we don't want to
103
- # pull in ActiveSupport for just one method in this case
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
- # Array(exports).reduce({}) do |a, b|
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
- merged[key] = Array(exports).reduce({}) do |a, b|
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[args] ||= module_for(args)
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
@@ -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 block. They can wrap
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.is_a?(Class)
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
- @block = block
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 which synchronizes get ([]), set ([]=) and include? methods.
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
- @store = {}
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 new value from the underlying hash in a thread-safe way
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
- # Assign a new value to the underlying hash in a thread-safe way
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
- raise Wireless::NameError, "resolver already exists: #{key}"
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
- # Returns true if the underlying hash contains the key, false otherwise
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wireless
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
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.1
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-15 00:00:00.000000000 Z
11
+ date: 2018-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler