first_and_only 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|