kgiszczak / shale

Shale is a Ruby object mapper and serializer for JSON, YAML, TOML, CSV and XML. It allows you to parse JSON, YAML, TOML, CSV and XML data and convert it into Ruby data structures, as well as serialize data structures into JSON, YAML, TOML, CSV or XML.
https://shalerb.org/
MIT License
626 stars 19 forks source link

Non obvious behaviour for usage with configuration block #3

Closed Tab10id closed 2 years ago

Tab10id commented 2 years ago

There two mappers that by my mind should work identically:

require 'shale'

class OnePerson < Shale::Mapper
  xml do
    root 'Person'
  end

  attribute :name, Shale::Type::String
end

class AnotherPerson < Shale::Mapper  
  attribute :name, Shale::Type::String

  xml do
    root 'Person'
  end
end

puts OnePerson.new(name: 'John Doe').to_xml(:pretty)
# <Person>
#   <name>John Doe</name>
# </Person>

puts AnotherPerson.new(name: 'John Doe').to_xml(:pretty)
# <Person/>

After little research I recognized that xml method fully override config setted by attribute method calls. It's not critical but I think that it should be documented as minimum.

kgiszczak commented 2 years ago

Thanks for pointing that out.

That's an oversight on my side. Both should work the same and the second version's output is intended. When you use xml block it should override default mapping created by attribute methods.

I'll fix that in the next version and also update the docs to be clear about this behavior.

Tab10id commented 2 years ago

Thanks. Do you have plans to implement regular way to fix original mapping without fully override? In my case I have a bunch of attributes and I just want to rename a root element. Without this feature I need to double lines of this class.

kgiszczak commented 2 years ago

Full override is intended, because in most cases when you define your own mapping you don't need the default one.

But you always have access to the mapping object and can modify it however you want, so in your case something like this would work:

class Person < Shale::Mapper
  attribute :name, Shale::Type::String
end

Person.xml_mapping.root('foo')

Person.new(name: 'John Doe').to_xml(:pretty)
# =>
#
# <foo>
#   <name>John Doe</name>
# </foo>
Tab10id commented 2 years ago

Ok. I like it. Little fix:

class Person < Shale::Mapper
  attribute :name, Shale::Type::String

  xml_mapping.root('foo')
end

Person.new(name: 'John Doe').to_xml(:pretty)
# =>
#
# <foo>
#   <name>John Doe</name>
# </foo>