module Polytag

Polytag Module

Constants

VERSION

Public Class Methods

get(type = :tag, foc = nil, data = {}) { |result| ... } click to toggle source
# File lib/polytag.rb, line 25
def get(type = :tag, foc = nil, data = {})
  force_name_search = false
  retried = false

  # Allow hashes to be passed
  if type.is_a?(Hash) || type.is_a?(ActiveRecord::Base)
    data = type
    type = nil
    foc  = nil
  elsif foc.is_a?(Hash) || foc.is_a?(ActiveRecord::Base)
    data = foc
    foc  = nil
  end

  # Reject nil or false data keys
  # also grab the foc value
  if data.is_a?(Hash)
    data = data.delete_if { |k, v| v.nil? || v == false }
    foc  = data.delete(:foc) if data.has_key?(:foc)
    data[:tag_group] = :default if data[:owner] && ! data[:tag_group]
  end

  # Ensure that we are processing the right data if the data comes in in a unexpected way
  if data.is_a?(Hash) && data.keys.size == 1 && [:tag, :tag_group].include?(data.keys.first)
    type = data.keys.first
    data = data[data.keys.first]
  end

  # Return the model if the model passed matches
  if data.is_a?(ActiveRecord::Base)
    return data if data.instance_of?(Tag) && (type ? type == :tag : true)
    return data if data.instance_of?(TagGroup) && (type ? type == :tag_group : true)
    return data if data.instance_of?(Connection) && (type ? type == :connection : true)
    raise NotAPolytagModel, "#{data.inspect} is not a Polytag model in the requested form."
  elsif data.is_a?(String) || data.is_a?(Symbol)
    data = "#{data}".strip
  end

  # Handle how the results are returned
  if data.is_a?(Hash) && data.keys.sort == [:owner, :tag, :tag_group, :tagged]

    # Get the tag owner
    tag_owner = get_tag_owner_or_taggable(:hash, data[:owner])

    # Get the tag group
    querydata = {owner: tag_owner, tag_group: data[:tag_group], foc: foc}
    tag_group = get(:tag_group, foc, querydata)

    # Get the tag
    querydata = {tag: data[:tag], tag_group: tag_group, foc: foc}
    tag       = get(:tag, foc, querydata)

    # Create the data we are using to create the connection
    querydata = tag_owner.merge foc: foc,
      polytag_tag_id: tag.id,
      polytag_tag_group_id: tag_group.id,
      tagged: data[:tagged]

    __connection_processor(querydata)
  elsif data.is_a?(Hash) && data.keys.sort == [:tag, :tag_group, :tagged]

    # Get the tag group
    querydata = {tag_group: data[:tag_group], foc: foc}
    tag_group = get(:tag_group, foc, querydata)

    # Get the tag
    querydata = {tag: data[:tag], tag_group: tag_group, foc: foc}
    tag       = get(:tag, foc, querydata)

    # Create the data we are using to create the connection
    querydata = {}.merge foc: foc,
      polytag_tag_group_id: tag_group.id,
      polytag_tag_id: tag.id,
      tagged: data[:tagged]

    __connection_processor(querydata)
  elsif data.is_a?(Hash) && data.keys.sort == [:tag, :tagged]

    # Get the tag
    querydata = {tag: data[:tag], foc: foc}
    tag = get(:tag, foc, querydata) do |ar|
      ar.where(polytag_tag_group_id: nil)
    end

    # If we expected a result and we don't have one raise
    raise CantFindPolytagModel if foc && ! tag

    # Create the data we are using to create the connection
    querydata = {}.merge foc: foc,
      polytag_tag_group_id: nil,
      polytag_tag_id: tag.id,
      tagged: data[:tagged]

    __connection_processor(querydata)
  elsif data.is_a?(Hash) && data.keys.sort == [:owner, :tag, :tag_group]

    # Get the tag owner
    tag_owner = get_tag_owner_or_taggable(data[:owner])

    # Get the tag group
    querydata = {owner: tag_owner, tag_group: data[:tag_group], foc: foc}
    tag_group = get(:tag_group, foc || :first, querydata)

    # Get the tag
    querydata = {tag: data[:tag], tag_group: tag_group, foc: foc}
    tag       = get(:tag, foc, querydata)

    return tag
  elsif data.is_a?(Hash) && data.keys.sort == [:owner, :tag_group]
    # Get the tag group with owner
    tag_owner = get_tag_owner_or_taggable(:hash, data[:owner])

    get(:tag_group, foc, data[:tag_group]) do |ar|
      ar.where(tag_owner)
    end
  elsif data.is_a?(Hash) && data.keys.sort == [:tag, :tag_group]
    # Get the tag with tag group
    tag_group = get(:tag_group, foc || :first, data[:tag_group])

    get(:tag, foc, data[:tag]) do |ar|
      ar.where(polytag_tag_group_id: tag_group.id)
    end
  elsif type && ! force_name_search && __numerical_string_id?(data)

    # Force foc to be a first if the option is requested
    foc = :first if foc

    result = const_get("#{type}".camelize).where(id: data.to_i)
    result = yield(result) if block_given?
    result = result.__send__(foc) if foc
    return result
  elsif type && (force_name_search || data.is_a?(String))

    result = const_get("#{type}".camelize).where(name: data)
    result = yield(result) if block_given?
    result = result.__send__(foc) if foc
    return result
  else
    raise CantFindPolytagModel, "Can't find a polytag model with #{data.inspect}."
  end
rescue ActiveRecord::RecordNotFound => e
  raise e if e.instance_of?(CantFindPolytagModel) || retried
  force_name_search = true
  retried = true
  foc = :first
  retry
end
get_tag_owner_or_taggable(result = :object, data = {}) click to toggle source
# File lib/polytag.rb, line 182
def get_tag_owner_or_taggable(result = :object, data = {})

  # Ensure result is is always set
  if result.is_a?(Hash) || result.is_a?(ActiveRecord::Base)
    data   = result
    result = :object
  end

  if data.is_a?(Hash)
    data = if data.keys.sort == [:id, :type]
      data[:type].camelize.constantize.find(data[:id])
    elsif data.keys.sort == [:owner_id, :owner_type]
      data[:owner_type].camelize.constantize.find(data[:owner_id])
    elsif data.keys.sort == [:tagged_id, :tagged_type]
      data[:tagged_type].camelize.constantize.find(data[:tagged_id])
    end
  end

  # The class we need to test
  dclass = data.class

  # Ensure that the model we return is a taggable or tag owner
  if __inherits(dclass, ActiveRecord::Base, Concerns::TagOwner) || __inherits(dclass, ActiveRecord::Base, Concerns::Taggable)

    # Return hash options for the type
    if result == :hash
      if __inherits(dclass, ActiveRecord::Base, Concerns::TagOwner)
        return {owner_type: "#{dclass}", owner_id: data.id}
      elsif __inherits(dclass, ActiveRecord::Base, Concerns::Taggable)
        return {tagged_type: "#{dclass}", tagged_id: data.id}
      end
    end

    # Return the raw object
    return data if result == :object
  end

  # Raise if not a taggable or tag owner object
  raise NotTagOwnerOrTaggable, "The model #{data.inspect} is not a polytag tag owner or taggable model."
end
tag_group_owner?(owner = {}, raise_on_error = false) click to toggle source
# File lib/polytag.rb, line 173
def tag_group_owner?(owner = {}, raise_on_error = false)
  owner = get_polymorphic(owner)
  return true if __inherits(data.class, ActiveRecord::Base, Concerns::TagOwner)
  raise NotTagOwner, "This model #{owner.inspect} is not a polytag tag owner."
rescue NotTagOwner, NotTagOwnerOrTaggable => e
  raise e unless raise_on_error
  false
end

Private Class Methods

__connection_processor(data) click to toggle source
# File lib/polytag.rb, line 231
def __connection_processor(data)
  foc = data.delete(:foc)

  # Get the item we are trying to tag
  taggable  = get_tag_owner_or_taggable(:hash, data.delete(:tagged))

  # Get the connection where statement
  connection_hash = data.merge(taggable)

  # Find the object and create it if applicable
  result = Connection.where(connection_hash)
  result = result.__send__(foc) if foc
  result
end
__inherits(klass, *ancestors) click to toggle source
# File lib/polytag.rb, line 225
def __inherits(klass, *ancestors)
  ancestors.inject(true) do |result, ancestor|
    result && klass.ancestors.include?(ancestor)
  end
end
__numerical_string_id?(data) click to toggle source
# File lib/polytag.rb, line 246
def __numerical_string_id?(data)
  (data.is_a?(String) && data.match(/^\d+$/)) || data.is_a?(Fixnum)
end