object_protocol 0.1.0 → 0.2.0

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: 22d8c5fa9114c23a89499e798c017b41402b0f10f45ffc53236db31222035e0c
4
- data.tar.gz: e076ce8bd8b02a476f723b7cfe188ca66f6901e2d0e123b0a3210ddbd276c50c
3
+ metadata.gz: dd7f3c31f616d5f7d1d1e41c6aa8414dc0aa07b175f587e692878453d9735166
4
+ data.tar.gz: 43e98c4fa8a3f5efeb4dffe1eaf41e353dac9628b1ede3748ad3c8b79d7ab5de
5
5
  SHA512:
6
- metadata.gz: 7df2d672dff3d0467c2ef90e0adda6196d2d255638b7f73f1d15cb5dbe7e18459851f0305d8975abee91348453fa4e29f56f059e213d1e2f2ad3f76c9f6cd753
7
- data.tar.gz: fcc960d6e186b61093cf4ff2ea6487257e0ce813cc77af1ce3aff2c075af850e10e40bd4da9856cf7a5a8f7862c53cd6b247290d48ad8772c599f51fc9aeedf7
6
+ metadata.gz: a9af361219c6a9ee45684da4f7cc6196703827f2aa75ecd0562432f88ec47329759c027aaac141b24135be0f4cb23fa9a6733448bedcf6d4215173c0a3052063
7
+ data.tar.gz: 9a88fd2277328dfc311c350ee1c491fb25d5eb9470060593e2f25c68bec2e1cffc342b64f775c1fbb283ac6acad5a03b5594a8e573a6a19a1caff9a6ca53ce5f
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /spec/examples.txt
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/CHANGELOG.md CHANGED
@@ -1,12 +1,15 @@
1
1
  ## ROADMAP
2
2
 
3
3
  * in_order
4
- * in_any_order
5
- * doesnt_send (and figure out how it relates to ordering steps)
6
- * add diffs to failure message
4
+ * doesnt_send (and figure out how it relates to ordering message expectations)
7
5
  * add color to failure message diffs
8
6
  * make failure message diff colors configurable via env vars for acessibility
9
7
 
8
+ ## RELEASE 0.2.0
9
+
10
+ * FEATURE: `in_any_order` lets you declare a subset of protocol messages that you don't care about the order of, just that they are sent & received with the correct arguments (if any).
11
+ * ENHANCEMENT: `ObjectProtocol#bind` now provides a helpful error message if you try to bind the wrong participant names.
12
+
10
13
  ## RELEASE 0.1.0
11
14
 
12
15
  * FEATURE: ObjectProtocols can be instantiated and used in tests
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- object_protocol (0.1.0)
4
+ object_protocol (0.2.0)
5
5
  binding_of_caller
6
6
 
7
7
  GEM
@@ -13,7 +13,7 @@ GEM
13
13
  coderay (1.1.2)
14
14
  debug_inspector (0.0.3)
15
15
  diff-lcs (1.3)
16
- git (1.4.0)
16
+ git (1.5.0)
17
17
  method_source (0.9.0)
18
18
  pry (0.11.3)
19
19
  coderay (~> 1.1.0)
@@ -35,7 +35,7 @@ GEM
35
35
  diff-lcs (>= 1.2.0, < 2.0)
36
36
  rspec-support (~> 3.7.0)
37
37
  rspec-support (3.7.1)
38
- structured_changelog (0.10.0)
38
+ structured_changelog (0.10.2)
39
39
  git
40
40
 
41
41
  PLATFORMS
data/README.adoc ADDED
@@ -0,0 +1,133 @@
1
+ = ObjectProtocol
2
+ :ext-relative: .adoc
3
+ :source-highlighter: coderay
4
+ :sectanchors:
5
+ :linkattrs:
6
+ :toc: left
7
+ ifdef::env-github[]
8
+ :tip-caption: :bulb:
9
+ :note-caption: :information_source:
10
+ :important-caption: :heavy_exclamation_mark:
11
+ :caution-caption: :fire:
12
+ :warning-caption: :warning:
13
+ endif::[]
14
+
15
+ == Goals
16
+
17
+ . Write message expectation tests with less boilerplate than when using RSpec spies
18
+ . Resulting protocols should be usable as documentation
19
+ . Be able to specify which object sent the message and which object received it (traditional message expectation test only allow the latter)
20
+
21
+ == What's An Object Protocol?
22
+
23
+ Protocols are the types of an OO program. They structure and order the messages passed between communicating agents.
24
+
25
+ == Using Object Protocols
26
+
27
+ === A Simple Logger
28
+
29
+ Given a unrealistically simple logger:
30
+ [source,ruby]
31
+ ----
32
+ class UnrealisticLogger
33
+ def initialize(device)
34
+ @device = device
35
+ end
36
+
37
+ def log(message)
38
+ @device << message
39
+ end
40
+
41
+ def rotate
42
+ @device.shift
43
+ end
44
+ end
45
+ ----
46
+
47
+ we can write a protocol for what happens when we call `#log` on an instance of `UnrealisticLogger`
48
+
49
+ [source.ruby]
50
+ ----
51
+ UnrealisticLoggingProtocol = ObjectProtocol.new(:device, :logger) do # <1>
52
+ logger.sends(:<<).to(device) # <2>
53
+ end
54
+ ----
55
+ <1> we need to tell the protocol the names of the participants
56
+ <2> we switch to using local variables here instead of the symbols
57
+
58
+ Then, we can test this protocol:
59
+
60
+ [source,ruby]
61
+ ----
62
+ require 'object_protocol/rspec'
63
+
64
+ RSpec.describe UnrealisticLoggingProtocol do
65
+ it "is satisfied by calling #log on the logger" do
66
+ device = []
67
+ logger = UnrealisticLogger.new(device) # <1>
68
+
69
+ UnrealisticLoggingProtocol.bind( # <2>
70
+ device: device,
71
+ logger: logger,
72
+ )
73
+
74
+ expect(UnrealisticLoggingProtocol).to be_satisfied_by do
75
+ logger.log("message") # <3>
76
+ end
77
+ end
78
+ end
79
+ ----
80
+ <1> instantiate the participants
81
+ <2> bind participants to the protocol
82
+ <3> test an execution against the protocol
83
+
84
+ Ok, but what was all that?
85
+
86
+ Well, we created a protocol that we can use as documentation, and tested it with a lot less fanfare and boilerplate than you'd need with traditional message expectation tests!
87
+
88
+ In real life, you'd use an actual object defined in your codebase in place of a fake logger, but you'd probably inject fake or stub collaborator objects to avoid side-effects.
89
+
90
+ == How Does This All Work, Anyway?
91
+
92
+ During an execution (the block passed to `satisfied_by`), we spy on every public method (except `#__send__` and `#object_id` because of the warnings) defined on each bound participant object. We record the message, the sender, the receiver, and the arguments (if any) that were passed. Then, we invoke the actual method behavior defined by that object.
93
+
94
+ === Interaction with `#method_missing`
95
+
96
+ `#method_missing` is rarely a message that is sent from one object to another. We currently record the name of the missing method as the "sent message" and record any arguments beyond that message name, as well as the sender and receiver.
97
+
98
+ == Installation
99
+
100
+ Add this line to your application's Gemfile:
101
+
102
+ [source,ruby]
103
+ ----
104
+ gem 'object_protocol'
105
+ ----
106
+
107
+ And then execute:
108
+
109
+ [source,shell]
110
+ ----
111
+ $ bundle
112
+ ----
113
+
114
+ Or install it yourself as:
115
+
116
+ [source,shell]
117
+ ----
118
+ $ gem install object_protocol
119
+ ----
120
+
121
+ == Development
122
+
123
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
124
+
125
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to https://rubygems.org[rubygems.org].
126
+
127
+ == Contributing
128
+
129
+ Bug reports and pull requests are welcome on GitHub at https://github.com/yarmiganosca/object_protocol. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the http://contributor-covenant.org[Contributor Covenant] code of conduct.
130
+
131
+ == Code of Conduct
132
+
133
+ Everyone interacting in the ObjectProtocol project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the https://github.com/yarmiganosca/object_protocol/blob/master/CODE_OF_CONDUCT.md[code of conduct].
@@ -1,30 +1,65 @@
1
1
  require "object_protocol/version"
2
2
  require 'object_protocol/stand_in'
3
3
  require 'object_protocol/satisfaction_attempt'
4
+ require 'object_protocol/unordered_message_sequence_expectation'
4
5
 
5
6
  class ObjectProtocol
6
7
  attr_reader :participants_by_name
7
8
 
8
- def initialize(*participant_names, &protocol)
9
+ def initialize(*participant_names, &expectations)
10
+ @participant_names = participant_names.map(&:to_sym)
11
+
9
12
  participant_names.each(&method(:define_stand_in))
10
- instance_exec(&protocol)
13
+ instance_exec(&expectations)
11
14
  participant_names.each(&method(:undefine_stand_in))
12
15
  end
13
16
 
17
+ def in_any_order(&expectations)
18
+ unordered_message_sequence_expectation = UnorderedMessageSequenceExpectation.new(protocol: self)
19
+ self.expectations << unordered_message_sequence_expectation
20
+
21
+ expectation_sequence_stack.push(unordered_message_sequence_expectation)
22
+ instance_exec(&expectations)
23
+ expectation_sequence_stack.pop
24
+ end
25
+
14
26
  def bind(**participants_by_name)
15
- @participants_by_name = participants_by_name
27
+ bind_attempt_participant_names = participants_by_name.keys.map(&:to_sym)
28
+
29
+ missing_participant_names = participant_names - bind_attempt_participant_names
30
+ extra_participant_names = bind_attempt_participant_names - participant_names
31
+
32
+ if missing_participant_names.empty? && extra_participant_names.empty?
33
+ @participants_by_name = participants_by_name
34
+
35
+ @participants_by_name.each(&method(:define_participant))
36
+
37
+ self
38
+ else
39
+ key_error_message_parts = []
16
40
 
17
- @participants_by_name.each(&method(:define_participant))
41
+ if missing_participant_names.any?
42
+ key_error_message_parts << "These keys are required by this protocol but weren't provided: #{missing_participant_names.join(', ')}"
43
+ end
18
44
 
19
- self
45
+ if extra_participant_names.any?
46
+ key_error_message_parts << "These keys aren't used in this protocol but were provided: #{extra_participant_names.join(', ')}"
47
+ end
48
+
49
+ raise KeyError, key_error_message_parts.join("\n ") # makes the second line indent correctly
50
+ end
20
51
  end
21
52
 
22
53
  def satisfied_by?(&blk)
23
54
  SatisfactionAttempt.new(self, &blk).to_bool
24
55
  end
25
56
 
26
- def steps
27
- @steps ||= []
57
+ def add_expectation(expectation)
58
+ expectation_sequence_stack.last.expectations << expectation
59
+ end
60
+
61
+ def expectations
62
+ @expectations ||= []
28
63
  end
29
64
 
30
65
  def participant_by_name(name)
@@ -40,11 +75,13 @@ class ObjectProtocol
40
75
  end
41
76
 
42
77
  def to_rspec_matcher_failure_message_lines
43
- steps.map(&:to_rspec_matcher_failure_message_line)
78
+ expectations.flat_map(&:to_rspec_matcher_failure_message_lines)
44
79
  end
45
80
 
46
81
  private
47
82
 
83
+ attr_reader :participant_names
84
+
48
85
  def define_participant(name, participant)
49
86
  instance_variable_set("@#{name}_participant", participant)
50
87
 
@@ -65,4 +102,8 @@ class ObjectProtocol
65
102
  instance_eval("undef :#{name}")
66
103
  remove_instance_variable("@#{name}_stand_in")
67
104
  end
105
+
106
+ def expectation_sequence_stack
107
+ @expectation_sequence_stack ||= [self]
108
+ end
68
109
  end
@@ -62,13 +62,23 @@ class ObjectProtocol
62
62
  args
63
63
  end
64
64
 
65
- sent_message = SentMessage.new(
66
- sender: sender,
67
- receiver: self,
68
- name: method_name,
69
- )
70
-
71
- sent_message.with(arguments) if arguments.any?
65
+ if method_name == :method_missing
66
+ sent_message = SentMessage.new(
67
+ sender: sender,
68
+ receiver: self,
69
+ name: arguments.first
70
+ )
71
+
72
+ sent_message.with(arguments[1..-1]) if arguments.size > 1
73
+ else
74
+ sent_message = SentMessage.new(
75
+ sender: sender,
76
+ receiver: self,
77
+ name: method_name
78
+ )
79
+
80
+ sent_message.with(arguments) if arguments.any?
81
+ end
72
82
 
73
83
  execution.messages << sent_message
74
84
  end
@@ -1,10 +1,13 @@
1
+ require 'object_protocol/satisfiable_message_expectation'
2
+
1
3
  class ObjectProtocol
2
- class Step
4
+ class MessageExpectation
3
5
  attr_reader :sender, :message, :receiver, :arguments
4
6
 
5
- def initialize(sender:, message:)
6
- @sender = sender
7
- @message = message
7
+ def initialize(protocol:, sender:, message:)
8
+ @protocol = protocol
9
+ @sender = sender
10
+ @message = message
8
11
 
9
12
  @arguments_specified = false
10
13
  end
@@ -23,17 +26,21 @@ class ObjectProtocol
23
26
  self
24
27
  end
25
28
 
29
+ def to_satisfiable
30
+ SatisfiableMessageExpectation.new(protocol: protocol, message_expectation: self)
31
+ end
32
+
26
33
  def inspect
27
- "<Step[#{sender.name}, :#{message}, #{receiver.name}]>"
34
+ "<#{self.class.name.split('::').last}[#{sender.name}, :#{message}, #{receiver.name}]>"
28
35
  end
29
36
 
30
- def to_rspec_matcher_failure_message_line
37
+ def to_rspec_matcher_failure_message_lines
31
38
  fragment_base = "#{sender.name}.sends(:#{message}).to(#{receiver.name})"
32
39
 
33
40
  if arguments_specified?
34
- "#{fragment_base}.with(#{arguments})"
41
+ ["#{fragment_base}.with(#{arguments})"]
35
42
  else
36
- fragment_base
43
+ [fragment_base]
37
44
  end
38
45
  end
39
46
 
@@ -51,5 +58,9 @@ class ObjectProtocol
51
58
  def arguments_specified?
52
59
  @arguments_specified
53
60
  end
61
+
62
+ private
63
+
64
+ attr_reader :protocol
54
65
  end
55
66
  end
@@ -18,9 +18,9 @@ class SatisfactionAttemptVerifier
18
18
  def failure_message
19
19
  [
20
20
  "expected",
21
- protocol.to_rspec_matcher_failure_message_lines.map(&" ".method(:+)).flatten,
21
+ *protocol.to_rspec_matcher_failure_message_lines.flat_map(&" ".method(:+)),
22
22
  "to be satisfied by",
23
- attempt.to_rspec_matcher_failure_message_lines.map(&" ".method(:+)).flatten,
23
+ *attempt.to_rspec_matcher_failure_message_lines.flat_map(&" ".method(:+)),
24
24
  ].join("\n")
25
25
  end
26
26
 
@@ -1,5 +1,4 @@
1
1
  require 'object_protocol/execution'
2
- require 'object_protocol/satisfiable_step'
3
2
 
4
3
  class ObjectProtocol
5
4
  class SatisfactionAttempt
@@ -11,21 +10,19 @@ class ObjectProtocol
11
10
  def to_bool
12
11
  execution.call(protocol)
13
12
 
14
- unsatisfied_steps = protocol.steps.map do |step|
15
- SatisfiableStep.new(protocol: protocol, step: step)
16
- end
13
+ satisfiable_expectations = protocol.expectations.map(&:to_satisfiable)
17
14
 
18
15
  execution.messages.each do |sent_message|
19
- next_step = unsatisfied_steps.first
16
+ next_expectation = satisfiable_expectations.first
20
17
 
21
- next_step.attempt_to_apply_sent_message(sent_message)
18
+ next_expectation.attempt_to_apply_sent_message(sent_message)
22
19
 
23
- if next_step.satisfied?
24
- unsatisfied_steps.shift
20
+ if next_expectation.satisfied?
21
+ satisfiable_expectations.shift
25
22
  end
26
23
  end
27
24
 
28
- unsatisfied_steps.empty?
25
+ satisfiable_expectations.empty?
29
26
  end
30
27
 
31
28
  def to_rspec_matcher_failure_message_lines
@@ -1,14 +1,14 @@
1
1
  require 'forwardable'
2
2
 
3
3
  class ObjectProtocol
4
- class SatisfiableStep
4
+ class SatisfiableMessageExpectation
5
5
  extend Forwardable
6
6
 
7
- delegate %i(sender receiver message arguments arguments_specified?) => :@step
7
+ delegate %i(sender receiver message arguments arguments_specified?) => :@message_expectation
8
8
 
9
- def initialize(protocol:, step:)
10
- @protocol = protocol
11
- @step = step
9
+ def initialize(protocol:, message_expectation:)
10
+ @protocol = protocol
11
+ @message_expectation = message_expectation
12
12
 
13
13
  @satisfied = false
14
14
  end
@@ -36,6 +36,10 @@ class ObjectProtocol
36
36
  !!@satisfied
37
37
  end
38
38
 
39
+ def unsatisfied?
40
+ !satisfied?
41
+ end
42
+
39
43
  private
40
44
 
41
45
  attr_reader :protocol
@@ -0,0 +1,36 @@
1
+ require 'forwardable'
2
+
3
+ class ObjectProtocol
4
+ class SatisfiableUnorderedMessageSequenceExpectation
5
+ extend Forwardable
6
+
7
+ def initialize(protocol:, sequence_expectation:)
8
+ @protocol = protocol
9
+ @sequence_expectation = sequence_expectation
10
+ end
11
+
12
+ def attempt_to_apply_sent_message(sent_message)
13
+ return if satisfied?
14
+
15
+ satisfiable_expectations.each do |satisfiable_expectation|
16
+ if satisfiable_expectation.unsatisfied?
17
+ satisfiable_expectation.attempt_to_apply_sent_message(sent_message)
18
+
19
+ break if satisfiable_expectation.satisfied?
20
+ end
21
+ end
22
+ end
23
+
24
+ def satisfied?
25
+ satisfiable_expectations.all?(&:satisfied?)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :protocol, :sequence_expectation
31
+
32
+ def satisfiable_expectations
33
+ @satisfiable_expectations ||= sequence_expectation.expectations.map(&:to_satisfiable)
34
+ end
35
+ end
36
+ end
@@ -1,4 +1,4 @@
1
- require 'object_protocol/step'
1
+ require 'object_protocol/message_expectation'
2
2
 
3
3
  class ObjectProtocol
4
4
  class StandIn
@@ -10,9 +10,11 @@ class ObjectProtocol
10
10
  end
11
11
 
12
12
  def sends(message)
13
- Step.new(sender: self, message: message).tap do |step|
14
- protocol.steps << step
15
- end
13
+ MessageExpectation.new(
14
+ protocol: protocol,
15
+ sender: self,
16
+ message: message
17
+ ).tap(&protocol.method(:add_expectation))
16
18
  end
17
19
 
18
20
  private
@@ -0,0 +1,31 @@
1
+ require 'object_protocol/satisfiable_unordered_message_sequence_expectation'
2
+
3
+ class ObjectProtocol
4
+ class UnorderedMessageSequenceExpectation
5
+ def initialize(protocol:)
6
+ @protocol = protocol
7
+ end
8
+
9
+ def expectations
10
+ @expectations ||= []
11
+ end
12
+
13
+ def to_rspec_matcher_failure_message_lines
14
+ [
15
+ "in_any_order",
16
+ *expectations.flat_map(&:to_rspec_matcher_failure_message_lines).map(&" ".method(:+)),
17
+ ]
18
+ end
19
+
20
+ def to_satisfiable
21
+ SatisfiableUnorderedMessageSequenceExpectation.new(
22
+ protocol: protocol,
23
+ sequence_expectation: self
24
+ )
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :protocol
30
+ end
31
+ end
@@ -1,3 +1,3 @@
1
1
  class ObjectProtocol
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: object_protocol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hoffman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-07-04 00:00:00.000000000 Z
11
+ date: 2018-08-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -109,18 +109,20 @@ files:
109
109
  - Gemfile
110
110
  - Gemfile.lock
111
111
  - LICENSE
112
- - README.md
112
+ - README.adoc
113
113
  - Rakefile
114
114
  - bin/console
115
115
  - bin/setup
116
116
  - lib/object_protocol.rb
117
117
  - lib/object_protocol/execution.rb
118
+ - lib/object_protocol/message_expectation.rb
118
119
  - lib/object_protocol/rspec.rb
119
120
  - lib/object_protocol/satisfaction_attempt.rb
120
- - lib/object_protocol/satisfiable_step.rb
121
+ - lib/object_protocol/satisfiable_message_expectation.rb
122
+ - lib/object_protocol/satisfiable_unordered_message_sequence_expectation.rb
121
123
  - lib/object_protocol/sent_message.rb
122
124
  - lib/object_protocol/stand_in.rb
123
- - lib/object_protocol/step.rb
125
+ - lib/object_protocol/unordered_message_sequence_expectation.rb
124
126
  - lib/object_protocol/version.rb
125
127
  - object_protocol.gemspec
126
128
  homepage: https://www.github.com/yarmiganosca/object_protocol
data/README.md DELETED
@@ -1,39 +0,0 @@
1
- # ObjectProtocol
2
-
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/object_protocol`. To experiment with that code, run `bin/console` for an interactive prompt.
4
-
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
- ## Installation
8
-
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'object_protocol'
13
- ```
14
-
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install object_protocol
22
-
23
- ## Usage
24
-
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
-
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/yarmiganosca/object_protocol. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
-
37
- ## Code of Conduct
38
-
39
- Everyone interacting in the ObjectProtocol project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/yarmiganosca/object_protocol/blob/master/CODE_OF_CONDUCT.md).