module RailsConnector module AttributeContent extend ActiveSupport::Concern included do private attr_accessor :data_from_cms end def respond_to?(method_id, include_private=false) if has_attribute?(method_id) true else super end end def method_missing(method_name, *args) if has_attribute?(method_name) read_attribute(method_name.to_s) else super end end def read_attribute(attribute_name) @attribute_cache.fetch(attribute_name) do (raw_value, attribute_type) = data_from_cms.value_and_type_of(attribute_name) @attribute_cache[attribute_name] = prepare_attribute_value(raw_value, attribute_type, attribute_name) end end def has_attribute?(name) data_from_cms.has_custom_attribute?(name.to_s) end # @return [String] def type_of_attribute(field_name) data_from_cms.type_of(field_name.to_s) end # Returns the value of an internal or external attribute specified by its name. # Passing an invalid key will not raise an error, but return +nil+. # @api public def [](key) key = key.to_s has_attribute?(key) ? read_attribute(key) : nil end # Hook method to control which widget classes should be available for this page. # Override it to allow only certain classes or none. # Must return either +NilClass+, or +Array+. # # If +nil+ is returned (default), then all widget classes will be available for this page. # # If +Array+ is returned, then it should include desired class names. # Each class name must be either a +String+ or a +Symbol+. # Only this class names will be available for this page. # Order of the class names will be preserved. # # @param [String] field_name Name of the widget field. # @return [nil, Array] # @api public def valid_widget_classes_for(field_name) end private def update_data(data) self.data_from_cms = data @attribute_cache = {} end def prepare_attribute_value(attribute_value, attribute_type, attribute_name) case attribute_type when "html" StringTagging.tag_as_html(attribute_value) when "date" DateAttribute.parse(attribute_value) if attribute_value when "linklist" build_links(attribute_value) when "reference" BasicObj.find([attribute_value]).first when "referencelist" BasicObj.find(attribute_value).compact when "widget" build_widgets(attribute_value, attribute_name) else attribute_value end end def build_links(link_definitions) if link_definitions.present? link_definitions = link_definitions.map(&:with_indifferent_access) object_ids = link_definitions.map { |link_data| link_data[:destination] }.compact.uniq objects = object_ids.empty? ? [] : Obj.find(object_ids) link_definitions.each_with_object([]) do |link_data, links| obj = objects.detect { |o| o && o.id == link_data[:destination] } link = Link.new(link_data.merge(obj: obj)) links << link if link.resolved? end else [] end end def build_widgets(widget_data, attribute_name) if widget_data if widget_data['layout'] # Old style widget fields have a 'layout' key. Deprecation.warn("Key 'layout' found in attribute of type 'widget' in obj '#{id}'." \ ' Please migrate the content so that is uses embedded widgets.') end (widget_data['list'] || []).map do |list_item| widget_from_pool(list_item['widget']).tap do |widget| widget.container = self widget.container_field_name = attribute_name end end else [] end end module ClassMethods # Instantiate an {BasicObj Obj} instance from obj_data. # If a subclass of Obj with the same name as the property +_obj_class+ exists, # the instantiated Obj will be an instance of that subclass. def instantiate(obj_data) obj_class = obj_data.value_of('_obj_class') type_computer.compute_type(obj_class).new(obj_data) end end end end