yarn_skein 0.1.1
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 +7 -0
- data/.github/actions/ruby-setup/action.yml +23 -0
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/ci.yml +24 -0
- data/.github/workflows/release.yml +61 -0
- data/CHANGELOG.md +11 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/LICENSE +21 -0
- data/README.md +200 -0
- data/Rakefile +58 -0
- data/lib/yarn_skein/fiber_blend.rb +34 -0
- data/lib/yarn_skein/version.rb +6 -0
- data/lib/yarn_skein/weight_category.rb +30 -0
- data/lib/yarn_skein/yarn.rb +100 -0
- data/lib/yarn_skein.rb +17 -0
- data/sig/yarn_skein.rbs +4 -0
- metadata +73 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4fdd27e3f715f8b7f200f6bd5f604e503dad0bb61cb2d0f8cd0100680c902e77
|
|
4
|
+
data.tar.gz: 0a121d542d7559e6a5b1c6ef36df30f0a7676c0b64f25c3194eaf90124c8cf7b
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2a0f0c9d824f8689a18265c0d20dde24267649a4ff2f0d174330dff7f0de0e9e60575075d9f25fe32865fa3c9530f206c2f72652afbad050bb5070ce3f28ed10
|
|
7
|
+
data.tar.gz: 76e392350e32d039c950b9b9680b027e2554982335e8b2c01abe2f76f30e29dae1dca3630b4316411e41eff25d28659591f7fb573f6932505036e6e05c2a2925
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Ruby Setup
|
|
2
|
+
description: Setup Ruby and install dependencies
|
|
3
|
+
|
|
4
|
+
runs:
|
|
5
|
+
using: composite
|
|
6
|
+
steps:
|
|
7
|
+
- uses: actions/checkout@v5
|
|
8
|
+
|
|
9
|
+
- uses: jdx/mise-action@v3
|
|
10
|
+
with:
|
|
11
|
+
install: true
|
|
12
|
+
cache: true
|
|
13
|
+
mise_toml: |
|
|
14
|
+
[tools]
|
|
15
|
+
ruby = "4.0.1"
|
|
16
|
+
|
|
17
|
+
- uses: actions/cache@v4
|
|
18
|
+
with:
|
|
19
|
+
path: vendor/bundle
|
|
20
|
+
key: bundle-${{ runner.os }}-${{ hashFiles('Gemfile.lock') }}
|
|
21
|
+
|
|
22
|
+
- run: bundle install --jobs 4 --retry 3
|
|
23
|
+
shell: bash
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
env:
|
|
3
|
+
BUNDLE_PATH: vendor/bundle
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches:
|
|
8
|
+
- main
|
|
9
|
+
pull_request:
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
lint:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v5
|
|
16
|
+
- uses: ./.github/actions/ruby-setup
|
|
17
|
+
- run: bundle exec standardrb
|
|
18
|
+
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v5
|
|
23
|
+
- uses: ./.github/actions/ruby-setup
|
|
24
|
+
- run: bundle exec rspec
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
|
|
18
|
+
- name: Extract version from tag
|
|
19
|
+
id: version
|
|
20
|
+
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
|
21
|
+
|
|
22
|
+
- name: Verify gem version matches tag
|
|
23
|
+
run: |
|
|
24
|
+
GEM_VERSION=$(ruby -e "puts Gem::Specification.load('yarn_skein.gemspec').version")
|
|
25
|
+
if [ "$GEM_VERSION" != "${{ steps.version.outputs.VERSION }}" ]; then
|
|
26
|
+
echo "❌ Version mismatch: gemspec=$GEM_VERSION tag=${{ steps.version.outputs.VERSION }}"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
echo "✅ Gem version matches tag"
|
|
30
|
+
|
|
31
|
+
- name: Verify changelog entry
|
|
32
|
+
run: |
|
|
33
|
+
if ! grep -q "## \[${{ steps.version.outputs.VERSION }}\]" CHANGELOG.md; then
|
|
34
|
+
echo "❌ Version ${{ steps.version.outputs.VERSION }} not found in CHANGELOG.md"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
echo "✅ Changelog entry verified for ${{ steps.version.outputs.VERSION }}"
|
|
38
|
+
|
|
39
|
+
- name: Setup Ruby
|
|
40
|
+
uses: ruby/setup-ruby@v1
|
|
41
|
+
with:
|
|
42
|
+
ruby-version: "4.0.1"
|
|
43
|
+
bundler-cache: true
|
|
44
|
+
|
|
45
|
+
- name: Build gem
|
|
46
|
+
run: gem build yarn_skein.gemspec
|
|
47
|
+
|
|
48
|
+
- name: Configure RubyGems credentials
|
|
49
|
+
run: |
|
|
50
|
+
mkdir -p ~/.gem
|
|
51
|
+
echo ":rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > ~/.gem/credentials
|
|
52
|
+
chmod 0600 ~/.gem/credentials
|
|
53
|
+
|
|
54
|
+
- name: Publish gem
|
|
55
|
+
run: gem push *.gem
|
|
56
|
+
|
|
57
|
+
- name: Create GitHub release
|
|
58
|
+
uses: softprops/action-gh-release@v2
|
|
59
|
+
with:
|
|
60
|
+
tag_name: ${{ github.ref_name }}
|
|
61
|
+
generate_release_notes: true
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## [0.1.1] - 2026-03-14
|
|
5
|
+
### Added
|
|
6
|
+
- `YarnSkein::Yarn` for modeling a skein's brand, line, yardage, skein weight, and optional fiber content
|
|
7
|
+
- Grist and `yards_per_100g` helpers for comparing yarn density with typed `fiber_units` measurements
|
|
8
|
+
- Weight category lookup via `YarnSkein::WeightCategory` for standard yarn classifications
|
|
9
|
+
- Skein math helpers including `skeins_required`, `total_yardage`, and similar-weight comparisons
|
|
10
|
+
- `YarnSkein::FiberBlend` for representing fiber composition percentages
|
|
11
|
+
- Initial release
|
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
"yarn_skein" follows [The Ruby Community Conduct Guideline](https://www.ruby-lang.org/en/conduct) in all "collaborative space", which is defined as community communications channels (such as mailing lists, submitted patches, commit comments, etc.):
|
|
4
|
+
|
|
5
|
+
* Participants will be tolerant of opposing views.
|
|
6
|
+
* Participants must ensure that their language and actions are free of personal attacks and disparaging personal remarks.
|
|
7
|
+
* When interpreting the words and actions of others, participants should always assume good intentions.
|
|
8
|
+
* Behaviour which can be reasonably considered harassment will not be tolerated.
|
|
9
|
+
|
|
10
|
+
If you have any concerns about behaviour within this project, please contact us at ["TODO: Write your email address"](mailto:"TODO: Write your email address").
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Meagan Waller
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# yarn_skein
|
|
2
|
+
|
|
3
|
+
A Ruby domain model for yarn.
|
|
4
|
+
|
|
5
|
+
`yarn_skein` models the real structural properties of yarn used in knitting, crochet, and other fiber crafts. It builds on typed measurements (via `fiber_units`) to represent yarn attributes like yardage, skein weight, and grist in a clear and safe way.
|
|
6
|
+
|
|
7
|
+
Instead of passing around raw numbers like `210` or `100`, yarn properties are expressed using typed units.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
210.yards
|
|
11
|
+
100.grams
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This makes calculations such as **grist**, **weight category**, and **yardage estimates** straightforward and reliable.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add to your Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "yarn_skein"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Then install:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bundle install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Basic Usage
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require "yarn_skein"
|
|
34
|
+
|
|
35
|
+
yarn = YarnSkein::Yarn.new(
|
|
36
|
+
brand: "Malabrigo",
|
|
37
|
+
line: "Rios",
|
|
38
|
+
yardage: 210.yards,
|
|
39
|
+
skein_weight: 100.grams
|
|
40
|
+
)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Yarn Properties
|
|
44
|
+
|
|
45
|
+
A yarn object models the real characteristics of a skein.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
yarn.brand
|
|
49
|
+
# => "Malabrigo"
|
|
50
|
+
|
|
51
|
+
yarn.line
|
|
52
|
+
# => "Rios"
|
|
53
|
+
|
|
54
|
+
yarn.yardage
|
|
55
|
+
# => 210 yards
|
|
56
|
+
|
|
57
|
+
yarn.skein_weight
|
|
58
|
+
# => 100 grams
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Grist
|
|
62
|
+
|
|
63
|
+
**Grist** is the relationship between yardage and weight.
|
|
64
|
+
|
|
65
|
+
It measures how many yards of yarn exist per gram of fiber.
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
yarn.grist
|
|
69
|
+
# => 2.1 yards per gram
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Internally:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
yardage / skein_weight
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This metric helps compare yarns with different fiber types or densities.
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
|
|
82
|
+
| Yarn | Yardage | Weight | Grist |
|
|
83
|
+
| -------------- | ------- | ------ | ----- |
|
|
84
|
+
| Wool DK | 210 yd | 100 g | 2.1 |
|
|
85
|
+
| Wool Fingering | 400 yd | 100 g | 4.0 |
|
|
86
|
+
|
|
87
|
+
Higher grist = lighter yarn.
|
|
88
|
+
|
|
89
|
+
## Weight Category
|
|
90
|
+
|
|
91
|
+
The gem can estimate a yarn’s **standard weight category** from its yardage.
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
yarn.weight_category
|
|
95
|
+
# => :worsted
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Weight categories follow Craft Yarn Council guidelines.
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
|
|
102
|
+
| Category | Approx yards per 100g |
|
|
103
|
+
| ----------- | --------------------- |
|
|
104
|
+
| lace | 800+ |
|
|
105
|
+
| fingering | 350–450 |
|
|
106
|
+
| sport | 300–350 |
|
|
107
|
+
| dk | 220–300 |
|
|
108
|
+
| worsted | 180–220 |
|
|
109
|
+
| aran | 140–180 |
|
|
110
|
+
| bulky | 100–140 |
|
|
111
|
+
| super_bulky | <100 |
|
|
112
|
+
|
|
113
|
+
## Fiber Content
|
|
114
|
+
|
|
115
|
+
Yarns often contain multiple fibers.
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
YarnSkein::Yarn.new(
|
|
119
|
+
brand: "Malabrigo",
|
|
120
|
+
line: "Rios",
|
|
121
|
+
fiber_content: [
|
|
122
|
+
["merino wool", 100]
|
|
123
|
+
],
|
|
124
|
+
yardage: 210.yards,
|
|
125
|
+
skein_weight: 100.grams
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Future versions may support richer fiber modeling.
|
|
130
|
+
|
|
131
|
+
## Example: Comparing Yarns
|
|
132
|
+
|
|
133
|
+
```ruby
|
|
134
|
+
rios = YarnSkein::Yarn.new(
|
|
135
|
+
brand: "Malabrigo",
|
|
136
|
+
line: "Rios",
|
|
137
|
+
yardage: 210.yards,
|
|
138
|
+
skein_weight: 100.grams
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
sock = YarnSkein::Yarn.new(
|
|
142
|
+
brand: "Malabrigo",
|
|
143
|
+
line: "Sock",
|
|
144
|
+
yardage: 440.yards,
|
|
145
|
+
skein_weight: 100.grams
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
rios.grist
|
|
149
|
+
# => 2.1
|
|
150
|
+
|
|
151
|
+
sock.grist
|
|
152
|
+
# => 4.4
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Future Capabilities
|
|
156
|
+
|
|
157
|
+
Planned features include:
|
|
158
|
+
|
|
159
|
+
* yarn substitution recommendations
|
|
160
|
+
* yardage calculators
|
|
161
|
+
* gauge compatibility helpers
|
|
162
|
+
* skein count estimators
|
|
163
|
+
* stash modeling
|
|
164
|
+
* fiber blend utilities
|
|
165
|
+
|
|
166
|
+
Example future API:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
pattern.required_yardage
|
|
170
|
+
# => 420 yards
|
|
171
|
+
|
|
172
|
+
yarn.skeins_required(pattern)
|
|
173
|
+
# => 2
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Relationship to fiber_units
|
|
177
|
+
|
|
178
|
+
`yarn_skein` builds on the `fiber_units` gem.
|
|
179
|
+
|
|
180
|
+
`fiber_units` provides the measurement system:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
4.inches
|
|
184
|
+
210.yards
|
|
185
|
+
100.grams
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
`yarn_skein` uses those units to model yarn properties.
|
|
189
|
+
|
|
190
|
+
## Development
|
|
191
|
+
|
|
192
|
+
Run tests:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
bundle exec rspec
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT License
|
data/Rakefile
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rspec/core/rake_task"
|
|
5
|
+
require "yard"
|
|
6
|
+
require "yard/rake/yardoc_task"
|
|
7
|
+
require "standard/rake"
|
|
8
|
+
|
|
9
|
+
# YARD Documentation tasks
|
|
10
|
+
begin
|
|
11
|
+
namespace :doc do
|
|
12
|
+
desc "Generate YARD documentation"
|
|
13
|
+
YARD::Rake::YardocTask.new(:generate) do |t|
|
|
14
|
+
t.files = ["lib/**/*.rb", "-", "README.md"]
|
|
15
|
+
t.options = [
|
|
16
|
+
"--output-dir", "doc/yard",
|
|
17
|
+
"--markup", "markdown",
|
|
18
|
+
"--title", "YarnSkein - Yarn metadata and skein calculations",
|
|
19
|
+
"--readme", "README.md"
|
|
20
|
+
]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
desc "Regenerate documentation with cache reset"
|
|
24
|
+
task regenerate: ["doc:clean", "doc:generate"]
|
|
25
|
+
|
|
26
|
+
desc "Clean generated documentation"
|
|
27
|
+
task :clean do
|
|
28
|
+
rm_rf "doc/yard"
|
|
29
|
+
rm_rf ".yardoc"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
desc "Start YARD server for local documentation viewing"
|
|
33
|
+
task :serve do
|
|
34
|
+
sh "bundle exec yard server --reload --port 8808"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
desc "Validate YARD documentation coverage"
|
|
38
|
+
task :coverage do
|
|
39
|
+
sh "bundle exec yard stats --list-undoc"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "Generate complete documentation with coverage report"
|
|
43
|
+
task complete: [:generate, :coverage]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Add shorthand aliases
|
|
47
|
+
task yard: "doc:generate"
|
|
48
|
+
task yard_server: "doc:serve"
|
|
49
|
+
task yard_clean: "doc:clean"
|
|
50
|
+
rescue LoadError
|
|
51
|
+
# YARD is only available in development/test environments
|
|
52
|
+
# Silence this warning in production where it's not needed
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
task :default do
|
|
56
|
+
Rake::Task["standard"].invoke
|
|
57
|
+
Rake::Task["spec"].invoke
|
|
58
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module YarnSkein
|
|
2
|
+
# Represents the fiber composition of a yarn as percentages by fiber type.
|
|
3
|
+
class FiberBlend
|
|
4
|
+
attr_reader :fibers
|
|
5
|
+
|
|
6
|
+
# @param fibers [Hash{Symbol => Numeric}] percentages keyed by fiber name
|
|
7
|
+
# @raise [ArgumentError] if the percentages do not sum to 100
|
|
8
|
+
def initialize(fibers)
|
|
9
|
+
total = fibers.values.sum
|
|
10
|
+
|
|
11
|
+
unless total == 100
|
|
12
|
+
raise ArgumentError, "Fiber percentages must sum to 100, but got #{total}"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
@fibers = fibers
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Return the percentage for a given fiber, or zero if it is absent.
|
|
19
|
+
#
|
|
20
|
+
# @param fiber [Symbol]
|
|
21
|
+
# @return [Numeric]
|
|
22
|
+
def percentage(fiber)
|
|
23
|
+
fibers[fiber] || 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Check whether the blend contains a specific fiber.
|
|
27
|
+
#
|
|
28
|
+
# @param fiber [Symbol]
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def contains?(fiber)
|
|
31
|
+
fibers.key?(fiber)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module YarnSkein
|
|
2
|
+
# Maps yardage density to conventional yarn weight categories.
|
|
3
|
+
module WeightCategory
|
|
4
|
+
# Yardage-per-100g ranges for each supported weight category.
|
|
5
|
+
CATEGORY_RANGES = {
|
|
6
|
+
lace: 800..Float::INFINITY,
|
|
7
|
+
fingering: 350...800,
|
|
8
|
+
sport: 300...350,
|
|
9
|
+
dk: 220...300,
|
|
10
|
+
worsted: 180...220,
|
|
11
|
+
aran: 140...180,
|
|
12
|
+
bulky: 100...140,
|
|
13
|
+
super_bulky: 0...100
|
|
14
|
+
}.freeze
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
# Find the closest standard weight category for a yarn's yardage density.
|
|
19
|
+
#
|
|
20
|
+
# @param yards_per_100g [Numeric]
|
|
21
|
+
# @return [Symbol, nil]
|
|
22
|
+
def for(yards_per_100g)
|
|
23
|
+
CATEGORY_RANGES.each do |category, range|
|
|
24
|
+
return category if range.cover?(yards_per_100g)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module YarnSkein
|
|
2
|
+
# Value object describing a yarn line and one skein's physical properties.
|
|
3
|
+
#
|
|
4
|
+
# Yardage and weight are stored as `fiber_units` measurements so related
|
|
5
|
+
# calculations remain unit-safe.
|
|
6
|
+
class Yarn
|
|
7
|
+
attr_reader :brand, :line, :yardage, :skein_weight, :fiber_content
|
|
8
|
+
|
|
9
|
+
# @param brand [String] yarn manufacturer or brand name
|
|
10
|
+
# @param line [String] product line name
|
|
11
|
+
# @param yardage [FiberUnits::Length] yardage contained in one skein
|
|
12
|
+
# @param skein_weight [FiberUnits::Weight] weight of one skein
|
|
13
|
+
# @param fiber_content [FiberBlend, nil] optional fiber composition details
|
|
14
|
+
# @raise [ArgumentError] if yardage or skein weight are not typed fiber_units values
|
|
15
|
+
def initialize(brand:, line:, yardage:, skein_weight:, fiber_content: nil)
|
|
16
|
+
raise ArgumentError, "yardage must be a FiberUnits::Length" unless yardage.is_a?(FiberUnits::Length)
|
|
17
|
+
raise ArgumentError, "skein_weight must be a FiberUnits::Weight" unless skein_weight.is_a?(FiberUnits::Weight)
|
|
18
|
+
|
|
19
|
+
@brand = brand
|
|
20
|
+
@line = line
|
|
21
|
+
@yardage = yardage
|
|
22
|
+
@skein_weight = skein_weight
|
|
23
|
+
@fiber_content = fiber_content
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Return the yarn grist as a ratio of yardage to weight.
|
|
27
|
+
#
|
|
28
|
+
# @return [FiberUnits::Ratio]
|
|
29
|
+
def grist
|
|
30
|
+
yardage / skein_weight
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Resolve the yarn's conventional weight category from its yardage density.
|
|
34
|
+
#
|
|
35
|
+
# @return [Symbol, nil]
|
|
36
|
+
def weight_category
|
|
37
|
+
YarnSkein::WeightCategory.for(yards_per_100g)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Calculate how many yards this yarn yields per 100 grams.
|
|
41
|
+
#
|
|
42
|
+
# @return [Float]
|
|
43
|
+
def yards_per_100g
|
|
44
|
+
grams = skein_weight.to(:grams).value
|
|
45
|
+
yards = yardage.to(:yards).value
|
|
46
|
+
|
|
47
|
+
(yards / grams) * 100
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Compare this yarn's density to another yarn within a 15% tolerance.
|
|
51
|
+
#
|
|
52
|
+
# @param other [Yarn]
|
|
53
|
+
# @return [Boolean]
|
|
54
|
+
def similar_weight_to?(other)
|
|
55
|
+
ratio = yards_per_100g / other.yards_per_100g.to_f
|
|
56
|
+
ratio.between?(0.85, 1.15)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Calculate how many skeins are needed to satisfy a target yardage.
|
|
60
|
+
#
|
|
61
|
+
# @param required_yardage [FiberUnits::Length]
|
|
62
|
+
# @return [Integer]
|
|
63
|
+
# @raise [ArgumentError] if the argument is not a typed length value
|
|
64
|
+
def skeins_required(required_yardage)
|
|
65
|
+
unless required_yardage.is_a?(FiberUnits::Length)
|
|
66
|
+
raise ArgumentError, "required_yardage must be a FiberUnits::Length"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
yards_needed = required_yardage.to(:yards).value
|
|
70
|
+
yards_per_skein = yardage.to(:yards).value
|
|
71
|
+
|
|
72
|
+
(yards_needed / yards_per_skein).ceil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Calculate the total yardage provided by a number of skeins.
|
|
76
|
+
#
|
|
77
|
+
# @param skein_count [Numeric]
|
|
78
|
+
# @return [FiberUnits::Length]
|
|
79
|
+
# @raise [ArgumentError] if the skein count is not positive
|
|
80
|
+
def total_yardage(skein_count)
|
|
81
|
+
unless skein_count.is_a?(Numeric) && skein_count > 0
|
|
82
|
+
raise ArgumentError, "skein_count must be positive"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
yardage * skein_count
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Compare yarn identity by brand, line, yardage, and skein weight.
|
|
89
|
+
#
|
|
90
|
+
# @param other [Object]
|
|
91
|
+
# @return [Boolean]
|
|
92
|
+
def ==(other)
|
|
93
|
+
other.is_a?(Yarn) &&
|
|
94
|
+
brand == other.brand &&
|
|
95
|
+
line == other.line &&
|
|
96
|
+
yardage == other.yardage &&
|
|
97
|
+
skein_weight == other.skein_weight
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
data/lib/yarn_skein.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fiber_units"
|
|
4
|
+
require_relative "yarn_skein/version"
|
|
5
|
+
require_relative "yarn_skein/weight_category"
|
|
6
|
+
require_relative "yarn_skein/fiber_blend"
|
|
7
|
+
require_relative "yarn_skein/yarn"
|
|
8
|
+
|
|
9
|
+
# Domain objects for describing yarn skeins, fiber blends, and weight classes.
|
|
10
|
+
#
|
|
11
|
+
# `YarnSkein` builds on `fiber_units` so yardage and weight remain typed
|
|
12
|
+
# values throughout calculations such as grist, total yardage, and category
|
|
13
|
+
# lookups.
|
|
14
|
+
module YarnSkein
|
|
15
|
+
# Base error class for library-specific failures.
|
|
16
|
+
class Error < StandardError; end
|
|
17
|
+
end
|
data/sig/yarn_skein.rbs
ADDED
metadata
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: yarn_skein
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Meagan Waller
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: fiber_units
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: FiberUnits provides a comprehensive system for managing and converting
|
|
27
|
+
measurements in fiber arts projects.
|
|
28
|
+
email:
|
|
29
|
+
- meagan@meaganwaller.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".github/actions/ruby-setup/action.yml"
|
|
35
|
+
- ".github/dependabot.yml"
|
|
36
|
+
- ".github/workflows/ci.yml"
|
|
37
|
+
- ".github/workflows/release.yml"
|
|
38
|
+
- CHANGELOG.md
|
|
39
|
+
- CODE_OF_CONDUCT.md
|
|
40
|
+
- LICENSE
|
|
41
|
+
- README.md
|
|
42
|
+
- Rakefile
|
|
43
|
+
- lib/yarn_skein.rb
|
|
44
|
+
- lib/yarn_skein/fiber_blend.rb
|
|
45
|
+
- lib/yarn_skein/version.rb
|
|
46
|
+
- lib/yarn_skein/weight_category.rb
|
|
47
|
+
- lib/yarn_skein/yarn.rb
|
|
48
|
+
- sig/yarn_skein.rbs
|
|
49
|
+
homepage: https://github.com/meaganewaller/yarn_skein
|
|
50
|
+
licenses:
|
|
51
|
+
- MIT
|
|
52
|
+
metadata:
|
|
53
|
+
homepage_uri: https://github.com/meaganewaller/yarn_skein
|
|
54
|
+
source_code_uri: https://github.com/meaganewaller/yarn_skein
|
|
55
|
+
changelog_uri: https://github.com/meaganewaller/yarn_skein/blob/main/CHANGELOG.md
|
|
56
|
+
rdoc_options: []
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: 3.2.0
|
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
requirements: []
|
|
70
|
+
rubygems_version: 4.0.3
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: Typed measurements for fiber arts.
|
|
73
|
+
test_files: []
|