Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inheritance in format (e.g. xml do) specific blocks #7

Open
ronaldtse opened this issue May 24, 2024 · 5 comments
Open

Inheritance in format (e.g. xml do) specific blocks #7

ronaldtse opened this issue May 24, 2024 · 5 comments
Labels
enhancement New feature or request

Comments

@ronaldtse
Copy link

ronaldtse commented May 24, 2024

Shale provides format specific blocks such as:

class X < Shale::Mapper
  attribute :a, Shale::Type::String
  attribute :b, Shale::Type::String

  xml do
    root 'foo'
    map_attribute 'A', to: :a
    map_attribute 'B', to: :b
  end
end

When a class inherits from it, the xml block mapping is completely wiped:

class Y < X
  xml do
    root 'bar'
    # map_attribute 'A', to: :a # This is gone!
    map_attribute 'B', to: :b
  end
end

It is very inconvenient to have to re-specify all format-specific mappings.

This situation is terrible for versioned namespaces because let's say there is UML version 2013 vs version 2016, the objects are slightly different, but the entire format block really needs to get rewritten.

Here's an example of the ideal situation:

class Uml2013 < Shale::Mapper
  attribute :a, Uml2013String
  attribute :b, Uml2013Note

  xml do
    root 'uml'
    map_attribute 'A', to: :a
    map_attribute 'B', to: :b
  end
end

class Uml2016 < Uml2013
  attribute :b, Uml2016Note
  xml do
    map_attribute 'B', to: :b
  end
end

The correct situation is to have format specific mappings retained unless overridden. There could also be a command that removes all mappings should that be necessary, such as xml! do.

@andrew2net
Copy link

@ronaldtse In some cases we can use modules to share code:

module BibliographicItem
  def self.included(base)
    base.class_eval do
      attribute :schema_version, Shale::Type::String
      ...

      xml do
        map_attribute "schema-version", to: :schema_version
        ...
      end
    end
  end
end

class Bibitem < Shale::Mapper
  include BibliographicItem

  @xml_mapping.instance_eval do
    root "bibitem"
    ...
  end
end

class Bibdata < Shale::Mapper
  include BibliographicItem

  @xml_mapping.instance_eval do
    root "bibdata"
    ...
  end
end

@opoudjis
Copy link

This is a pain for me as well, and I have already been yelling at the clown set up of this software. Repeating all accessors every time we inherit a class defeats the entire point of using classes to define accessors to XML and YAML.

@ronaldtse
Copy link
Author

For @opoudjis it’s even more painful because he needs to override only one element in a tree of Mapper classes, and it forces him to inherit the entire tree of classes but only override one.

@ronaldtse
Copy link
Author

This is a pain for me as well, and I have already been yelling at the clown set up of this software. Repeating all accessors every time we inherit a class defeats the entire point of using classes to define accessors to XML and YAML.

But can you design a syntax that address this? We can then find a way to implement it.

@opoudjis
Copy link

opoudjis commented Jun 5, 2024

The most obvious thing to do would be to treat the do block like a class method (even if it isn't one), and introduce super.

So:

class Uml2013 < Shale::Mapper
  attribute :a, Uml2013String
  attribute :b, Uml2013Note

  xml do
    root 'uml'
    map_attribute 'A', to: :a
    map_attribute 'B', to: :b
  end
end

class Uml2016 < Uml2013
  attribute :b, Uml2016Note
  attribute :c, Uml2013Note
  xml do
    super
    map_attribute 'C', to: :c
  end
end

The other thing to do would be to modularise mappings in the XML block into class methods, which could then be overridden:

class Uml2013 < Shale::Mapper
  attribute :a, Uml2013String
  attribute :b, Uml2013Note

  xml do
    root 'uml'
    mapA
    mapB
  end

  def mapA
    map_attribute 'A', to: :a
  end

  def mapB
    map_attribute 'B', to: :b
  end
end

class Uml2016 < Uml2013
  attribute :b, Uml2016Note
  attribute :c, Uml2013Note

  def mapB
    map_attribute 'B', to: :b
    map_attribute 'C', to: :c
  end

end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants