simple-service 0.1.5 → 0.2.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 +4 -4
- data/.rubocop.yml +10 -2
- data/Gemfile +3 -1
- data/Makefile +5 -2
- data/TODO.txt +3 -0
- data/VERSION +1 -1
- data/doc/Simple/Service/Action/Comment/Extractor.html +1 -1
- data/doc/Simple/Service/Action/Comment.html +1 -1
- data/doc/Simple/Service/Action/MethodReflection.html +1 -1
- data/doc/Simple/Service/Action/Parameter.html +2 -2
- data/doc/Simple/Service/Action.html +59 -150
- data/doc/Simple/Service/ArgumentError.html +1 -1
- data/doc/Simple/Service/ClassMethods.html +5 -5
- data/doc/Simple/Service/Context.html +5 -5
- data/doc/Simple/Service/ContextMissingError.html +1 -1
- data/doc/Simple/Service/ContextReadOnlyError.html +1 -1
- data/doc/Simple/Service/ExtraArguments.html +1 -1
- data/doc/Simple/Service/GemHelper.html +1 -1
- data/doc/Simple/Service/MissingArguments.html +1 -1
- data/doc/Simple/Service/NoSuchAction.html +1 -1
- data/doc/Simple/Service.html +89 -87
- data/doc/Simple.html +1 -1
- data/doc/_index.html +7 -19
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +3 -3
- data/doc/file.TODO.html +70 -0
- data/doc/file_list.html +5 -0
- data/doc/index.html +3 -3
- data/doc/method_list.html +59 -115
- data/doc/top-level-namespace.html +1 -1
- data/lib/simple/service/action/comment.rb +1 -1
- data/lib/simple/service/action.rb +10 -3
- data/lib/simple/service/errors.rb +4 -3
- data/lib/simple/service.rb +33 -28
- data/lib/simple/workflow/context.rb +105 -0
- data/lib/simple/workflow/current_context.rb +33 -0
- data/lib/simple/workflow/reloader.rb +84 -0
- data/lib/simple/workflow/rspec_helper.rb +15 -0
- data/lib/simple/workflow.rb +96 -0
- data/lib/simple-workflow.rb +3 -0
- data/scripts/test +2 -0
- data/simple-service.gemspec +1 -0
- data/spec/simple/service/action_invoke3_spec.rb +0 -8
- data/spec/simple/service/action_invoke_spec.rb +82 -20
- data/spec/simple/service/service_spec.rb +13 -56
- data/spec/simple/workflow/context_spec.rb +90 -0
- data/spec/simple/workflow/current_context_spec.rb +41 -0
- data/spec/simple/workflow/reloader_spec/example1.rb +10 -0
- data/spec/simple/workflow/reloader_spec/example2.rb +7 -0
- data/spec/simple/workflow/reloader_spec.rb +48 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/spec_services.rb +1 -3
- metadata +42 -7
- data/doc/Simple/Service/Action/IndieHash.html +0 -506
- data/lib/simple/service/context.rb +0 -94
- data/spec/simple/service/context_spec.rb +0 -69
data/lib/simple/service.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
module Simple # @private
|
2
2
|
end
|
3
3
|
|
4
|
+
module Simple::Service # @private
|
5
|
+
end
|
6
|
+
|
4
7
|
require "expectation"
|
8
|
+
require "logger"
|
5
9
|
|
6
10
|
require_relative "service/errors"
|
7
11
|
require_relative "service/action"
|
8
|
-
require_relative "service/context"
|
9
12
|
require_relative "service/version"
|
10
13
|
|
11
14
|
# <b>The Simple::Service interface</b>
|
@@ -31,37 +34,43 @@ require_relative "service/version"
|
|
31
34
|
# end
|
32
35
|
#
|
33
36
|
# 2. <em>Discover services:</em> To discover services in a service module use the #actions method. This returns a Hash
|
34
|
-
# of actions.
|
37
|
+
# of actions.
|
35
38
|
#
|
36
39
|
# Simple::Service.actions(GodMode)
|
37
40
|
# => {:build_universe=>#<Simple::Service::Action...>, ...}
|
38
41
|
#
|
39
|
-
#
|
42
|
+
# TODO: why a Hash? It feels much better if Simple::Service.actions returns an array of names.
|
40
43
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
+
#
|
45
|
+
# 3. <em>Invoke a service:</em> run <tt>Simple::Service.invoke3</tt> or <tt>Simple::Service.invoke</tt>.
|
46
|
+
#
|
47
|
+
# Simple::Service.invoke3(GodMode, :build_universe, "TestWorld", c: 1e9)
|
44
48
|
# => 42
|
45
49
|
#
|
46
50
|
module Simple::Service
|
47
|
-
|
48
|
-
|
51
|
+
module ServiceExpectations
|
52
|
+
def expect!(*args, &block)
|
53
|
+
Expectation.expect!(*args, &block)
|
54
|
+
rescue ::Expectation::Error => e
|
55
|
+
raise ArgumentError, e.to_s
|
56
|
+
end
|
49
57
|
end
|
50
58
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def self.service?(service)
|
55
|
-
verify_service! service
|
56
|
-
true
|
57
|
-
rescue ::ArgumentError
|
58
|
-
false
|
59
|
+
def self.included(klass) # @private
|
60
|
+
klass.extend ClassMethods
|
61
|
+
klass.include ServiceExpectations
|
59
62
|
end
|
60
63
|
|
61
|
-
# Raises an error if the passed in object is not a
|
64
|
+
# Raises an error if the passed in object is not a Simple::Service
|
62
65
|
def self.verify_service!(service) # @private
|
63
|
-
|
64
|
-
|
66
|
+
expect! service => Module
|
67
|
+
|
68
|
+
# rubocop:disable Style/GuardClause
|
69
|
+
unless service.include?(::Simple::Service)
|
70
|
+
raise ::ArgumentError,
|
71
|
+
"#{service.name} is not a Simple::Service, did you 'include Simple::Service'"
|
72
|
+
end
|
73
|
+
# rubocop:enable Style/GuardClause
|
65
74
|
end
|
66
75
|
|
67
76
|
# returns a Hash with all actions in the +service+ module
|
@@ -73,6 +82,8 @@ module Simple::Service
|
|
73
82
|
|
74
83
|
# returns the action with the given name.
|
75
84
|
def self.action(service, name)
|
85
|
+
expect! name => Symbol
|
86
|
+
|
76
87
|
actions = self.actions(service)
|
77
88
|
actions[name] || begin
|
78
89
|
raise ::Simple::Service::NoSuchAction.new(service, name)
|
@@ -86,10 +97,8 @@ module Simple::Service
|
|
86
97
|
#
|
87
98
|
# As the main purpose of this module is to call services with outside data,
|
88
99
|
# the +.invoke+ action is usually preferred.
|
89
|
-
#
|
90
|
-
# *Note:* You cannot call this method if the context is not set.
|
91
100
|
def self.invoke3(service, name, *args, **flags)
|
92
|
-
flags = flags.
|
101
|
+
flags = flags.transform_keys(&:to_s)
|
93
102
|
invoke service, name, args: args, flags: flags
|
94
103
|
end
|
95
104
|
|
@@ -133,14 +142,10 @@ module Simple::Service
|
|
133
142
|
# by the method we raise an ArgumentError.
|
134
143
|
#
|
135
144
|
# Entries in the +named_args+ Hash that are not defined in the action itself are ignored.
|
136
|
-
|
137
|
-
# <b>Note:</b> You cannot call this method if the context is not set.
|
138
145
|
def self.invoke(service, name, args: {}, flags: {})
|
139
|
-
raise ContextMissingError, "Need to set context before calling ::Simple::Service.invoke3" unless context
|
140
|
-
|
141
146
|
expect! args => [Hash, Array], flags: Hash
|
142
|
-
args.
|
143
|
-
flags.
|
147
|
+
args.each_key { |key| expect! key => String } if args.is_a?(Hash)
|
148
|
+
flags.each_key { |key| expect! key => String }
|
144
149
|
|
145
150
|
action(service, name).invoke(args: args, flags: flags)
|
146
151
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "simple/immutable"
|
2
|
+
|
3
|
+
module Simple::Workflow
|
4
|
+
# A context object
|
5
|
+
#
|
6
|
+
# Each service executes with a current context. The system manages a stack of
|
7
|
+
# contexts; whenever a service execution is done the current context is reverted
|
8
|
+
# to its previous value.
|
9
|
+
#
|
10
|
+
# A context object can store a large number of values; the only way to set or
|
11
|
+
# access a value is via getters and setters. These are implemented via
|
12
|
+
# +method_missing(..)+.
|
13
|
+
#
|
14
|
+
# Also, once a value is set in the context it is not possible to change or
|
15
|
+
# unset it.
|
16
|
+
class Context < Simple::Immutable
|
17
|
+
SELF = self
|
18
|
+
|
19
|
+
# returns a new Context object.
|
20
|
+
#
|
21
|
+
# Parameters:
|
22
|
+
#
|
23
|
+
# - hsh (Hash or nil): sets values for this context
|
24
|
+
# - previous_context (Context or nil): if +previous_context+ is provided,
|
25
|
+
# values that are not defined in the \a +hsh+ argument are read from the
|
26
|
+
# +previous_context+ instead (or the previous context's +previous_context+,
|
27
|
+
# etc.)
|
28
|
+
def initialize(hsh, previous_context = nil)
|
29
|
+
expect! hsh => [Hash, nil]
|
30
|
+
expect! previous_context => [SELF, nil]
|
31
|
+
|
32
|
+
@previous_context = previous_context
|
33
|
+
super(hsh || {})
|
34
|
+
end
|
35
|
+
|
36
|
+
def reload!(a_module)
|
37
|
+
if @previous_context
|
38
|
+
@previous_context.reload!(a_module)
|
39
|
+
return a_module
|
40
|
+
end
|
41
|
+
|
42
|
+
@reloaded_modules ||= []
|
43
|
+
return if @reloaded_modules.include?(a_module)
|
44
|
+
|
45
|
+
::Simple::Workflow::Reloader.reload(a_module)
|
46
|
+
@reloaded_modules << a_module
|
47
|
+
a_module
|
48
|
+
end
|
49
|
+
|
50
|
+
def fetch_attribute!(sym, raise_when_missing:)
|
51
|
+
unless @previous_context
|
52
|
+
return super(sym, raise_when_missing: raise_when_missing)
|
53
|
+
end
|
54
|
+
|
55
|
+
first_error = nil
|
56
|
+
|
57
|
+
# check this context first. We catch any NameError, to be able to look up
|
58
|
+
# the attribute also in the previous_context.
|
59
|
+
begin
|
60
|
+
return super(sym, raise_when_missing: true)
|
61
|
+
rescue NameError => e
|
62
|
+
first_error = e
|
63
|
+
end
|
64
|
+
|
65
|
+
# check previous_context
|
66
|
+
begin
|
67
|
+
return @previous_context.fetch_attribute!(sym, raise_when_missing: raise_when_missing)
|
68
|
+
rescue NameError
|
69
|
+
:nop
|
70
|
+
end
|
71
|
+
|
72
|
+
# Not in +self+, not in +previous_context+, and +raise_when_missing+ is true:
|
73
|
+
raise(first_error)
|
74
|
+
end
|
75
|
+
|
76
|
+
# def inspect
|
77
|
+
# if @previous_context
|
78
|
+
# "#{object_id} [" + @hsh.keys.map(&:inspect).join(", ") + "; #{@previous_context.inspect}]"
|
79
|
+
# else
|
80
|
+
# "#{object_id} [" + @hsh.keys.map(&:inspect).join(", ") + "]"
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
IDENTIFIER = "[a-z_][a-z0-9_]*" # @private
|
87
|
+
|
88
|
+
def method_missing(sym, *args, &block)
|
89
|
+
raise ArgumentError, "#{self.class.name}##{sym}: Block given" if block
|
90
|
+
raise ArgumentError, "#{self.class.name}##{sym}: Extra args #{args.inspect}" unless args.empty?
|
91
|
+
|
92
|
+
if sym !~ /\A(#{IDENTIFIER})(\?)?\z/
|
93
|
+
raise ArgumentError, "#{self.class.name}: Invalid context key '#{sym}'"
|
94
|
+
end
|
95
|
+
|
96
|
+
# rubocop:disable Lint/OutOfRangeRegexpRef
|
97
|
+
fetch_attribute!($1, raise_when_missing: $2.nil?)
|
98
|
+
# rubocop:enable Lint/OutOfRangeRegexpRef
|
99
|
+
end
|
100
|
+
|
101
|
+
def respond_to_missing?(sym, include_private = false)
|
102
|
+
super || @previous_context&.respond_to_missing?(sym, include_private)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Simple::Workflow
|
2
|
+
module CurrentContext
|
3
|
+
# Returns the current context.
|
4
|
+
#
|
5
|
+
# This method never returns nil - it raises a ContextMissingError exception if
|
6
|
+
# the context was not initialized (via <tt>Simple::Workflow.with_context</tt>).
|
7
|
+
def current_context
|
8
|
+
Thread.current[:"Simple::Workflow.current_context"] || raise(ContextMissingError)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns a logger
|
12
|
+
#
|
13
|
+
# Returns a logger if a context is set and contains a logger.
|
14
|
+
def logger
|
15
|
+
current_context = Thread.current[:"Simple::Workflow.current_context"]
|
16
|
+
current_context&.logger?
|
17
|
+
end
|
18
|
+
|
19
|
+
# yields a block with a given context, and restores the previous context
|
20
|
+
# object afterwards.
|
21
|
+
def with_context(ctx = nil, &block)
|
22
|
+
old_ctx = Thread.current[:"Simple::Workflow.current_context"]
|
23
|
+
|
24
|
+
Thread.current[:"Simple::Workflow.current_context"] = Context.new(ctx, old_ctx)
|
25
|
+
|
26
|
+
block.call
|
27
|
+
ensure
|
28
|
+
Thread.current[:"Simple::Workflow.current_context"] = old_ctx
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
extend CurrentContext
|
33
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# The Simple::Workflow::Reloader provides a way to locate and reload a module
|
2
|
+
module Simple::Workflow::Reloader
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def reload(a_module)
|
6
|
+
source_paths = locate(a_module)
|
7
|
+
if source_paths.empty?
|
8
|
+
logger&.warn "#{a_module}: cannot reload module: cannot find sources"
|
9
|
+
return
|
10
|
+
end
|
11
|
+
|
12
|
+
source_paths.each do |source_path|
|
13
|
+
logger&.debug "#{a_module}: reload #{source_path}"
|
14
|
+
end
|
15
|
+
|
16
|
+
logger&.info "#{a_module}: reloaded module"
|
17
|
+
end
|
18
|
+
|
19
|
+
# This method tries to identify source files for a module's functions.
|
20
|
+
def locate(a_module)
|
21
|
+
expect! a_module => Module
|
22
|
+
|
23
|
+
@registered_source_paths ||= {}
|
24
|
+
@registered_source_paths[a_module.name] ||= locate_source_paths(a_module)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def logger
|
30
|
+
::Simple::Workflow.logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def locate_source_paths(a_module)
|
34
|
+
source_paths = []
|
35
|
+
|
36
|
+
source_paths.concat locate_by_instance_methods(a_module)
|
37
|
+
source_paths.concat locate_by_methods(a_module)
|
38
|
+
source_paths.concat locate_by_name(a_module)
|
39
|
+
|
40
|
+
source_paths.uniq
|
41
|
+
end
|
42
|
+
|
43
|
+
def locate_by_instance_methods(a_module)
|
44
|
+
method_names = a_module.instance_methods(false)
|
45
|
+
methods = method_names.map { |sym| a_module.instance_method(sym) }
|
46
|
+
methods.map(&:source_location).map(&:first)
|
47
|
+
end
|
48
|
+
|
49
|
+
def locate_by_methods(a_module)
|
50
|
+
method_names = a_module.methods(false)
|
51
|
+
methods = method_names.map { |sym| a_module.method(sym) }
|
52
|
+
methods.map(&:source_location).map(&:first)
|
53
|
+
end
|
54
|
+
|
55
|
+
def locate_by_name(a_module)
|
56
|
+
source_file = "#{underscore(a_module.name)}.rb"
|
57
|
+
|
58
|
+
$LOAD_PATH.each do |dir|
|
59
|
+
full_path = File.join(dir, source_file)
|
60
|
+
return [full_path] if File.exist?(full_path)
|
61
|
+
end
|
62
|
+
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Makes an underscored, lowercase form from the expression in the string.
|
67
|
+
#
|
68
|
+
# Changes '::' to '/' to convert namespaces to paths.
|
69
|
+
#
|
70
|
+
# This is copied and slightly changed (we don't support any custom
|
71
|
+
# inflections) from activesupport's lib/active_support/inflector/methods.rb
|
72
|
+
#
|
73
|
+
def underscore(camel_cased_word)
|
74
|
+
return camel_cased_word unless /[A-Z-]|::/.match?(camel_cased_word)
|
75
|
+
|
76
|
+
word = camel_cased_word.to_s.gsub("::", "/")
|
77
|
+
|
78
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
79
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
80
|
+
word.tr!("-", "_")
|
81
|
+
word.downcase!
|
82
|
+
word
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ::Simple::Workflow::RSpecHelper
|
2
|
+
def self.included(base)
|
3
|
+
base.let(:current_context) { {} }
|
4
|
+
|
5
|
+
base.around do |example|
|
6
|
+
if (ctx = current_context)
|
7
|
+
::Simple::Workflow.with_context(ctx) do
|
8
|
+
example.run
|
9
|
+
end
|
10
|
+
else
|
11
|
+
example.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "simple/service"
|
2
|
+
|
3
|
+
require_relative "workflow/context"
|
4
|
+
require_relative "workflow/current_context"
|
5
|
+
require_relative "workflow/reloader"
|
6
|
+
|
7
|
+
if defined?(RSpec)
|
8
|
+
require_relative "workflow/rspec_helper"
|
9
|
+
end
|
10
|
+
|
11
|
+
module Simple::Workflow
|
12
|
+
class ContextMissingError < ::StandardError
|
13
|
+
def to_s
|
14
|
+
"Simple::Workflow.current_context not initialized; remember to call Simple::Workflow.with_context/1"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module HelperMethods
|
19
|
+
def invoke(*args, **kwargs)
|
20
|
+
Simple::Workflow.invoke(self, *args, **kwargs)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceHelperMethods
|
25
|
+
private
|
26
|
+
|
27
|
+
def current_context
|
28
|
+
Simple::Workflow.current_context
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ModuleMethods
|
33
|
+
def register_workflow(mod)
|
34
|
+
expect! mod => Module
|
35
|
+
|
36
|
+
mod.extend ::Simple::Workflow::HelperMethods
|
37
|
+
mod.include ::Simple::Workflow::InstanceHelperMethods
|
38
|
+
mod.extend mod
|
39
|
+
mod.include Simple::Service
|
40
|
+
end
|
41
|
+
|
42
|
+
def reload_on_invocation?
|
43
|
+
!!@reload_on_invocation
|
44
|
+
end
|
45
|
+
|
46
|
+
def reload_on_invocation!
|
47
|
+
@reload_on_invocation = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def invoke(workflow, *args, **kwargs)
|
51
|
+
# This call to Simple::Workflow.current_context raises a ContextMissingError
|
52
|
+
# if the context is not set.
|
53
|
+
_ = ::Simple::Workflow.current_context
|
54
|
+
|
55
|
+
expect! workflow => [Module, String]
|
56
|
+
|
57
|
+
workflow_module = lookup_workflow!(workflow)
|
58
|
+
|
59
|
+
# We will reload workflow modules only once per invocation.
|
60
|
+
if Simple::Workflow.reload_on_invocation?
|
61
|
+
Simple::Workflow.current_context.reload!(workflow_module)
|
62
|
+
end
|
63
|
+
|
64
|
+
Simple::Service.invoke(workflow_module, :call, args: args, flags: kwargs.transform_keys(&:to_s))
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def lookup_workflow!(workflow)
|
70
|
+
workflow_module = lookup_workflow(workflow)
|
71
|
+
|
72
|
+
verify_workflow! workflow_module
|
73
|
+
|
74
|
+
workflow_module
|
75
|
+
end
|
76
|
+
|
77
|
+
def lookup_workflow(workflow)
|
78
|
+
case workflow
|
79
|
+
when Module
|
80
|
+
workflow
|
81
|
+
when String
|
82
|
+
Object.const_get workflow
|
83
|
+
else
|
84
|
+
expect! workflow => [Module, String]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def verify_workflow!(workflow_module)
|
89
|
+
return if Simple::Service.actions(workflow_module).key?(:call)
|
90
|
+
|
91
|
+
raise ArgumentError, "#{workflow_module.name} is not a Simple::Workflow"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
extend ModuleMethods
|
96
|
+
end
|
data/scripts/test
ADDED
data/simple-service.gemspec
CHANGED
@@ -3,14 +3,6 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe "Simple::Service.invoke3" do
|
6
|
-
# the context to use in the around hook below. By default this is nil -
|
7
|
-
# which gives us an empty context.
|
8
|
-
let(:context) { nil }
|
9
|
-
|
10
|
-
around do |example|
|
11
|
-
::Simple::Service.with_context(context) { example.run }
|
12
|
-
end
|
13
|
-
|
14
6
|
let(:service) { InvokeTestService }
|
15
7
|
let(:action) { nil }
|
16
8
|
|
@@ -1,21 +1,12 @@
|
|
1
1
|
# rubocop:disable Style/WordArray
|
2
|
-
|
3
2
|
require "spec_helper"
|
4
3
|
|
5
4
|
describe "Simple::Service.invoke" do
|
6
|
-
# the context to use in the around hook below. By default this is nil -
|
7
|
-
# which gives us an empty context.
|
8
|
-
let(:context) { nil }
|
9
|
-
|
10
|
-
around do |example|
|
11
|
-
::Simple::Service.with_context(context) { example.run }
|
12
|
-
end
|
13
|
-
|
14
5
|
let(:service) { InvokeTestService }
|
15
6
|
let(:action) { nil }
|
16
7
|
|
17
8
|
# a shortcut
|
18
|
-
def invoke!(args
|
9
|
+
def invoke!(args: {}, flags: {})
|
19
10
|
@actual = ::Simple::Service.invoke(service, action, args: args, flags: flags)
|
20
11
|
# rescue ::StandardError => e
|
21
12
|
rescue ::Simple::Service::ArgumentError => e
|
@@ -42,7 +33,14 @@ describe "Simple::Service.invoke" do
|
|
42
33
|
|
43
34
|
context "calling with extra named args" do
|
44
35
|
it "ignores extra args" do
|
45
|
-
invoke!("foo" => "foo", "bar" => "bar")
|
36
|
+
invoke!(args: { "foo" => "foo", "bar" => "bar" })
|
37
|
+
expect(actual).to eq("service2 return")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "calling with extra flags" do
|
42
|
+
it "ignores extra args" do
|
43
|
+
invoke!(flags: { "foo" => "foo", "bar" => "bar" })
|
46
44
|
expect(actual).to eq("service2 return")
|
47
45
|
end
|
48
46
|
end
|
@@ -67,21 +65,28 @@ describe "Simple::Service.invoke" do
|
|
67
65
|
|
68
66
|
context "with the required number of args" do
|
69
67
|
it "runs" do
|
70
|
-
invoke!("a" => "foo", "b" => "bar")
|
68
|
+
invoke!(args: { "a" => "foo", "b" => "bar" })
|
69
|
+
expect(actual).to eq(["foo", "bar", "speed-of-light", 2.781])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with the required number of args and flags" do
|
74
|
+
it "merges flags and args to provide arguments" do
|
75
|
+
invoke!(args: { "a" => "foo" }, flags: { "b" => "bar" })
|
71
76
|
expect(actual).to eq(["foo", "bar", "speed-of-light", 2.781])
|
72
77
|
end
|
73
78
|
end
|
74
79
|
|
75
80
|
context "with the allowed number of args" do
|
76
81
|
it "runs" do
|
77
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4")
|
82
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4" })
|
78
83
|
expect(actual).to eq(%w[foo bar baz number4])
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
82
87
|
context "calling with extra named args" do
|
83
88
|
it "ignores extra args" do
|
84
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4", "extra3" => 3)
|
89
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4", "extra3" => 3 })
|
85
90
|
expect(actual).to eq(%w[foo bar baz number4])
|
86
91
|
end
|
87
92
|
end
|
@@ -106,21 +111,21 @@ describe "Simple::Service.invoke" do
|
|
106
111
|
|
107
112
|
context "with the required number of args" do
|
108
113
|
it "runs" do
|
109
|
-
invoke!("a" => "foo", "b" => "bar")
|
114
|
+
invoke!(args: { "a" => "foo", "b" => "bar" })
|
110
115
|
expect(actual).to eq(["foo", "bar", "speed-of-light", 2.781])
|
111
116
|
end
|
112
117
|
end
|
113
118
|
|
114
119
|
context "with the allowed number of args" do
|
115
120
|
it "runs" do
|
116
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4")
|
121
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4" })
|
117
122
|
expect(actual).to eq(%w[foo bar baz number4])
|
118
123
|
end
|
119
124
|
end
|
120
125
|
|
121
126
|
context "with extra named args" do
|
122
127
|
it "ignores extra args" do
|
123
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "extra3" => 3)
|
128
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "extra3" => 3 })
|
124
129
|
expect(actual).to eq(["foo", "bar", "baz", 2.781])
|
125
130
|
end
|
126
131
|
end
|
@@ -144,23 +149,80 @@ describe "Simple::Service.invoke" do
|
|
144
149
|
|
145
150
|
context "with the required number of args" do
|
146
151
|
it "runs" do
|
147
|
-
invoke!("a" => "foo")
|
152
|
+
invoke!(args: { "a" => "foo" })
|
148
153
|
expect(actual).to eq(["foo", "default-b", "speed-of-light", 2.781])
|
149
154
|
end
|
150
155
|
end
|
151
156
|
|
152
157
|
context "with the allowed number of args" do
|
153
158
|
it "runs" do
|
154
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4")
|
159
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4" })
|
155
160
|
expect(actual).to eq(%w[foo bar baz number4])
|
156
161
|
end
|
157
162
|
end
|
158
163
|
|
159
164
|
context "with extra named args" do
|
160
165
|
it "ignores extra args" do
|
161
|
-
invoke!("a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4", "extra3" => 3)
|
166
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "c" => "baz", "e" => "number4", "extra3" => 3 })
|
162
167
|
expect(actual).to eq(["foo", "bar", "baz", "number4"])
|
163
168
|
end
|
164
169
|
end
|
165
170
|
end
|
171
|
+
|
172
|
+
context "calling an action w/mixed and variadic parameters" do
|
173
|
+
# reminder: this is the definition of variadic_params
|
174
|
+
#
|
175
|
+
# def variadic_params(a, b = "queen bee", *args, e: 2.781)
|
176
|
+
# [a, b, args, e]
|
177
|
+
# end
|
178
|
+
|
179
|
+
let(:action) { :variadic_params }
|
180
|
+
|
181
|
+
context "without args" do
|
182
|
+
it "raises MissingArguments" do
|
183
|
+
invoke!
|
184
|
+
expect(actual).to be_a(::Simple::Service::MissingArguments)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "with the required number of args" do
|
189
|
+
it "runs" do
|
190
|
+
invoke!(args: { "a" => "foo" })
|
191
|
+
expect(actual).to eq(["foo", "queen bee", [], 2.781])
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "with the allowed number of args" do
|
196
|
+
it "runs" do
|
197
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "args" => ["baz"] }, flags: { "e" => "number4" })
|
198
|
+
expect(actual).to eq(["foo", "bar", ["baz"], "number4"])
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context "with variadic args" do
|
203
|
+
it "sends the variadic args from the args: parameter" do
|
204
|
+
invoke!(args: { "a" => "foo", "b" => "bar", "args" => ["baz", "extra"] }, flags: { "e" => "number4", "extra3" => 2 })
|
205
|
+
expect(actual).to eq(["foo", "bar", ["baz", "extra"], "number4"])
|
206
|
+
end
|
207
|
+
|
208
|
+
it "sends the variadic args from the flags: parameter" do
|
209
|
+
invoke!(args: { "a" => "foo", "b" => "bar" }, flags: { "args" => ["baz", "extra"], "e" => "number4", "extra3" => 2 })
|
210
|
+
expect(actual).to eq(["foo", "bar", ["baz", "extra"], "number4"])
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "calling with symbolized Hashes" do
|
216
|
+
it "raises ArgumentError" do
|
217
|
+
hsh = { a: "foo", "b" => "KJH" }
|
218
|
+
|
219
|
+
expect do
|
220
|
+
invoke!(args: hsh)
|
221
|
+
end.to raise_error(Expectation::Matcher::Mismatch)
|
222
|
+
|
223
|
+
expect do
|
224
|
+
invoke!(flags: hsh)
|
225
|
+
end.to raise_error(Expectation::Matcher::Mismatch)
|
226
|
+
end
|
227
|
+
end
|
166
228
|
end
|