Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,6 @@ jobs:
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: coverage/.resultset.json
# CodeClimate has become qlth.sh, this integration is currently broken.
# coverage:
# needs: test
# runs-on: ubuntu-latest
# env:
# BUNDLE_WITHOUT: optional
# steps:
# - uses: actions/[email protected]
# - uses: ruby/setup-ruby@v1
# with:
# bundler-cache: true
# - name: Publish code coverage
# uses: paambaati/codeclimate-action@v9
# env:
# CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
# with:
# coverageCommand: bundle exec rake

yard:
runs-on: ubuntu-latest
Expand Down
10 changes: 10 additions & 0 deletions .qlty/qlty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,13 @@ threshold = 5
[[source]]
name = "default"
default = true

[[plugin]]
name = "rubocop"
package_file = "Gemfile.qlty"
package_filters = ["rubocop"]
config_files = [".rubocop.yml", ".rubocop_todo.yml"]

[[plugin]]
name = "reek"
mode = "comment"
4 changes: 1 addition & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ group :optional do
gem "guard-rspec"
gem "pry"
gem "redcarpet", platform: :mri # redcarpet doesn't support jruby
gem "rubocop"
gem "rubocop-rake"
gem "rubocop-rspec"
gem "ruby-maven", platform: :jruby
gem "ruby-prof", platform: :mri
gem "simplecov-html"
gem "solargraph"
gem "terminal-notifier"
gem "terminal-notifier-guard"
gem "webrick"
eval_gemfile("Gemfile.qlty")
end

gem "bigdecimal"
Expand Down
46 changes: 25 additions & 21 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
ruby-units (4.1.0)
ruby-units (5.0.0)

GEM
remote: https://rubygems.org/
Expand All @@ -20,12 +20,10 @@ GEM
reline (>= 0.3.8)
diff-lcs (1.6.2)
docile (1.4.1)
e2mmap (0.1.0)
erb (6.0.1)
erb (6.0.1-java)
ffi (1.17.2-arm64-darwin)
ffi (1.17.2-java)
ffi (1.17.2-x86_64-darwin)
ffi (1.17.2-x86_64-linux-gnu)
formatador (1.2.3)
reline
Expand Down Expand Up @@ -74,13 +72,12 @@ GEM
racc (~> 1.4)
nokogiri (1.18.10-java)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-darwin)
racc (~> 1.4)
nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4)
notiffany (0.1.3)
nenv (~> 0.1)
shellany (~> 0.0)
observer (0.1.2)
ostruct (0.6.3)
parallel (1.27.0)
parser (3.3.10.0)
Expand Down Expand Up @@ -110,15 +107,17 @@ GEM
rb-fsevent (0.11.2)
rb-inotify (0.11.1)
ffi (~> 1.0)
rdoc (7.0.1)
rbs (3.10.0)
logger
rdoc (7.0.2)
erb
psych (>= 4.0.0)
tsort
redcarpet (3.6.1)
regexp_parser (2.11.3)
reline (0.6.3)
io-console (~> 0.5)
reverse_markdown (2.1.1)
reverse_markdown (3.0.1)
nokogiri
rexml (3.4.4)
rspec (3.13.2)
Expand All @@ -134,7 +133,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-support (3.13.6)
rubocop (1.82.0)
rubocop (1.82.1)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
Expand Down Expand Up @@ -167,21 +166,27 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4)
solargraph (0.48.0)
solargraph (0.57.0)
backport (~> 1.2)
benchmark
bundler (>= 1.17.2)
benchmark (~> 0.4)
bundler (~> 2.0)
diff-lcs (~> 1.4)
e2mmap
jaro_winkler (~> 1.5)
jaro_winkler (~> 1.6, >= 1.6.1)
kramdown (~> 2.3)
kramdown-parser-gfm (~> 1.1)
logger (~> 1.6)
observer (~> 0.1)
ostruct (~> 0.6)
parser (~> 3.0)
reverse_markdown (>= 1.0.5, < 3)
rubocop (>= 0.52)
prism (~> 1.4)
rbs (>= 3.6.1, <= 4.0.0.dev.4)
reverse_markdown (~> 3.0)
rubocop (~> 1.76)
thor (~> 1.0)
tilt (~> 2.0)
yard (~> 0.9, >= 0.9.24)
yard-activesupport-concern (~> 0.0)
yard-solargraph (~> 0.1)
spoon (0.0.6)
ffi
stringio (3.2.0)
Expand All @@ -195,17 +200,16 @@ GEM
unicode-emoji (4.2.0)
webrick (1.9.2)
yard (0.9.38)
yard-activesupport-concern (0.0.1)
yard (>= 0.8)
yard-solargraph (0.1.0)
yard (~> 0.9)

PLATFORMS
arm64-darwin-21
arm64-darwin-22
arm64-darwin-23
arm64-darwin-24
arm64-darwin-25
java
universal-java-11
universal-java-18
x86_64-darwin-19
x86_64-linux

DEPENDENCIES
Expand All @@ -232,4 +236,4 @@ DEPENDENCIES
yard

BUNDLED WITH
4.0.2
2.7.2
10 changes: 10 additions & 0 deletions Gemfile.qlty
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

source "https://rubygems.org"

# These are gems used by qlty to perform code quality checks.
# They are installed by the qlty cli tool and are included for local development, but are not installed in CI.

gem "rubocop"
gem "rubocop-rake"
gem "rubocop-rspec"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ manipulations to ensure an accurate result.

## Installation

This package requires Ruby 3.2 or later.

This package may be installed using:

```bash
Expand Down Expand Up @@ -52,6 +54,7 @@ unit = "1 mm".to_unit # convert string object
unit = object.to_unit # convert any object using object.to_s
unit = Unit.new('1/4 cup') # Rational number
unit = Unit.new('1+1i mm') # Complex Number
unit = Unit.new(scalar: 1.5, numerator: ["<meter>"], denominator: ["<second>"]) # keyword arguments
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README shows an example of using keyword arguments to create a Unit (line 57), but this constructor pattern lacks test coverage. Consider adding a test to verify that Unit.new(scalar: 1.5, numerator: ["<meter>"], denominator: ["<second>"]) works as expected and produces the correct unit.

Copilot uses AI. Check for mistakes.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

```

### Rules
Expand Down
12 changes: 9 additions & 3 deletions lib/ruby_units/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ class Configuration
# @return [Symbol] the format to use when generating output (:rational or :exponential) (default: :rational)
attr_reader :format

def initialize
self.format = :rational
self.separator = true
# Initialize configuration with keyword arguments
#
# @param separator [Boolean] whether to include a space between the scalar and the unit (default: true)
# @param format [Symbol] the format to use when generating output (default: :rational)
# @param _options [Hash] additional keyword arguments (ignored, for forward compatibility)
# @return [Configuration] a new configuration instance
def initialize(separator: true, format: :rational, **_options)
self.separator = separator
self.format = format
end

# Use a space for the separator to use when generating output.
Expand Down
8 changes: 2 additions & 6 deletions lib/ruby_units/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,11 @@ def definition=(unit)

# is this definition for a prefix?
# @return [Boolean]
def prefix?
kind == :prefix
end
def prefix? = kind == :prefix

# Is this definition the unity definition?
# @return [Boolean]
def unity?
prefix? && scalar == 1
end
def unity? = prefix? && scalar == 1

# is this a base unit?
# units are base units if the scalar is one, and the unit is defined in terms of itself.
Expand Down
77 changes: 43 additions & 34 deletions lib/ruby_units/unit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,10 @@ def self.eliminate_terms(q, n, d)
end
num = UNITY_ARRAY if num.empty?
den = UNITY_ARRAY if den.empty?
{ scalar: q, numerator: num.flatten, denominator: den.flatten }
scalar = q
numerator = num.flatten
denominator = den.flatten
{ scalar:, numerator:, denominator: }
end

# Creates a new unit from the current one with all common terms eliminated.
Expand Down Expand Up @@ -519,32 +522,32 @@ def initialize(*options)
end

case options[0]
when Unit
copy(options[0])
in Unit => unit
copy(unit)
return
when Hash
@scalar = options[0][:scalar] || 1
@numerator = options[0][:numerator] || UNITY_ARRAY
@denominator = options[0][:denominator] || UNITY_ARRAY
@signature = options[0][:signature]
when Array
initialize(*options[0])
in Hash => hash
@scalar = hash[:scalar] || 1
@numerator = hash[:numerator] || UNITY_ARRAY
@denominator = hash[:denominator] || UNITY_ARRAY
@signature = hash[:signature]
in Array => array
initialize(*array)
return
when Numeric
@scalar = options[0]
in Numeric => num
@scalar = num
@numerator = @denominator = UNITY_ARRAY
when Time
@scalar = options[0].to_f
in Time => time
@scalar = time.to_f
@numerator = ["<second>"]
@denominator = UNITY_ARRAY
when DateTime, Date
@scalar = options[0].ajd
in DateTime | Date => date
@scalar = date.ajd
@numerator = ["<day>"]
@denominator = UNITY_ARRAY
when /^\s*$/
in /^\s*$/ => _empty
raise ArgumentError, "No Unit Specified"
when String
parse(options[0])
in String => str
parse(str)
else
raise ArgumentError, "Invalid Unit Format"
end
Expand Down Expand Up @@ -1334,45 +1337,51 @@ def -@
def abs
return @scalar.abs if unitless?

self.class.new(@scalar.abs, @numerator, @denominator)
self.class.new(scalar: @scalar.abs, numerator: @numerator, denominator: @denominator)
end

# ceil of a unit
# Forwards all arguments to the scalar's ceil method
# @return [Numeric,Unit]
def ceil(*args)
return @scalar.ceil(*args) if unitless?
def ceil(...)
return @scalar.ceil(...) if unitless?

self.class.new(@scalar.ceil(*args), @numerator, @denominator)
self.class.new(scalar: @scalar.ceil(...), numerator: @numerator, denominator: @denominator)
end

# Floor of a unit
# Forwards all arguments to the scalar's floor method
# @return [Numeric,Unit]
def floor(*args)
return @scalar.floor(*args) if unitless?
def floor(...)
return @scalar.floor(...) if unitless?

self.class.new(@scalar.floor(*args), @numerator, @denominator)
self.class.new(scalar: @scalar.floor(...), numerator: @numerator, denominator: @denominator)
end

# Round the unit according to the rules of the scalar's class. Call this
# with the arguments appropriate for the scalar's class (e.g., Integer,
# Rational, etc..). Because unit conversions can often result in Rational
# scalars (to preserve precision), it may be advisable to use +to_s+ to
# format output instead of using +round+.
# Forwards all arguments to the scalar's round method
# @example
# RubyUnits::Unit.new('21870 mm/min').convert_to('m/min').round(1) #=> 2187/100 m/min
# RubyUnits::Unit.new('21870 mm/min').convert_to('m/min').to_s('%0.1f') #=> 21.9 m/min
#
# @return [Numeric,Unit]
def round(*args, **kwargs)
return @scalar.round(*args, **kwargs) if unitless?
def round(...)
return @scalar.round(...) if unitless?

self.class.new(@scalar.round(*args, **kwargs), @numerator, @denominator)
self.class.new(scalar: @scalar.round(...), numerator: @numerator, denominator: @denominator)
end

# Truncate the unit according to the scalar's truncate method
# Forwards all arguments to the scalar's truncate method
# @return [Numeric, Unit]
def truncate(*args)
return @scalar.truncate(*args) if unitless?
def truncate(...)
return @scalar.truncate(...) if unitless?

self.class.new(@scalar.truncate(*args), @numerator, @denominator)
self.class.new(scalar: @scalar.truncate(...), numerator: @numerator, denominator: @denominator)
end

# Returns next unit in a range. Increments the scalar by 1.
Expand All @@ -1385,7 +1394,7 @@ def truncate(*args)
def succ
raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i

self.class.new(@scalar.to_i.succ, @numerator, @denominator)
self.class.new(scalar: @scalar.to_i.succ, numerator: @numerator, denominator: @denominator)
end

alias next succ
Expand All @@ -1400,7 +1409,7 @@ def succ
def pred
raise ArgumentError, "Non Integer Scalar" unless @scalar == @scalar.to_i

self.class.new(@scalar.to_i.pred, @numerator, @denominator)
self.class.new(scalar: @scalar.to_i.pred, numerator: @numerator, denominator: @denominator)
end

# Tries to make a Time object from current unit. Assumes the current unit hold the duration in seconds from the epoch.
Expand Down
Loading