direct 2.0.0 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 812ea6d0360b4161fba6c6fcd38d409715900ff08cdd57f3d4cf4df5df2de900
4
- data.tar.gz: b3b95cafabb8381687d8a033f4c14b9d57164b101236c61a4faa960a5b73890b
3
+ metadata.gz: 0c4b78452caed73b8ce6c197b9825690120664cfedf0a695e0b8dee7d3f93e9e
4
+ data.tar.gz: d16639349d3041cc46fd0aa8edf6cc6565e24ff70b97dd275bc6c4dbeba1c853
5
5
  SHA512:
6
- metadata.gz: 737c8f8dfd54bef81cb284c5bf0fceb02b8ff53df4b9fa31231bbd78d1b9f86bcf83f34e60befce7ed87d368843b63e35de73de16a6578aae5f09cc1a60d5f56
7
- data.tar.gz: fffe7ce4459bd5ea399d4103a19ed561955b4e5c241a407c772d4f03669cf9328f108e5fbf704c664dcafd06ccbcdf826b50235e870e7f8522a40f0004ef3e2e
6
+ metadata.gz: 2193d4a0199deb76f94bdcb22fe6042d393a32a0a23581f6957f066d6609ad51e8590436a284fa818a939b3fcd00aa495724d229d9af65c18e9dd86bc0e670ff
7
+ data.tar.gz: cd31472b4a110ceeab0cff5af7700314b71e10f911b12a426491acc94019879d92374cefdc301cc258cf3c5197f097bc9d32ae8e5209569dd7102f26131a6fe8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## Version 2.0.1
2
+
3
+ Introduce an ExceptionHandler to allow for multiple exception blocks
4
+ Drop Ruby 2.6 support
5
+ Fix bug where strict_defer was not accepting callable and object arguments.
6
+ Drop Ruby 2.5 and below
7
+
1
8
  ## Version 2.0.0
2
9
 
3
10
  A bug in deferred execution did not return the success/failure results. Fixes test names that cause a collision and incorrect test scenarios.
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Jim Gay
3
+ Copyright (c) 2018-2022 Jim Gay
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -34,7 +34,7 @@ SomeClass.new.procedure.success{ |deferred_object, result, object|
34
34
  puts "it worked!"
35
35
  }.failure { |deferred_object, result, object|
36
36
  puts "it failed :-("
37
- }.exception{ |deferred_object, exception, object|
37
+ }.exception { |deferred_object, exception, object|
38
38
  puts "Oh no! An exception was raised!"
39
39
  }.execute
40
40
  ```
@@ -51,6 +51,16 @@ SomeClass.new.procedure.success{ |deferred_object, result, object|
51
51
  }.execute
52
52
  ```
53
53
 
54
+ You can also handle different exceptions with different blocks:
55
+
56
+ ```ruby
57
+ SomeClass.new.procedure.exception(SomeLibrary::SomeSpecialError){ |deferred_object, exception, object|
58
+ puts "Oh no! A Special Error!"
59
+ }.exception(ArgumentError){ |deferred_object, exception, object|
60
+ puts "Oops! The arguments are wrong!"
61
+ }.execute
62
+ ```
63
+
54
64
  The `defer` method uses built-in classes but you can build your own to manage executing named blocks
55
65
 
56
66
  ```ruby
data/Rakefile CHANGED
@@ -7,4 +7,4 @@ Rake::TestTask.new(:test) do |t|
7
7
  t.test_files = FileList["test/**/*_test.rb"]
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: :test
@@ -0,0 +1,31 @@
1
+ module Direct
2
+ # This class monitors exception types with related blocks.
3
+ class ExceptionHandler
4
+ def initialize
5
+ @handlers = {}
6
+ end
7
+
8
+ # All classes, including StandardError, for which this object
9
+ # maintains a block to execute.
10
+ def classes
11
+ [StandardError, @handlers.keys.flatten].flatten
12
+ end
13
+
14
+ # Pass a single or multiple exception classes and the block
15
+ # to be used to handle them.
16
+ def monitor(*classes, &block)
17
+ @handlers[classes.flatten] = block
18
+ end
19
+
20
+ # This will find the first handler given to `monitor` which matches
21
+ # the provided exception's class and will execute it with the
22
+ # deferred object, the exception object, and any given object to the
23
+ # deferred object.
24
+ def call(deferred, exception, object)
25
+ if_none = proc { raise "No handler for this exception: #{exception.class}!" }
26
+ result = @handlers.find(if_none) { |key, val| key.include?(exception.class) }
27
+
28
+ result.last.call(deferred, exception, object)
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,5 @@
1
+ require "direct/exception_handler"
2
+
1
3
  module Direct
2
4
  class Executable
3
5
  include Direct.allow_missing_directions
@@ -40,18 +42,20 @@ module Direct
40
42
  # puts "The #{thing} did something!"
41
43
  # }.execute
42
44
  #
43
- def initialize(callable=nil, object: nil, &block)
44
- @object = object
45
+ def initialize(callable = nil, *args, **kwargs, &block)
46
+ @object = kwargs.delete(:object)
47
+ @args = args
48
+ @kwargs = kwargs
49
+ @exception_handler = kwargs.delete(:exception_handler) || ExceptionHandler.new
45
50
  @execution = callable || block
46
- @exception_classes = [StandardError]
47
51
  end
48
- attr_reader :execution, :exception_classes, :object
52
+ attr_reader :execution, :exception_handler, :object, :args, :kwargs
49
53
 
50
54
  # Tell the object what to do for a success path
51
55
  #
52
56
  # Returns itself
53
57
  #
54
- def success(callable=nil, &block)
58
+ def success(callable = nil, &block)
55
59
  direct(:success, (callable || block))
56
60
  self
57
61
  end
@@ -60,7 +64,7 @@ module Direct
60
64
  #
61
65
  # Returns itself
62
66
  #
63
- def failure(callable=nil, &block)
67
+ def failure(callable = nil, &block)
64
68
  direct(:failure, (callable || block))
65
69
  self
66
70
  end
@@ -81,18 +85,22 @@ module Direct
81
85
  # }
82
86
  #
83
87
  def exception(*classes, &block)
84
- unless classes.empty?
85
- @exception_classes = classes
86
- end
87
- direct(:exception, block)
88
+ classes = [StandardError] if classes.empty?
89
+ exception_handler.monitor(classes, &block)
90
+
91
+ direct(:exception, exception_handler)
88
92
  self
89
93
  end
90
94
 
95
+ def exception_classes
96
+ exception_handler.classes
97
+ end
98
+
91
99
  def run_exception_block(exception)
92
100
  if __directions.key?(:exception)
93
- as_directed(:exception, exception, object)
101
+ as_directed(:exception, exception, object, *args, **kwargs)
94
102
  else
95
- as_directed(:failure, exception, object)
103
+ as_directed(:failure, exception, object, *args, **kwargs)
96
104
  end
97
105
  end
98
106
  private :run_exception_block
@@ -102,15 +110,23 @@ module Direct
102
110
  # provided to the exception path.
103
111
  #
104
112
  def value
105
- result = execution.()
106
- if result
107
- as_directed(:success, result, object) || result
108
- else
109
- as_directed(:failure, result, object) || result
110
- end
113
+ trigger_directions
111
114
  rescue *exception_classes => exception
112
115
  run_exception_block(exception)
113
116
  end
114
- alias execute value
117
+ alias_method :execute, :value
118
+
119
+ def trigger_directions
120
+ result = execution.call
121
+ if result
122
+ as_directed(:success, result, object, *args, **kwargs)
123
+ else
124
+ as_directed(:failure, result, object, *args, **kwargs)
125
+ end || result
126
+ end
127
+ private :trigger_directions
128
+
115
129
  end
130
+
131
+ private_constant :Executable
116
132
  end
data/lib/direct/group.rb CHANGED
@@ -1,16 +1,16 @@
1
- require 'concurrent'
1
+ require "concurrent"
2
2
  module Direct
3
3
  class Group
4
4
  def initialize
5
- @map = Concurrent::Map.new{|collection, key|
6
- collection.put(key, Concurrent::Array.new)
5
+ @map = Concurrent::Map.new { |collection, key|
6
+ collection.put(key, Concurrent::Set.new)
7
7
  }
8
8
  end
9
9
 
10
10
  attr_reader :map
11
11
  private :map
12
12
 
13
- def store(key, callable=nil, &block)
13
+ def store(key, callable = nil, &block)
14
14
  map[key] << (callable || block)
15
15
  self
16
16
  end
@@ -31,4 +31,6 @@ module Direct
31
31
  map.keys.inspect
32
32
  end
33
33
  end
34
+
35
+ private_constant :Group
34
36
  end
@@ -1,5 +1,5 @@
1
1
  module Direct
2
- class StrictExecutable
2
+ class StrictExecutable < Executable
3
3
  include Direct
4
4
 
5
5
  # It is intended that you initialize objects via Direct.strict_defer
@@ -42,77 +42,20 @@ module Direct
42
42
  # puts "#{thing} failed!"
43
43
  # }.execute
44
44
  #
45
- def initialize(callable=nil, object: nil, &block)
46
- @object = object
47
- @execution = callable || block
48
- @exception_classes = [StandardError]
49
- end
50
- attr_reader :execution, :exception_classes, :object
51
-
52
- # Tell the object what to do for a success path
53
- #
54
- # Returns itself
55
- #
56
- def success(callable=nil, &block)
57
- direct(:success, (callable || block))
58
- self
59
- end
60
-
61
- # Tell the object what to do for a failure path
62
- #
63
- # Returns itself
64
- #
65
- def failure(callable=nil, &block)
66
- direct(:failure, (callable || block))
67
- self
68
- end
69
-
70
- # Tell the object what to do for an exception path.
71
- #
72
- # You may optionally provide a list of modules rescued by
73
- # the value method in the case of an exception.
74
- #
75
- # Returns itself
76
- #
77
- # Example:
78
- #
79
- # Direct.strict_defer {
80
- # # something...
81
- # }.exception(NoMethodError) { |deferred, exception, object|
82
- # ExceptionNotifier.notify(exception)
83
- # }
84
- #
85
- def exception(*classes, &block)
86
- unless classes.empty?
87
- @exception_classes = classes
88
- end
89
- direct(:exception, block)
90
- self
91
- end
92
-
93
- def run_exception_block(exception)
94
- if __directions.key?(:exception)
95
- as_directed(:exception, exception, object)
96
- else
97
- as_directed(:failure, exception, object)
98
- end
99
- end
100
- private :run_exception_block
101
45
 
102
- # Return the value of the success or failure path
103
- # and rescue from StandardError or from the modules
104
- # provided to the exception path.
46
+ # Override the behavior of the parent class to *not* fall back to the
47
+ # result.
105
48
  #
106
- def value
107
- result = execution.()
49
+ def trigger_directions
50
+ result = execution.call
108
51
  if result
109
- as_directed(:success, result)
52
+ as_directed(:success, result, object, *args, **kwargs)
110
53
  else
111
- as_directed(:failure, result)
54
+ as_directed(:failure, result, object, *args, **kwargs)
112
55
  end
113
- rescue *exception_classes => exception
114
- run_exception_block(exception)
115
56
  end
116
- alias execute value
57
+ private :trigger_directions
117
58
  end
59
+
60
+ private_constant :StrictExecutable
118
61
  end
@@ -1,3 +1,3 @@
1
1
  module Direct
2
- VERSION = "2.0.0"
2
+ VERSION = "2.0.1"
3
3
  end
data/lib/direct.rb CHANGED
@@ -52,8 +52,8 @@ module Direct
52
52
  # failure{|result| puts "it failed!" }.
53
53
  # value
54
54
  #
55
- def self.strict_defer(&block)
56
- StrictExecutable.new(&block)
55
+ def self.strict_defer(callable = nil, *args, object: nil, **kwargs, &block)
56
+ StrictExecutable.new(callable, *args, object: object, **kwargs, &block)
57
57
  end
58
58
 
59
59
  # Wrap a block of code to return an object for handling
@@ -68,8 +68,8 @@ module Direct
68
68
  # end
69
69
  # do_it.value
70
70
  #
71
- def self.defer(*args, object: nil, &block)
72
- Executable.new(*args, object: object, &block)
71
+ def self.defer(callable = nil, *args, object: nil, **kwargs, &block)
72
+ Executable.new(callable, *args, object: object, **kwargs, &block)
73
73
  end
74
74
 
75
75
  # Tell the object what to do in a given scenario.
@@ -85,9 +85,9 @@ module Direct
85
85
  # puts "it failed!"
86
86
  # }.do_it
87
87
  #
88
- # Your blocks will always receive the object itself as the first argument.
88
+ # Your blocks will *always* receive the object itself as the first argument.
89
89
  #
90
- def direct(key, callable=nil, &block)
90
+ def direct(key, callable = nil, &block)
91
91
  __directions.store(key, callable || block)
92
92
  self
93
93
  end
@@ -103,10 +103,12 @@ module Direct
103
103
  # end
104
104
  #
105
105
  # This will raise an error if the provided key is not found
106
- def as_directed(key, *args)
106
+ #
107
+ # The current value for self will be sent as the first argument to the block
108
+ def as_directed(key, ...)
107
109
  return if allow_missing_directions? && __directions.empty?
108
110
  __directions.fetch(key).map do |block|
109
- block.call(self, *args)
111
+ block.call(self, ...)
110
112
  end
111
113
  rescue KeyError
112
114
  return if allow_missing_directions?
@@ -120,6 +122,6 @@ module Direct
120
122
  end
121
123
 
122
124
  def __directions
123
- @__directions ||= Direct::Group.new
125
+ @__directions ||= Group.new
124
126
  end
125
127
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: direct
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Gay
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-09 00:00:00.000000000 Z
11
+ date: 2022-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -31,18 +31,13 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - ".gitignore"
35
- - ".travis.yml"
36
34
  - CHANGELOG.md
37
35
  - CODE_OF_CONDUCT.md
38
- - Gemfile
39
36
  - LICENSE.txt
40
37
  - README.md
41
38
  - Rakefile
42
- - bin/console
43
- - bin/setup
44
- - direct.gemspec
45
39
  - lib/direct.rb
40
+ - lib/direct/exception_handler.rb
46
41
  - lib/direct/executable.rb
47
42
  - lib/direct/group.rb
48
43
  - lib/direct/strict_executable.rb
@@ -59,14 +54,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
54
  requirements:
60
55
  - - ">="
61
56
  - !ruby/object:Gem::Version
62
- version: '0'
57
+ version: '2.7'
63
58
  required_rubygems_version: !ruby/object:Gem::Requirement
64
59
  requirements:
65
60
  - - ">="
66
61
  - !ruby/object:Gem::Version
67
62
  version: '0'
68
63
  requirements: []
69
- rubygems_version: 3.1.2
64
+ rubygems_version: 3.4.1
70
65
  signing_key:
71
66
  specification_version: 4
72
67
  summary: Direct objects to perform arbitrary blocks by name
data/.gitignore DELETED
@@ -1,10 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
- Gemfile.lock
10
- coverage
data/.travis.yml DELETED
@@ -1,28 +0,0 @@
1
- sudo: false
2
- before_install:
3
- - gem update --system
4
- - gem install bundler
5
- language: ruby
6
- cache: bundler
7
- rvm:
8
- - 2.3.6
9
- - 2.4.4
10
- - 2.5.3
11
- - 2.6.1
12
- - ruby-head
13
- - jruby-head
14
- env:
15
- global:
16
- - CC_TEST_REPORTER_ID=3c4cb48bcc4e29bb3292e99b7736c6dd7c13d0f6a908785e164d03666f5070cc
17
- matrix:
18
- allow_failures:
19
- - rvm: ruby-head
20
- - rvm: jruby-head
21
- before_script:
22
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
23
- - chmod +x ./cc-test-reporter
24
- - ./cc-test-reporter before-build
25
- script:
26
- - bundle exec rake
27
- after_script:
28
- - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
data/Gemfile DELETED
@@ -1,8 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gemspec
4
-
5
- gem "rake", "~> 12.3"
6
- gem "minitest", "~> 5.11"
7
- gem "pry-byebug"
8
- gem "simplecov", require: false, group: :test
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "direct"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/direct.gemspec DELETED
@@ -1,25 +0,0 @@
1
-
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "direct/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "direct"
8
- spec.version = Direct::VERSION
9
- spec.authors = ["Jim Gay"]
10
- spec.email = ["jim@saturnflyer.com"]
11
-
12
- spec.summary = %q{Direct objects to perform arbitrary blocks by name}
13
- spec.description = %q{Direct objects to perform arbitrary blocks by name}
14
- spec.homepage = "https://github.com/saturnflyer/direct"
15
- spec.license = "MIT"
16
-
17
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
19
- end
20
- spec.bindir = "exe"
21
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
- spec.require_paths = ["lib"]
23
-
24
- spec.add_dependency "concurrent-ruby", ">= 1.0"
25
- end