class Doing::Items

A collection of Item objects

Attributes

sections[RW]

Public Class Methods

new() click to toggle source
Calls superclass method
# File lib/doing/items/items.rb, line 13
def initialize
  super
  @sections = []
end

Public Instance Methods

add_section(section, log: false) click to toggle source

Add a new section to the sections array. Accepts either a Section object, or a title string that will be converted into a Section.

@param section [Section] The section to add. A String value will be converted to Section automatically. @param log [Boolean] Add a log message notifying the user about the creation of the section.

@return nothing

# File lib/doing/items/sections.rb, line 59
def add_section(section, log: false)
  section = section.is_a?(Section) ? section : Section.new(section.cap_first)

  return if section?(section)

  @sections.push(section)
  Doing.logger.info('New section:', %("#{section}" added)) if log
end
all_tags() click to toggle source

Get all tags on Items in self

@return [Array] array of tags

# File lib/doing/items/util.rb, line 26
def all_tags
  each_with_object([]) do |entry, tags|
    tags.concat(entry.tags).sort!.uniq!
  end
end
between_dates(start, finish) click to toggle source

Filter Items by date. String arguments will be chronified

@param start [Time,String] Filter items after this date @param finish [Time,String] Filter items before this date

@return [Items] array of items with dates between targets

# File lib/doing/items/filter.rb, line 62
def between_dates(start, finish)
  start = start.chronify(guess: :begin, future: false) if start.is_a?(String)
  finish = finish.chronify(guess: :end) if finish.is_a?(String)
  WWID.new.filter_items(self, opt: { date_filter: [start, finish] })
end
dedup(match_section: true) click to toggle source

Remove duplicated entries. Duplicate entries must have matching start date, title, note, and section

@return [Items] Items array with duplicate entries removed

# File lib/doing/items/util.rb, line 60
def dedup(match_section: true)
  unique = Items.new
  each do |item|
    unique.push(item) unless unique.include?(item, match_section: match_section)
  end

  unique
end
dedup!(match_section: true) click to toggle source

@see dedup

# File lib/doing/items/util.rb, line 70
def dedup!(match_section: true)
  replace dedup(match_section: match_section)
end
delete(item) click to toggle source

Create a deep copy of Items

def clone Marshal.load(Marshal.dump(self)) end

# File lib/doing/items/util.rb, line 10
def delete(item)
  deleted = nil
  each_with_index do |i, idx|
    if i.equal?(item, match_section: true)
      deleted = delete_at(idx)
      break
    end
  end
  deleted
end
delete_item(item, single: false) click to toggle source

Delete an item from the index

@param item The item

# File lib/doing/items/modify.rb, line 10
def delete_item(item, single: false)
  deleted = delete(item)
  Doing.logger.count(:deleted)
  Doing.logger.info('Entry deleted:', deleted.title) if single
  deleted
end
delete_section(section, log: false) click to toggle source
# File lib/doing/items/sections.rb, line 68
def delete_section(section, log: false)
  return unless section?(section)

  raise DoingRuntimeError, 'Section not empty' if in_section(section).count.positive?

  @sections.each do |sect|
    next unless sect.title == section && in_section(sect).count.zero?

    @sections.delete(sect)
    Doing.logger.info('Removed section:', %("#{section}" removed)) if log
  end

  Doing.logger.error('Not found:', %("#{section}" not found))
end
diff(items) click to toggle source

Return Items containing items that don’t exist in receiver

@param items [Items] Receiver

@return [Hash] Hash of added and deleted items

# File lib/doing/items/util.rb, line 40
def diff(items)
  a = clone
  b = items.clone

  a.delete_if do |item|
    if b.include?(item)
      b.delete(item)
      true
    else
      false
    end
  end
  { added: b, deleted: a }
end
find_id(id) click to toggle source

Find an item by ID

@param id The identifier to match

# File lib/doing/items/items.rb, line 42
def find_id(id)
  select { |item| item.id == id }[0]
end
guess_section(frag, distance: 2) click to toggle source

Return the best section match for a search query

@param frag The search query @param distance The distance apart characters can be (fuzziness)

@return [Section] (first) matching section object

# File lib/doing/items/sections.rb, line 32
def guess_section(frag, distance: 2)
  section = nil
  re = frag.to_rx(distance: distance, case_type: :ignore)
  @sections.each do |sect|
    next unless sect.title =~ /#{re}/i

    Doing.logger.debug('Match:', %(Assuming "#{sect.title}" from "#{frag}"))
    section = sect
    break
  end

  section
end
in_section(section) click to toggle source

Get a new Items object containing only items in a specified section

@param section [String] section title

@return [Items] Array of items

# File lib/doing/items/filter.rb, line 12
def in_section(section)
  sect = section.is_a?(Section) ? section.title : section
  if sect =~ /^all$/i
    dup
  else
    items = Items.new.concat(select { |item| !item.nil? && item.section == section })
    items.add_section(section, log: false)
    items
  end
end
include?(item, match_section: true) click to toggle source

Test if self includes Item

@param item [Item] The item to search for @param match_section [Boolean] Section must match

@return [Boolean] True if Item exists

# File lib/doing/items/items.rb, line 26
def include?(item, match_section: true)
  includes = false
  each do |other_item|
    if other_item.equal?(item, match_section: match_section)
      includes = true
      break
    end
  end

  includes
end
index_for_id(id) click to toggle source

Return the index for an entry matching ID

@param id The identifier to match

# File lib/doing/items/items.rb, line 51
def index_for_id(id)
  i = nil
  each_with_index do |item, idx|
    if item.id == id
      i = idx
      break
    end
  end
  i
end
inspect() click to toggle source

@private

# File lib/doing/items/items.rb, line 76
def inspect
  sections = @sections.map { |s| "<Section:#{s.title} #{in_section(s.title).count} items>" }.join(', ')
  "#<Doing::Items #{count} items, #{@sections.count} sections: #{sections}>"
end
section?(section) click to toggle source

Test if section already exists

@param section [String] section title

@return [Boolean] true if section exists

# File lib/doing/items/sections.rb, line 19
def section?(section)
  section = section.is_a?(Section) ? section.title.downcase : section.downcase
  @sections.map { |i| i.title.downcase }.include?(section)
end
section_titles() click to toggle source

List sections, title only

@return [Array] section titles

# File lib/doing/items/sections.rb, line 9
def section_titles
  @sections.map(&:title)
end
tagged(tags, bool: :and) click to toggle source

Search items by tags

@param tags [Array,String] The tags by which to filter @param bool [Symbol] The bool with which to combine multiple tags

@return [Items] array of items matching tag filter

# File lib/doing/items/filter.rb, line 46
def tagged(tags, bool: :and)
  WWID.new.filter_items(self, opt: { tag: tags, bool: bool })
end
to_s() click to toggle source

Output sections and items in Doing file format

# File lib/doing/items/items.rb, line 63
def to_s
  out = []
  @sections.each do |section|
    out.push(section.original)
    items = in_section(section.title).sort_by { |i| [i.date, i.title] }
    items.reverse! if Doing.setting('doing_file_sort').normalize_order == :desc
    items.each { |item| out.push(item.to_s) }
  end

  out.join("\n")
end
update_item(old_item, new_item) click to toggle source

Update an item in the index with a modified item

@param old_item The old item @param new_item The new item

# File lib/doing/items/modify.rb, line 23
def update_item(old_item, new_item)
  s_idx = index { |item| item.equal?(old_item) }

  raise ItemNotFound, 'Unable to find item in index, did it mutate?' unless s_idx

  return if fetch(s_idx).equal?(new_item)

  self[s_idx] = new_item
  Doing.logger.count(:updated)
  Doing.logger.info('Entry updated:', self[s_idx].title.trunc(60))
  new_item
end