wireless 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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