class SafariBookmarksParser::Parser

Constants

READING_LIST_KEY

Attributes

reading_list[R]
root_folder[R]
root_folder_without_reading_list[R]

Public Class Methods

new() click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 22
def initialize
  @root_folder = nil
  @root_folder_without_reading_list = nil
  @reading_list = nil
end
parse(binary_plist_path) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 16
def self.parse(binary_plist_path)
  new.parse(binary_plist_path)
end

Public Instance Methods

parse(binary_plist_path) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 28
def parse(binary_plist_path)
  root_node = parse_xml_plist(binary_plist_to_xml_plist(binary_plist_path))

  parse_combined(root_node)
  parse_splitted(root_node)

  self
end

Private Instance Methods

accept_leaf(node, folder_names) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 71
def accept_leaf(node, folder_names)
  url = node.fetch('URLString')
  title = node.fetch('URIDictionary').fetch('title')

  Bookmark.new(url: url, title: title, folder_names: folder_names)
end
accept_list(node, folder_names) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 61
def accept_list(node, folder_names)
  title = node.fetch('Title')

  children = node.fetch('Children', []).map do |child|
    traverse(child, folder_names + [title])
  end.compact

  BookmarkFolder.new(title: title, folder_names: folder_names, children: children)
end
binary_plist_readable?(binary_plist_path) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 94
    def binary_plist_readable?(binary_plist_path)
      return if File.readable?(binary_plist_path)

      message = <<~MESSAGE
        Couldn't read #{binary_plist_path}
        In "System Preferences" -> "Security & Privacy" -> "Privacy" -> "Full Disk Access", check "Terminal".
      MESSAGE

      raise Error, message
    end
binary_plist_to_xml_plist(binary_plist_path) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 82
def binary_plist_to_xml_plist(binary_plist_path)
  binary_plist_readable?(binary_plist_path)

  Tempfile.open(['Bookmarks', '.xml']) do |tempfile|
    command = ['plutil', '-convert', 'xml1', '-o', tempfile.path, binary_plist_path]

    raise Error, "plutil returned #{$CHILD_STATUS.exitstatus}" unless system(*command)

    tempfile.read
  end
end
parse_combined(root_node) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 39
def parse_combined(root_node)
  @root_folder = traverse(root_node)
end
parse_splitted(root_node) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 43
def parse_splitted(root_node)
  root_folder = traverse(root_node)

  index = root_folder.children.find_index {|child| child.title == READING_LIST_KEY }
  @reading_list = root_folder.children.delete_at(index) if index

  @root_folder_without_reading_list = root_folder
end
parse_xml_plist(xml_plist) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 78
def parse_xml_plist(xml_plist)
  Plist.parse_xml(xml_plist)
end
traverse(node, folder_names = []) click to toggle source
# File lib/safari_bookmarks_parser/parser.rb, line 52
def traverse(node, folder_names = [])
  case node.fetch('WebBookmarkType')
  when 'WebBookmarkTypeList'
    accept_list(node, folder_names)
  when 'WebBookmarkTypeLeaf'
    accept_leaf(node, folder_names)
  end
end