class Compostr::CustomPostType

Base class to inherit from for Classes that map to Wordpress Custom Post Types.

Besides the post_id, title, content, excerpt and featured_image (id) that define a post, the CustomPostType likely will own custom field values. These are specified with wp_custom_field_single and wp_custom_field_multi (depending on their type).

To loop over the fields, use @fields and @multi_fields.

Attributes

content[RW]
excerpt[RW]
fields[RW]

TODO rename to single_fields?

multi_fields[RW]

TODO rename to single_fields?

post_id[RW]
title[RW]

Public Class Methods

additional_field_action(action) click to toggle source

Define whether additional custom fields should be

:ignore -> ignored (default)
:delete -> marked for deletion
:add    -> added

Other values for action will silently be ignored.

# File lib/compostr/custom_post_type.rb, line 94
def self.additional_field_action(action)
  if [:ignore, :delete, :add].include? action.to_sym
    # @additional_field_action = :action
    self.class_eval("@additional_field_action = :#{action}")
  end
end
from_content_hash(content_hash) click to toggle source

From a Hash as returned by RubyPress's getPost(s) method populate and return a new CustomPostType-instance.

Custom field values will be created as specified by the wp_custom_field_single/multi definitions.

# File lib/compostr/custom_post_type.rb, line 200
def self.from_content_hash content_hash
  return nil if content_hash.nil?
  entity = new(post_id: content_hash["post_id"],
               content: content_hash["post_content"],
               excerpt: content_hash["post_excerpt"],
               title:   content_hash["post_title"])

  custom_fields_list = content_hash["custom_fields"] || []

  supported_fields.each do |field_key|
    #puts "iterating over supported field #{field_key}"
    if is_single_field? field_key
      # Here: duplicate deletion possible
      field = custom_fields_list.find{|f| f["key"] == field_key}
      if field
        entity.send("#{field_key}=".to_sym, field["value"])
        entity.field?(field_key).id = field["id"]
      end
    else
      fields = custom_fields_list.select{|f| f["key"] == field_key}
      values = fields.map{|f| f["value"]}
      entity.send("#{field_key}=".to_sym, values)
      # Not elegant: Set the id one per one
      fields.each do |f|
        entity.set_field_id(field_key, f["value"], f["id"])
      end
    end
  end
  # if additional fields add, add these

  entity
end
is_multi_field?(field_name) click to toggle source
# File lib/compostr/custom_post_type.rb, line 305
def self.is_multi_field?(field_name)
  supported_multi_fields.include? field_name
end
is_single_field?(field_name) click to toggle source
# File lib/compostr/custom_post_type.rb, line 313
def self.is_single_field?(field_name)
  supported_single_fields.include? field_name
end
new(**kwargs) click to toggle source
# File lib/compostr/custom_post_type.rb, line 105
def initialize **kwargs
  @fields = Hash.new
  # This one is painful, maybe field? and field!?
  #@fields.default_proc = proc do |hash, key|
  #  hash[key] = CustomFieldValue.new(nil, key, nil)
  #end
  @multi_fields = Hash.new
  @multi_fields.default_proc = proc do |hash, key|
    hash[key] = []
  end
  kwargs.each do |k,v|
    if k == :title
      # strip ?
      @title = v
    elsif k == :content
      self.content= v
    elsif k == :excerpt
      self.excerpt= v
    elsif k == :post_id
      self.post_id= v
    elsif k == :featured_image_id
      @featured_image_id = v
    # Better: has_custom_field?
    elsif respond_to?(k.to_sym)
      self.send(((k.to_s) + "=").to_sym, v)
    elsif additional_field_action == :add
      @fields[k.to_sym] = CustomFieldValue.new(nil, k.to_sym, v)
    end
  end
end
supported_fields() click to toggle source

Returns list of field keys generally supported by this Custom Post Type.

# File lib/compostr/custom_post_type.rb, line 174
def self.supported_fields
  supported_single_fields | supported_multi_fields
end
supported_multi_fields() click to toggle source

Returns list of multiple-valued field keys generally supported by this Custom Post Type.

# File lib/compostr/custom_post_type.rb, line 186
def self.supported_multi_fields
  instance_variable_get(:@supported_multi_fields) || []
end
supported_single_fields() click to toggle source

Returns list of single-valued field keys generally supported by this Custom Post Type.

# File lib/compostr/custom_post_type.rb, line 180
def self.supported_single_fields
  instance_variable_get(:@supported_single_fields) || []
end
wp_custom_field_multi(field_key) click to toggle source

Specify a field that will make and take a fine array.

# File lib/compostr/custom_post_type.rb, line 55
def self.wp_custom_field_multi(field_key)
  # def field_key=(new_value)
  #   multi_field('field_key') = new_value.map{|v| CustomFieldValue.new(nil, 'field_key', v)}
  # end
  # TODO recycle!
  self.class_eval("def #{field_key.to_s}=(new_value); @multi_fields['#{field_key.to_s}'] = new_value.map{|v| CustomFieldValue.new(nil, '#{field_key.to_s}', v)}; end")
  # def field_key
  #   multi_field(field_key).map(&:value).compact
  # end
  self.class_eval("def #{field_key.to_s}; return multi_field('#{field_key.to_s}').map(&:value).compact; end")

  # Add field to @supported_(multi_)fields.
  # This is declared in the class, thus a kindof CLASS variable!
  self.class_eval("(@supported_multi_fields ||= []) << '#{field_key}'")
end
wp_custom_field_single(field_key) click to toggle source

Defines accessor methods for the field, which will only allow a single value.

Note that the accessor only wears strings and automatically strips

# File lib/compostr/custom_post_type.rb, line 39
def self.wp_custom_field_single(field_key)
  # def field_key=(new_value)
  #   field!('field_key') = new_value.to_s.strip
  # end
  self.class_eval("def #{field_key.to_s}=(new_value); field!('#{field_key.to_s}').value = WPString.wp_string(new_value); end")
  # def field_key
  #   field?(field_key).value
  # end
  self.class_eval("def #{field_key.to_s}; return field?('#{field_key.to_s}').value; end")

  # Add field to @supported_(single_)fields.
  # This is declared in the class, thus a kindof CLASS variable!
  self.class_eval("(@supported_single_fields ||= []) << '#{field_key}'")
end
wp_post_content_alias(content_alias) click to toggle source

Alias the post_content getter and setter with another 'name'.

# File lib/compostr/custom_post_type.rb, line 78
def self.wp_post_content_alias(content_alias)
  self.class_eval("alias :#{content_alias.to_sym}= :content=")
  self.class_eval("alias :#{content_alias.to_sym}  :content")
end
wp_post_title_alias(title_alias) click to toggle source

Alias the post_title getter and setter with another 'name'.

# File lib/compostr/custom_post_type.rb, line 72
def self.wp_post_title_alias(title_alias)
  self.class_eval("alias :#{title_alias.to_sym}= :title=")
  self.class_eval("alias :#{title_alias.to_sym}  :title")
end
wp_post_type(wp_post_type) click to toggle source

Define accessor method to the POST_TYPE (Class#post_type and Instance#post_type).

# File lib/compostr/custom_post_type.rb, line 18
def self.wp_post_type(wp_post_type)
  # TODO syntax: define_method("....")

  # Class wide variable (could also be a constant)
  self.class_eval("POST_TYPE = '#{wp_post_type}'.freeze")
  # Class accessor method
  # def self.post_type
  #   POST_TYPE
  # end
  self.class_eval("def self.post_type; POST_TYPE; end")
  # Instance accessor method
  # def post_type
  #   POST_TYPE
  # end
  self.class_eval("def post_type; POST_TYPE; end")
end

Public Instance Methods

additional_field_action() click to toggle source
# File lib/compostr/custom_post_type.rb, line 101
def additional_field_action
  self.class.instance_variable_get(:@additional_field_action) || :ignore
end
custom_fields_hash() click to toggle source
# File lib/compostr/custom_post_type.rb, line 136
def custom_fields_hash
  @fields.values.map(&:to_hash)
end
diff(other_cpt_object) click to toggle source

Returns hash where keys are field names where the values differ. values of returned hash are arrays like [own_value, other_different_value]. Returns empty hash to signalize equaliness.

# File lib/compostr/custom_post_type.rb, line 335
def diff(other_cpt_object)
  if other_cpt_object.nil?
    other_cpt_object = NullCustomPostType.new
  end

  diff_fields = {}
  # Fields exclusive to this one.
  (@fields.keys - other_cpt_object.fields.keys).each do |f|
    diff_fields[f] = [@fields[f].value, nil]
  end
  # Fields exclusive to the other.
  (other_cpt_object.fields.keys - @fields.keys).each do |f|
    diff_fields[f] = [nil, other_cpt_object.fields[f].value]
  end
  # Mutual fields
  (@fields.keys | other_cpt_object.fields.keys).each do |f|
    field_value = field?(f).value
    other_field_value = other_cpt_object.field?(f).value
    if other_field_value != field_value
      diff_fields[f] = [field_value, other_field_value]
    end
  end
  # Multi-Fields exclusive to this one.
  (@multi_fields.keys - other_cpt_object.multi_fields.keys).each do |f|
    diff_fields[f] = [@multi_fields[f].map(&:value), nil]
  end
  # Multi-Fields exclusive to the other.
  (other_cpt_object.multi_fields.keys - @multi_fields.keys).each do |f|
    diff_fields[f] = [nil, other_cpt_object.multi_fields[f].map(&:value)]
  end
  # Mutual Multi-fields
  (@multi_fields.keys | other_cpt_object.multi_fields.keys).each do |f|
    field_values = multi_field(f).map(&:value).compact
    other_field_values = other_cpt_object.multi_field(f).map(&:value).compact
    if other_field_values != field_values
      diff_fields[f] = [field_values, other_field_values]
    end
  end
  if @title.to_s.strip != other_cpt_object.title.to_s.strip
    diff_fields["title"] = [@title, other_cpt_object.title]
  end
  if @excerpt.to_s.strip != other_cpt_object.excerpt.to_s.strip
    diff_fields["excerpt"] = [@excerpt, other_cpt_object.excerpt]
  end
  if @featured_image_id != other_cpt_object.featured_image_id
    diff_fields["featured_image_id"] = [@featured_image_id, other_cpt_object.featured_image_id]
  end
  if @content.to_s.strip != other_cpt_object.content.to_s.strip
    diff_fields["content"] = [@content, other_cpt_object.content]
  end
  diff_fields
end
different_from?(other_cpt_object) click to toggle source
# File lib/compostr/custom_post_type.rb, line 388
def different_from? other_cpt_object
  !diff(other_cpt_object).empty?
end
field!(field_name) click to toggle source

Access (or create) a CustomFieldValue that can hold a single value.

# File lib/compostr/custom_post_type.rb, line 154
def field!(field_name)
  # ||= would probably do, too.
  if @fields.key? field_name
    @fields[field_name]
  else
    @fields[field_name] = CustomFieldValue.new(nil, field_name, nil)
  end
end
field?(field_name) click to toggle source

Access the given field, returns a NullCustomFieldValue if not found. The NullCustomFieldValue does not accept setting any values and returns nil for id, key and value.

Use this to (readonly) access a field with given name.

# File lib/compostr/custom_post_type.rb, line 145
def field?(field_name)
  if @fields.key? field_name
    @fields[field_name]
  else
    NullCustomFieldValue.new
  end
end
has_custom_field?(field_name) click to toggle source

True iff supported fields include field_name

# File lib/compostr/custom_post_type.rb, line 191
def has_custom_field? field_name
  supported_fields.include? field_name
end
in_wordpress?() click to toggle source

Fetched from wordpress/ Post-ID known?

# File lib/compostr/custom_post_type.rb, line 318
def in_wordpress?
  post_id.to_s != '' && !!post_id
end
integrate_field_ids(other_entity) click to toggle source

When additional_field_action == :ignore (the default) sets (wp) ids of fields for which values are set.

If additional_field_action == :add CustomFieldValues of other_entity are copied if not yet existing in this entity (otherwise only the id of the fields are set.

Finally, if additional_field_action == :delete , mark the fields which are NOT set in this entity but in the other entity ready for deletion.

The ids are taken from other_entity (if available, left empty otherwise).

# File lib/compostr/custom_post_type.rb, line 260
def integrate_field_ids other_entity
  # TODO rename and/or restructure this method
  # new from old
  fields.values.each do |f|
    if f.key.start_with? 'ref'
      puts "foreign fields : #{other_entity.fields.keys}"
      puts "foreign fields : #{other_entity.fields.values.map{|v| v.id.to_s + ' ' + v.id.class.to_s}}"
    puts "integrate field : #{f.key} #{f.inspect}"
    puts "    other field : #{other_entity.field?(f.key).inspect}"
    end
    f.id = other_entity.field?(f.key).id
  end

  if additional_field_action == :add
    # old to new
    other_entity.fields.values.each do |f|
      if !@fields.key?(f.key)
        @fields[f.key] = f
      end
    end
  elsif additional_field_action == :delete
    other_entity.fields.values.each do |f|
      if !@fields.key?(f.key)
        # This field will be deleted when used to edit Post
        @fields[f.key] = CustomFieldValue.new(f.id, nil, nil)
      end
    end
  end

  @multi_fields.each do |field_name, mf|
    ids = other_entity.multi_field(field_name).map(&:id)
    mf.each do |mf_entry|
      mf_entry.id = ids.delete_at(0) # keep order, use #pop otherwise
    end
    # If any ids left, delete these custom fields
    ids.each do |id|
      multi_field(field_name) << CustomFieldValue.new(id, nil, nil)
    end
  end
end
is_multi_field?(field_name) click to toggle source
# File lib/compostr/custom_post_type.rb, line 301
def is_multi_field?(field_name)
  self.class.is_multi_field?(field_name)
end
is_single_field?(field_name) click to toggle source
# File lib/compostr/custom_post_type.rb, line 309
def is_single_field?(field_name)
  self.class.is_single_field?(field_name)
end
multi_field(field_name) click to toggle source

Access a CustomFieldValue that can hold multiple values (array).

# File lib/compostr/custom_post_type.rb, line 164
def multi_field(field_name)
  @multi_fields[field_name]
end
set_field_id(field_key, field_value, field_id) click to toggle source
# File lib/compostr/custom_post_type.rb, line 322
def set_field_id field_key, field_value, field_id
  if is_single_field? field_key
    # ????!! field!
    field?(field_key).id = field_id
  else
    multi_field(field_key).find{|f| f.key == field_key && f.value == field_value}.id = field_id
  end
end
supported_fields() click to toggle source

Returns list of field keys generally supported by this Custom Post Type.

# File lib/compostr/custom_post_type.rb, line 169
def supported_fields
  self.class.supported_fields
end
to_content_hash() click to toggle source
# File lib/compostr/custom_post_type.rb, line 233
def to_content_hash
  content = {
    post_type:     post_type,
    post_status:   'publish',
    post_data:     Time.now,
    post_title:    title    || '', # why does content need '@'?
    post_content:  @content || '',
    post_excerpt:  @excerpt || '',
    custom_fields: @fields.map{|k,v| v.to_hash} | @multi_fields.flat_map{|k,v| v.flat_map(&:to_hash)}
  }
  if featured_image_id
    content[:post_thumbnail] = featured_image_id.to_s
  end
  content
end