redi_search 1.0.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -156
  3. data/.travis.yml +47 -15
  4. data/Appraisals +4 -8
  5. data/Gemfile +3 -4
  6. data/README.md +74 -73
  7. data/Rakefile +15 -3
  8. data/bin/console +36 -0
  9. data/gemfiles/{rails_6.gemfile → activerecord_51.gemfile} +1 -5
  10. data/gemfiles/{rails_51.gemfile → activerecord_52.gemfile} +1 -5
  11. data/lib/redi_search.rb +6 -7
  12. data/lib/redi_search/add.rb +9 -5
  13. data/lib/redi_search/{alter.rb → add_field.rb} +13 -5
  14. data/lib/redi_search/client.rb +8 -6
  15. data/lib/redi_search/client/response.rb +4 -0
  16. data/lib/redi_search/configuration.rb +1 -11
  17. data/lib/redi_search/create.rb +6 -4
  18. data/lib/redi_search/document.rb +5 -4
  19. data/lib/redi_search/document/display.rb +9 -9
  20. data/lib/redi_search/document/finder.rb +10 -2
  21. data/lib/redi_search/index.rb +9 -9
  22. data/lib/redi_search/lazily_load.rb +6 -11
  23. data/lib/redi_search/log_subscriber.rb +23 -52
  24. data/lib/redi_search/model.rb +43 -39
  25. data/lib/redi_search/schema.rb +3 -3
  26. data/lib/redi_search/schema/tag_field.rb +1 -1
  27. data/lib/redi_search/schema/text_field.rb +1 -1
  28. data/lib/redi_search/search.rb +12 -25
  29. data/lib/redi_search/search/clauses.rb +6 -7
  30. data/lib/redi_search/search/clauses/application_clause.rb +20 -5
  31. data/lib/redi_search/search/clauses/boolean.rb +5 -5
  32. data/lib/redi_search/search/clauses/highlight.rb +18 -2
  33. data/lib/redi_search/search/clauses/limit.rb +7 -5
  34. data/lib/redi_search/search/clauses/return.rb +1 -1
  35. data/lib/redi_search/search/clauses/slop.rb +1 -1
  36. data/lib/redi_search/search/clauses/sort_by.rb +1 -1
  37. data/lib/redi_search/search/clauses/where.rb +10 -2
  38. data/lib/redi_search/search/result.rb +9 -9
  39. data/lib/redi_search/search/term.rb +7 -6
  40. data/lib/redi_search/spellcheck.rb +3 -4
  41. data/lib/redi_search/spellcheck/result.rb +1 -1
  42. data/lib/redi_search/validatable.rb +49 -0
  43. data/lib/redi_search/validations/inclusion.rb +26 -0
  44. data/lib/redi_search/validations/numericality.rb +45 -0
  45. data/lib/redi_search/validations/presence.rb +29 -0
  46. data/lib/redi_search/version.rb +1 -1
  47. data/redi_search.gemspec +0 -2
  48. metadata +9 -41
  49. data/bin/test +0 -7
  50. data/gemfiles/rails_52.gemfile +0 -17
@@ -1,15 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redi_search/validatable"
4
+
3
5
  module RediSearch
4
6
  class Search
5
7
  module Clauses
6
8
  class ApplicationClause
7
- include ActiveModel::Validations
9
+ include Validatable
10
+
11
+ class << self
12
+ def clause_term(term, **validations)
13
+ attr_reader term
14
+
15
+ validations.each do |validation_type, options|
16
+ define_validation(term, validation_type, options)
17
+ end
18
+ end
19
+
20
+ private
8
21
 
9
- def self.clause_term(term, *validations)
10
- attr_reader term
11
- validations.each do |validation|
12
- validates term, validation
22
+ def define_validation(term, type, options)
23
+ if options.is_a? Hash
24
+ public_send("validates_#{type}_of", term, **options)
25
+ else
26
+ public_send("validates_#{type}_of", term)
27
+ end
13
28
  end
14
29
  end
15
30
  end
@@ -11,13 +11,13 @@ module RediSearch
11
11
  @prior_clause = prior_clause
12
12
  @not = false
13
13
 
14
- initialize_term(term, **term_options) if term.present?
14
+ initialize_term(term, **term_options) if term
15
15
  end
16
16
 
17
17
  def to_s
18
- raise ArgumentError, "missing query terms" if term.blank?
18
+ raise ArgumentError, "missing query terms" unless term
19
19
 
20
- [prior_clause.presence, queryify_term].compact.join(operand)
20
+ [prior_clause, queryify_term].compact.join(operand)
21
21
  end
22
22
 
23
23
  def_delegator :to_s, :inspect
@@ -25,7 +25,7 @@ module RediSearch
25
25
  def not(term, **term_options)
26
26
  @not = true
27
27
 
28
- initialize_term(term, **term_options) if term.present?
28
+ initialize_term(term, **term_options) if term
29
29
 
30
30
  search
31
31
  end
@@ -35,7 +35,7 @@ module RediSearch
35
35
  attr_reader :prior_clause, :term, :search
36
36
 
37
37
  def operand
38
- raise NotImplementedError
38
+ raise NotImplementedError, "#{__method__} needs to be defined"
39
39
  end
40
40
 
41
41
  def not_operator
@@ -23,9 +23,9 @@ module RediSearch
23
23
  attr_reader :fields, :opening_tag, :closing_tag
24
24
 
25
25
  def tags_clause
26
- return if opening_tag.blank? && closing_tag.blank?
26
+ return if !opening_tag? && !closing_tag?
27
27
 
28
- if opening_tag.present? && closing_tag.present?
28
+ if opening_tag? && closing_tag?
29
29
  ["TAGS", opening_tag, closing_tag]
30
30
  else
31
31
  arg_error("Missing opening or closing tag")
@@ -41,6 +41,22 @@ module RediSearch
41
41
  def arg_error(msg)
42
42
  raise ArgumentError, "Highlight: #{msg}"
43
43
  end
44
+
45
+ def opening_tag?
46
+ if opening_tag.respond_to? :empty?
47
+ !opening_tag.empty?
48
+ else
49
+ opening_tag
50
+ end
51
+ end
52
+
53
+ def closing_tag?
54
+ if closing_tag.respond_to? :empty?
55
+ !closing_tag.empty?
56
+ else
57
+ closing_tag
58
+ end
59
+ end
44
60
  end
45
61
  end
46
62
  end
@@ -6,10 +6,12 @@ module RediSearch
6
6
  class Search
7
7
  module Clauses
8
8
  class Limit < ApplicationClause
9
- clause_term :total, presence: true,
10
- numericality: { greater_than_or_equal_to: 0 }
11
- clause_term :offset, presence: true,
12
- numericality: { greater_than_or_equal_to: 0 }
9
+ clause_term :total, presence: true, numericality: {
10
+ within: 0..Float::INFINITY, only_integer: true
11
+ }
12
+ clause_term :offset, presence: true, numericality: {
13
+ within: 0..Float::INFINITY, only_integer: true
14
+ }
13
15
 
14
16
  def initialize(total:, offset: 0)
15
17
  @total = total
@@ -19,7 +21,7 @@ module RediSearch
19
21
  def clause
20
22
  validate!
21
23
 
22
- ["LIMIT", [offset, total]]
24
+ ["LIMIT", offset, total]
23
25
  end
24
26
  end
25
27
  end
@@ -15,7 +15,7 @@ module RediSearch
15
15
  def clause
16
16
  validate!
17
17
 
18
- ["RETURN", fields.size, fields]
18
+ ["RETURN", fields.size, *fields]
19
19
  end
20
20
  end
21
21
  end
@@ -6,7 +6,7 @@ module RediSearch
6
6
  class Search
7
7
  module Clauses
8
8
  class Slop < ApplicationClause
9
- clause_term :slop, numericality: { greater_than_or_equal_to: 0 }
9
+ clause_term :slop, numericality: { within: 0..Float::INFINITY }
10
10
 
11
11
  def initialize(slop:)
12
12
  @slop = slop
@@ -7,7 +7,7 @@ module RediSearch
7
7
  module Clauses
8
8
  class SortBy < ApplicationClause
9
9
  clause_term :field, presence: true
10
- clause_term :order, presence: true, inclusion: { in: %i(asc desc) }
10
+ clause_term :order, presence: true, inclusion: { within: %i(asc desc) }
11
11
 
12
12
  def initialize(field:, order: :asc)
13
13
  @field = field
@@ -16,7 +16,7 @@ module RediSearch
16
16
 
17
17
  def to_s
18
18
  [
19
- prior_clause.presence,
19
+ prior_clause,
20
20
  "(#{not_operator}@#{field}:#{queryify_term})"
21
21
  ].compact.join(" ")
22
22
  end
@@ -50,7 +50,7 @@ module RediSearch
50
50
  end
51
51
 
52
52
  def initialize_term(condition)
53
- return if condition.blank?
53
+ return if condition?(condition)
54
54
 
55
55
  condition, *options = condition.to_a
56
56
 
@@ -65,6 +65,14 @@ module RediSearch
65
65
  Term.new(condition[1], **options.to_h)
66
66
  end
67
67
  end
68
+
69
+ def condition?(condition)
70
+ if condition.respond_to?(:empty?)
71
+ condition.empty?
72
+ else
73
+ !condition
74
+ end
75
+ end
68
76
  end
69
77
  end
70
78
  end
@@ -6,10 +6,10 @@ module RediSearch
6
6
  extend Forwardable
7
7
  include Enumerable
8
8
 
9
- def initialize(index, used_clauses, count, documents)
9
+ def initialize(search, count, documents)
10
10
  @count = count
11
- @used_clauses = used_clauses
12
- @results = parse_results(index, documents)
11
+ @search = search
12
+ @results = parse_results(documents)
13
13
  end
14
14
 
15
15
  def count
@@ -22,11 +22,11 @@ module RediSearch
22
22
 
23
23
  def_delegators :results, :each, :empty?, :[], :last
24
24
 
25
- #:nocov:
26
25
  def inspect
27
26
  results
28
27
  end
29
28
 
29
+ #:nocov:
30
30
  def pretty_print(printer)
31
31
  printer.pp(results)
32
32
  end
@@ -34,7 +34,7 @@ module RediSearch
34
34
 
35
35
  private
36
36
 
37
- attr_reader :results
37
+ attr_reader :results, :search
38
38
 
39
39
  def response_slice
40
40
  slice_length = 2
@@ -45,20 +45,20 @@ module RediSearch
45
45
  end
46
46
 
47
47
  def with_scores?
48
- @used_clauses.include? "with_scores"
48
+ search.used_clauses.include? Search::Clauses::WithScores
49
49
  end
50
50
 
51
51
  def no_content?
52
- @used_clauses.include? "no_content"
52
+ search.used_clauses.include? Search::Clauses::NoContent
53
53
  end
54
54
 
55
- def parse_results(index, documents)
55
+ def parse_results(documents)
56
56
  documents.each_slice(response_slice).map do |slice|
57
57
  document_id = slice[0]
58
58
  fields = slice.last unless no_content?
59
59
  score = slice[1].to_f if with_scores?
60
60
 
61
- Document.new(index, document_id, Hash[*fields.to_a], score)
61
+ Document.new(search.index, document_id, Hash[*fields.to_a], score)
62
62
  end
63
63
  end
64
64
  end
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "redi_search/validatable"
4
+
3
5
  module RediSearch
4
6
  class Search
5
7
  class Term
6
- include ActiveModel::Validations
8
+ include Validatable
7
9
 
8
- validates :fuzziness, numericality: {
9
- only_integer: true, less_than: 4, greater_than: 0, allow_blank: true
10
- }
11
- validates :option, inclusion: { in: %i(fuzziness optional prefix) },
12
- allow_nil: true
10
+ validates_numericality_of :fuzziness, within: 1..3, only_integer: true,
11
+ allow_nil: true
12
+ validates_inclusion_of :option, within: %i(fuzziness optional prefix),
13
+ allow_nil: true
13
14
 
14
15
  def initialize(term, **options)
15
16
  @term = term
@@ -2,15 +2,14 @@
2
2
 
3
3
  require "redi_search/lazily_load"
4
4
  require "redi_search/spellcheck/result"
5
+ require "redi_search/validatable"
5
6
 
6
7
  module RediSearch
7
8
  class Spellcheck
8
9
  include LazilyLoad
9
- include ActiveModel::Validations
10
+ include Validatable
10
11
 
11
- validates :distance, numericality: {
12
- greater_than: 0, less_than: 5
13
- }
12
+ validates_numericality_of :distance, within: 1..4, only_integer: true
14
13
 
15
14
  def initialize(index, terms, distance: 1)
16
15
  @index = index
@@ -14,7 +14,6 @@ module RediSearch
14
14
  end
15
15
  end
16
16
 
17
- #:nocov:
18
17
  def inspect
19
18
  inspection = %w(term suggestions).map do |field_name|
20
19
  "#{field_name}: #{public_send(field_name)}"
@@ -23,6 +22,7 @@ module RediSearch
23
22
  "#<#{self.class} #{inspection}>"
24
23
  end
25
24
 
25
+ #:nocov:
26
26
  def pretty_print(printer) # rubocop:disable Metrics/MethodLength
27
27
  printer.object_address_group(self) do
28
28
  printer.seplist(
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redi_search/validations/inclusion"
4
+ require "redi_search/validations/presence"
5
+ require "redi_search/validations/numericality"
6
+
7
+ module RediSearch
8
+ class ValidationError < StandardError
9
+ end
10
+
11
+ module Validatable
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ module ClassMethods
17
+ attr_accessor :validations
18
+
19
+ def validates_inclusion_of(field, within:, **options)
20
+ self.validations = [
21
+ *validations.to_a,
22
+ Validations::Inclusion.new(field: field, within: within, **options)
23
+ ]
24
+ end
25
+
26
+ def validates_presence_of(field)
27
+ self.validations = [
28
+ *validations.to_a,
29
+ Validations::Presence.new(field: field)
30
+ ]
31
+ end
32
+
33
+ def validates_numericality_of(field, within:, **options)
34
+ self.validations = [
35
+ *validations.to_a,
36
+ Validations::Numericality.new(
37
+ field: field, within: within, **options
38
+ )
39
+ ]
40
+ end
41
+ end
42
+
43
+ def validate!
44
+ self.class.validations.to_a.each do |validator|
45
+ validator.validate!(self)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ module Validations
5
+ class Inclusion
6
+ def initialize(field:, within:, allow_nil: false)
7
+ @field = field
8
+ @within = within
9
+ @allow_nil = allow_nil
10
+ end
11
+
12
+ def validate!(object)
13
+ value = object.send(field)
14
+
15
+ return true if within.include?(value) || (allow_nil? && value.nil?)
16
+
17
+ raise ValidationError, "#{value.inspect} not included in #{within}"
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :field, :within, :allow_nil
23
+ alias allow_nil? allow_nil
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redi_search/validations/inclusion"
4
+
5
+ module RediSearch
6
+ module Validations
7
+ class Numericality
8
+ def initialize(field:, within:, only_integer: false, allow_nil: false)
9
+ @field = field
10
+ @within = within
11
+ @only_integer = only_integer
12
+ @allow_nil = allow_nil
13
+ end
14
+
15
+ def validate!(object)
16
+ value = object.send(field)
17
+
18
+ return true if value.nil? && allow_nil?
19
+
20
+ validate_numberness!(value)
21
+ validate_inclusion!(object)
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :field, :within, :only_integer, :allow_nil
27
+ alias only_integer? only_integer
28
+ alias allow_nil? allow_nil
29
+
30
+ def validate_numberness!(value)
31
+ raise(ValidationError, "#{field} must be a number") unless
32
+ value.is_a?(Numeric)
33
+
34
+ raise(ValidationError, "#{field} must be an Integer") if
35
+ only_integer? && !value.is_a?(Integer)
36
+
37
+ true
38
+ end
39
+
40
+ def validate_inclusion!(object)
41
+ Inclusion.new(field: field, within: within).validate!(object)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RediSearch
4
+ module Validations
5
+ class Presence
6
+ def initialize(field:)
7
+ @field = field
8
+ end
9
+
10
+ def validate!(object)
11
+ return true if value_present?(object.send(field))
12
+
13
+ raise RediSearch::ValidationError, "#{field} can't be blank"
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :field
19
+
20
+ def value_present?(value)
21
+ if value.respond_to?(:empty?)
22
+ !value.empty?
23
+ else
24
+ value
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end