Module: TsVectorTags

Defined in:
lib/ts_vector_tags.rb

Defined Under Namespace

Modules: Standardizer Classes: InvalidTsQueryError

Constant Summary collapse

TSQUERY_VALIDATOR =

Regexp to reject injection attacks with ts_queries

/^[[:alnum:]\_\*\:\(\)\&\!\|[:space:]]+$/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.acceptable_tsquery?(query) ⇒ Boolean

Returns:

  • (Boolean)


22
23
24
25
# File 'lib/ts_vector_tags.rb', line 22

def self.acceptable_tsquery?(query)
  # TODO: Check for balanced parantheses
  query =~ TSQUERY_VALIDATOR
end

.included(base) ⇒ Object



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/ts_vector_tags.rb', line 27

def self.included(base)
  base.class_eval do
    # Accepts a comma separated list of tags and applies the 'and'-operator to them
    scope :with_tags, lambda { |tags|
      where("tags_vector @@ ?::tsquery ", TsVectorTags::Standardizer.tagify(tags).join(' & '))
    }

    # Accepts a proper ts_query an allows complex logical expressions like "foo & !(bar | bling)"
    scope :with_tags_query, lambda { |query|
      raise InvalidTsQueryError, "Invalid tag query '#{query}'" unless TsVectorTags.acceptable_tsquery?(query)
      # "!foo" will not match empty tsvectors, so we have to cheat using coalesce :-(
      where("coalesce(tags_vector, '-invalid-tag-') @@ '#{query}'::tsquery")
    }

    # Make sure empty vectors are always saved as null values
    before_save lambda {
      self.tags_vector = nil if self.tags_vector && self.tags_vector.strip.size == 0
    }
  end
end

Instance Method Details

#tagsObject



53
54
55
56
# File 'lib/ts_vector_tags.rb', line 53

def tags
  return [] unless self.tags_vector
  self.tags_vector.scan(/'(.+?)'/).flatten
end

#tags=(value) ⇒ Object



48
49
50
51
# File 'lib/ts_vector_tags.rb', line 48

def tags=(value)
  return self.tags_vector = "" if value.nil?
  self.tags_vector = TsVectorTags::Standardizer.tagify(value).map{ |tag| "'#{tag}'" }.join(' ')
end