Skip to content

Commit

Permalink
Merge pull request #195 from dry-rb/refine-accessors
Browse files Browse the repository at this point in the history
Refine accessors to support invalid method names
  • Loading branch information
flash-gordon authored Jan 6, 2025
2 parents d0d5414 + 07a7581 commit f2d8e8b
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
1 change: 1 addition & 0 deletions changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
fixed:
- 'Fixed coercion errors for structs (issue #192 via #193)
(@flash-gordon)'
- "Invalid method names are now allowed as struct attributes (issue #169 via #195) (@flash-gordon)"
changed:
- 'Missing attribute error now includes the name of the class (issue #170 via #191)
(@phillipoertel + @cllns)'
Expand Down
25 changes: 17 additions & 8 deletions lib/dry/struct/class_interface.rb
Original file line number Diff line number Diff line change
Expand Up @@ -466,18 +466,27 @@ def build_type(name, type = Undefined, &)
end
private :build_type

# @api private
def define_accessors(keys)
keys.each do |key|
next if instance_methods.include?(key)

class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{key} # def email
@attributes[#{key.inspect}] # @attributes[:email]
end # end
RUBY
(keys - instance_methods).each do |key|
if valid_method_name?(key)
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{key} # def email
@attributes[#{key.inspect}] # @attributes[:email]
end # end
RUBY
else
define_method(key) { @attributes[key] }
end
end
end
private :define_accessors

# @api private
def valid_method_name?(key)
key.to_s.match?(/\A[a-zA-Z_]\w*\z/)
end
private :valid_method_name?
end
end
end
26 changes: 26 additions & 0 deletions spec/integration/attributes_from_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ class User < Dry::Struct
expect(Test::User.attribute_names).to eql(%i[address])
end

context "when attribute name is not a valid method name" do
before do
module Test
class InvalidName < Dry::Struct
attribute :"123", "string"
attribute :":", "string"
attribute :"with space", "string"
attribute :"with-dash", "string"
end
end
end

it "adds an accessor" do
odd_struct = Test::InvalidName.new(
"123": "John",
":": "Jane",
"with space": "Doe",
"with-dash": "Smith"
)
expect(odd_struct.public_send(:"123")).to eql("John")
expect(odd_struct.public_send(:":")).to eql("Jane")
expect(odd_struct.public_send("with space")).to eql("Doe")
expect(odd_struct.public_send("with-dash")).to eql("Smith")
end
end

context "inheritance" do
before do
class Test::Person < Dry::Struct
Expand Down

0 comments on commit f2d8e8b

Please sign in to comment.