Shadow Classes

Fiona 7 offers two operation modes, the standalone mode, which is exclusively oriented towards Scrivito, and the legacy mode in which Fiona 7 is also compatible with the Rails Connector.

In compatibility mode (legacy) the CMS object model classes are derived from RailsConnector::BasicObj (mostly indirectly via Obj). The name of an object class must be exactly the same as the name of the model class on which it is based. This poses a problem if one wants to use the APIs of the Scrivito SDK because this SDK expects the names to be identical, too.

Since there is no multiple inheritance in Ruby, this conflict cannot be solved by means of the programming language itself. Fiona 7 solves it by introducing the concept of shadow classes: Using the in_place block in a class derived from RailsConnector::BasicObj causes an additional, hidden class to be created.

Fiona 7 uses this hidden class as a replacement for the missing global class to handle all internal functions. If, for example, attributes defined in the Homepage are queried, the Homepage class that inherits from Scrivito::BasicObj is loaded first if it exists. If it doesn't, another attempt is made, this time with ShadowClasses::Homepage.

If, for example, you want to equip the Homepage object class with a headline attribute you want to be editable in place, take a look at the definition of the Homepage class in the application:

class Homepage < Obj
end

The Obj class in turn inherits from RailsConnector::BasicObj, as usual:

class Obj < RailsConnector::BasicObj
end

Now, the headline attribute should be added to the Homepage class using the in_place block:

class Homepage < Obj
  in_place do
    attribute :headline, :string
  end
end

This modification makes it possible to use fiona7_tag @obj, :headline to make the headline attribute of the Homepage editable in place where appropriate.

The in_place block internally creates the ShadowClasses::Homepage class which inherits from ShadowClasses::Obj. If the in_place block is used in the Obj class, the definition can be found in ShadowClasses::Obj, meaning that two classes are automatically created that look roughly like this:

class ShadowClasses::Obj < Scrivito::BasicObj
end

class ShadowClasses::Homepage < ShadowClasses::Obj
  attribute :headline, :string
end

Hence, you can define an attribute globally for all existing object classes by placing it into the in_place block of the Obj class:

class Obj < RailsConnector::BasicObj
  in_place do
    attribute :show_in_navigation, :enum, values: ['yes', 'no']
  end
end

Inside the in_place block, all functions can be used that are usually available for classes derived from Scrivito::BasicObj, e.g. valid_widget_classes_for for restricting the available widgets, or description_for_editor for specifying a descriptive text for page or widget types.

The class hierachy of the application is currently (1.5.x) not reflected in the shadow classes, meaning that only the attributes and methods are available that are defined in Obj or in an object class itself.

The following does not work as intended:

class ArticlePage < Obj
  in_place do
    attribute :headline, :string
  end
end

class NewsPage < ArticlePage
  in_place do
    attribute :summary, :html
  end
end

In this case, the NewsPage object class only includes the summary attribute (as well as the attributes originating from Obj), but not headline.

If inheritance with shadow classes cannot be provided otherwise (e.g. by means of mixins), you can apply the following pattern as a last resort:

class ArticlePage < Obj
  def self.inherited(subclass)
    super
    subclass.class_eval do
      in_place do
        attribute :headline, :string
      end
    end
  end
end

class NewsPage < ArticlePage
  in_place do
    attribute :summary, :html
  end
end