class Item < ActiveRecord::Base set_table_name 'objects' # Neo & PHP Impress call them objects, but the class name is a conflict (duh!) set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category cattr_reader :per_page @@per_page = 30 has_many :parent_swf_asset_relationships, :foreign_key => 'parent_id' has_many :swf_assets, :through => :parent_swf_asset_relationships scope :alphabetize, order('name ASC') # Not defining validations, since this app is currently read-only def species_support_ids @species_support_ids_array ||= read_attribute('species_support_ids').split(',').map(&:to_i) end def species_support_ids=(replacement) @species_support_ids_array = nil replacement = replacement.join(',') if replacement.is_a?(Array) write_attribute('species_support_ids', replacement) end def supported_species @supported_species ||= species_support_ids.empty? ? Species.all : species_support_ids.map { |id| Species.find(id) } end def self.search(query) raise ArgumentError, "Please provide a search query" unless query query = query.strip raise ArgumentError, "Search queries should be at least 3 characters" if query.length < 3 query_conditions = [Condition.new] in_phrase = false query.each_char do |c| if c == ' ' && !in_phrase query_conditions << Condition.new elsif c == '"' in_phrase = !in_phrase elsif c == ':' && !in_phrase query_conditions.last.to_property! elsif c == '-' && !in_phrase && query_conditions.last.empty? query_conditions.last.negate! else query_conditions.last << c end end query_conditions.inject(self) do |scope, condition| condition.narrow(scope) end end private class Condition < String def to_property! @property = self.clone self.replace '' end def negate! @negative = true end def narrow(scope) items = Table(:objects) if @property == 'species' species = Species.find_by_name(self) raise ArgumentError, "Species \"#{self.humanize}\" does not exist" unless species # TODO: add a many-to-many table to handle this relationship, if # performance becomes an issue ids = items[:species_support_ids] condition = ids.eq('').or(ids.matches_any( species.id, "#{species.id},%", "%,#{species.id},%", "%,#{species.id}" )) elsif @property == 'description' || @property.blank? column = @property == 'description' ? :description : :name condition = items[column].matches("%#{self}%") else raise ArgumentError, "Unknown search filter \"#{@property}\"" end condition = condition.not if @negative scope.where(condition) end def inspect @property ? "#{@property}:#{super}" : super end end end