standard-procedure-plumbing 0.3.2 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +135 -93
- data/lib/plumbing/actor/async.rb +38 -0
- data/lib/plumbing/actor/inline.rb +22 -0
- data/lib/plumbing/actor/kernel.rb +9 -0
- data/lib/plumbing/actor/rails.rb +15 -0
- data/lib/plumbing/actor/threaded.rb +62 -0
- data/lib/plumbing/actor/transporter.rb +61 -0
- data/lib/plumbing/actor.rb +63 -0
- data/lib/plumbing/config.rb +8 -8
- data/lib/plumbing/pipe.rb +2 -3
- data/lib/plumbing/rubber_duck/module.rb +13 -0
- data/lib/plumbing/rubber_duck/object.rb +3 -2
- data/lib/plumbing/rubber_duck.rb +14 -4
- data/lib/plumbing/version.rb +1 -1
- data/lib/plumbing.rb +1 -1
- data/spec/examples/actor_spec.rb +81 -0
- data/spec/examples/pipe_spec.rb +4 -4
- data/spec/examples/rubber_duck_spec.rb +93 -10
- data/spec/plumbing/a_pipe.rb +11 -9
- data/spec/plumbing/actor/transporter_spec.rb +158 -0
- data/spec/plumbing/actor_spec.rb +208 -0
- data/spec/plumbing/pipe_spec.rb +8 -0
- data/spec/plumbing/rubber_duck_spec.rb +46 -48
- metadata +29 -10
- data/lib/plumbing/valve/async.rb +0 -43
- data/lib/plumbing/valve/inline.rb +0 -20
- data/lib/plumbing/valve/message.rb +0 -5
- data/lib/plumbing/valve.rb +0 -71
- data/spec/examples/valve_spec.rb +0 -88
- data/spec/plumbing/valve_spec.rb +0 -167
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative "actor/kernel"
|
2
|
+
require_relative "actor/inline"
|
3
|
+
|
4
|
+
module Plumbing
|
5
|
+
module Actor
|
6
|
+
def self.included base
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
# Create a new actor instance and build a proxy for it using the current mode
|
12
|
+
# @return [Object] the proxy for the actor instance
|
13
|
+
def start(*, **, &)
|
14
|
+
build_proxy_for(new(*, **, &))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Define the async messages that this actor can respond to
|
18
|
+
# @param names [Array<Symbol>] the names of the async messages
|
19
|
+
def async(*names) = async_messages.concat(names.map(&:to_sym))
|
20
|
+
|
21
|
+
# List the async messages that this actor can respond to
|
22
|
+
def async_messages = @async_messages ||= []
|
23
|
+
|
24
|
+
def inherited subclass
|
25
|
+
subclass.async_messages.concat async_messages
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def build_proxy_for(target)
|
31
|
+
proxy_class_for(target.class).new(target)
|
32
|
+
end
|
33
|
+
|
34
|
+
def proxy_class_for target_class
|
35
|
+
Plumbing.config.actor_proxy_class_for(target_class) || register_actor_proxy_class_for(target_class)
|
36
|
+
end
|
37
|
+
|
38
|
+
def proxy_base_class = const_get PROXY_BASE_CLASSES[Plumbing.config.mode]
|
39
|
+
|
40
|
+
PROXY_BASE_CLASSES = {
|
41
|
+
inline: "Plumbing::Actor::Inline",
|
42
|
+
async: "Plumbing::Actor::Async",
|
43
|
+
threaded: "Plumbing::Actor::Threaded",
|
44
|
+
rails: "Plumbing::Actor::Rails"
|
45
|
+
}.freeze
|
46
|
+
private_constant :PROXY_BASE_CLASSES
|
47
|
+
|
48
|
+
def register_actor_proxy_class_for target_class
|
49
|
+
Plumbing.config.register_actor_proxy_class_for(target_class, build_proxy_class)
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_proxy_class
|
53
|
+
Class.new(proxy_base_class).tap do |proxy_class|
|
54
|
+
async_messages.each do |message|
|
55
|
+
proxy_class.define_method message do |*args, &block|
|
56
|
+
send_message(message, *args, &block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/plumbing/config.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# Pipes, pipelines,
|
1
|
+
# Pipes, pipelines, actors and rubber ducks
|
2
2
|
module Plumbing
|
3
|
-
Config = Data.define :mode, :
|
4
|
-
def
|
5
|
-
|
3
|
+
Config = Data.define :mode, :actor_proxy_classes, :timeout do
|
4
|
+
def actor_proxy_class_for target_class
|
5
|
+
actor_proxy_classes[target_class]
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def register_actor_proxy_class_for target_class, proxy_class
|
9
|
+
actor_proxy_classes[target_class] = proxy_class
|
10
10
|
end
|
11
11
|
end
|
12
12
|
private_constant :Config
|
@@ -23,7 +23,7 @@ module Plumbing
|
|
23
23
|
# @option timeout [Integer] the timeout (in seconds) to use (30s is the default)
|
24
24
|
# @yield optional block - after the block has completed its execution, the configuration is restored to its previous state (useful for test suites)
|
25
25
|
def self.configure(**params, &block)
|
26
|
-
new_config = Config.new(**config.to_h.merge(params).merge(
|
26
|
+
new_config = Config.new(**config.to_h.merge(params).merge(actor_proxy_classes: {}))
|
27
27
|
if block.nil?
|
28
28
|
set_configuration_to new_config
|
29
29
|
else
|
@@ -45,7 +45,7 @@ module Plumbing
|
|
45
45
|
private_class_method :set_configuration_and_yield
|
46
46
|
|
47
47
|
def self.configs
|
48
|
-
@configs ||= [Config.new(mode: :inline, timeout: 30,
|
48
|
+
@configs ||= [Config.new(mode: :inline, timeout: 30, actor_proxy_classes: {})]
|
49
49
|
end
|
50
50
|
private_class_method :configs
|
51
51
|
end
|
data/lib/plumbing/pipe.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
module Plumbing
|
2
2
|
# A basic pipe
|
3
3
|
class Pipe
|
4
|
-
include Plumbing::
|
4
|
+
include Plumbing::Actor
|
5
5
|
|
6
|
-
|
7
|
-
query :add_observer, :is_observer?
|
6
|
+
async :notify, :<<, :remove_observer, :add_observer, :is_observer?, :shutdown
|
8
7
|
|
9
8
|
# Push an event into the pipe
|
10
9
|
# @param event [Plumbing::Event] the event to push into the pipe
|
@@ -2,9 +2,10 @@ module Plumbing
|
|
2
2
|
class RubberDuck
|
3
3
|
::Object.class_eval do
|
4
4
|
# Cast the object to a duck-type
|
5
|
+
# @param type [Plumbing::RubberDuck, Module]
|
5
6
|
# @return [Plumbing::RubberDuck::Proxy] the duck-type proxy
|
6
|
-
def as
|
7
|
-
|
7
|
+
def as type
|
8
|
+
Plumbing::RubberDuck.cast self, type: type
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/lib/plumbing/rubber_duck.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module Plumbing
|
2
2
|
# A type-checker for duck-types
|
3
3
|
class RubberDuck
|
4
|
+
require_relative "rubber_duck/module"
|
4
5
|
require_relative "rubber_duck/object"
|
5
6
|
require_relative "rubber_duck/proxy"
|
6
7
|
|
@@ -26,10 +27,19 @@ module Plumbing
|
|
26
27
|
is_a_proxy?(object) || build_proxy_for(object)
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
class << self
|
31
|
+
# Define a new rubber duck type
|
32
|
+
# @param *methods [Array<Symbol>] the methods that the duck-type should respond to
|
33
|
+
def define *methods
|
34
|
+
new(*methods)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Cast the object to the given type
|
38
|
+
# @param object [Object] to be csat
|
39
|
+
# @param to [Module, Plumbing::RubberDuck] the type to cast into
|
40
|
+
def cast object, type:
|
41
|
+
type.proxy_for object
|
42
|
+
end
|
33
43
|
end
|
34
44
|
|
35
45
|
private
|
data/lib/plumbing/version.rb
CHANGED
data/lib/plumbing.rb
CHANGED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.shared_examples "an example actor" do |runs_in_background|
|
4
|
+
it "queries an object" do
|
5
|
+
@person = Employee.start "Alice"
|
6
|
+
|
7
|
+
expect(await { @person.name }).to eq "Alice"
|
8
|
+
expect(await { @person.job_title }).to eq "Sales assistant"
|
9
|
+
|
10
|
+
@time = Time.now
|
11
|
+
# `greet_slowly` is a query so will block until a response is received
|
12
|
+
expect(await { @person.greet_slowly }).to eq "H E L L O"
|
13
|
+
expect(Time.now - @time).to be > 0.1
|
14
|
+
|
15
|
+
@time = Time.now
|
16
|
+
# we're not awaiting the result, so this should run in the background (unless we're using inline mode)
|
17
|
+
@person.greet_slowly
|
18
|
+
|
19
|
+
expect(Time.now - @time).to be < 0.1 if runs_in_background
|
20
|
+
expect(Time.now - @time).to be > 0.1 if !runs_in_background
|
21
|
+
end
|
22
|
+
|
23
|
+
it "commands an object" do
|
24
|
+
@person = Employee.start "Alice"
|
25
|
+
@person.promote
|
26
|
+
@job_title = await { @person.job_title }
|
27
|
+
expect(@job_title).to eq "Sales manager"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.describe "Actor example: " do
|
32
|
+
# standard:disable Lint/ConstantDefinitionInBlock
|
33
|
+
class Employee
|
34
|
+
include Plumbing::Actor
|
35
|
+
async :name, :job_title, :greet_slowly, :promote
|
36
|
+
|
37
|
+
def initialize(name)
|
38
|
+
@name = name
|
39
|
+
@job_title = "Sales assistant"
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :name, :job_title
|
43
|
+
|
44
|
+
def promote
|
45
|
+
sleep 0.5
|
46
|
+
@job_title = "Sales manager"
|
47
|
+
end
|
48
|
+
|
49
|
+
def greet_slowly
|
50
|
+
sleep 0.2
|
51
|
+
"H E L L O"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# standard:enable Lint/ConstantDefinitionInBlock
|
55
|
+
|
56
|
+
context "inline mode" do
|
57
|
+
around :example do |example|
|
58
|
+
Plumbing.configure mode: :inline, &example
|
59
|
+
end
|
60
|
+
|
61
|
+
it_behaves_like "an example actor", false
|
62
|
+
end
|
63
|
+
|
64
|
+
context "async mode" do
|
65
|
+
around :example do |example|
|
66
|
+
Plumbing.configure mode: :async do
|
67
|
+
Kernel::Async(&example)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it_behaves_like "an example actor", true
|
72
|
+
end
|
73
|
+
|
74
|
+
context "threaded mode" do
|
75
|
+
around :example do |example|
|
76
|
+
Plumbing.configure mode: :threaded, &example
|
77
|
+
end
|
78
|
+
|
79
|
+
it_behaves_like "an example actor", true
|
80
|
+
end
|
81
|
+
end
|
data/spec/examples/pipe_spec.rb
CHANGED
@@ -6,7 +6,7 @@ RSpec.describe "Pipe examples" do
|
|
6
6
|
@source = Plumbing::Pipe.start
|
7
7
|
|
8
8
|
@result = []
|
9
|
-
@
|
9
|
+
@source.add_observer do |event|
|
10
10
|
@result << event.type
|
11
11
|
end
|
12
12
|
|
@@ -22,7 +22,7 @@ RSpec.describe "Pipe examples" do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
@result = []
|
25
|
-
@
|
25
|
+
@filter.add_observer do |event|
|
26
26
|
@result << event.type
|
27
27
|
end
|
28
28
|
|
@@ -55,7 +55,7 @@ RSpec.describe "Pipe examples" do
|
|
55
55
|
@filter = EveryThirdEvent.new(source: @source)
|
56
56
|
|
57
57
|
@result = []
|
58
|
-
@
|
58
|
+
@filter.add_observer do |event|
|
59
59
|
@result << event.type
|
60
60
|
end
|
61
61
|
|
@@ -73,7 +73,7 @@ RSpec.describe "Pipe examples" do
|
|
73
73
|
@junction = Plumbing::Junction.start @first_source, @second_source
|
74
74
|
|
75
75
|
@result = []
|
76
|
-
@
|
76
|
+
@junction.add_observer do |event|
|
77
77
|
@result << event.type
|
78
78
|
end
|
79
79
|
|
@@ -1,26 +1,109 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
RSpec.describe "Rubber Duck examples" do
|
4
|
-
it "casts objects
|
4
|
+
it "casts objects into duck types" do
|
5
5
|
# standard:disable Lint/ConstantDefinitionInBlock
|
6
|
-
|
7
|
-
|
6
|
+
module DuckExample
|
7
|
+
Person = Plumbing::RubberDuck.define :first_name, :last_name, :email
|
8
|
+
LikesFood = Plumbing::RubberDuck.define :favourite_food
|
9
|
+
|
10
|
+
PersonData = Struct.new(:first_name, :last_name, :email, :favourite_food)
|
11
|
+
CarData = Struct.new(:make, :model, :colour)
|
12
|
+
end
|
8
13
|
|
9
|
-
PersonData = Struct.new(:first_name, :last_name, :email, :favourite_food)
|
10
|
-
CarData = Struct.new(:make, :model, :colour)
|
11
14
|
# standard:enable Lint/ConstantDefinitionInBlock
|
12
15
|
|
13
|
-
@porsche_911 = CarData.new "Porsche", "911", "black"
|
14
|
-
expect { @porsche_911.as Person }.to raise_error(TypeError)
|
16
|
+
@porsche_911 = DuckExample::CarData.new "Porsche", "911", "black"
|
17
|
+
expect { @porsche_911.as DuckExample::Person }.to raise_error(TypeError)
|
18
|
+
|
19
|
+
@alice = DuckExample::PersonData.new "Alice", "Aardvark", "alice@example.com", "Ice cream"
|
20
|
+
|
21
|
+
@person = @alice.as DuckExample::Person
|
22
|
+
expect(@person.first_name).to eq "Alice"
|
23
|
+
expect(@person.email).to eq "alice@example.com"
|
24
|
+
expect { @person.favourite_food }.to raise_error(NoMethodError)
|
25
|
+
|
26
|
+
@hungry = @person.as DuckExample::LikesFood
|
27
|
+
expect(@hungry.favourite_food).to eq "Ice cream"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "casts objects into modules" do
|
31
|
+
# standard:disable Lint/ConstantDefinitionInBlock
|
32
|
+
module ModuleExample
|
33
|
+
module Person
|
34
|
+
def first_name = @first_name
|
35
|
+
|
36
|
+
def last_name = @last_name
|
37
|
+
|
38
|
+
def email = @email
|
39
|
+
end
|
40
|
+
|
41
|
+
module LikesFood
|
42
|
+
def favourite_food = @favourite_food
|
43
|
+
end
|
44
|
+
|
45
|
+
PersonData = Struct.new(:first_name, :last_name, :email, :favourite_food)
|
46
|
+
CarData = Struct.new(:make, :model, :colour)
|
47
|
+
end
|
48
|
+
# standard:enable Lint/ConstantDefinitionInBlock
|
49
|
+
@porsche_911 = ModuleExample::CarData.new "Porsche", "911", "black"
|
50
|
+
expect { @porsche_911.as ModuleExample::Person }.to raise_error(TypeError)
|
51
|
+
|
52
|
+
@alice = ModuleExample::PersonData.new "Alice", "Aardvark", "alice@example.com", "Ice cream"
|
53
|
+
|
54
|
+
@person = @alice.as ModuleExample::Person
|
55
|
+
expect(@person.first_name).to eq "Alice"
|
56
|
+
expect(@person.email).to eq "alice@example.com"
|
57
|
+
expect { @person.favourite_food }.to raise_error(NoMethodError)
|
58
|
+
|
59
|
+
@hungry = @person.as ModuleExample::LikesFood
|
60
|
+
expect(@hungry.favourite_food).to eq "Ice cream"
|
61
|
+
end
|
62
|
+
|
63
|
+
it "casts objects into clases" do
|
64
|
+
# standard:disable Lint/ConstantDefinitionInBlock
|
65
|
+
module ClassExample
|
66
|
+
class Person
|
67
|
+
def initialize first_name, last_name, email
|
68
|
+
@first_name = first_name
|
69
|
+
@last_name = last_name
|
70
|
+
@email = email
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :first_name
|
74
|
+
attr_reader :last_name
|
75
|
+
attr_reader :email
|
76
|
+
end
|
77
|
+
|
78
|
+
class PersonWhoLikesFood < Person
|
79
|
+
def initialize first_name, last_name, email, favourite_food
|
80
|
+
super(first_name, last_name, email)
|
81
|
+
@favourite_food = favourite_food
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :favourite_food
|
85
|
+
end
|
86
|
+
|
87
|
+
class CarData
|
88
|
+
def initialize make, model, colour
|
89
|
+
@make = make
|
90
|
+
@model = model
|
91
|
+
@colour = colour
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
# standard:enable Lint/ConstantDefinitionInBlock
|
96
|
+
@porsche_911 = ClassExample::CarData.new "Porsche", "911", "black"
|
97
|
+
expect { @porsche_911.as ClassExample::Person }.to raise_error(TypeError)
|
15
98
|
|
16
|
-
@alice =
|
99
|
+
@alice = ClassExample::PersonWhoLikesFood.new "Alice", "Aardvark", "alice@example.com", "Ice cream"
|
17
100
|
|
18
|
-
@person = @alice.as Person
|
101
|
+
@person = @alice.as ClassExample::Person
|
19
102
|
expect(@person.first_name).to eq "Alice"
|
20
103
|
expect(@person.email).to eq "alice@example.com"
|
21
104
|
expect { @person.favourite_food }.to raise_error(NoMethodError)
|
22
105
|
|
23
|
-
@hungry = @person.as
|
106
|
+
@hungry = @person.as ClassExample::PersonWhoLikesFood
|
24
107
|
expect(@hungry.favourite_food).to eq "Ice cream"
|
25
108
|
end
|
26
109
|
end
|
data/spec/plumbing/a_pipe.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
RSpec.shared_examples "a pipe" do
|
2
2
|
it "adds a block observer" do
|
3
3
|
@pipe = described_class.start
|
4
|
-
@observer =
|
5
|
-
|
4
|
+
@observer = await do
|
5
|
+
@pipe.add_observer do |event|
|
6
|
+
puts event.type
|
7
|
+
end
|
6
8
|
end
|
7
|
-
expect(@pipe.is_observer?(@observer)).to eq true
|
9
|
+
expect(await { @pipe.is_observer?(@observer) }).to eq true
|
8
10
|
end
|
9
11
|
|
10
12
|
it "adds a callable observer" do
|
@@ -13,13 +15,13 @@ RSpec.shared_examples "a pipe" do
|
|
13
15
|
|
14
16
|
@pipe.add_observer @proc
|
15
17
|
|
16
|
-
expect(@pipe.is_observer?(@proc)).to eq true
|
18
|
+
expect(await { @pipe.is_observer?(@proc) }).to eq true
|
17
19
|
end
|
18
20
|
|
19
21
|
it "does not allow an observer without a #call method" do
|
20
22
|
@pipe = described_class.start
|
21
23
|
|
22
|
-
expect { @pipe.add_observer(Object.new) }.to raise_error(TypeError)
|
24
|
+
expect { await { @pipe.add_observer(Object.new) } }.to raise_error(TypeError)
|
23
25
|
end
|
24
26
|
|
25
27
|
it "removes an observer" do
|
@@ -28,10 +30,10 @@ RSpec.shared_examples "a pipe" do
|
|
28
30
|
|
29
31
|
@pipe.remove_observer @proc
|
30
32
|
|
31
|
-
expect(@pipe.is_observer?(@proc)).to eq false
|
33
|
+
expect(await { @pipe.is_observer?(@proc) }).to eq false
|
32
34
|
end
|
33
35
|
|
34
|
-
it "does not send notifications for objects
|
36
|
+
it "does not send notifications for objects which are not events" do
|
35
37
|
@pipe = described_class.start
|
36
38
|
@results = []
|
37
39
|
@observer = @pipe.add_observer do |event|
|
@@ -40,7 +42,7 @@ RSpec.shared_examples "a pipe" do
|
|
40
42
|
|
41
43
|
@pipe << Object.new
|
42
44
|
|
43
|
-
sleep 0.
|
45
|
+
sleep 0.1
|
44
46
|
expect(@results).to eq []
|
45
47
|
end
|
46
48
|
|
@@ -101,6 +103,6 @@ RSpec.shared_examples "a pipe" do
|
|
101
103
|
|
102
104
|
@pipe.shutdown
|
103
105
|
|
104
|
-
expect(@pipe.is_observer?(@observer)).to eq false
|
106
|
+
expect(await { @pipe.is_observer?(@observer) }).to eq false
|
105
107
|
end
|
106
108
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../../lib/plumbing/actor/transporter"
|
3
|
+
|
4
|
+
RSpec.describe Plumbing::Actor::Transporter do
|
5
|
+
# standard:disable Lint/ConstantDefinitionInBlock
|
6
|
+
class Record
|
7
|
+
include GlobalID::Identification
|
8
|
+
attr_reader :id
|
9
|
+
def initialize id
|
10
|
+
@id = id
|
11
|
+
end
|
12
|
+
|
13
|
+
def == other
|
14
|
+
other.id == @id
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# standard:enable Lint/ConstantDefinitionInBlock
|
18
|
+
before do
|
19
|
+
GlobalID.app = "rspec"
|
20
|
+
GlobalID::Locator.use :rspec do |gid, options|
|
21
|
+
Record.new gid.model_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "marshalling" do
|
26
|
+
it "passes simple arguments" do
|
27
|
+
@transporter = described_class.new
|
28
|
+
|
29
|
+
@transport = @transporter.marshal "Hello"
|
30
|
+
expect(@transport).to eq ["Hello"]
|
31
|
+
|
32
|
+
@transport = @transporter.marshal 1, 2, 3
|
33
|
+
expect(@transport).to eq [1, 2, 3]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "copies arrays" do
|
37
|
+
@transporter = described_class.new
|
38
|
+
|
39
|
+
@source = [[1, 2, 3], [:this, :that]]
|
40
|
+
|
41
|
+
@transport = @transporter.marshal(*@source)
|
42
|
+
expect(@transport).to eq @source
|
43
|
+
expect(@transport.first.object_id).to_not eq @source.first.object_id
|
44
|
+
expect(@transport.last.object_id).to_not eq @source.last.object_id
|
45
|
+
end
|
46
|
+
|
47
|
+
it "copies hashss" do
|
48
|
+
@transporter = described_class.new
|
49
|
+
|
50
|
+
@source = [{first: "1", second: 2}]
|
51
|
+
|
52
|
+
@transport = @transporter.marshal(*@source)
|
53
|
+
expect(@transport).to eq @source
|
54
|
+
expect(@transport.first.object_id).to_not eq @source.first.object_id
|
55
|
+
end
|
56
|
+
|
57
|
+
it "converts objects to Global ID strings" do
|
58
|
+
@transporter = described_class.new
|
59
|
+
|
60
|
+
@record = Record.new 123
|
61
|
+
@global_id = @record.to_global_id.to_s
|
62
|
+
|
63
|
+
@transport = @transporter.marshal @record
|
64
|
+
|
65
|
+
expect(@transport).to eq [@global_id]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "converts objects within arrays to Global ID strings" do
|
69
|
+
@transporter = described_class.new
|
70
|
+
|
71
|
+
@record = Record.new 123
|
72
|
+
@global_id = @record.to_global_id.to_s
|
73
|
+
|
74
|
+
@transport = @transporter.marshal [:this, @record]
|
75
|
+
|
76
|
+
expect(@transport).to eq [[:this, @global_id]]
|
77
|
+
end
|
78
|
+
|
79
|
+
it "converts objects within hashes to Global ID strings" do
|
80
|
+
@transporter = described_class.new
|
81
|
+
|
82
|
+
@record = Record.new 123
|
83
|
+
@global_id = @record.to_global_id.to_s
|
84
|
+
|
85
|
+
@transport = @transporter.marshal this: "that", the_other: {embedded: @record}
|
86
|
+
|
87
|
+
expect(@transport).to eq [{this: "that", the_other: {embedded: @global_id}}]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "unmarshalling" do
|
92
|
+
it "passes simple arguments" do
|
93
|
+
@transporter = described_class.new
|
94
|
+
|
95
|
+
@transport = @transporter.unmarshal "Hello"
|
96
|
+
expect(@transport).to eq ["Hello"]
|
97
|
+
|
98
|
+
@transport = @transporter.unmarshal 1, 2, 3
|
99
|
+
expect(@transport).to eq [1, 2, 3]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "passes arrays" do
|
103
|
+
@transporter = described_class.new
|
104
|
+
|
105
|
+
@transport = @transporter.unmarshal [1, 2, 3], [:this, :that]
|
106
|
+
|
107
|
+
expect(@transport.first.object_id).to_not eq [1, 2, 3]
|
108
|
+
expect(@transport.last.object_id).to_not eq [:this, :that]
|
109
|
+
end
|
110
|
+
|
111
|
+
it "passes hashss and keyword arguments" do
|
112
|
+
@transporter = described_class.new
|
113
|
+
|
114
|
+
@transport = @transporter.unmarshal first: "1", second: 2
|
115
|
+
expect(@transport).to eq [{first: "1", second: 2}]
|
116
|
+
end
|
117
|
+
|
118
|
+
it "passes mixtures of arrays and hashes" do
|
119
|
+
@transporter = described_class.new
|
120
|
+
|
121
|
+
@transport = @transporter.unmarshal :this, :that, first: "1", second: 2
|
122
|
+
expect(@transport).to eq [:this, :that, {first: "1", second: 2}]
|
123
|
+
end
|
124
|
+
|
125
|
+
it "converts Global ID strings to objects" do
|
126
|
+
@transporter = described_class.new
|
127
|
+
|
128
|
+
@record = Record.new "123"
|
129
|
+
@global_id = @record.to_global_id.to_s
|
130
|
+
|
131
|
+
@transport = @transporter.unmarshal @global_id
|
132
|
+
|
133
|
+
expect(@transport).to eq [@record]
|
134
|
+
end
|
135
|
+
|
136
|
+
it "converts Global ID strings within arrays to objects" do
|
137
|
+
@transporter = described_class.new
|
138
|
+
|
139
|
+
@record = Record.new "123"
|
140
|
+
@global_id = @record.to_global_id.to_s
|
141
|
+
|
142
|
+
@transport = @transporter.unmarshal :this, @global_id
|
143
|
+
|
144
|
+
expect(@transport).to eq [:this, @record]
|
145
|
+
end
|
146
|
+
|
147
|
+
it "converts Global ID strings within hashes to objects" do
|
148
|
+
@transporter = described_class.new
|
149
|
+
|
150
|
+
@record = Record.new "123"
|
151
|
+
@global_id = @record.to_global_id.to_s
|
152
|
+
|
153
|
+
@transport = @transporter.unmarshal this: "that", the_other: {embedded: @global_id}
|
154
|
+
|
155
|
+
expect(@transport).to eq [{this: "that", the_other: {embedded: @record}}]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|