yarn_skein 0.1.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fdd27e3f715f8b7f200f6bd5f604e503dad0bb61cb2d0f8cd0100680c902e77
4
- data.tar.gz: 0a121d542d7559e6a5b1c6ef36df30f0a7676c0b64f25c3194eaf90124c8cf7b
3
+ metadata.gz: de030bc1badf27891aebbba5deb39a73ab035fbe4be31842f4e9d2bb8105ecdb
4
+ data.tar.gz: 9ae3781907723962ea50c3b25a3f98705c321f251f981a6a558b3a81218ed442
5
5
  SHA512:
6
- metadata.gz: 2a0f0c9d824f8689a18265c0d20dde24267649a4ff2f0d174330dff7f0de0e9e60575075d9f25fe32865fa3c9530f206c2f72652afbad050bb5070ce3f28ed10
7
- data.tar.gz: 76e392350e32d039c950b9b9680b027e2554982335e8b2c01abe2f76f30e29dae1dca3630b4316411e41eff25d28659591f7fb573f6932505036e6e05c2a2925
6
+ metadata.gz: 5c4bbcb7d360334e2f79d733f4d53d2371414f2c6e73bc48b67d1a3e027e2c48a1e822d56fbc424ef6c42dfacbbcd471f00ab55497baac641ea510f4564748a0
7
+ data.tar.gz: cc0e8ac28ad96f6977471b3fca483989ca8b0e2142040dce66632714c0c3b022d972ec363f395af645f4143a41757357bf48ef362833a399a183d08a0093b708
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  ## [Unreleased]
2
2
 
3
3
 
4
+ ## [0.2.0] - 2026-03-21
5
+
6
+ ### Added
7
+ - `YarnSkein::Substitution` for finding compatible yarn substitutes from a catalog
8
+ - Matches by weight category and grist tolerance (default 15%, configurable)
9
+ - Optional fiber content filter
10
+ - Added missing `rake` and `simplecov-json` dev dependencies
11
+
4
12
  ## [0.1.1] - 2026-03-14
5
13
  ### Added
6
14
  - `YarnSkein::Yarn` for modeling a skein's brand, line, yardage, skein weight, and optional fiber content
data/Rakefile CHANGED
@@ -1,58 +1,10 @@
1
1
  # frozen_string_literal: true
2
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"
3
+ require "rake/testtask"
8
4
 
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
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.pattern = "test/**/*_test.rb"
53
8
  end
54
9
 
55
- task :default do
56
- Rake::Task["standard"].invoke
57
- Rake::Task["spec"].invoke
58
- end
10
+ task default: :test
@@ -0,0 +1,30 @@
1
+ module YarnSkein
2
+ class Substitution
3
+ DEFAULT_TOLERANCE = 0.15
4
+
5
+ attr_reader :target, :catalog
6
+
7
+ def initialize(target:, catalog:)
8
+ @target = target
9
+ @catalog = catalog
10
+ end
11
+
12
+ def matches(tolerance: DEFAULT_TOLERANCE, fiber: nil)
13
+ catalog.select do |yarn|
14
+ next false if yarn == target
15
+ next false unless yarn.weight_category == target.weight_category
16
+ next false unless within_grist_tolerance?(yarn, tolerance)
17
+ next false if fiber && !yarn.fiber_content&.contains?(fiber)
18
+
19
+ true
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def within_grist_tolerance?(yarn, tolerance)
26
+ ratio = yarn.yards_per_100g / target.yards_per_100g.to_f
27
+ ratio.between?(1.0 - tolerance, 1.0 + tolerance)
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :nocov:
3
4
  module YarnSkein
4
5
  # Current gem version.
5
- VERSION = "0.1.1"
6
+ VERSION = "0.2.0"
6
7
  end
8
+ # :nocov:
data/lib/yarn_skein.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "yarn_skein/version"
5
5
  require_relative "yarn_skein/weight_category"
6
6
  require_relative "yarn_skein/fiber_blend"
7
7
  require_relative "yarn_skein/yarn"
8
+ require_relative "yarn_skein/substitution"
8
9
 
9
10
  # Domain objects for describing yarn skeins, fiber blends, and weight classes.
10
11
  #
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class YarnSkeinFiberBlendTest < Minitest::Test
6
+ # -----------------------------
7
+ # Initialization
8
+ # -----------------------------
9
+
10
+ def test_stores_fiber_percentages
11
+ blend = YarnSkein::FiberBlend.new(
12
+ merino_wool: 80,
13
+ nylon: 20
14
+ )
15
+
16
+ assert_equal(
17
+ {
18
+ merino_wool: 80,
19
+ nylon: 20
20
+ },
21
+ blend.fibers
22
+ )
23
+ end
24
+
25
+ def test_raises_error_if_percentages_do_not_sum_to_100
26
+ error = assert_raises(ArgumentError) do
27
+ YarnSkein::FiberBlend.new(
28
+ merino_wool: 50,
29
+ nylon: 30
30
+ )
31
+ end
32
+
33
+ assert_match(/must sum to 100/, error.message)
34
+ end
35
+
36
+ # -----------------------------
37
+ # yards_per_100g
38
+ # -----------------------------
39
+
40
+ def test_calculates_yards_per_100_grams
41
+ yarn = YarnSkein::Yarn.new(
42
+ brand: "Malabrigo",
43
+ line: "Rios",
44
+ yardage: 210.yards,
45
+ skein_weight: 100.grams
46
+ )
47
+
48
+ assert_equal 210, yarn.yards_per_100g
49
+ end
50
+
51
+ # -----------------------------
52
+ # percentage
53
+ # -----------------------------
54
+
55
+ def test_returns_percentage_of_fiber
56
+ blend = YarnSkein::FiberBlend.new(
57
+ merino_wool: 80,
58
+ nylon: 20
59
+ )
60
+
61
+ assert_equal 80, blend.percentage(:merino_wool)
62
+ end
63
+
64
+ def test_returns_zero_for_missing_fibers
65
+ blend = YarnSkein::FiberBlend.new(
66
+ merino_wool: 100
67
+ )
68
+
69
+ assert_equal 0, blend.percentage(:nylon)
70
+ end
71
+
72
+ # -----------------------------
73
+ # contains?
74
+ # -----------------------------
75
+
76
+ def test_contains_returns_true_if_fiber_exists
77
+ blend = YarnSkein::FiberBlend.new(
78
+ merino_wool: 80,
79
+ nylon: 20
80
+ )
81
+
82
+ assert blend.contains?(:nylon)
83
+ end
84
+
85
+ def test_contains_returns_false_if_fiber_does_not_exist
86
+ blend = YarnSkein::FiberBlend.new(
87
+ merino_wool: 100
88
+ )
89
+
90
+ refute blend.contains?(:nylon)
91
+ end
92
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class YarnSkeinSubstitutionTest < Minitest::Test
6
+ def build_yarn(line:, yardage:, skein_weight:, fiber_content: nil)
7
+ YarnSkein::Yarn.new(
8
+ brand: "TestBrand",
9
+ line: line,
10
+ yardage: yardage,
11
+ skein_weight: skein_weight,
12
+ fiber_content: fiber_content
13
+ )
14
+ end
15
+
16
+ def target_yarn
17
+ @target_yarn ||= build_yarn(line: "Target", yardage: 210.yards, skein_weight: 100.grams)
18
+ end
19
+
20
+ # 210 yards/100g => worsted, yards_per_100g = 210
21
+
22
+ def close_match
23
+ @close_match ||= build_yarn(line: "Close", yardage: 200.yards, skein_weight: 100.grams)
24
+ end
25
+
26
+ def exact_match
27
+ @exact_match ||= build_yarn(line: "Exact", yardage: 210.yards, skein_weight: 100.grams)
28
+ end
29
+
30
+ def too_far
31
+ @too_far ||= build_yarn(line: "TooFar", yardage: 300.yards, skein_weight: 100.grams)
32
+ end
33
+
34
+ def different_category
35
+ @different_category ||= build_yarn(line: "Bulky", yardage: 120.yards, skein_weight: 100.grams)
36
+ end
37
+
38
+ def wool_yarn
39
+ @wool_yarn ||= build_yarn(
40
+ line: "Wool",
41
+ yardage: 200.yards,
42
+ skein_weight: 100.grams,
43
+ fiber_content: YarnSkein::FiberBlend.new(wool: 100)
44
+ )
45
+ end
46
+
47
+ def cotton_yarn
48
+ @cotton_yarn ||= build_yarn(
49
+ line: "Cotton",
50
+ yardage: 200.yards,
51
+ skein_weight: 100.grams,
52
+ fiber_content: YarnSkein::FiberBlend.new(cotton: 100)
53
+ )
54
+ end
55
+
56
+ # -----------------------------
57
+ # matches
58
+ # -----------------------------
59
+
60
+ def test_returns_yarns_matching_weight_category_and_grist
61
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [close_match, too_far])
62
+
63
+ assert_equal [close_match], sub.matches
64
+ end
65
+
66
+ def test_excludes_target_yarn_from_results
67
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [target_yarn, close_match])
68
+
69
+ assert_equal [close_match], sub.matches
70
+ end
71
+
72
+ def test_excludes_different_weight_category
73
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [different_category])
74
+
75
+ assert_empty sub.matches
76
+ end
77
+
78
+ def test_returns_empty_for_empty_catalog
79
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [])
80
+
81
+ assert_empty sub.matches
82
+ end
83
+
84
+ def test_returns_empty_when_no_matches
85
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [too_far, different_category])
86
+
87
+ assert_empty sub.matches
88
+ end
89
+
90
+ # -----------------------------
91
+ # tolerance
92
+ # -----------------------------
93
+
94
+ def test_configurable_tolerance
95
+ # close_match is 200/210 = ~0.952, within 15% but outside 2%
96
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [close_match])
97
+
98
+ assert_equal [close_match], sub.matches(tolerance: 0.15)
99
+ assert_empty sub.matches(tolerance: 0.02)
100
+ end
101
+
102
+ # -----------------------------
103
+ # fiber filter
104
+ # -----------------------------
105
+
106
+ def test_filters_by_fiber_content
107
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [wool_yarn, cotton_yarn])
108
+
109
+ assert_equal [wool_yarn], sub.matches(fiber: :wool)
110
+ end
111
+
112
+ def test_fiber_filter_excludes_yarns_without_fiber_content
113
+ yarn_no_fiber = build_yarn(line: "NoFiber", yardage: 200.yards, skein_weight: 100.grams)
114
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [yarn_no_fiber, wool_yarn])
115
+
116
+ assert_equal [wool_yarn], sub.matches(fiber: :wool)
117
+ end
118
+
119
+ def test_includes_exact_grist_match
120
+ sub = YarnSkein::Substitution.new(target: target_yarn, catalog: [exact_match])
121
+
122
+ assert_equal [exact_match], sub.matches
123
+ end
124
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov"
4
+ require "simplecov-json"
5
+
6
+ require "bundler/setup"
7
+ require "yarn_skein"
8
+
9
+ require "minitest/autorun"
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class YarnSkein::VersionTest < Minitest::Test
6
+ def test_has_a_version_number
7
+ refute_nil YarnSkein::VERSION
8
+ assert_match(/^\d+\.\d+\.\d+$/, YarnSkein::VERSION)
9
+ end
10
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class YarnSkeinWeightCategoryTest < Minitest::Test
6
+ def test_returns_lace_for_1000_yards_per_100g
7
+ assert_equal :lace, YarnSkein::WeightCategory.for(1000)
8
+ end
9
+
10
+ def test_returns_fingering_for_440_yards_per_100g
11
+ assert_equal :fingering, YarnSkein::WeightCategory.for(440)
12
+ end
13
+
14
+ def test_returns_sport_for_320_yards_per_100g
15
+ assert_equal :sport, YarnSkein::WeightCategory.for(320)
16
+ end
17
+
18
+ def test_returns_dk_for_250_yards_per_100g
19
+ assert_equal :dk, YarnSkein::WeightCategory.for(250)
20
+ end
21
+
22
+ def test_returns_worsted_for_210_yards_per_100g
23
+ assert_equal :worsted, YarnSkein::WeightCategory.for(210)
24
+ end
25
+
26
+ def test_returns_bulky_for_120_yards_per_100g
27
+ assert_equal :bulky, YarnSkein::WeightCategory.for(120)
28
+ end
29
+
30
+ def test_returns_super_bulky_for_90_yards_per_100g
31
+ assert_equal :super_bulky, YarnSkein::WeightCategory.for(90)
32
+ end
33
+
34
+ def test_returns_nil_for_negative_yards_per_100g
35
+ assert_nil YarnSkein::WeightCategory.for(-10)
36
+ end
37
+ end
data/test/yarn_test.rb ADDED
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class YarnSkeinYarnTest < Minitest::Test
6
+ def build_yarn(**attrs)
7
+ YarnSkein::Yarn.new(
8
+ brand: "Malabrigo",
9
+ line: "Rios",
10
+ yardage: 210.yards,
11
+ skein_weight: 100.grams,
12
+ **attrs
13
+ )
14
+ end
15
+
16
+ # -----------------------------
17
+ # Initialization
18
+ # -----------------------------
19
+
20
+ def test_initialization_stores_attributes
21
+ yarn = build_yarn
22
+
23
+ assert_equal "Malabrigo", yarn.brand
24
+ assert_equal "Rios", yarn.line
25
+ assert_equal 210.yards, yarn.yardage
26
+ assert_equal 100.grams, yarn.skein_weight
27
+ end
28
+
29
+ # -----------------------------
30
+ # Grist
31
+ # -----------------------------
32
+
33
+ def test_grist_returns_ratio
34
+ yarn = build_yarn
35
+
36
+ assert_instance_of FiberUnits::Ratio, yarn.grist
37
+ end
38
+
39
+ # -----------------------------
40
+ # Weight category
41
+ # -----------------------------
42
+
43
+ def test_weight_category_worsted
44
+ yarn = build_yarn
45
+
46
+ assert_equal :worsted, yarn.weight_category
47
+ end
48
+
49
+ def test_weight_category_fingering
50
+ yarn = build_yarn(
51
+ line: "Sock",
52
+ yardage: 400.yards
53
+ )
54
+
55
+ assert_equal :fingering, yarn.weight_category
56
+ end
57
+
58
+ def test_weight_category_bulky
59
+ yarn = build_yarn(
60
+ line: "Chunky",
61
+ yardage: 100.yards
62
+ )
63
+
64
+ assert_equal :bulky, yarn.weight_category
65
+ end
66
+
67
+ # -----------------------------
68
+ # Validation
69
+ # -----------------------------
70
+
71
+ def test_requires_yardage_to_be_length
72
+ error = assert_raises(ArgumentError) do
73
+ YarnSkein::Yarn.new(
74
+ brand: "Malabrigo",
75
+ line: "Rios",
76
+ yardage: 210,
77
+ skein_weight: 100.grams
78
+ )
79
+ end
80
+
81
+ assert_match(/yardage/i, error.message)
82
+ end
83
+
84
+ def test_requires_skein_weight_to_be_weight
85
+ error = assert_raises(ArgumentError) do
86
+ YarnSkein::Yarn.new(
87
+ brand: "Malabrigo",
88
+ line: "Rios",
89
+ yardage: 210.yards,
90
+ skein_weight: 100
91
+ )
92
+ end
93
+
94
+ assert_match(/skein_weight/i, error.message)
95
+ end
96
+
97
+ # -----------------------------
98
+ # yards_per_100g
99
+ # -----------------------------
100
+
101
+ def test_calculates_yards_per_100g
102
+ yarn = build_yarn
103
+
104
+ assert_equal 210, yarn.yards_per_100g
105
+ end
106
+
107
+ # -----------------------------
108
+ # similar_weight_to?
109
+ # -----------------------------
110
+
111
+ def test_similar_weight_for_same_category
112
+ rios = build_yarn
113
+
114
+ cascade = YarnSkein::Yarn.new(
115
+ brand: "Cascade",
116
+ line: "220",
117
+ yardage: 220.yards,
118
+ skein_weight: 100.grams
119
+ )
120
+
121
+ assert rios.similar_weight_to?(cascade)
122
+ end
123
+
124
+ def test_not_similar_for_different_categories
125
+ rios = build_yarn
126
+
127
+ sock = YarnSkein::Yarn.new(
128
+ brand: "Malabrigo",
129
+ line: "Sock",
130
+ yardage: 440.yards,
131
+ skein_weight: 100.grams
132
+ )
133
+
134
+ refute rios.similar_weight_to?(sock)
135
+ end
136
+
137
+ def test_not_similar_for_very_different_yarns
138
+ rios = build_yarn
139
+
140
+ lace = YarnSkein::Yarn.new(
141
+ brand: "Malabrigo",
142
+ line: "Lace",
143
+ yardage: 800.yards,
144
+ skein_weight: 100.grams
145
+ )
146
+
147
+ refute rios.similar_weight_to?(lace)
148
+ end
149
+
150
+ # -----------------------------
151
+ # skeins_required
152
+ # -----------------------------
153
+
154
+ def test_skeins_required_exact
155
+ yarn = build_yarn
156
+
157
+ assert_equal 2, yarn.skeins_required(420.yards)
158
+ end
159
+
160
+ def test_skeins_required_rounds_up
161
+ yarn = build_yarn
162
+
163
+ assert_equal 2, yarn.skeins_required(300.yards)
164
+ end
165
+
166
+ def test_skeins_required_handles_unit_conversion
167
+ yarn = build_yarn
168
+
169
+ assert_equal 2, yarn.skeins_required(384.meters)
170
+ end
171
+
172
+ def test_skeins_required_requires_length
173
+ yarn = build_yarn
174
+
175
+ assert_raises(ArgumentError) do
176
+ yarn.skeins_required(300)
177
+ end
178
+ end
179
+
180
+ # -----------------------------
181
+ # total_yardage
182
+ # -----------------------------
183
+
184
+ def test_total_yardage_returns_length
185
+ yarn = build_yarn
186
+
187
+ result = yarn.total_yardage(2)
188
+
189
+ assert_instance_of FiberUnits::Length, result
190
+ end
191
+
192
+ def test_total_yardage_calculates_correct_value
193
+ yarn = build_yarn
194
+
195
+ assert_equal 630.yards, yarn.total_yardage(3)
196
+ end
197
+
198
+ def test_total_yardage_requires_positive_count
199
+ yarn = build_yarn
200
+
201
+ assert_raises(ArgumentError) do
202
+ yarn.total_yardage(0)
203
+ end
204
+ end
205
+
206
+ # -----------------------------
207
+ # Equality
208
+ # -----------------------------
209
+
210
+ def test_equal_yarns
211
+ yarn1 = build_yarn
212
+ yarn2 = build_yarn
213
+
214
+ assert_equal yarn1, yarn2
215
+ end
216
+
217
+ def test_different_yarns_not_equal
218
+ yarn1 = build_yarn
219
+
220
+ yarn2 = YarnSkein::Yarn.new(
221
+ brand: "Malabrigo",
222
+ line: "Sock",
223
+ yardage: 400.yards,
224
+ skein_weight: 100.grams
225
+ )
226
+
227
+ refute_equal yarn1, yarn2
228
+ end
229
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yarn_skein
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Meagan Waller
@@ -15,9 +15,23 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '0'
18
+ version: '0.1'
19
19
  type: :runtime
20
20
  prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
21
35
  version_requirements: !ruby/object:Gem::Requirement
22
36
  requirements:
23
37
  - - ">="
@@ -31,10 +45,6 @@ executables: []
31
45
  extensions: []
32
46
  extra_rdoc_files: []
33
47
  files:
34
- - ".github/actions/ruby-setup/action.yml"
35
- - ".github/dependabot.yml"
36
- - ".github/workflows/ci.yml"
37
- - ".github/workflows/release.yml"
38
48
  - CHANGELOG.md
39
49
  - CODE_OF_CONDUCT.md
40
50
  - LICENSE
@@ -42,10 +52,17 @@ files:
42
52
  - Rakefile
43
53
  - lib/yarn_skein.rb
44
54
  - lib/yarn_skein/fiber_blend.rb
55
+ - lib/yarn_skein/substitution.rb
45
56
  - lib/yarn_skein/version.rb
46
57
  - lib/yarn_skein/weight_category.rb
47
58
  - lib/yarn_skein/yarn.rb
48
59
  - sig/yarn_skein.rbs
60
+ - test/fiber_blend_test.rb
61
+ - test/substitution_test.rb
62
+ - test/test_helper.rb
63
+ - test/version_test.rb
64
+ - test/weight_category_test.rb
65
+ - test/yarn_test.rb
49
66
  homepage: https://github.com/meaganewaller/yarn_skein
50
67
  licenses:
51
68
  - MIT
@@ -67,7 +84,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
67
84
  - !ruby/object:Gem::Version
68
85
  version: '0'
69
86
  requirements: []
70
- rubygems_version: 4.0.3
87
+ rubygems_version: 3.6.9
71
88
  specification_version: 4
72
89
  summary: Typed measurements for fiber arts.
73
90
  test_files: []
@@ -1,23 +0,0 @@
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
@@ -1,8 +0,0 @@
1
- version: 2
2
- updates:
3
- - package-ecosystem: bundler
4
- directory: "/"
5
- schedule:
6
- interval: daily
7
- time: "04:00"
8
- open-pull-requests-limit: 10
@@ -1,24 +0,0 @@
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
@@ -1,61 +0,0 @@
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