fiber_units 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 18525eb39ab69336575ce1c0eb327ee299ed037cdf3bbf854b7e5f4af70520e3
4
+ data.tar.gz: 645a4ed7eb065fa3b2c704e3dc46cca2a8ef4f1bfaafb01495fc982c1afc9ccb
5
+ SHA512:
6
+ metadata.gz: 85beb04dc7b1db098924f40820db8272cff73c72d08e8ec75f62a392efea3b35edb256bccf6797bfde682bfbc0096203ef5f90cba022356c2ea9fb00928c29e1
7
+ data.tar.gz: 267047384cc77ac732c5cf912d8db8cca2b9ca4e177cb84428e926d1639cca7bed95d8289b35fdac272fbe586b678001321c64bc6da392db5d4569eddac93e0a
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-03-09
4
+
5
+ - Initial release
@@ -0,0 +1,10 @@
1
+ # Code of Conduct
2
+
3
+ "fiber_units" 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.txt 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,285 @@
1
+ # fiber_units
2
+
3
+ Typed measurement units for fiber arts and crafting systems.
4
+
5
+ `fiber_units` provides a small, type-safe unit system for working with measurements common in knitting, crochet, and other crafts.
6
+
7
+ It supports:
8
+
9
+ * length units (`inches`, `centimeters`, `yards`, etc.)
10
+ * weight units (`grams`, `ounces`, `pounds`, etc.)
11
+ * stitch counts
12
+ * row counts
13
+ * safe arithmetic between compatible units
14
+ * automatic unit conversion
15
+
16
+ ## Installation
17
+
18
+ Add to your Gemfile:
19
+
20
+ ```ruby
21
+ gem "fiber_units"
22
+ ```
23
+
24
+ Then run:
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ Or install directly:
31
+
32
+ ```bash
33
+ gem install fiber_units
34
+ ```
35
+
36
+ ## Basic Usage
37
+
38
+ ```ruby
39
+ require "fiber_units"
40
+
41
+ 4.inches
42
+ 10.centimeters
43
+ 210.yards
44
+ 100.grams
45
+ ```
46
+
47
+ Each returns a typed measurement object.
48
+
49
+ Example:
50
+
51
+ ```ruby
52
+ length = 4.inches
53
+
54
+ length.value
55
+ # => 4
56
+
57
+ length.unit
58
+ # => :inches
59
+ ```
60
+
61
+ ## Unit Conversion
62
+
63
+ Convert between compatible units:
64
+
65
+ ```ruby
66
+ 4.inches.to(:centimeters)
67
+ # => 10.16 cm
68
+
69
+ 210.yards.to(:meters)
70
+ # => 192.024 m
71
+
72
+ 100.grams.to(:ounces)
73
+ # => 3.527 oz
74
+ ```
75
+
76
+ ## Arithmetic
77
+
78
+ Compatible units can be combined safely.
79
+
80
+ ```ruby
81
+ 4.inches + 2.inches
82
+ # => 6 inches
83
+
84
+ 10.inches - 4.inches
85
+ # => 6 inches
86
+ ```
87
+
88
+ Cross-unit arithmetic works automatically.
89
+
90
+ ```ruby
91
+ 2.yards + 36.inches
92
+ # => 3 yards
93
+ ```
94
+
95
+ Internally calculations happen in **base units** and then convert back.
96
+
97
+ ## Dimension Safety
98
+
99
+ Invalid operations raise errors.
100
+
101
+ ```ruby
102
+ 4.inches + 100.grams
103
+ # => FiberUnits::DimensionError
104
+ ```
105
+
106
+ This prevents subtle math bugs.
107
+
108
+ ## Ratios
109
+
110
+ Dividing measurements produces ratios.
111
+
112
+ ```ruby
113
+ 210.yards / 100.grams
114
+ # => 2.1 yards/grams
115
+ ```
116
+
117
+ This is useful for yarn metrics like **grist**.
118
+
119
+ ## Stitch and Row Counts
120
+
121
+ Fiber-specific counts are supported.
122
+
123
+ ```ruby
124
+ 20.stitches
125
+ 32.rows
126
+ ```
127
+
128
+ Arithmetic works as expected:
129
+
130
+ ```ruby
131
+ 20.stitches + 10.stitches
132
+ # => 30 stitches
133
+ ```
134
+
135
+ ## Supported Length Units
136
+
137
+ ```
138
+ millimeters
139
+ centimeters
140
+ meters
141
+ inches
142
+ feet
143
+ yards
144
+ ```
145
+
146
+ Example:
147
+
148
+ ```ruby
149
+ 5.centimeters
150
+ 2.yards
151
+ 12.inches
152
+ ```
153
+
154
+ ## Supported Weight Units
155
+
156
+ ```
157
+ grams
158
+ kilograms
159
+ ounces
160
+ pounds
161
+ ```
162
+
163
+ Example:
164
+
165
+ ```ruby
166
+ 100.grams
167
+ 8.ounces
168
+ 1.pounds
169
+ ```
170
+
171
+ ## Internal Model
172
+
173
+ All measurements inherit from a shared base class:
174
+
175
+ ```
176
+ FiberUnits::Measurement
177
+ ```
178
+
179
+ Each dimension defines its unit conversion table:
180
+
181
+ ```ruby
182
+ class Length < Measurement
183
+ FACTORS = {
184
+ millimeters: 1.0,
185
+ centimeters: 10.0,
186
+ meters: 1000.0,
187
+ inches: 25.4,
188
+ feet: 304.8,
189
+ yards: 914.4
190
+ }
191
+ end
192
+ ```
193
+
194
+ Arithmetic works by:
195
+
196
+ 1. converting to base units
197
+ 2. performing the calculation
198
+ 3. converting back to the original unit
199
+
200
+ ## Example: Yarn Grist
201
+
202
+ ```ruby
203
+ yardage = 210.yards
204
+ weight = 100.grams
205
+
206
+ grist = yardage / weight
207
+ # => 2.1 yards/gram
208
+ ```
209
+
210
+ ## Example: Gauge Math
211
+
212
+ ```ruby
213
+ stitches = 18.stitches
214
+ width = 4.inches
215
+
216
+ spi = stitches / width
217
+ # => 4.5 stitches per inch
218
+ ```
219
+
220
+ ## Extending the Library
221
+
222
+ Adding a new measurement dimension is simple.
223
+
224
+ Example:
225
+
226
+ ```ruby
227
+ class Area < FiberUnits::Measurement
228
+ FACTORS = {
229
+ square_millimeters: 1.0,
230
+ square_centimeters: 100.0,
231
+ square_meters: 1_000_000.0
232
+ }
233
+ end
234
+ ```
235
+
236
+ Everything else (conversion, arithmetic) works automatically.
237
+
238
+ ## Philosophy
239
+
240
+ Craft software often relies on raw integers or floats for measurements, which leads to subtle bugs.
241
+
242
+ `fiber_units` aims to make craft math:
243
+
244
+ * **explicit**
245
+ * **type-safe**
246
+ * **composable**
247
+ * **pleasant to read**
248
+
249
+ Example:
250
+
251
+ ```ruby
252
+ 2.yards + 36.inches
253
+ ```
254
+
255
+ reads like real-world math.
256
+
257
+ ## Development
258
+
259
+ Run tests:
260
+
261
+ ```bash
262
+ bundle exec rspec
263
+ ```
264
+
265
+ The library includes extensive tests covering:
266
+
267
+ * conversions
268
+ * arithmetic
269
+ * ratios
270
+ * DSL methods
271
+ * dimension safety
272
+
273
+ ## License
274
+
275
+ MIT License
276
+
277
+ # Future Ideas
278
+
279
+ Planned features include:
280
+
281
+ * comparison operators (`4.inches > 10.cm`)
282
+ * gauge utilities
283
+ * yarn grist helpers
284
+ * area and volume units
285
+ * pattern math helpers
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "standard/rake"
9
+
10
+ task default: %i[spec standard]
File without changes
File without changes
File without changes
@@ -0,0 +1,22 @@
1
+ module FiberUnits
2
+ module Conversions
3
+ module LengthConversion
4
+ FACTORS = {
5
+ millimeters: 1.0,
6
+ centimeters: 10.0,
7
+ meters: 1000.0,
8
+ inches: 25.4,
9
+ feet: 304.8,
10
+ yards: 914.4
11
+ }.freeze
12
+
13
+ def self.convert(value, from, to)
14
+ raise FiberUnits::InvalidUnitError unless FACTORS.key?(from)
15
+ raise FiberUnits::InvalidUnitError unless FACTORS.key?(to)
16
+
17
+ base = value * FACTORS[from]
18
+ base / FACTORS[to]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module FiberUnits
2
+ module Conversions
3
+ module WeightConversion
4
+ FACTORS = {
5
+ grams: 1.0,
6
+ kilograms: 1000.0,
7
+ ounces: 28.3495,
8
+ pounds: 453.592
9
+ }.freeze
10
+
11
+ def self.convert(value, from, to)
12
+ raise FiberUnits::InvalidUnitError unless FACTORS.key?(from)
13
+ raise FiberUnits::InvalidUnitError unless FACTORS.key?(to)
14
+
15
+ base = value * FACTORS[from]
16
+ base / FACTORS[to]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module FiberUnits
2
+ class Length < Measurement
3
+ BASE_UNIT = :millimeters
4
+
5
+ FACTORS = {
6
+ millimeters: 1,
7
+ centimeters: 10,
8
+ meters: 1000,
9
+ inches: 25.4,
10
+ feet: 304.8,
11
+ yards: 914.4
12
+ }
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module FiberUnits
2
+ class RowCount
3
+ attr_reader :value
4
+
5
+ def initialize(value, _unit = nil)
6
+ @value = value
7
+ end
8
+
9
+ def +(other)
10
+ RowCount.new(value + other.value)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module FiberUnits
2
+ class StitchCount
3
+ attr_reader :value
4
+
5
+ def initialize(value, _unit = nil)
6
+ @value = value
7
+ end
8
+
9
+ def +(other)
10
+ StitchCount.new(value + other.value)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ module FiberUnits
2
+ class Weight < Measurement
3
+ FACTORS = {
4
+ grams: 1.0,
5
+ kilograms: 1000.0,
6
+ ounces: 28.3495,
7
+ pounds: 453.592
8
+ }.freeze
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ module FiberUnits
2
+ class DimensionError < StandardError; end
3
+ class InvalidUnitError < StandardError; end
4
+ end
File without changes
@@ -0,0 +1,74 @@
1
+ module FiberUnits
2
+ class Measurement
3
+ attr_reader :value, :unit
4
+
5
+ def initialize(value, unit)
6
+ @value = value
7
+ @unit = unit
8
+ end
9
+
10
+ def +(other)
11
+ ensure_same_dimension!(other)
12
+
13
+ base = to_base + other.to_base
14
+ new_value = base / self.class::FACTORS[unit]
15
+ new_value = new_value.round(12)
16
+
17
+ self.class.new(new_value, unit)
18
+ end
19
+
20
+ def -(other)
21
+ ensure_same_dimension!(other)
22
+
23
+ base = to_base - other.to_base
24
+ new_value = base / self.class::FACTORS[unit]
25
+ new_value = new_value.round(12)
26
+
27
+ self.class.new(new_value, unit)
28
+ end
29
+
30
+ def *(other)
31
+ self.class.new(value * other, unit)
32
+ end
33
+
34
+ def /(other)
35
+ if other.is_a?(Measurement)
36
+ Ratio.new(self, other)
37
+ else
38
+ self.class.new(value / other, unit)
39
+ end
40
+ end
41
+
42
+ def to(target_unit)
43
+ raise FiberUnits::InvalidUnitError unless self.class::FACTORS.key?(target_unit)
44
+
45
+ base = to_base
46
+ self.class.new(base / self.class::FACTORS[target_unit], target_unit)
47
+ end
48
+
49
+ def to_base
50
+ raise NotImplementedError unless self.class.const_defined?(:FACTORS)
51
+
52
+ value * self.class::FACTORS[unit]
53
+ end
54
+
55
+ def self.from_base(base_value)
56
+ factors = self::FACTORS
57
+
58
+ base_unit = factors.key(1.0)
59
+
60
+ raise FiberUnits::InvalidUnitError unless base_unit
61
+
62
+ new(base_value / factors[base_unit], base_unit)
63
+ end
64
+
65
+ private
66
+
67
+ def ensure_same_dimension!(other)
68
+ unless other.is_a?(self.class)
69
+ raise FiberUnits::DimensionError,
70
+ "Cannot combine #{self.class} with #{other.class}"
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,18 @@
1
+ module FiberUnits
2
+ class Ratio
3
+ attr_reader :numerator, :denominator
4
+
5
+ def initialize(numerator, denominator)
6
+ @numerator = numerator
7
+ @denominator = denominator
8
+ end
9
+
10
+ def value
11
+ numerator.value.to_f / denominator.value
12
+ end
13
+
14
+ def to_s
15
+ "#{value.round(2)} #{numerator.unit}/#{denominator.unit}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FiberUnits
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,18 @@
1
+ require_relative "fiber_units/version"
2
+ require_relative "fiber_units/errors"
3
+ require_relative "fiber_units/measurement"
4
+ require_relative "fiber_units/ratio"
5
+
6
+ require_relative "fiber_units/conversions/length_conversion"
7
+ require_relative "fiber_units/conversions/weight_conversion"
8
+
9
+ require_relative "fiber_units/dimensions/length"
10
+ require_relative "fiber_units/dimensions/weight"
11
+ require_relative "fiber_units/dimensions/stitch_count"
12
+ require_relative "fiber_units/dimensions/row_count"
13
+
14
+ require_relative "numeric_extensions"
15
+
16
+ module FiberUnits
17
+ # Your code goes here...
18
+ end
@@ -0,0 +1,37 @@
1
+ module FiberUnits
2
+ module NumericExtensions
3
+ def self.install!
4
+ install_length_units
5
+ install_weight_units
6
+ install_fiber_counts
7
+ end
8
+
9
+ def self.install_length_units
10
+ FiberUnits::Conversions::LengthConversion::FACTORS.keys.each do |unit|
11
+ Numeric.define_method(unit) do
12
+ FiberUnits::Length.new(self, unit)
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.install_weight_units
18
+ FiberUnits::Conversions::WeightConversion::FACTORS.keys.each do |unit|
19
+ Numeric.define_method(unit) do
20
+ FiberUnits::Weight.new(self, unit)
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.install_fiber_counts
26
+ Numeric.define_method(:stitches) do
27
+ FiberUnits::StitchCount.new(self)
28
+ end
29
+
30
+ Numeric.define_method(:rows) do
31
+ FiberUnits::RowCount.new(self)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ FiberUnits::NumericExtensions.install!
@@ -0,0 +1,4 @@
1
+ module FiberUnits
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fiber_units
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
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
+ description: A Ruby gem providing typed measurement units for various fiber arts,
13
+ allowing for easy conversion and manipulation of units like yards and meters.
14
+ email:
15
+ - meagan@meaganwaller.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - CHANGELOG.md
21
+ - CODE_OF_CONDUCT.md
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - examples/gauge_example.rb
26
+ - examples/stash_calculations.rb
27
+ - examples/yarn_grist_example.rb
28
+ - lib/fiber_units.rb
29
+ - lib/fiber_units/conversions/length_conversion.rb
30
+ - lib/fiber_units/conversions/weight_conversion.rb
31
+ - lib/fiber_units/dimensions/length.rb
32
+ - lib/fiber_units/dimensions/row_count.rb
33
+ - lib/fiber_units/dimensions/stitch_count.rb
34
+ - lib/fiber_units/dimensions/weight.rb
35
+ - lib/fiber_units/errors.rb
36
+ - lib/fiber_units/formatting/formatter.rb
37
+ - lib/fiber_units/measurement.rb
38
+ - lib/fiber_units/ratio.rb
39
+ - lib/fiber_units/version.rb
40
+ - lib/numeric_extensions.rb
41
+ - sig/fiber_units.rbs
42
+ homepage: https://github.com/meaganewaller/fiber_units
43
+ licenses:
44
+ - MIT
45
+ metadata:
46
+ homepage_uri: https://github.com/meaganewaller/fiber_units
47
+ source_code_uri: https://github.com/meaganewaller/fiber_units
48
+ changelog_uri: https://github.com/meaganewaller/fiber_units/blob/main/CHANGELOG.md
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 3.2.0
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 4.0.3
64
+ specification_version: 4
65
+ summary: Typed measurement units for fiber arts.
66
+ test_files: []