simple-service 0.1.3 → 0.2.0

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -2
  3. data/.rubocop.yml +10 -2
  4. data/Gemfile +3 -1
  5. data/Makefile +7 -11
  6. data/README.md +67 -2
  7. data/TODO.txt +3 -0
  8. data/VERSION +1 -1
  9. data/doc/Simple/Service/Action/Comment/Extractor.html +347 -0
  10. data/doc/Simple/Service/Action/Comment.html +451 -0
  11. data/doc/Simple/Service/Action/MethodReflection.html +285 -0
  12. data/doc/Simple/Service/Action/Parameter.html +816 -0
  13. data/doc/Simple/Service/Action.html +923 -0
  14. data/doc/Simple/Service/ArgumentError.html +128 -0
  15. data/doc/Simple/Service/ClassMethods.html +187 -0
  16. data/doc/Simple/Service/Context.html +379 -0
  17. data/doc/Simple/Service/ContextMissingError.html +124 -0
  18. data/doc/Simple/Service/ContextReadOnlyError.html +206 -0
  19. data/doc/Simple/Service/ExtraArguments.html +428 -0
  20. data/doc/Simple/Service/GemHelper.html +190 -0
  21. data/doc/Simple/Service/MissingArguments.html +426 -0
  22. data/doc/Simple/Service/NoSuchAction.html +433 -0
  23. data/doc/Simple/Service.html +865 -0
  24. data/doc/Simple.html +117 -0
  25. data/doc/_index.html +274 -0
  26. data/doc/class_list.html +51 -0
  27. data/doc/css/common.css +1 -0
  28. data/doc/css/full_list.css +58 -0
  29. data/doc/css/style.css +496 -0
  30. data/doc/file.README.html +146 -0
  31. data/doc/file.TODO.html +70 -0
  32. data/doc/file_list.html +61 -0
  33. data/doc/frames.html +17 -0
  34. data/doc/index.html +146 -0
  35. data/doc/js/app.js +303 -0
  36. data/doc/js/full_list.js +216 -0
  37. data/doc/js/jquery.js +4 -0
  38. data/doc/method_list.html +483 -0
  39. data/doc/top-level-namespace.html +110 -0
  40. data/lib/simple/service/action/comment.rb +2 -2
  41. data/lib/simple/service/action/method_reflection.rb +1 -1
  42. data/lib/simple/service/action/parameter.rb +1 -1
  43. data/lib/simple/service/action.rb +34 -46
  44. data/lib/simple/service/errors.rb +4 -3
  45. data/lib/simple/service/version.rb +2 -2
  46. data/lib/simple/service.rb +109 -34
  47. data/lib/simple/workflow/context.rb +105 -0
  48. data/lib/simple/workflow/current_context.rb +33 -0
  49. data/lib/simple/workflow/reloader.rb +84 -0
  50. data/lib/simple/workflow/rspec_helper.rb +15 -0
  51. data/lib/simple/workflow.rb +96 -0
  52. data/lib/simple-workflow.rb +3 -0
  53. data/scripts/test +2 -0
  54. data/simple-service.gemspec +1 -0
  55. data/spec/simple/service/action_invoke3_spec.rb +258 -0
  56. data/spec/simple/service/action_invoke_spec.rb +49 -87
  57. data/spec/simple/service/service_spec.rb +40 -32
  58. data/spec/simple/workflow/context_spec.rb +90 -0
  59. data/spec/simple/workflow/current_context_spec.rb +41 -0
  60. data/spec/simple/workflow/reloader_spec/example1.rb +10 -0
  61. data/spec/simple/workflow/reloader_spec/example2.rb +7 -0
  62. data/spec/simple/workflow/reloader_spec.rb +48 -0
  63. data/spec/spec_helper.rb +2 -1
  64. data/spec/support/spec_services.rb +8 -2
  65. metadata +74 -9
  66. data/lib/simple/service/action/indie_hash.rb +0 -37
  67. data/lib/simple/service/context.rb +0 -94
  68. data/spec/simple/service/action_invoke2_spec.rb +0 -166
  69. data/spec/simple/service/context_spec.rb +0 -69
@@ -1,15 +1,11 @@
1
1
  require "spec_helper"
2
2
 
3
+ # rubocop:disable Style/WordArray
4
+
3
5
  describe "Simple::Service" do
4
6
  context "when running against a NoService module" do
5
7
  let(:service) { NoServiceModule }
6
8
 
7
- describe ".service?" do
8
- it "returns false on a NoService module" do
9
- expect(Simple::Service.service?(service)).to eq(false)
10
- end
11
- end
12
-
13
9
  describe ".actions" do
14
10
  it "raises an argument error" do
15
11
  expect { Simple::Service.actions(service) }.to raise_error(ArgumentError)
@@ -22,11 +18,9 @@ describe "Simple::Service" do
22
18
  end
23
19
  end
24
20
 
25
- describe ".invoke" do
21
+ describe ".invoke3" do
26
22
  it "raises an argument error" do
27
- ::Simple::Service.with_context do
28
- expect { Simple::Service.invoke(service, :service1, {}, {}, context: nil) }.to raise_error(::ArgumentError)
29
- end
23
+ expect { Simple::Service.invoke3(service, :service1, {}, {}, context: nil) }.to raise_error(::ArgumentError)
30
24
  end
31
25
  end
32
26
  end
@@ -34,12 +28,6 @@ describe "Simple::Service" do
34
28
  # running against a proper service module
35
29
  let(:service) { SpecService }
36
30
 
37
- describe ".service?" do
38
- it "returns true" do
39
- expect(Simple::Service.service?(service)).to eq(true)
40
- end
41
- end
42
-
43
31
  describe ".actions" do
44
32
  it "returns a Hash of actions on a Service module" do
45
33
  actions = Simple::Service.actions(service)
@@ -76,30 +64,50 @@ describe "Simple::Service" do
76
64
  end
77
65
  end
78
66
 
67
+ describe ".invoke3" do
68
+ def invoke3
69
+ Simple::Service.invoke3(service, :service1, "my_a", "my_b", d: "my_d")
70
+ end
71
+
72
+ it "calls Action#invoke with the right arguments" do
73
+ action = Simple::Service.actions(service)[:service1]
74
+ expect(action).to receive(:invoke).with(args: ["my_a", "my_b"], flags: { "d" => "my_d" })
75
+
76
+ invoke3
77
+ end
78
+ end
79
+
79
80
  describe ".invoke" do
80
- let(:positionals) { { a: "my_a", b: "my_b" } }
81
- let(:named) { { d: "my_d" } }
81
+ context "with a args array" do
82
+ def invoke
83
+ Simple::Service.invoke(service, :service1, args: ["my_a", "my_b"], flags: { "d" => "my_d" })
84
+ end
82
85
 
83
- context "when context is not set" do
84
- it "raises a ContextMissingError" do
86
+ it "calls Action#invoke with the right arguments" do
85
87
  action = Simple::Service.actions(service)[:service1]
86
- expect(action).not_to receive(:invoke)
88
+ expect(action).to receive(:invoke).with(args: ["my_a", "my_b"], flags: { "d" => "my_d" }).and_call_original
87
89
 
88
- expect do
89
- Simple::Service.invoke(service, :service1, *positionals, named_args: named)
90
- end.to raise_error(::Simple::Service::ContextMissingError)
90
+ invoke
91
91
  end
92
92
  end
93
+ end
93
94
 
94
- context "when context is not set" do
95
- it "properly delegates call to action object" do
96
- action = Simple::Service.actions(service)[:service1]
97
- expect(action).to receive(:invoke).with(*positionals, named_args: named)
95
+ describe "documentation example" do
96
+ def invoke(*args, **flags)
97
+ Simple::Service.invoke(SpecTestService, :foo, *args, **flags)
98
+ end
98
99
 
99
- ::Simple::Service.with_context do
100
- Simple::Service.invoke(service, :service1, *positionals, named_args: named)
101
- end
102
- end
100
+ def invoke3(*args, **flags)
101
+ Simple::Service.invoke3(SpecTestService, :foo, *args, **flags)
102
+ end
103
+
104
+ it "calls Action#invoke with the right arguments" do
105
+ expected = ["bar-value", "baz-value"]
106
+
107
+ expect(invoke3("bar-value", baz: "baz-value")).to eq(expected)
108
+ expect(invoke3(bar: "bar-value", baz: "baz-value")).to eq(expected)
109
+ expect(invoke(args: ["bar-value"], flags: { "baz" => "baz-value" })).to eq(expected)
110
+ expect(invoke(args: { "bar" => "bar-value", "baz" => "baz-value" })).to eq(expected)
103
111
  end
104
112
  end
105
113
  end
@@ -0,0 +1,90 @@
1
+ require "spec_helper"
2
+
3
+ describe Simple::Workflow::Context do
4
+ RSpec.shared_examples "context requesting" do
5
+ it "inherits from Simple::Immutable" do
6
+ expect(context).to be_a(Simple::Immutable)
7
+ end
8
+
9
+ describe "checking for identifier" do
10
+ it "raises an ArgumentError if the key is invalid" do
11
+ expect { context.oneTwoThree? }.to raise_error(ArgumentError)
12
+ end
13
+
14
+ it "returns nil if the key is not set" do
15
+ expect(context.two?).to be_nil
16
+ end
17
+
18
+ it "returns the value if the key is set" do
19
+ expect(context.one?).to eq 1
20
+ end
21
+ end
22
+
23
+ describe "fetching identifier" do
24
+ it "raises an ArgumentError if the identifier is invalid" do
25
+ expect { context.one! }.to raise_error(ArgumentError)
26
+ expect { context.oneTwoThree }.to raise_error(ArgumentError)
27
+ end
28
+
29
+ it "raises a NoMethodError if the key is not set" do
30
+ expect { context.two }.to raise_error(NameError)
31
+ end
32
+
33
+ it "returns the value if the key is set" do
34
+ expect(context.one).to eq 1
35
+ end
36
+ end
37
+
38
+ describe "#merge" do
39
+ context "with symbolized keys" do
40
+ it "sets a value if it does not exist yet" do
41
+ expect(context.two?).to eq(nil)
42
+ new_context = Simple::Workflow::Context.new({two: 2}, context)
43
+ expect(new_context.two).to eq(2)
44
+
45
+ # doesn't change the source context
46
+ expect(context.two?).to eq(nil)
47
+ end
48
+
49
+ it "sets a value if it does exist" do
50
+ new_context = Simple::Workflow::Context.new({one: 2}, context)
51
+ expect(new_context.one).to eq(2)
52
+
53
+ # doesn't change the source context
54
+ expect(context.one).to eq(1)
55
+ end
56
+ end
57
+
58
+ context "with stringified keys" do
59
+ it "sets a value if it does not exist yet" do
60
+ expect(context.two?).to eq(nil)
61
+ new_context = Simple::Workflow::Context.new({"two" => 2}, context)
62
+ expect(new_context.two).to eq(2)
63
+
64
+ # doesn't change the source context
65
+ expect(context.two?).to eq(nil)
66
+ end
67
+
68
+ it "sets a value if it does exist" do
69
+ new_context = Simple::Workflow::Context.new({"one" => 2}, context)
70
+ expect(new_context.one).to eq(2)
71
+
72
+ # doesn't change the source context
73
+ expect(context.one).to eq(1)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ context "with a symbolized context" do
80
+ let(:context) { Simple::Workflow::Context.new(one: 1) }
81
+
82
+ it_behaves_like "context requesting"
83
+ end
84
+
85
+ context "with a stringified context" do
86
+ let(:context) { Simple::Workflow::Context.new("one" => 1) }
87
+
88
+ it_behaves_like "context requesting"
89
+ end
90
+ end
@@ -0,0 +1,41 @@
1
+ require "spec_helper"
2
+
3
+ describe Simple::Workflow::CurrentContext do
4
+ describe ".with_context" do
5
+ it "merges the current context for the duration of the block" do
6
+ block_called = false
7
+
8
+ Simple::Workflow.with_context(a: "a", foo: "bar") do
9
+ expect(Simple::Workflow.current_context.a).to eq("a")
10
+ expect(Simple::Workflow.current_context.foo).to eq("bar")
11
+
12
+ # layering
13
+ Simple::Workflow.with_context() do
14
+ expect(Simple::Workflow.current_context.foo).to eq("bar")
15
+
16
+ expect(Simple::Workflow.current_context.unknown?).to be_nil
17
+ expect {
18
+ Simple::Workflow.current_context.unknown
19
+ }.to raise_error(NameError)
20
+ end
21
+
22
+ # overwrite value
23
+ Simple::Workflow.with_context(a: "b") do
24
+ expect(Simple::Workflow.current_context.a).to eq("b")
25
+ block_called = true
26
+ end
27
+
28
+ # overwrite value w/nil
29
+ Simple::Workflow.with_context(a: nil) do
30
+ expect(Simple::Workflow.current_context.a).to be_nil
31
+ Simple::Workflow.with_context(a: "c") do
32
+ expect(Simple::Workflow.current_context.a).to eq("c")
33
+ end
34
+ end
35
+ expect(Simple::Workflow.current_context.a).to eq("a")
36
+ end
37
+
38
+ expect(block_called).to eq(true)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,10 @@
1
+ module ReloaderSpecExample1
2
+ def foo; end
3
+ end
4
+
5
+ module ReloaderSpecExample2
6
+ end
7
+
8
+ module ReloaderSpecExample3
9
+ def foo; end
10
+ end
@@ -0,0 +1,7 @@
1
+ module ReloaderSpecExample1
2
+ def self.bar; end
3
+ end
4
+
5
+ module ReloaderSpecExample2
6
+ def self.bar; end
7
+ end
@@ -0,0 +1,48 @@
1
+ require "spec_helper"
2
+
3
+ require_relative "reloader_spec/example1"
4
+ require_relative "reloader_spec/example2"
5
+
6
+ describe "Simple::Service::Reloader" do
7
+ describe ".locate" do
8
+ def locate(mod)
9
+ Simple::Workflow::Reloader.locate(mod)
10
+ end
11
+
12
+ it "Returns all source files of a module" do
13
+ root = Dir.getwd
14
+
15
+ expected = [
16
+ "#{root}/lib/simple/workflow/reloader.rb"
17
+ ]
18
+ expect(locate(Simple::Workflow::Reloader)).to contain_exactly(*expected)
19
+
20
+ expected = [
21
+ "#{__dir__}/reloader_spec/example1.rb",
22
+ "#{__dir__}/reloader_spec/example2.rb"
23
+ ]
24
+ expect(locate(ReloaderSpecExample1)).to contain_exactly(*expected)
25
+
26
+ expected = [
27
+ "#{__dir__}/reloader_spec/example2.rb"
28
+ ]
29
+ expect(locate(ReloaderSpecExample2)).to contain_exactly(*expected)
30
+
31
+ expected = [
32
+ "#{__dir__}/reloader_spec/example1.rb"
33
+ ]
34
+ expect(locate(ReloaderSpecExample3)).to contain_exactly(*expected)
35
+ end
36
+ end
37
+
38
+ describe ".reload" do
39
+ def reload(mod)
40
+ Simple::Workflow::Reloader.reload(mod)
41
+ end
42
+
43
+ it "Reloads a module" do
44
+ # [TODO] this doesn't really check reloading...
45
+ reload Simple::Workflow::Reloader
46
+ end
47
+ end
48
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  ENV["RACK_ENV"] = "test"
2
2
  ENV["RAILS_ENV"] = "test"
3
3
 
4
- require "byebug"
4
+ require "pry-byebug"
5
5
  require "rspec"
6
6
 
7
7
  require "simplecov"
@@ -18,6 +18,7 @@ SimpleCov.start do
18
18
  end
19
19
 
20
20
  require "simple/service"
21
+ require "simple/workflow"
21
22
 
22
23
  Dir.glob("./spec/support/**/*.rb").sort.each { |path| load path }
23
24
 
@@ -1,5 +1,3 @@
1
- # rubocop:disable Naming/UncommunicativeMethodParamName
2
-
3
1
  module NoServiceModule
4
2
  end
5
3
 
@@ -48,3 +46,11 @@ module InvokeTestService
48
46
  [a, b, args, e]
49
47
  end
50
48
  end
49
+
50
+ module SpecTestService
51
+ include Simple::Service
52
+
53
+ def foo(bar, baz:)
54
+ [bar, baz]
55
+ end
56
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - radiospiel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-29 00:00:00.000000000 Z
11
+ date: 2021-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: expectation
@@ -24,6 +24,26 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: simple-immutable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '1.1'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.1'
27
47
  description: Pretty simple and somewhat abstract service description
28
48
  email: eno@radiospiel.org
29
49
  executables: []
@@ -37,33 +57,74 @@ files:
37
57
  - Makefile
38
58
  - README.md
39
59
  - Rakefile
60
+ - TODO.txt
40
61
  - VERSION
41
62
  - bin/bundle
42
63
  - bin/console
43
64
  - bin/rake
44
65
  - bin/rspec
66
+ - doc/Simple.html
67
+ - doc/Simple/Service.html
68
+ - doc/Simple/Service/Action.html
69
+ - doc/Simple/Service/Action/Comment.html
70
+ - doc/Simple/Service/Action/Comment/Extractor.html
71
+ - doc/Simple/Service/Action/MethodReflection.html
72
+ - doc/Simple/Service/Action/Parameter.html
73
+ - doc/Simple/Service/ArgumentError.html
74
+ - doc/Simple/Service/ClassMethods.html
75
+ - doc/Simple/Service/Context.html
76
+ - doc/Simple/Service/ContextMissingError.html
77
+ - doc/Simple/Service/ContextReadOnlyError.html
78
+ - doc/Simple/Service/ExtraArguments.html
79
+ - doc/Simple/Service/GemHelper.html
80
+ - doc/Simple/Service/MissingArguments.html
81
+ - doc/Simple/Service/NoSuchAction.html
82
+ - doc/_index.html
83
+ - doc/class_list.html
84
+ - doc/css/common.css
85
+ - doc/css/full_list.css
86
+ - doc/css/style.css
87
+ - doc/file.README.html
88
+ - doc/file.TODO.html
89
+ - doc/file_list.html
90
+ - doc/frames.html
91
+ - doc/index.html
92
+ - doc/js/app.js
93
+ - doc/js/full_list.js
94
+ - doc/js/jquery.js
95
+ - doc/method_list.html
96
+ - doc/top-level-namespace.html
45
97
  - lib/simple-service.rb
98
+ - lib/simple-workflow.rb
46
99
  - lib/simple/service.rb
47
100
  - lib/simple/service/action.rb
48
101
  - lib/simple/service/action/comment.rb
49
- - lib/simple/service/action/indie_hash.rb
50
102
  - lib/simple/service/action/method_reflection.rb
51
103
  - lib/simple/service/action/parameter.rb
52
- - lib/simple/service/context.rb
53
104
  - lib/simple/service/errors.rb
54
105
  - lib/simple/service/version.rb
106
+ - lib/simple/workflow.rb
107
+ - lib/simple/workflow/context.rb
108
+ - lib/simple/workflow/current_context.rb
109
+ - lib/simple/workflow/reloader.rb
110
+ - lib/simple/workflow/rspec_helper.rb
55
111
  - log/.gitkeep
56
112
  - scripts/release
57
113
  - scripts/release.rb
58
114
  - scripts/stats
115
+ - scripts/test
59
116
  - scripts/watch
60
117
  - simple-service.gemspec
61
- - spec/simple/service/action_invoke2_spec.rb
118
+ - spec/simple/service/action_invoke3_spec.rb
62
119
  - spec/simple/service/action_invoke_spec.rb
63
120
  - spec/simple/service/action_spec.rb
64
- - spec/simple/service/context_spec.rb
65
121
  - spec/simple/service/service_spec.rb
66
122
  - spec/simple/service/version_spec.rb
123
+ - spec/simple/workflow/context_spec.rb
124
+ - spec/simple/workflow/current_context_spec.rb
125
+ - spec/simple/workflow/reloader_spec.rb
126
+ - spec/simple/workflow/reloader_spec/example1.rb
127
+ - spec/simple/workflow/reloader_spec/example2.rb
67
128
  - spec/spec_helper.rb
68
129
  - spec/support/spec_services.rb
69
130
  homepage: http://github.com/radiospiel/simple-service
@@ -84,16 +145,20 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
145
  - !ruby/object:Gem::Version
85
146
  version: '0'
86
147
  requirements: []
87
- rubygems_version: 3.0.4
148
+ rubygems_version: 3.1.4
88
149
  signing_key:
89
150
  specification_version: 4
90
151
  summary: Pretty simple and somewhat abstract service description
91
152
  test_files:
92
- - spec/simple/service/action_invoke2_spec.rb
153
+ - spec/simple/service/action_invoke3_spec.rb
93
154
  - spec/simple/service/action_invoke_spec.rb
94
155
  - spec/simple/service/action_spec.rb
95
- - spec/simple/service/context_spec.rb
96
156
  - spec/simple/service/service_spec.rb
97
157
  - spec/simple/service/version_spec.rb
158
+ - spec/simple/workflow/context_spec.rb
159
+ - spec/simple/workflow/current_context_spec.rb
160
+ - spec/simple/workflow/reloader_spec.rb
161
+ - spec/simple/workflow/reloader_spec/example1.rb
162
+ - spec/simple/workflow/reloader_spec/example2.rb
98
163
  - spec/spec_helper.rb
99
164
  - spec/support/spec_services.rb
@@ -1,37 +0,0 @@
1
- class Simple::Service::Action
2
- # The IndieHash class defines as much of the Hash interface as necessary for simple-service
3
- # to successfully run.
4
- class IndieHash
5
- def initialize(hsh)
6
- @hsh = hsh.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
7
- end
8
-
9
- def keys
10
- @hsh.keys
11
- end
12
-
13
- def fetch_values(*keys)
14
- keys = keys.map(&:to_s)
15
- @hsh.fetch_values(*keys)
16
- end
17
-
18
- def key?(sym)
19
- @hsh.key?(sym.to_s)
20
- end
21
-
22
- def [](sym)
23
- @hsh[sym.to_s]
24
- end
25
-
26
- def merge(other_hsh)
27
- @hsh = @hsh.merge(other_hsh.send(:__hsh__))
28
- self
29
- end
30
-
31
- private
32
-
33
- def __hsh__
34
- @hsh
35
- end
36
- end
37
- end
@@ -1,94 +0,0 @@
1
- module Simple::Service
2
- # Returns the current context.
3
- def self.context
4
- Thread.current[:"Simple::Service.context"]
5
- end
6
-
7
- # yields a block with a given context, and restores the previous context
8
- # object afterwards.
9
- def self.with_context(ctx = nil, &block)
10
- old_ctx = Thread.current[:"Simple::Service.context"]
11
- new_ctx = old_ctx ? old_ctx.merge(ctx) : Context.new(ctx)
12
-
13
- Thread.current[:"Simple::Service.context"] = new_ctx
14
-
15
- block.call
16
- ensure
17
- Thread.current[:"Simple::Service.context"] = old_ctx
18
- end
19
- end
20
-
21
- module Simple::Service
22
- # A context object
23
- #
24
- # Each service executes with a current context. The system manages a stack of
25
- # contexts; whenever a service execution is done the current context is reverted
26
- # to its previous value.
27
- #
28
- # A context object can store a large number of values; the only way to set or
29
- # access a value is via getters and setters. These are implemented via
30
- # +method_missing(..)+.
31
- #
32
- # Also, once a value is set in the context it is not possible to change or
33
- # unset it.
34
- class Context
35
- def initialize(hsh = {}) # :nodoc:
36
- @hsh = hsh
37
- end
38
-
39
- # returns a new Context object, which merges the values in the +overlay+
40
- # argument (which must be a Hash or nil) with the values in this context.
41
- #
42
- # The overlay is allowed to change values in the current context.
43
- #
44
- # It does not change this context.
45
- def merge(overlay)
46
- expect! overlay => [Hash, nil]
47
-
48
- overlay ||= {}
49
- new_context_hsh = @hsh.merge(overlay)
50
- ::Simple::Service::Context.new(new_context_hsh)
51
- end
52
-
53
- private
54
-
55
- IDENTIFIER_PATTERN = "[a-z][a-z0-9_]*" # :nodoc:
56
- IDENTIFIER_REGEXP = Regexp.compile("\\A#{IDENTIFIER_PATTERN}\\z") # :nodoc:
57
- ASSIGNMENT_REGEXP = Regexp.compile("\\A(#{IDENTIFIER_PATTERN})=\\z") # :nodoc:
58
-
59
- def method_missing(sym, *args, &block)
60
- raise ArgumentError, "Block given" if block
61
-
62
- if args.count == 0 && sym =~ IDENTIFIER_REGEXP
63
- self[sym]
64
- elsif args.count == 1 && sym =~ ASSIGNMENT_REGEXP
65
- self[$1.to_sym] = args.first
66
- else
67
- super
68
- end
69
- end
70
-
71
- def respond_to_missing?(sym, include_private = false)
72
- # :nocov:
73
- return true if IDENTIFIER_REGEXP.match?(sym)
74
- return true if ASSIGNMENT_REGEXP.match?(sym)
75
-
76
- super
77
- # :nocov:
78
- end
79
-
80
- def [](key)
81
- @hsh[key]
82
- end
83
-
84
- def []=(key, value)
85
- existing_value = @hsh[key]
86
-
87
- unless existing_value.nil? || existing_value == value
88
- raise ::Simple::Service::ContextReadOnlyError, key
89
- end
90
-
91
- @hsh[key] = value
92
- end
93
- end
94
- end