first_and_only 1.0.0 → 2.0.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/.travis.yml +7 -0
- data/README.md +34 -6
- data/first_and_only.gemspec +3 -2
- data/lib/first_and_only.rb +6 -2
- data/lib/first_and_only/version.rb +1 -1
- data/spec/first_and_only_spec.rb +79 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/vim_formatter.rb +35 -0
- metadata +35 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f854f3edf7c69ae6b88c87c29678b91678ed91c
|
4
|
+
data.tar.gz: c2d49e72515a4d1bac8367c76ecfaa473efed78c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a477760c26ecaabfc516a8968ccab37467bcba33ee5197d15eae078f56f235a71acb02868c0932bc0203cef3bf99a44f3d17c71b0e72e349aaf4c50b5de921c2
|
7
|
+
data.tar.gz: cd2cd50bbed8f4f40ed478f5d20ade952272a5047cc7f6144c17657374e177a13df713f918bb8687947bab43ea946e03e14b107839e29e582d508fa800056eab
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,24 +1,52 @@
|
|
1
1
|
# FirstAndOnly
|
2
2
|
|
3
|
-
|
3
|
+
Get the first element from an enumerable, and assert that there is only one element to take.
|
4
|
+
|
5
|
+
```
|
6
|
+
[:just_one].first_and_only! # => :just_one
|
7
|
+
```
|
8
|
+
|
9
|
+
```
|
10
|
+
[].first_and_only! # => 0 (Enumerable::FirstAndOnly::CountNotOne)
|
11
|
+
```
|
12
|
+
|
13
|
+
```
|
14
|
+
[:one, :and_two].first_and_only! # => 2 (Enumerable::FirstAndOnly::CountNotOne)
|
15
|
+
```
|
16
|
+
|
17
|
+
## Why?
|
18
|
+
|
19
|
+
Sometimes in a spec you have a collection object that you know only has one element. You set it up this way. Later spec refactors may accidentally introduce a second element. To prevent accidental false positives, and to make fixing failures introduced by this easier, you can use `#first_and_only!` to codify the "there is only one" assumption.
|
20
|
+
|
21
|
+
Another common use case for `#first_and_only` is `id` assertions on HTML documents. A valid HTML document can only have one occurrence of each `id` attribute, but authoring tools and browsers don't typically enforce this. If you hang your JavaScript of the "only one" assumption of `id` attributes, you should enforce this with a specification.
|
22
|
+
|
23
|
+
This gem follows the same line of thinking as the [fetching gem](https://github.com/covermymeds/fetching-gem)
|
4
24
|
|
5
25
|
## Installation
|
6
26
|
|
7
27
|
Add this line to your application's Gemfile:
|
8
28
|
|
9
|
-
|
29
|
+
```
|
30
|
+
gem 'first_and_only'
|
31
|
+
```
|
10
32
|
|
11
33
|
And then execute:
|
12
34
|
|
13
|
-
|
35
|
+
```
|
36
|
+
$ bundle
|
37
|
+
```
|
14
38
|
|
15
39
|
Or install it yourself as:
|
16
40
|
|
17
|
-
|
41
|
+
```
|
42
|
+
$ gem install first_and_only
|
43
|
+
```
|
18
44
|
|
19
|
-
##
|
45
|
+
## Running specs
|
20
46
|
|
21
|
-
|
47
|
+
```
|
48
|
+
bundle exec rspec
|
49
|
+
```
|
22
50
|
|
23
51
|
## Contributing
|
24
52
|
|
data/first_and_only.gemspec
CHANGED
@@ -17,11 +17,11 @@ Get the first element from an enumerable, and assert that there is only one elem
|
|
17
17
|
```
|
18
18
|
|
19
19
|
```
|
20
|
-
[].first_and_only! # => 0 (Enumerable::FirstAndOnly::
|
20
|
+
[].first_and_only! # => 0 (Enumerable::FirstAndOnly::CountNotOne)
|
21
21
|
```
|
22
22
|
|
23
23
|
```
|
24
|
-
[:one, :and_two].first_and_only! # => 2 (Enumerable::FirstAndOnly::
|
24
|
+
[:one, :and_two].first_and_only! # => 2 (Enumerable::FirstAndOnly::CountNotOne)
|
25
25
|
```
|
26
26
|
SUMMARY
|
27
27
|
spec.homepage = "https://github.com/dapplebeforedawn/first-and-only-gem"
|
@@ -34,4 +34,5 @@ Get the first element from an enumerable, and assert that there is only one elem
|
|
34
34
|
|
35
35
|
spec.add_development_dependency "bundler", "~> 1.3"
|
36
36
|
spec.add_development_dependency "rake"
|
37
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
37
38
|
end
|
data/lib/first_and_only.rb
CHANGED
@@ -2,11 +2,15 @@ require "first_and_only/version"
|
|
2
2
|
|
3
3
|
module Enumerable
|
4
4
|
def first_and_only!
|
5
|
-
fail(FirstAndOnly::
|
5
|
+
fail(FirstAndOnly::CountNotOne.new count) if first(2).count != 1
|
6
6
|
first
|
7
7
|
end
|
8
8
|
|
9
9
|
module FirstAndOnly
|
10
|
-
|
10
|
+
class CountNotOne < StandardError
|
11
|
+
def initialize(count)
|
12
|
+
super("Expected the count to be 1, but it was #{count}.")
|
13
|
+
end
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Enumerable#first_and_only!" do
|
4
|
+
|
5
|
+
shared_examples_for "it_has_one_element" do
|
6
|
+
specify { expect(subject.first_and_only!).to eq(:element) }
|
7
|
+
end
|
8
|
+
|
9
|
+
shared_examples_for "it_is_empty" do
|
10
|
+
specify do
|
11
|
+
expect { subject.first_and_only! }.to raise_error(
|
12
|
+
Enumerable::FirstAndOnly::CountNotOne,
|
13
|
+
"Expected the count to be 1, but it was 0."
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
shared_examples_for "it_has_more_than_one_element" do
|
19
|
+
specify do
|
20
|
+
fail "bad example" if subject.count < 2
|
21
|
+
|
22
|
+
expect { subject.first_and_only! }.to raise_error(
|
23
|
+
Enumerable::FirstAndOnly::CountNotOne,
|
24
|
+
"Expected the count to be 1, but it was #{subject.count}."
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "an array" do
|
30
|
+
|
31
|
+
context "with one element" do
|
32
|
+
subject { [:element] }
|
33
|
+
it_behaves_like "it_has_one_element"
|
34
|
+
end
|
35
|
+
|
36
|
+
context "that is empty" do
|
37
|
+
subject { [] }
|
38
|
+
it_behaves_like "it_is_empty"
|
39
|
+
end
|
40
|
+
|
41
|
+
context "with two elements" do
|
42
|
+
subject { [:element, :element] }
|
43
|
+
it_behaves_like "it_has_more_than_one_element"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "any enumerable" do
|
48
|
+
let(:my_enumerable) do
|
49
|
+
Class.new do
|
50
|
+
include Enumerable
|
51
|
+
|
52
|
+
def initialize *args
|
53
|
+
@args = args
|
54
|
+
end
|
55
|
+
|
56
|
+
def each
|
57
|
+
@args.each { |arg| yield(arg) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with one element" do
|
63
|
+
subject { my_enumerable.new(:element) }
|
64
|
+
it_behaves_like "it_has_one_element"
|
65
|
+
end
|
66
|
+
|
67
|
+
context "empty" do
|
68
|
+
subject { my_enumerable.new() }
|
69
|
+
it_behaves_like "it_is_empty"
|
70
|
+
end
|
71
|
+
|
72
|
+
context "with more than on element" do
|
73
|
+
subject { my_enumerable.new(:element, :element) }
|
74
|
+
it_behaves_like "it_has_more_than_one_element"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "first_and_only"
|
2
|
+
require "rspec/autorun"
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
Pathname(__FILE__).dirname.join("support").each_child { |f| require f }
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# Run specs in random order to surface order dependencies. If you find an
|
9
|
+
# order dependency and want to debug it, you can fix the order by providing
|
10
|
+
# the seed, which is printed after each run.
|
11
|
+
# --seed 1234
|
12
|
+
config.order = "random"
|
13
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Adapted from https://github.com/bronson/vim-runtest/blob/master/rspec_formatter.rb.
|
2
|
+
require "rspec/core/formatters/base_text_formatter"
|
3
|
+
|
4
|
+
class VimFormatter < RSpec::Core::Formatters::BaseTextFormatter
|
5
|
+
|
6
|
+
def example_failed example
|
7
|
+
exception = example.execution_result[:exception]
|
8
|
+
path = Regexp.last_match[1] if exception.backtrace.find do |frame|
|
9
|
+
frame =~ %r{\b(spec/.*_spec\.rb:\d+)(?::|\z)}
|
10
|
+
end
|
11
|
+
message = format_message exception.message
|
12
|
+
path = format_caller(path || " ")
|
13
|
+
output.puts "#{path}: #{example.example_group.description.strip} " \
|
14
|
+
"#{example.description.strip}: #{message.strip}" if path
|
15
|
+
end
|
16
|
+
|
17
|
+
def example_pending *_args; end
|
18
|
+
|
19
|
+
def dump_failures *_args; end
|
20
|
+
|
21
|
+
def dump_pending *_args; end
|
22
|
+
|
23
|
+
def message _msg; end
|
24
|
+
|
25
|
+
def dump_summary *_args; end
|
26
|
+
|
27
|
+
def seed *_args; end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def format_message msg
|
32
|
+
msg.gsub("\n", " ")[0, 80]
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
metadata
CHANGED
@@ -1,43 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: first_and_only
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Lorenz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-10-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.14.1
|
41
55
|
description: |
|
42
56
|
Get the first element from an enumerable, and assert that there is only one element to take.
|
43
57
|
email:
|
@@ -46,7 +60,8 @@ executables: []
|
|
46
60
|
extensions: []
|
47
61
|
extra_rdoc_files: []
|
48
62
|
files:
|
49
|
-
- .gitignore
|
63
|
+
- ".gitignore"
|
64
|
+
- ".travis.yml"
|
50
65
|
- Gemfile
|
51
66
|
- LICENSE.txt
|
52
67
|
- README.md
|
@@ -54,6 +69,9 @@ files:
|
|
54
69
|
- first_and_only.gemspec
|
55
70
|
- lib/first_and_only.rb
|
56
71
|
- lib/first_and_only/version.rb
|
72
|
+
- spec/first_and_only_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/support/vim_formatter.rb
|
57
75
|
homepage: https://github.com/dapplebeforedawn/first-and-only-gem
|
58
76
|
licenses:
|
59
77
|
- MIT
|
@@ -64,20 +82,23 @@ require_paths:
|
|
64
82
|
- lib
|
65
83
|
required_ruby_version: !ruby/object:Gem::Requirement
|
66
84
|
requirements:
|
67
|
-
- -
|
85
|
+
- - ">="
|
68
86
|
- !ruby/object:Gem::Version
|
69
87
|
version: '0'
|
70
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
89
|
requirements:
|
72
|
-
- -
|
90
|
+
- - ">="
|
73
91
|
- !ruby/object:Gem::Version
|
74
92
|
version: '0'
|
75
93
|
requirements: []
|
76
94
|
rubyforge_project:
|
77
|
-
rubygems_version: 2.
|
95
|
+
rubygems_version: 2.4.8
|
78
96
|
signing_key:
|
79
97
|
specification_version: 4
|
80
|
-
summary:
|
81
|
-
=> 0 (Enumerable::FirstAndOnly::
|
82
|
-
=> 2 (Enumerable::FirstAndOnly::
|
83
|
-
test_files:
|
98
|
+
summary: "``` [:just_one].first_and_only! # => :just_one ``` ``` [].first_and_only!
|
99
|
+
\ # => 0 (Enumerable::FirstAndOnly::CountNotOne) ``` ``` [:one, :and_two].first_and_only!
|
100
|
+
\ # => 2 (Enumerable::FirstAndOnly::CountNotOne) ```"
|
101
|
+
test_files:
|
102
|
+
- spec/first_and_only_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
- spec/support/vim_formatter.rb
|