Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# v0.6.1 2026-04-30
- Added `.validate_duration` and `.validates_durations` to `NxtSupport::DurationAttributeAccessor`

# v0.6.0 2024-09-27
- Added `NxtSupport::Uuid`

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
nxt_support (0.6.0)
nxt_support (0.6.1)
activerecord
activesupport
nxt_init
Expand Down
12 changes: 11 additions & 1 deletion lib/nxt_support/models/duration_attribute_accessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ def duration_attribute_writer(*attrs)
define_duration_attribute_writer(attr_name)
end
end

def validates_durations(*attrs, **opts)
Comment thread
nsommer marked this conversation as resolved.
validates_each(*attrs, **opts) do |record, attr, value|
record.errors.add attr, "is not a valid iso8601 duration." unless record.is_valid_iso8601_duration?(value)
end
end

def validates_duration(attr, **opts)
validates_durations(attr, **opts)
end

private

Expand Down Expand Up @@ -55,7 +65,7 @@ def define_duration_attribute_writer(attr_name)
def is_valid_iso8601_duration?(string)
ActiveSupport::Duration.parse(string)
true
rescue ActiveSupport::Duration::ISO8601Parser::ParsingError
rescue ActiveSupport::Duration::ISO8601Parser::ParsingError, TypeError
false
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/nxt_support/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module NxtSupport
VERSION = "0.6.0".freeze
VERSION = "0.6.1".freeze
end
100 changes: 99 additions & 1 deletion spec/models/duration_attribute_accessor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
end

it 'raises an ArgumentError' do
expect { course }.to raise_error(ArgumentError).with_message(/'1 month' is not a valid iso8601 string/)
expect { course }.to raise_error(ArgumentError).with_message(/is not a valid iso8601 string/)
end
end

Expand All @@ -101,4 +101,102 @@
end
end
end

describe '.validates_durations' do
subject(:klass) do
Class.new do
include ActiveModel::Model
include NxtSupport::DurationAttributeAccessor

attr_accessor :class_duration, :topic_duration

validates_durations :class_duration
end
end

context 'when the value is a valid ISO8601 duration string' do
it 'does not add an error' do
record = klass.new(class_duration: 'PT1H')
expect(record).to be_valid
end
end

context 'when the value is an invalid string' do
it 'adds an error with the invalid value in the message' do
record = klass.new(class_duration: 'not_a_duration')
expect(record).not_to be_valid
expect(record.errors[:class_duration]).to include('is not a valid iso8601 duration.')
end
end

context 'when the value is nil and allow_nil is not set' do
it 'adds an error' do
record = klass.new(class_duration: nil)
expect(record).not_to be_valid
expect(record.errors[:class_duration]).to include('is not a valid iso8601 duration.')
end
end

context 'when the value is nil and allow_nil is false' do
subject(:klass) do
Class.new do
include ActiveModel::Model
include NxtSupport::DurationAttributeAccessor

attr_accessor :class_duration

validates_durations :class_duration, allow_nil: false
end
end

it 'adds an error' do
record = klass.new(class_duration: nil)
expect(record).not_to be_valid
expect(record.errors[:class_duration]).to include('is not a valid iso8601 duration.')
end
end

context 'with allow_nil: true' do
subject(:klass) do
Class.new do
include ActiveModel::Model
include NxtSupport::DurationAttributeAccessor

attr_accessor :class_duration

validates_durations :class_duration, allow_nil: true
end
end

it 'does not add an error for nil' do
record = klass.new(class_duration: nil)
expect(record).to be_valid
end

it 'still adds an error for an invalid string' do
record = klass.new(class_duration: 'not_a_duration')
expect(record).not_to be_valid
end
end

context 'with multiple attributes' do
subject(:klass) do
Class.new do
include ActiveModel::Model
include NxtSupport::DurationAttributeAccessor

attr_accessor :class_duration, :topic_duration

validates_durations :class_duration, :topic_duration
end
end

it 'validates all specified attributes' do
record = klass.new(class_duration: 'not_valid', topic_duration: 'also_not_valid')
expect(record).not_to be_valid
expect(record.errors[:class_duration]).to be_present
expect(record.errors[:topic_duration]).to be_present
end
end
end
end