nameq 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a09226e4e96b8ff278f6439518ba191254ef5d25
4
+ data.tar.gz: 983748dd980081fd37d19a605ad5843a998a41f4
5
+ SHA512:
6
+ metadata.gz: a8fcf0a8a5083ea74a1ffe149f64f115d3169683e70fe26a89d139143a09ae4af1cc01edfad61f54bc62ff10ea4c74b92c5dacc7d4f53cad7069d86fda4fc292
7
+ data.tar.gz: 39cd8cbe32de12c35a3f6f8df5028f01d7c628e0fa5e222d3edfba4c9f86c37df39dd23312cb9aca4b791fba26c5f9dbd446f3f67417a0710eb40a29718aad71
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: kevinstuffandthings
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior.
15
+
16
+ **Expected behavior**
17
+ A clear and concise description of what you expected to happen.
18
+
19
+ **Logs**
20
+ If applicable, add code/outputs to help explain your problem.
21
+
22
+ **Additional context**
23
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: kevinstuffandthings
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,33 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: Ruby
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ jobs:
17
+ test:
18
+
19
+ runs-on: ubuntu-latest
20
+
21
+ steps:
22
+ - uses: actions/checkout@v2
23
+ - name: Set up Ruby
24
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
25
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
26
+ # uses: ruby/setup-ruby@v1
27
+ uses: ruby/setup-ruby@ec106b438a1ff6ff109590de34ddc62c540232e0
28
+ with:
29
+ ruby-version: 2.6
30
+ - name: Install dependencies
31
+ run: bundle install
32
+ - name: Run tests
33
+ run: bundle exec rspec
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ .bundle
4
+ .yardoc
5
+ doc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4
4
+ - 2.5
5
+ - 2.6
6
+ script:
7
+ - bundle exec rspec
@@ -0,0 +1 @@
1
+ --protected lib/**/*.rb
@@ -0,0 +1,76 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, sex characteristics, gender identity and expression,
9
+ level of experience, education, socio-economic status, nationality, personal
10
+ appearance, race, religion, or sexual identity and orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at kevinstuffandthings@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72
+
73
+ [homepage]: https://www.contributor-covenant.org
74
+
75
+ For answers to common questions about this code of conduct, see
76
+ https://www.contributor-covenant.org/faq
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kevin McDonald
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # NameQ [![Build Status](https://travis-ci.com/kevinstuffandthings/nameq.svg?branch=master)](https://travis-ci.com/kevinstuffandthings/nameq) [![Gem Version](https://badge.fury.io/rb/nameq.svg)](https://badge.fury.io/rb/nameq)
2
+
3
+ For when you need to name things, but not worry about whether or not those names are already taken.
4
+
5
+ Originally developed for [Simulmedia](https://simulmedia.com).
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ # update with the version of your choice
12
+ gem 'nameq'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```bash
18
+ $ bundle install
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```bash
24
+ $ gem install nameq
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ Build a pool of regular text strings. Each time you take an item from the pool, the pool will take note.
30
+
31
+ ```ruby
32
+ pool = NameQ::TextPool.new(['Kevin', 'Lynne', 'You', 'You (1)'])
33
+
34
+ pool.take('Someone Else')
35
+ # => "Someone Else"
36
+
37
+ pool.take('Kevin')
38
+ # => "Kevin (1)"
39
+
40
+ pool.take('You')
41
+ # => "You (2)"
42
+
43
+ pool.take('Kevin')
44
+ # => "Kevin (2)"
45
+
46
+ pool.take('Someone Else')
47
+ # => "Someone Else (1)"
48
+ ```
49
+
50
+ Or point to a directory. In addition to the `take` behavior mentioned in the `TextPool`, new entries in the specified directory will
51
+ be considered on each pool operation.
52
+
53
+ ```ruby
54
+ pool = NameQ::Directory.new('/tmp/nameq')
55
+
56
+ pool.take('not-a-file')
57
+ # => 'not-a-file'
58
+
59
+ pool.take('Rakefile')
60
+ # => 'Rakefile (1)'
61
+ ```
62
+
63
+ Either type of pool can be treated case-insensitively:
64
+
65
+ ```ruby
66
+ NameQ::Directory.new('/tmp/nameq', case_sensitive: false)
67
+ ```
68
+
69
+ # Problems?
70
+ Please submit an [issue](https://github.com/kevinstuffandthings/nameq/issues).
71
+ We'll figure out how to get you up and running with NameQ as smoothly as possible.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ require "bundler/setup"
3
+ require "bundler/gem_tasks"
4
+
5
+ Dir.glob('lib/tasks/*.rake').each { |r| load r }
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require "nameq/version"
3
+
4
+ %w[string_entry filename_entry suffix list pool].each { |f| require "nameq/support/#{f}" }
5
+ %w[text_pool directory].each { |f| require "nameq/#{f}" }
6
+
7
+
8
+ # NameQ
9
+ module NameQ
10
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ class Directory < Support::Pool
4
+ # Set up a new directory-based pool.
5
+ # @param name [String] the location on disk of the directory
6
+ # @param case_sensitive [Boolean] should uniqueness of names consider case-sensitivity?
7
+ # @return [Directory]
8
+ def initialize(name, case_sensitive: true)
9
+ list = Support::List.new(case_sensitive: case_sensitive) do
10
+ Dir[File.join(name, '*')].map { |f| File.basename(f) }
11
+ end
12
+ super list
13
+ end
14
+
15
+ protected
16
+
17
+ def entry_factory
18
+ Support::FilenameEntry
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require_relative './string_entry'
3
+
4
+ module NameQ
5
+ module Support
6
+ class FilenameEntry < StringEntry
7
+ def resolve(suffix = nil)
8
+ return @text if suffix.nil?
9
+ extension = File.extname(@text)
10
+ basename = suffix.strip(@text.chomp(extension))
11
+ "#{basename}#{suffix}#{extension}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ module Support
4
+ class List
5
+ def initialize(items = nil, case_sensitive: true, &refill)
6
+ @items = items || []
7
+ @comparator = case_sensitive ? :eql? : :casecmp?
8
+ @refill = refill
9
+ end
10
+
11
+ def add(name)
12
+ @items << name
13
+ name
14
+ end
15
+
16
+ def include?(name)
17
+ all.any? { |a| a.send(@comparator, name) }
18
+ end
19
+
20
+ private
21
+
22
+ def all
23
+ @items + (@refill&.call || [])
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ module Support
4
+ class Pool
5
+ # Take a name from the available pool of names,
6
+ # suffixed if necessary.
7
+ # @param name [String] the name to find a variant of within the pool
8
+ # @return [String] the resolved name from the pool
9
+ def take(name)
10
+ return list.add(name) unless list.include?(name)
11
+ resolve(entry_factory.new(name)).tap { |n| list.add(n) }
12
+ end
13
+
14
+ protected
15
+
16
+ attr_reader :list
17
+
18
+ def initialize(list)
19
+ @list = list
20
+ end
21
+
22
+ def entry_factory
23
+ Support::StringEntry
24
+ end
25
+
26
+ private
27
+
28
+ def resolve(entry)
29
+ suffixes.each do |suffix|
30
+ resolution = entry.resolve(suffix)
31
+ return resolution unless list.include?(resolution)
32
+ end
33
+ end
34
+
35
+ def suffixes
36
+ (1 .. Float::INFINITY).lazy.map { |i| Suffix.new(i) }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ module Support
4
+ class StringEntry
5
+ def initialize(text)
6
+ @text = text
7
+ end
8
+
9
+ def resolve(suffix = nil)
10
+ return @text if suffix.nil?
11
+ "#{suffix.strip(@text)}#{suffix}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ module Support
4
+ class Suffix
5
+ TOKEN = '%{index}'
6
+ attr_reader :index
7
+
8
+ def initialize(index, template: nil)
9
+ @index = index
10
+ @template = template || " (#{TOKEN})"
11
+ end
12
+
13
+ def strip(text)
14
+ text.sub(regex, '')
15
+ end
16
+
17
+ def to_s
18
+ @template % {index: index}
19
+ end
20
+
21
+ private
22
+
23
+ def regex
24
+ @_regex ||= begin
25
+ first, last = @template.split(TOKEN)
26
+ Regexp.new(Regexp.quote(first) + "[0-9]+" + Regexp.quote(last) + "$")
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ class TextPool < Support::Pool
4
+ # Set up a new text-based pool.
5
+ # @param name [Array<String>] the list of items to be included in the initial pool
6
+ # @param case_sensitive [Boolean] should uniqueness of names consider case-sensitivity?
7
+ # @return [TextPool]
8
+ def initialize(items, case_sensitive: true)
9
+ super(Support::List.new(items, case_sensitive: case_sensitive))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module NameQ
3
+ VERSION = "0.0.1"
4
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ # coding: utf-8
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'nameq/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "nameq"
9
+ spec.version = NameQ::VERSION
10
+ spec.authors = ["Kevin McDonald"]
11
+ spec.email = ["kevinstuffandthings@gmail.com"]
12
+ spec.summary = %q{Create unique names via numeric suffixes}
13
+ spec.description = spec.summary
14
+ spec.homepage = "https://github.com/kevinstuffandthings/nameq"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler"
23
+ spec.add_development_dependency "pry"
24
+ spec.add_development_dependency "pry-byebug"
25
+ spec.add_development_dependency "rack-test"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "yard"
29
+ end
@@ -0,0 +1,46 @@
1
+ module NameQ
2
+ describe Directory do
3
+ let(:path) { '/path/to/somewhere' }
4
+ let(:subject) { described_class.new(path) }
5
+
6
+ it 'is a kind of pool' do
7
+ expect(subject).to be_a NameQ::Support::Pool
8
+ end
9
+
10
+ context 'internals' do
11
+ context 'underlying list' do
12
+ let(:list) { instance_double(NameQ::Support::List) }
13
+ let(:result) { subject.instance_variable_get('@list') }
14
+
15
+ it 'builds the right kind of list by default' do
16
+ expect(NameQ::Support::List).to receive(:new).with(case_sensitive: true).and_return list
17
+ expect(result).to eq list
18
+ end
19
+
20
+ context 'case-insensitive' do
21
+ let(:subject) { described_class.new(path, case_sensitive: false) }
22
+
23
+ it 'builds the right kind of list by default' do
24
+ expect(NameQ::Support::List).to receive(:new).with(case_sensitive: false).and_return list
25
+ expect(result).to eq list
26
+ end
27
+ end
28
+
29
+ context 'refresh' do
30
+ let(:path) { File.expand_path('.') }
31
+ let(:items) { result.send(:all) }
32
+
33
+ it 'has a block that does the right stuff' do
34
+ expect(items).to include('nameq.gemspec', 'Gemfile', 'Rakefile')
35
+ end
36
+ end
37
+ end
38
+
39
+ describe '#entry_factory' do
40
+ it 'has a special filename entry factory' do
41
+ expect(subject.send(:entry_factory)).to eq NameQ::Support::FilenameEntry
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ require 'nameq'
2
+
3
+ RSpec.configure do |config|
4
+ config.expect_with :rspec do |expectations|
5
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
6
+ end
7
+
8
+ config.mock_with :rspec do |mocks|
9
+ mocks.verify_partial_doubles = true
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ module NameQ
2
+ module Support
3
+ describe FilenameEntry do
4
+ let(:text) { 'Some Filename' }
5
+ let(:subject) { described_class.new(text) }
6
+
7
+ describe '#resolve' do
8
+ let(:suffix) { instance_double(NameQ::Support::Suffix, to_s: '-yup') }
9
+ let(:stripped_text) { "#{text} stripped" }
10
+ before(:each) { allow(suffix).to receive(:strip).with(text).and_return stripped_text }
11
+
12
+ context 'without extension' do
13
+ it 'resolves itself without a suffix' do
14
+ expect(subject.resolve).to eq text
15
+ end
16
+
17
+ it 'resolves itself with a suffix' do
18
+ expect(subject.resolve(suffix)).to eq "#{stripped_text}#{suffix}"
19
+ end
20
+ end
21
+
22
+ context 'with extension' do
23
+ let(:extension) { '.meh' }
24
+ let(:subject) { described_class.new("#{text}#{extension}") }
25
+
26
+ it 'resolves itself without a suffix' do
27
+ expect(subject.resolve).to eq "#{text}#{extension}"
28
+ end
29
+
30
+ it 'resolves itself with a suffix' do
31
+ expect(subject.resolve(suffix)).to eq "#{stripped_text}#{suffix}#{extension}"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,79 @@
1
+ module NameQ
2
+ module Support
3
+ describe List do
4
+ let(:array) { %w[One Two Three] }
5
+ let(:subject) { described_class.new(array.clone) }
6
+
7
+ context 'internals' do
8
+ describe '#all' do
9
+ let(:refiller) { %w[Four Five] }
10
+ let(:all) { subject.send(:all) }
11
+
12
+ context 'array only' do
13
+ it 'knows all its stuff' do
14
+ expect(all).to match_array array
15
+ end
16
+ end
17
+
18
+ context 'refiller only' do
19
+ let(:subject) { described_class.new { refiller } }
20
+
21
+ it 'knows all its stuff' do
22
+ expect(all).to match_array refiller
23
+ end
24
+ end
25
+
26
+ context 'all options' do
27
+ let(:subject) { described_class.new(array) { refiller } }
28
+
29
+ it 'knows all its stuff' do
30
+ expect(all).to match_array(array + refiller)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#include?' do
37
+ context 'default (case-sensitive)' do
38
+ it 'can miss' do
39
+ expect(subject.include?('Eight')).to be false
40
+ end
41
+
42
+ it 'can miss due to case' do
43
+ expect(subject.include?('two')).to be false
44
+ end
45
+
46
+ it 'can match' do
47
+ expect(subject.include?('Two')).to be true
48
+ end
49
+ end
50
+
51
+ context 'case-insensitive' do
52
+ let(:subject) { described_class.new(array, case_sensitive: false) }
53
+
54
+ it 'can miss' do
55
+ expect(subject.include?('Eight')).to be false
56
+ end
57
+
58
+ it 'can match outside case' do
59
+ expect(subject.include?('three')).to be true
60
+ end
61
+
62
+ it 'can match' do
63
+ expect(subject.include?('Three')).to be true
64
+ end
65
+ end
66
+ end
67
+
68
+ describe '#add' do
69
+ let(:value) { 'Eight' }
70
+
71
+ it 'adds items when you ask it to' do
72
+ expect(subject.send(:all)).to match_array array
73
+ expect(subject.add(value)).to eq value
74
+ expect(subject.send(:all)).to match_array(array + [value])
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,79 @@
1
+ module NameQ
2
+ module Support
3
+ describe Pool do
4
+ let(:list) { instance_double(NameQ::Support::List) }
5
+ let(:subject) { described_class.new(list) }
6
+
7
+ context 'internals' do
8
+ describe '#suffixes' do
9
+ let(:suffixes) { subject.send(:suffixes) }
10
+
11
+ it 'can get a suffix' do
12
+ result = suffixes.first
13
+ expect(result).to be_a NameQ::Support::Suffix
14
+ expect(result.index).to eq 1
15
+ end
16
+
17
+ it 'can get 10 suffixes and you have to just trust that it can get even more' do
18
+ count = 0
19
+ suffixes.each_with_index do |suffix, i|
20
+ expect(suffix).to be_a NameQ::Support::Suffix
21
+ expect(suffix.index).to eq i + 1
22
+ break if (count += 1) >= 10
23
+ end
24
+ expect(count).to eq 10
25
+ end
26
+ end
27
+
28
+ describe '#entry_factory' do
29
+ it 'has a default entry factory' do
30
+ expect(subject.send(:entry_factory)).to eq NameQ::Support::StringEntry
31
+ end
32
+ end
33
+
34
+ describe '#resolve' do
35
+ let(:resolution) { double }
36
+ let(:entry) { instance_double(NameQ::Support::StringEntry, resolve: resolution) }
37
+ let(:suffixes) { 3.times.map { |i| instance_double(NameQ::Support::Suffix, index: (i + 1) * 8) } }
38
+ before(:each) do
39
+ allow(subject).to receive(:suffixes).and_return suffixes
40
+ allow(list).to receive(:include?).and_return true
41
+ allow(list).to receive(:include?).with(resolution).and_return false
42
+ end
43
+
44
+ it 'can hit on the first try' do
45
+ expect(subject.send(:resolve, entry)).to eq resolution
46
+ end
47
+
48
+ it 'can hit... eventually...' do
49
+ [0, 1].each { |i| allow(entry).to receive(:resolve).with(suffixes[i]).and_return double }
50
+ expect(subject.send(:resolve, entry)).to eq resolution
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#take' do
56
+ let(:name) { double }
57
+
58
+ it 'can take a name when available' do
59
+ allow(list).to receive(:include?).with(name).and_return false
60
+ expect(list).to receive(:add).with(name).and_return name
61
+ expect(subject.take(name)).to eq name
62
+ end
63
+
64
+ context 'fallback' do
65
+ let(:entry) { double }
66
+ let(:resolved_name) { double }
67
+ before(:each) { allow(subject.send(:entry_factory)).to receive(:new).with(name).and_return entry }
68
+
69
+ it 'can fall back a few when needed' do
70
+ allow(list).to receive(:include?).with(name).and_return true
71
+ expect(subject).to receive(:resolve).with(entry).and_return resolved_name
72
+ expect(list).to receive(:add).with(resolved_name)
73
+ expect(subject.take(name)).to eq resolved_name
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,22 @@
1
+ module NameQ
2
+ module Support
3
+ describe StringEntry do
4
+ let(:text) { 'This is something' }
5
+ let(:subject) { described_class.new(text) }
6
+
7
+ describe '#resolve' do
8
+ let(:suffix) { instance_double(NameQ::Support::Suffix, to_s: '!') }
9
+ let(:stripped_text) { "#{text} stripped" }
10
+ before(:each) { allow(suffix).to receive(:strip).with(text).and_return stripped_text }
11
+
12
+ it 'resolves itself without a suffix' do
13
+ expect(subject.resolve).to eq text
14
+ end
15
+
16
+ it 'resolves itself with a suffix' do
17
+ expect(subject.resolve(suffix)).to eq "#{stripped_text}!"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ module NameQ
2
+ module Support
3
+ describe Suffix do
4
+ let(:index) { '88' }
5
+ let(:subject) { described_class.new(index) }
6
+ let(:custom_template) { '-[%{index}]-' }
7
+
8
+ describe '#strip' do
9
+ let(:text) { 'How about this' }
10
+
11
+ it 'can ignore templateless content' do
12
+ expect(subject.strip(text)).to eq text
13
+ end
14
+
15
+ it 'can strip the content of its template' do
16
+ expect(subject.strip("#{text} (7)")).to eq text
17
+ end
18
+
19
+ it 'is not tripped up by template-like content' do
20
+ expect(subject.strip("#{text} (7)!")).to eq "#{text} (7)!"
21
+ end
22
+
23
+ context 'overridden template' do
24
+ let(:subject) { described_class.new(index, template: custom_template) }
25
+
26
+ it 'can strip the content of its template' do
27
+ expect(subject.strip("#{text}-[7]-")).to eq text
28
+ end
29
+
30
+ it 'is not tripped up by template-like content' do
31
+ expect(subject.strip("#{text}-[7]-!")).to eq "#{text}-[7]-!"
32
+ end
33
+
34
+ it 'is not tripped up by default template-like content' do
35
+ expect(subject.strip("#{text} (7)")).to eq "#{text} (7)"
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#to_s' do
41
+ it 'works out fine with the default template' do
42
+ expect(subject.to_s).to eq ' (88)'
43
+ end
44
+
45
+ context 'overridden template' do
46
+ let(:subject) { described_class.new(index, template: custom_template) }
47
+
48
+ it 'works out fine with the overridden template' do
49
+ expect(subject.to_s).to eq '-[88]-'
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,37 @@
1
+ module NameQ
2
+ describe TextPool do
3
+ let(:items) { %w[all of these names] }
4
+ let(:subject) { described_class.new(items) }
5
+
6
+ it 'is a kind of pool' do
7
+ expect(subject).to be_a NameQ::Support::Pool
8
+ end
9
+
10
+ context 'internals' do
11
+ context 'underlying list' do
12
+ let(:list) { instance_double(NameQ::Support::List) }
13
+ let(:result) { subject.instance_variable_get('@list') }
14
+
15
+ it 'builds the right kind of list by default' do
16
+ expect(NameQ::Support::List).to receive(:new).with(items, case_sensitive: true).and_return list
17
+ expect(result).to eq list
18
+ end
19
+
20
+ context 'case-insensitive' do
21
+ let(:subject) { described_class.new(items, case_sensitive: false) }
22
+
23
+ it 'builds the right kind of list by default' do
24
+ expect(NameQ::Support::List).to receive(:new).with(items, case_sensitive: false).and_return list
25
+ expect(result).to eq list
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#entry_factory' do
31
+ it 'has a regular string entry factory' do
32
+ expect(subject.send(:entry_factory)).to eq NameQ::Support::StringEntry
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,180 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nameq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kevin McDonald
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-08-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Create unique names via numeric suffixes
112
+ email:
113
+ - kevinstuffandthings@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
119
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
120
+ - ".github/workflows/ruby.yml"
121
+ - ".gitignore"
122
+ - ".rspec"
123
+ - ".travis.yml"
124
+ - ".yardopts"
125
+ - CODE_OF_CONDUCT.md
126
+ - Gemfile
127
+ - LICENSE.txt
128
+ - README.md
129
+ - Rakefile
130
+ - lib/nameq.rb
131
+ - lib/nameq/directory.rb
132
+ - lib/nameq/support/filename_entry.rb
133
+ - lib/nameq/support/list.rb
134
+ - lib/nameq/support/pool.rb
135
+ - lib/nameq/support/string_entry.rb
136
+ - lib/nameq/support/suffix.rb
137
+ - lib/nameq/text_pool.rb
138
+ - lib/nameq/version.rb
139
+ - nameq.gemspec
140
+ - spec/directory_spec.rb
141
+ - spec/spec_helper.rb
142
+ - spec/support/filename_entry_spec.rb
143
+ - spec/support/list_spec.rb
144
+ - spec/support/pool_spec.rb
145
+ - spec/support/string_entry_spec.rb
146
+ - spec/support/suffix_spec.rb
147
+ - spec/text_pool_spec.rb
148
+ homepage: https://github.com/kevinstuffandthings/nameq
149
+ licenses:
150
+ - MIT
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.6.14
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Create unique names via numeric suffixes
172
+ test_files:
173
+ - spec/directory_spec.rb
174
+ - spec/spec_helper.rb
175
+ - spec/support/filename_entry_spec.rb
176
+ - spec/support/list_spec.rb
177
+ - spec/support/pool_spec.rb
178
+ - spec/support/string_entry_spec.rb
179
+ - spec/support/suffix_spec.rb
180
+ - spec/text_pool_spec.rb