minitag 0.1.0 → 0.3.3
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/.github/workflows/ci.yml +32 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +4 -1
- data/README.md +12 -7
- data/lib/minitag.rb +9 -11
- data/lib/minitag/context.rb +105 -0
- data/lib/minitag/tag_extension.rb +15 -13
- data/lib/minitag/tag_registry.rb +42 -0
- data/lib/minitag/version.rb +1 -1
- data/lib/minitest/minitag_plugin.rb +2 -2
- metadata +5 -6
- data/.travis.yml +0 -7
- data/lib/minitag/tag.rb +0 -30
- data/lib/minitag/tag_mapper.rb +0 -26
- data/lib/minitag/tag_matcher.rb +0 -43
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab98e9f2debb23b27f21435a3b2ac0102a835391accb97fd7e1bae69457f4e07
|
|
4
|
+
data.tar.gz: 538c9bc1789dd2080b3a8f8430c116d05693eb5c2dfee3f1204fe60c67e5e80a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '081ad2c7553abb897271b01e30d0561255ca5ee30128aac47f4dbee4ac29fd7a0de0e1954268c9325f9476823f6ffc16321b429fc9d98b0edc3fc20e10433ca6'
|
|
7
|
+
data.tar.gz: 372cd5e85be6c5f7c35faf779e2ab197ac8a4d218dd11b13c43c2b5be2449aa7c14d4898208cc93c5410a24b2ec4e6290171c1d67dca332d8f9325706cb2eace
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: continuous-integration
|
|
2
|
+
on: push
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
ci:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
steps:
|
|
8
|
+
- uses: actions/checkout@v2
|
|
9
|
+
- name: Set up Ruby 2.7
|
|
10
|
+
uses: actions/setup-ruby@v1
|
|
11
|
+
with:
|
|
12
|
+
ruby-version: "2.7"
|
|
13
|
+
- name: Cache gems
|
|
14
|
+
uses: actions/cache@v2
|
|
15
|
+
with:
|
|
16
|
+
path: vendor/bundle
|
|
17
|
+
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
|
18
|
+
restore-keys: |
|
|
19
|
+
${{ runner.os }}-gem-
|
|
20
|
+
- name: Install gems
|
|
21
|
+
run: |
|
|
22
|
+
sed -i '/ruby ".*"/,+1 d' Gemfile
|
|
23
|
+
bundle_version=`awk '/BUNDLED WITH/{getline; gsub(/ /, "", $0); print}' Gemfile.lock`
|
|
24
|
+
gem install bundler --no-document -v ${bundle_version}
|
|
25
|
+
bundle config retry "3"
|
|
26
|
+
bundle config jobs "$(nproc)"
|
|
27
|
+
bundle config path vendor/bundle
|
|
28
|
+
bundle install
|
|
29
|
+
- name: Run RuboCop
|
|
30
|
+
run: bundle exec rubocop --parallel
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: bundle exec rake test
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
[](https://badge.fury.io/rb/minitag)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Minitag
|
|
4
4
|
|
|
5
5
|
A simple gem that allow developers to tag their minitest tests and run tests
|
|
6
6
|
based on these tags.
|
|
@@ -31,25 +31,30 @@ Require `minitag` in our `test_helper.rb`:
|
|
|
31
31
|
|
|
32
32
|
We can tag specific tests with one or more tags.
|
|
33
33
|
|
|
34
|
+
It is important to point out that tags associated with a test have no concept of being inclusive or exclusive. This distinction is only valid for [tag filters](#running-tests-with-tag-filters).
|
|
34
35
|
|
|
35
36
|
```rb
|
|
36
37
|
class MyTest < Minitest::Test
|
|
37
38
|
tag 'my_tag', 'another_tag'
|
|
38
|
-
|
|
39
|
+
def test_hello_minitest
|
|
39
40
|
# ...
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
```
|
|
43
44
|
|
|
44
|
-
### Running tests
|
|
45
|
+
### Running tests with tag filters
|
|
45
46
|
|
|
46
47
|
We can now run our test suite with specific tags:
|
|
47
48
|
|
|
48
|
-
`$ bundle exec rake test --tag '
|
|
49
|
+
`$ bundle exec rake test --tag 'unit'`
|
|
50
|
+
|
|
51
|
+
or even multiple tags:
|
|
52
|
+
|
|
53
|
+
`$ bundle exec rake test --tag 'unit' --tag 'services' --tag '~model'`
|
|
49
54
|
|
|
50
|
-
###
|
|
55
|
+
### More on tag filters
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
Tag filters can be:
|
|
53
58
|
1. `inclusive`
|
|
54
59
|
2. `exclusive` with the `~` prefix:
|
|
55
60
|
|
data/lib/minitag.rb
CHANGED
|
@@ -2,33 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
require 'minitest'
|
|
4
4
|
require 'minitag/version'
|
|
5
|
-
require 'minitag/
|
|
6
|
-
require 'minitag/tag_mapper'
|
|
7
|
-
require 'minitag/tag_matcher'
|
|
5
|
+
require 'minitag/context'
|
|
8
6
|
require 'minitag/tag_extension'
|
|
9
7
|
|
|
10
8
|
# Namespace for classes or modules providing tagging functionality
|
|
11
9
|
# to Minitest::Test
|
|
12
10
|
module Minitag
|
|
13
11
|
class << self
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
# Execution context of the test suite.
|
|
13
|
+
def context
|
|
14
|
+
@context ||= Context.new
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
# Add filtering tag to context specified by the `--tag` or `-t` option.
|
|
18
|
+
def add_filter(tag)
|
|
19
|
+
context.add_filter(tag)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
# Tags from the last `tag` method awaiting to be associated with a test.
|
|
22
23
|
def pending_tags
|
|
23
24
|
@pending_tags || []
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
# Tags set from the `tag` method.
|
|
26
28
|
def pending_tags=(tags)
|
|
27
29
|
@pending_tags = Array(tags)
|
|
28
30
|
end
|
|
29
|
-
|
|
30
|
-
def tag_mapping
|
|
31
|
-
@tag_mapping ||= Minitag::TagMapper.new
|
|
32
|
-
end
|
|
33
31
|
end
|
|
34
32
|
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
require_relative './tag_registry'
|
|
5
|
+
|
|
6
|
+
module Minitag
|
|
7
|
+
# Represents the execution context of the test suite.
|
|
8
|
+
class Context
|
|
9
|
+
def initialize
|
|
10
|
+
@inclusive_filters = Set.new
|
|
11
|
+
@exclusive_filters = Set.new
|
|
12
|
+
@tag_registry = Minitag::TagRegistry.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Associates tags with a name taking into account its namespace.
|
|
16
|
+
#
|
|
17
|
+
# @param [String] namespace the namespace which a name belongs.
|
|
18
|
+
# @param [String] name the test name.
|
|
19
|
+
# @param [Array] tags the collection of tags.
|
|
20
|
+
#
|
|
21
|
+
# @return [void]
|
|
22
|
+
def add_tags(namespace:, name:, tags:)
|
|
23
|
+
@tag_registry.add(namespace: namespace, name: name, tags: tags)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Adds a filter tag.
|
|
27
|
+
# Tags with a ~ prefix are treated as exclusive filters or inclusive filters otherwise.
|
|
28
|
+
#
|
|
29
|
+
# param [String] name the name of the filter tag.
|
|
30
|
+
#
|
|
31
|
+
# Invariants:
|
|
32
|
+
# - A filter will always be a String without the ~ prefix.
|
|
33
|
+
#
|
|
34
|
+
# @return [void]
|
|
35
|
+
def add_filter(tag)
|
|
36
|
+
if tag.start_with?('~')
|
|
37
|
+
@exclusive_filters << tag[1..]
|
|
38
|
+
else
|
|
39
|
+
@inclusive_filters << tag
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Indicates when a context has no filters.
|
|
44
|
+
#
|
|
45
|
+
# @return [Boolean] whether a context has no filters.
|
|
46
|
+
def no_filters?
|
|
47
|
+
@inclusive_filters.empty? && @exclusive_filters.empty?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Detects whether the name associated with a namespace contains tags
|
|
51
|
+
# that matches the filtering criteria. For more information check the methods:
|
|
52
|
+
# - match_inclusive_filters?
|
|
53
|
+
# - match_exclusive_filters?
|
|
54
|
+
#
|
|
55
|
+
# @param [String] namespace the namespace which a test name belongs.
|
|
56
|
+
# @param [String] name the test name.
|
|
57
|
+
#
|
|
58
|
+
# Invariants:
|
|
59
|
+
# - Returns true when no filters are present.
|
|
60
|
+
#
|
|
61
|
+
# @return [Boolean] whether there was a match or not.
|
|
62
|
+
def match?(namespace:, name:)
|
|
63
|
+
return true if no_filters?
|
|
64
|
+
|
|
65
|
+
tags = @tag_registry.fetch(namespace: namespace, name: name)
|
|
66
|
+
match_inclusive_filters?(tags) && match_exclusive_filters?(tags)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Detects whether any of the tags matches the inclusive filters.
|
|
72
|
+
#
|
|
73
|
+
# @param [Set] tags the set of tags.
|
|
74
|
+
#
|
|
75
|
+
# Invariants:
|
|
76
|
+
# - Returns true when no inclusive filters are specified.
|
|
77
|
+
# - Returns false when inclusive filters are specified but there
|
|
78
|
+
# are no tags.
|
|
79
|
+
#
|
|
80
|
+
# @return [Boolean] whether there was a match or not.
|
|
81
|
+
def match_inclusive_filters?(tags)
|
|
82
|
+
return true if @inclusive_filters.empty?
|
|
83
|
+
return false if @inclusive_filters.any? && tags.empty?
|
|
84
|
+
|
|
85
|
+
(@inclusive_filters & tags).any?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Detects whether any of the tags matches the exclusive filters.
|
|
89
|
+
#
|
|
90
|
+
# @param [Set] tags the set of tags.
|
|
91
|
+
#
|
|
92
|
+
# Invariants:
|
|
93
|
+
# - Returns true when no exclusive filters are specified.
|
|
94
|
+
# - Returns true when exclusive filters are specified and there
|
|
95
|
+
# are no tags.
|
|
96
|
+
#
|
|
97
|
+
# return [Boolean] whether there was a match or not.
|
|
98
|
+
def match_exclusive_filters?(tags)
|
|
99
|
+
return true if @exclusive_filters.empty?
|
|
100
|
+
return true if @exclusive_filters.any? && tags.empty?
|
|
101
|
+
|
|
102
|
+
(@exclusive_filters & tags).none?
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -7,15 +7,24 @@ module Minitag
|
|
|
7
7
|
# - Associate tags with tests
|
|
8
8
|
# - Filter tests based on the specified tags
|
|
9
9
|
module TagExtension
|
|
10
|
+
# Add tags to be associated with the next test definition.
|
|
11
|
+
#
|
|
12
|
+
# It is important to notice that tags associated with a test have no concept
|
|
13
|
+
# of being inclusive or exclusive. This distinction is only valid for tag
|
|
14
|
+
# filters.
|
|
15
|
+
#
|
|
16
|
+
# @param [Array] tags the list of tags to be associated with a test case.
|
|
17
|
+
#
|
|
18
|
+
# @return [void]
|
|
10
19
|
def tag(*tags)
|
|
11
|
-
Minitag.pending_tags = tags
|
|
20
|
+
Minitag.pending_tags = tags.map { |tag| tag.to_s.strip.downcase }
|
|
12
21
|
end
|
|
13
22
|
|
|
14
23
|
define_method(:method_added) do |name|
|
|
15
24
|
if name[/\Atest_/]
|
|
16
|
-
Minitag.
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
Minitag.context.add_tags(
|
|
26
|
+
namespace: self, name: name, tags: Minitag.pending_tags
|
|
27
|
+
)
|
|
19
28
|
|
|
20
29
|
Minitag.pending_tags = []
|
|
21
30
|
end
|
|
@@ -23,17 +32,10 @@ module Minitag
|
|
|
23
32
|
|
|
24
33
|
def runnable_methods
|
|
25
34
|
methods = super.dup
|
|
26
|
-
return methods if Minitag.
|
|
27
|
-
|
|
28
|
-
execution_tags = Minitag.execution_tags
|
|
29
|
-
inclusive_tags = execution_tags.select(&:inclusive?).map(&:name)
|
|
30
|
-
exclusive_tags = execution_tags.select(&:exclusive?).map(&:name)
|
|
35
|
+
return methods if Minitag.context.no_filters?
|
|
31
36
|
|
|
32
37
|
methods.select do |runnable_method|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Minitag::TagMatcher.inclusive_match?(inclusive_tags, runnable_method_tags) &&
|
|
36
|
-
Minitag::TagMatcher.exclusive_match?(exclusive_tags, runnable_method_tags)
|
|
38
|
+
Minitag.context.match?(namespace: self, name: runnable_method)
|
|
37
39
|
end
|
|
38
40
|
end
|
|
39
41
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Minitag
|
|
6
|
+
# Stores tags associated with a test name, which belongs to a namespace.
|
|
7
|
+
# The namespace is usually the class which the test belongs to.
|
|
8
|
+
class TagRegistry
|
|
9
|
+
def initialize
|
|
10
|
+
@repository = Hash.new { |h, k| h[k] = Set.new }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Associates tags with a name taking into account its namespace.
|
|
14
|
+
#
|
|
15
|
+
# Duplicated tags will be removed during this operation.
|
|
16
|
+
#
|
|
17
|
+
# @param [String] namespace the context which a test name belongs.
|
|
18
|
+
# @param [String] name the test name.
|
|
19
|
+
# @param [Array] tags the collection of tags associated with a test.
|
|
20
|
+
#
|
|
21
|
+
# @return [void]
|
|
22
|
+
def add(namespace:, name:, tags:)
|
|
23
|
+
@repository[key(namespace, name)] = Set.new(tags)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Fetches tags associated with a test name and namespace.
|
|
27
|
+
#
|
|
28
|
+
# @param [String] namespace the context which a test name belongs.
|
|
29
|
+
# @param [String] name the test name.
|
|
30
|
+
#
|
|
31
|
+
# @return [Set] the tags associated with the specified namespace and test name.
|
|
32
|
+
def fetch(namespace:, name:)
|
|
33
|
+
@repository[key(namespace, name)]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def key(namespace, name)
|
|
39
|
+
"#{namespace}_#{name}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/minitag/version.rb
CHANGED
|
@@ -8,13 +8,13 @@ module Minitest
|
|
|
8
8
|
def self.plugin_minitag_options(opts, options)
|
|
9
9
|
opts.on '-t', '--tag TAG' do |tag|
|
|
10
10
|
options[:tags] ||= []
|
|
11
|
-
options[:tags] << tag
|
|
11
|
+
options[:tags] << tag.to_s.strip.downcase
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.plugin_minitag_init(options)
|
|
16
16
|
Array(options[:tags]).each do |tag|
|
|
17
|
-
Minitag.
|
|
17
|
+
Minitag.context.add_filter(tag)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minitag
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bernardo de Araujo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-07-
|
|
11
|
+
date: 2020-07-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -73,9 +73,9 @@ executables: []
|
|
|
73
73
|
extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
|
75
75
|
files:
|
|
76
|
+
- ".github/workflows/ci.yml"
|
|
76
77
|
- ".gitignore"
|
|
77
78
|
- ".rubocop.yml"
|
|
78
|
-
- ".travis.yml"
|
|
79
79
|
- CODE_OF_CONDUCT.md
|
|
80
80
|
- Gemfile
|
|
81
81
|
- Gemfile.lock
|
|
@@ -85,10 +85,9 @@ files:
|
|
|
85
85
|
- bin/console
|
|
86
86
|
- bin/setup
|
|
87
87
|
- lib/minitag.rb
|
|
88
|
-
- lib/minitag/
|
|
88
|
+
- lib/minitag/context.rb
|
|
89
89
|
- lib/minitag/tag_extension.rb
|
|
90
|
-
- lib/minitag/
|
|
91
|
-
- lib/minitag/tag_matcher.rb
|
|
90
|
+
- lib/minitag/tag_registry.rb
|
|
92
91
|
- lib/minitag/version.rb
|
|
93
92
|
- lib/minitest/minitag_plugin.rb
|
|
94
93
|
- minitag.gemspec
|
data/.travis.yml
DELETED
data/lib/minitag/tag.rb
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Minitag
|
|
4
|
-
# Represents a tag in our system.
|
|
5
|
-
# Provides helper methods to identify the type of a tag, which can
|
|
6
|
-
# only one of the following:
|
|
7
|
-
# - inclusive
|
|
8
|
-
# - exclusive (name with ~ as a prefix)
|
|
9
|
-
class Tag
|
|
10
|
-
attr_reader :name
|
|
11
|
-
|
|
12
|
-
def initialize(name)
|
|
13
|
-
@name = name.to_s
|
|
14
|
-
@exclusive = false
|
|
15
|
-
|
|
16
|
-
return unless @name.start_with?('~')
|
|
17
|
-
|
|
18
|
-
@name = @name[1..-1]
|
|
19
|
-
@exclusive = true
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def exclusive?
|
|
23
|
-
@exclusive
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def inclusive?
|
|
27
|
-
!exclusive?
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
data/lib/minitag/tag_mapper.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Minitag
|
|
4
|
-
# Stores the mapping between a test name and its existing tags.
|
|
5
|
-
class TagMapper
|
|
6
|
-
attr_reader :repository
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
@repository = Hash.new { |h, k| h[k] = [] }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def add(context:, name:, tag:)
|
|
13
|
-
@repository[key(context, name)] << Minitag::Tag.new(tag)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def fetch(context:, name:)
|
|
17
|
-
@repository[key(context, name)]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
def key(context, name)
|
|
23
|
-
"#{context}_#{name}"
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
data/lib/minitag/tag_matcher.rb
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Minitag
|
|
4
|
-
# Introduce different match? methods based on the type of the chosen tags.
|
|
5
|
-
class TagMatcher
|
|
6
|
-
# Detects if any of the desired tags match any of the existing ones.
|
|
7
|
-
#
|
|
8
|
-
# @param [Array] desired_tags the tags specified by `--tag`.
|
|
9
|
-
# @param [Array] existing_tags the tags specified by the `tag` method.
|
|
10
|
-
#
|
|
11
|
-
# Invariants:
|
|
12
|
-
# - Always returns true when no desired tags are specified.
|
|
13
|
-
# - Always returns false when desired tags are specified but there are
|
|
14
|
-
# no existing tags.
|
|
15
|
-
#
|
|
16
|
-
# return [boolean] whether there was a match or not.
|
|
17
|
-
def self.inclusive_match?(desired_tags, existing_tags)
|
|
18
|
-
return true if desired_tags.empty?
|
|
19
|
-
return false if desired_tags.any? && existing_tags.empty?
|
|
20
|
-
|
|
21
|
-
(desired_tags & existing_tags).any?
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Detects if there are no matches between any of the desired tags
|
|
25
|
-
# and existing ones.
|
|
26
|
-
#
|
|
27
|
-
# @param desired_tags [Array] tags specified by `--tag`.
|
|
28
|
-
# @param existing_tags [Array] tags specified by the `tag` method.
|
|
29
|
-
#
|
|
30
|
-
# Invariants:
|
|
31
|
-
# - Always returns true when no desired tags are specified.
|
|
32
|
-
# - Always returns true when desired tags are specified but there are
|
|
33
|
-
# no existing tags.
|
|
34
|
-
#
|
|
35
|
-
# return [boolean] whether there was no match or not.
|
|
36
|
-
def self.exclusive_match?(desired_tags, existing_tags)
|
|
37
|
-
return true if desired_tags.empty?
|
|
38
|
-
return true if desired_tags.any? && existing_tags.empty?
|
|
39
|
-
|
|
40
|
-
(desired_tags & existing_tags).none?
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|