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 .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ RSpec/ExampleLength:
# ========= Metrics =========
Metrics/ClassLength:
Max: 150

Metrics/MethodLength:
Max: 20
16 changes: 0 additions & 16 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,5 @@
# Changelog

## [0.4.1] - 2025-09-09

## What's Changed
* Update attributes method signatures to use optional keyword arguments… by @Syati in https://github.com/Syati/structured_params/pull/8


**Full Changelog**: https://github.com/Syati/structured_params/compare/v0.4.0...v0.4.1

## [0.4.0] - 2025-09-08

## What's Changed
* Add compact option to attributes and serialize_structured_value methods by @Syati in https://github.com/Syati/structured_params/pull/7


**Full Changelog**: https://github.com/Syati/structured_params/compare/v0.3.0...v0.4.0

## [0.3.0] - 2025-09-06

## What's Changed
Expand Down
36 changes: 26 additions & 10 deletions lib/structured_params/params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,26 @@ def errors
end

# Convert structured objects to Hash and get attributes
#: (?symbolize: false, ?compact: bool) -> Hash[String, untyped]
#: (?symbolize: true, ?compact: bool) -> Hash[Symbol, untyped]
def attributes(symbolize: false, compact: false)
#: (?symbolize: false, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[String, untyped]
#: (?symbolize: true, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[Symbol, untyped]
def attributes(symbolize: false, compact_mode: :none)
attrs = super()

self.class.structured_attributes.each_key do |name|
value = attrs[name.to_s]
attrs[name.to_s] = serialize_structured_value(value, compact: compact)
attrs[name.to_s] = serialize_structured_value(value, compact_mode: compact_mode)
end

result = symbolize ? attrs.deep_symbolize_keys : attrs
compact ? result.compact : result

case compact_mode
when :all_blank
result.compact_blank
when :nil_only
result.compact
else
result
end
end

private
Expand Down Expand Up @@ -152,14 +160,22 @@ def format_error_path(attr_name, index = nil)
end

# Serialize structured values
#: (bool, ?compact: bool) -> untyped
def serialize_structured_value(value, compact: false)
#: (untyped, ?compact_mode: :none | :nil_only | :all_blank) -> untyped
def serialize_structured_value(value, compact_mode: :none)
case value
when Array
result = value.map { |item| item.attributes(symbolize: false, compact: compact) }
compact ? result.compact : result
result = value.map { |item| item.attributes(symbolize: false, compact_mode: compact_mode) }

case compact_mode
when :all_blank
result.compact_blank
when :nil_only
result.compact
else
result
end
when StructuredParams::Params
value.attributes(symbolize: false, compact: compact)
value.attributes(symbolize: false, compact_mode: compact_mode)
else
value
end
Expand Down
12 changes: 6 additions & 6 deletions sig/structured_params/params.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ module StructuredParams
def errors: () -> ::StructuredParams::Errors

# Convert structured objects to Hash and get attributes
# : (?symbolize: false, ?compact: bool) -> Hash[String, untyped]
# : (?symbolize: true, ?compact: bool) -> Hash[Symbol, untyped]
def attributes: (?symbolize: false, ?compact: bool) -> Hash[String, untyped]
| (?symbolize: true, ?compact: bool) -> Hash[Symbol, untyped]
# : (?symbolize: false, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[String, untyped]
# : (?symbolize: true, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[Symbol, untyped]
def attributes: (?symbolize: false, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[String, untyped]
| (?symbolize: true, ?compact_mode: :none | :nil_only | :all_blank) -> Hash[Symbol, untyped]

private

Expand Down Expand Up @@ -70,8 +70,8 @@ module StructuredParams
def format_error_path: (Symbol, Integer?) -> String

# Serialize structured values
# : (bool, ?compact: bool) -> untyped
def serialize_structured_value: (bool, ?compact: bool) -> untyped
# : (untyped, ?compact_mode: :none | :nil_only | :all_blank) -> untyped
def serialize_structured_value: (untyped, ?compact_mode: :none | :nil_only | :all_blank) -> untyped

# Integrate structured parameter errors into parent errors
# : (untyped, String) -> void
Expand Down
58 changes: 52 additions & 6 deletions spec/params_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,11 @@

describe '#attributes' do
subject(:attributes) do
build(:user_parameter, **user_param_attributes).attributes(symbolize: symbolize, compact: compact)
build(:user_parameter, **user_param_attributes).attributes(symbolize: symbolize, compact_mode: compact_mode)
end

let(:user_param_attributes) { attributes_for(:user_parameter) }
let(:compact) { false }
let(:compact_mode) { :none }

context 'when symbolize: false' do
let(:symbolize) { false }
Expand All @@ -179,9 +179,9 @@
it { is_expected.to eq user_param_attributes.deep_symbolize_keys }
end

context 'with compact: true' do
context 'with compact_mode: :nil_only' do
let(:symbolize) { false }
let(:compact) { true }
let(:compact_mode) { :nil_only }

context 'with nil values' do
let(:user_param_attributes) do
Expand Down Expand Up @@ -224,9 +224,9 @@
end
end

context 'with symbolize: true and compact: true' do
context 'with symbolize: true and compact_mode: :nil_only' do
let(:symbolize) { true }
let(:compact) { true }
let(:compact_mode) { :nil_only }

let(:user_param_attributes) do
{
Expand Down Expand Up @@ -260,6 +260,52 @@
end
end
end

context 'with compact_mode: :all_blank' do
let(:symbolize) { false }
let(:compact_mode) { :all_blank }

context 'with blank values' do
let(:user_param_attributes) do
{
name: 'Tanaka Taro',
email: '',
age: 30,
address: {
postal_code: '123-4567',
prefecture: nil,
city: 'Shibuya-ku',
street: ''
},
hobbies: [
{ name: 'programming', level: 3, years_experience: nil },
{ name: '', level: 2, years_experience: 5 }
],
tags: %w[Ruby Rails Web]
}
end

let(:expected_result) do
{
'name' => 'Tanaka Taro',
'age' => 30,
'address' => {
'postal_code' => '123-4567',
'city' => 'Shibuya-ku'
},
'hobbies' => [
{ 'name' => 'programming', 'level' => 3 },
{ 'level' => 2, 'years_experience' => 5 }
],
'tags' => %w[Ruby Rails Web]
}
end

it 'removes blank values (nil, empty string, etc.) recursively' do
expect(attributes).to eq(expected_result)
end
end
end
end

describe 'edge cases' do
Expand Down