stretchy 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -1
  3. data/lib/stretchy/boosts/field_decay_boost.rb +1 -1
  4. data/lib/stretchy/boosts/field_value_boost.rb +2 -2
  5. data/lib/stretchy/boosts/filter_boost.rb +2 -2
  6. data/lib/stretchy/builders/match_builder.rb +4 -0
  7. data/lib/stretchy/builders/query_builder.rb +12 -3
  8. data/lib/stretchy/builders/shell_builder.rb +5 -0
  9. data/lib/stretchy/clauses/base.rb +31 -1
  10. data/lib/stretchy/clauses/boost_clause.rb +1 -2
  11. data/lib/stretchy/clauses/boost_match_clause.rb +2 -3
  12. data/lib/stretchy/clauses/match_clause.rb +46 -1
  13. data/lib/stretchy/filters/base.rb +2 -0
  14. data/lib/stretchy/filters/exists_filter.rb +1 -1
  15. data/lib/stretchy/filters/geo_filter.rb +1 -1
  16. data/lib/stretchy/filters/range_filter.rb +1 -1
  17. data/lib/stretchy/filters/terms_filter.rb +1 -1
  18. data/lib/stretchy/queries/filtered_query.rb +2 -2
  19. data/lib/stretchy/queries/match_query.rb +2 -2
  20. data/lib/stretchy/queries/more_like_this_query.rb +57 -0
  21. data/lib/stretchy/queries.rb +1 -0
  22. data/lib/stretchy/utils/validation.rb +4 -0
  23. data/lib/stretchy/version.rb +1 -1
  24. data/lib/stretchy_validations.rb +2 -0
  25. data/lib/validation/rule/decay.rb +1 -6
  26. data/lib/validation/rule/distance.rb +1 -6
  27. data/lib/validation/rule/field.rb +9 -8
  28. data/lib/validation/rule/inclusion.rb +3 -11
  29. data/lib/validation/rule/latitude.rb +1 -5
  30. data/lib/validation/rule/longitude.rb +1 -6
  31. data/lib/validation/rule/min_should_match.rb +18 -0
  32. data/lib/validation/rule/required.rb +2 -14
  33. data/lib/validation/rule/responds_to.rb +1 -10
  34. data/lib/validation/rule/stretchy_rule.rb +38 -0
  35. data/lib/validation/rule/type.rb +7 -20
  36. data/solano.yml +9 -0
  37. metadata +6 -4
  38. data/.travis.yml +0 -6
  39. data/lib/validation/rule/one_of.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90bdaff51a1aaeb776e881571be1b4a0c54da5a3
4
- data.tar.gz: 47f7feae0e64d75661da159d2930c2995524240a
3
+ metadata.gz: 0bc5fc45426e803f3fe65879afb93ed01e654e32
4
+ data.tar.gz: a5edc4764c1e5760b7fcd5458de681085ee2397d
5
5
  SHA512:
6
- metadata.gz: 181d7b11c8b58f9d816a3243d3289f02e7e3511bab007d7ea17a2c931ce121c6bc9391d4e23e3d7eb20a16ade4c0a68acde6dfa4d0c55c9a23aeeecfd7c1134c
7
- data.tar.gz: 357a659413a79aaa882bb7f6d7b702425eed047c752be9090a0145e3232228030c1f0c04b8fcbaf155186b7aee5199085c403fefea390b6274d368fa1f962c2e
6
+ metadata.gz: 1a9fbf2f0ca614fd6e6049acf79cdc63309395ba97d44cae7c4b9a2c284744c3f523871453e525655cddf81e96909b0ce429712c8cebad5a83d8ae01952864ee
7
+ data.tar.gz: 6a7404a23251bdbf7b728c9d33c0a72b455a93ac0c16fb014fcbcc328ee918b01a75603ba5b308f7b2df3974c161fbc2efa0499051e5ce747b43ef117ad224bf
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  # Stretchy
2
- [![Build Status](https://travis-ci.org/hired/stretchy.svg?branch=master)](https://travis-ci.org/hired/stretchy)
3
2
 
4
3
  Stretchy is a query builder for [Elasticsearch](https://www.elastic.co/products/elasticsearch). It helps you quickly construct the JSON to send to Elastic, which can get [rather complicated](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html).
5
4
 
@@ -81,6 +80,16 @@ query = query.match('welcome to my web site')
81
80
 
82
81
  Performs a match query for the given string. If given a hash, it will use a match query on the specified fields, otherwise it will default to `'_all'`. By default, a match query searches for any of the analyzed terms in the document, and scores them using Lucene's [practical scoring formula](https://www.elastic.co/guide/en/elasticsearch/guide/current/practical-scoring-function.html), which combines TF/IDF, the vector space model, and a few other niceties.
83
82
 
83
+ ### More Like
84
+
85
+ ```ruby
86
+ query = query.more_like(ids: [1, 2, 3])
87
+ .more_like(docs: other_search.results)
88
+ .more_like(like_text: 'puppies and kittens are great', fields: :about_me)
89
+ ```
90
+
91
+ Finds documents similar to a list of input documents. You must pass in one of the `:ids`, `:docs` or `:like_text` parameters, but everything else is optional. This method accepts any of the params available in the [Elasticsearch more_like_this query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html). It can also be chained with `.not` and `.should`.
92
+
84
93
  ### Where
85
94
 
86
95
  ```ruby
@@ -16,7 +16,7 @@ module Stretchy
16
16
  attribute :weight
17
17
 
18
18
  validations do
19
- rule :field, :field
19
+ rule :field, field: { required: true }
20
20
  rule :origin, type: {classes: [Numeric, Time, Date, Stretchy::Types::GeoPoint], required: true}
21
21
  rule :scale, :required
22
22
  rule :decay, :decay
@@ -9,9 +9,9 @@ module Stretchy
9
9
  attribute :factor
10
10
 
11
11
  validations do
12
- rule :field, :field
12
+ rule :field, field: { required: true }
13
13
  rule :modifier, inclusion: {in: MODIFIERS}
14
- rule :factor, type: {classes: Numeric}
14
+ rule :factor, type: {classes: Numeric}
15
15
  end
16
16
 
17
17
  def initialize(field, options = {})
@@ -9,8 +9,8 @@ module Stretchy
9
9
  attribute :weight, Numeric, default: DEFAULT_WEIGHT
10
10
 
11
11
  validations do
12
- rule :filter, type: {classes: Filters::Base}
13
- rule :weight, type: {classes: Numeric}
12
+ rule :filter, type: Filters::Base
13
+ rule :weight, type: Numeric
14
14
  end
15
15
 
16
16
  def initialize(options = {})
@@ -15,6 +15,10 @@ module Stretchy
15
15
  must.any? || must_not.any? || should.any? || should_not.any?
16
16
  end
17
17
 
18
+ def add_query(query, options = {})
19
+ builder_from_options(options).add_query(query)
20
+ end
21
+
18
22
  def add_matches(field, matches, options = {})
19
23
  builder_from_options(options).add_matches(field, matches, options)
20
24
  end
@@ -4,15 +4,20 @@ module Stretchy
4
4
 
5
5
  extend Forwardable
6
6
 
7
- delegate [:any?, :count, :length] => :matches
7
+ delegate [:any?, :count, :length] => :to_enum
8
8
 
9
- attr_reader :matches, :query_opts
9
+ attr_reader :queries, :matches, :query_opts
10
10
 
11
11
  def initialize
12
+ @queries = []
12
13
  @matches = Hash.new { [] }
13
14
  @query_opts = Hash.new { {} }
14
15
  end
15
16
 
17
+ def add_query(query)
18
+ @queries << query
19
+ end
20
+
16
21
  def add_matches(field, new_matches, options = {})
17
22
  @matches[field] += Array(new_matches)
18
23
  opts = {}
@@ -22,8 +27,12 @@ module Stretchy
22
27
  @query_opts[field] = opts
23
28
  end
24
29
 
30
+ def to_enum
31
+ queries + matches.values
32
+ end
33
+
25
34
  def to_queries
26
- matches.map do |field, phrases|
35
+ queries + matches.map do |field, phrases|
27
36
  opts = query_opts[field].merge(
28
37
  field: field,
29
38
  string: phrases.flatten.join(' ')
@@ -2,6 +2,8 @@ module Stretchy
2
2
  module Builders
3
3
  class ShellBuilder
4
4
 
5
+ extend Forwardable
6
+
5
7
  DEFAULT_LIMIT = 30
6
8
  DEFAULT_OFFSET = 0
7
9
 
@@ -9,6 +11,9 @@ module Stretchy
9
11
  :match_builder, :where_builder, :boost_builder,
10
12
  :aggregate_builder, :fields
11
13
 
14
+ delegate [:add_matches, :add_query] => :match_builder
15
+ delegate [:add_param, :add_geo, :add_range] => :where_builder
16
+
12
17
  def initialize(options = {})
13
18
  @index = options[:index] || Stretchy.index_name
14
19
  @match_builder = Stretchy::Builders::MatchBuilder.new
@@ -22,7 +22,7 @@ module Stretchy
22
22
  :took, :shards, :total, :max_score, :total_pages] => :query_results
23
23
  delegate [:to_search] => :base
24
24
  delegate [:where, :range, :geo, :terms, :not] => :build_where
25
- delegate [:match, :fulltext] => :build_match
25
+ delegate [:match, :fulltext, :more_like] => :build_match
26
26
 
27
27
  #
28
28
  # Generates a chainable query. The only required option for the
@@ -49,6 +49,23 @@ module Stretchy
49
49
  end
50
50
  end
51
51
 
52
+ #
53
+ # Exits any state the query is in (boost, inverse, should, etc)
54
+ # and returns to the root query state. You can use this before
55
+ # calling `.where` or other overridden methods to ensure they
56
+ # are being processed from the base state.
57
+ #
58
+ # If you have to call this method, please file an issue.
59
+ # End-of-chain methods (such as `.boost.where.not()`) should
60
+ # always return to the root state, and state is not
61
+ # something you should have to think about.
62
+ #
63
+ # @return [Base] Continue the query chain from the root state
64
+ #
65
+ def root
66
+ Base.new(base)
67
+ end
68
+
52
69
  #
53
70
  # Sets how many results to return, similar to
54
71
  # ActiveRecord's limit method.
@@ -160,6 +177,19 @@ module Stretchy
160
177
  !!base.explain
161
178
  end
162
179
 
180
+ #
181
+ # Filter for documents that do not match the specified fields and values
182
+ #
183
+ # @overload not(params)
184
+ # @param [String] A string that must not be matched anywhere in the document
185
+ # @overload not(params)
186
+ # @param [Hash] A hash of fields and strings or terms that must not be matched in those fields
187
+ #
188
+ # @return [MatchClause, WhereClause] inverted query state with match filters applied
189
+ #
190
+ # @see {MatchClause#not}
191
+ # @see {WhereClause#not}
192
+ #
163
193
  def not(params = {}, options = {})
164
194
  if params.is_a?(String)
165
195
  build_match.not(params, options)
@@ -76,10 +76,9 @@ module Stretchy
76
76
  def field(*args)
77
77
  options = args.last.is_a?(Hash) ? args.pop : {}
78
78
  args.each do |field|
79
- pp
80
79
  base.boost_builder.add_boost(Boosts::FieldValueBoost.new(field, options))
81
80
  end
82
- self
81
+ Base.new(base)
83
82
  end
84
83
 
85
84
  def not(*args)
@@ -30,12 +30,10 @@ module Stretchy
30
30
  def not(params = {})
31
31
  @inverse = true
32
32
  match_function(hashify_params(params))
33
- self
34
33
  end
35
34
 
36
35
  def boost_match(params = {}, options = {})
37
36
  match_function(hashify_params(params), options)
38
- self
39
37
  end
40
38
 
41
39
  def fulltext(params = {}, options = {})
@@ -45,7 +43,7 @@ module Stretchy
45
43
  clause = MatchClause.new.match(params, options)
46
44
  boost = clause.to_boost(weight)
47
45
  base.boost_builder.add_boost(boost) if boost
48
- self
46
+ Base.new(base)
49
47
  end
50
48
 
51
49
  #
@@ -89,6 +87,7 @@ module Stretchy
89
87
  clause = MatchClause.new.match(params, options)
90
88
  boost = clause.to_boost(weight)
91
89
  base.boost_builder.add_boost(boost) if boost
90
+ self
92
91
  end
93
92
 
94
93
  end
@@ -54,11 +54,56 @@ module Stretchy
54
54
  #
55
55
  # @see https://www.elastic.co/guide/en/elasticsearch/guide/current/proximity-relevance.html Elasticsearch guide: proximity for relevance
56
56
  def fulltext(params = {})
57
- add_params(params, min: FULLTEXT_MIN)
57
+ add_params(params)
58
58
  add_params(params, should: true, slop: FULLTEXT_SLOP)
59
59
  self
60
60
  end
61
61
 
62
+ #
63
+ # Adds a MoreLikeThis query to the chain. Pass in document ids,
64
+ # an array of index/name/ids, or a string to get documents that
65
+ # have similar terms.
66
+ #
67
+ # This method accepts all the options in the Elasticsearch
68
+ # `more_like_this` query, which are pretty extensive. See the
69
+ # documentation (link below) to get an idea of what is available.
70
+ #
71
+ # Only one of `:docs`, `:ids`, or `:like_text` is required, and
72
+ # one of those three must be present.
73
+ #
74
+ # @param params = {} [Hash] Params used to build the `more_like_this` query
75
+ # @option params [Array] :like_text A string to compare documents do
76
+ # @option params [Array] :ids A list of document ids to compare documents to
77
+ # @option params [Array] :docs An array of document hashes. You can pass the
78
+ # results of another query here, or a hash with fields '_index', '_type'
79
+ # and '_id'
80
+ # @option params [Array] :fields A list of fields to use in the comparison.
81
+ # Defaults to '_all'
82
+ # @option params [Array] :include Whether the source documents should be
83
+ # included in the result set. Defaults to `false`
84
+ #
85
+ # @return [MatchClause] allows continuing the query chain
86
+ #
87
+ # @example Getting more like a document by id
88
+ # query.more_like(ids: other_result_id)
89
+ #
90
+ # @example Getting more like a document from another query
91
+ # query.more_like(docs: other_query.results)
92
+ #
93
+ # @example Getting more like a document from a string
94
+ # query.more_like(like_text: 'puppies and kittens are great')
95
+ #
96
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html Elasticsearch more-like-this query documentation
97
+ #
98
+ def more_like(params = {}, options = {})
99
+ query = Queries::MoreLikeThisQuery.new(params)
100
+ options[:inverse] = true if inverse?
101
+ options[:should] = true if should?
102
+
103
+ base.add_query(query, options)
104
+ self
105
+ end
106
+
62
107
  #
63
108
  # Switches to inverted context. Matches applied here work the same way as
64
109
  # {#initialize}, but returned documents must **not** match these filters.
@@ -4,6 +4,8 @@ module Stretchy
4
4
  module Filters
5
5
  class Base
6
6
 
7
+ extend Forwardable
8
+
7
9
  include Utils::Validation
8
10
 
9
11
  def initialize
@@ -7,7 +7,7 @@ module Stretchy
7
7
  attribute :field
8
8
 
9
9
  validations do
10
- rule :field, :field
10
+ rule :field, field: { required: true }
11
11
  end
12
12
 
13
13
  # CAUTION: this will match empty strings
@@ -10,7 +10,7 @@ module Stretchy
10
10
  attribute :geo_point
11
11
 
12
12
  validations do
13
- rule :field, :field
13
+ rule :field, field: { required: true }
14
14
  rule :geo_point, type: {classes: Types::GeoPoint}
15
15
  rule :distance, :distance
16
16
  end
@@ -9,7 +9,7 @@ module Stretchy
9
9
  attribute :range, Types::Range
10
10
 
11
11
  validations do
12
- rule :field, :field
12
+ rule :field, field: { required: true }
13
13
  rule :range, type: {classes: Types::Range}
14
14
  end
15
15
 
@@ -8,7 +8,7 @@ module Stretchy
8
8
  attribute :terms
9
9
 
10
10
  validations do
11
- rule :field, :field
11
+ rule :field, field: { required: true }
12
12
  rule :terms, type: {classes: [Numeric, Time, String, Symbol], array: true}
13
13
  rule :terms, :not_empty
14
14
  end
@@ -22,8 +22,8 @@ module Stretchy
22
22
 
23
23
  def to_search
24
24
  json = {}
25
- json[:query] = @query.to_search if @query
26
- json[:filter] = @filter.to_search if @filter
25
+ json[:query] = @query.to_search if query
26
+ json[:filter] = @filter.to_search if filter
27
27
  { filtered: json }
28
28
  end
29
29
  end
@@ -16,13 +16,13 @@ module Stretchy
16
16
  attribute :max, Float
17
17
 
18
18
  validations do
19
- rule :field, :field
19
+ rule :field, field: { required: true }
20
20
  rule :string, type: String
21
21
  rule :string, :required
22
22
  rule :operator, inclusion: {in: OPERATORS}
23
23
  rule :type, inclusion: {in: MATCH_TYPES}
24
24
  rule :slop, type: Numeric
25
- rule :min, regular_expression: {regex: /\A(\d+)%?\Z/}
25
+ rule :min, :min_should_match
26
26
  rule :max, type: Numeric
27
27
  end
28
28
 
@@ -0,0 +1,57 @@
1
+ module Stretchy
2
+ module Queries
3
+ class MoreLikeThisQuery < Base
4
+
5
+ attribute :fields, Array[String]
6
+ attribute :like_text, String
7
+ attribute :ids, Array[Integer]
8
+ attribute :docs, Array
9
+ attribute :max_query_terms, Integer
10
+ attribute :min_term_freq, Integer
11
+ attribute :min_doc_freq, Integer
12
+ attribute :max_doc_freq, Integer
13
+ attribute :min_word_length, Integer
14
+ attribute :max_word_length, Integer
15
+ attribute :stop_words, Array[String]
16
+ attribute :analyzer, String
17
+ attribute :boost_terms, Integer
18
+ attribute :include, Axiom::Types::Boolean
19
+ attribute :boost, Float
20
+
21
+ validations do
22
+ rule :fields, field: { array: true }
23
+ end
24
+
25
+ def after_initialize(params = {})
26
+ if params[:docs]
27
+ @docs = coerce_docs(Array(params[:docs]))
28
+ end
29
+
30
+ require_one!(:like_text, :docs, :ids)
31
+ end
32
+
33
+ def coerce_docs(docs)
34
+ docs.map do |doc|
35
+ coerced = {
36
+ '_index' => doc['_index'] || doc[:_index],
37
+ '_type' => doc['_type'] || doc[:_type],
38
+ }
39
+
40
+ source = doc[:doc] || doc['']
41
+ if source
42
+ coerced['doc'] = source
43
+ else
44
+ coerced['_id'] = doc['_id'] || doc[:_id]
45
+ end
46
+ coerced
47
+ end
48
+ end
49
+
50
+ def to_search
51
+ {
52
+ more_like_this: json_attributes
53
+ }
54
+ end
55
+ end
56
+ end
57
+ end
@@ -4,3 +4,4 @@ require 'stretchy/queries/filtered_query'
4
4
  require 'stretchy/queries/function_score_query'
5
5
  require 'stretchy/queries/match_all_query'
6
6
  require 'stretchy/queries/match_query'
7
+ require 'stretchy/queries/more_like_this_query'
@@ -50,6 +50,10 @@ module Stretchy
50
50
  validator.errors
51
51
  end
52
52
 
53
+ def json_attributes
54
+ self.attributes.reject{|key, val| val.nil? || (val.respond_to?(:empty?) && val.empty?) }
55
+ end
56
+
53
57
  module Constructor
54
58
 
55
59
  def initialize(attributes = nil)
@@ -1,3 +1,3 @@
1
1
  module Stretchy
2
- VERSION = "0.4.3"
2
+ VERSION = "0.4.4"
3
3
  end
@@ -3,6 +3,8 @@ require 'validation/rule/not_empty'
3
3
  require 'validation/rule/regular_expression'
4
4
 
5
5
  # custom for Stretchy
6
+ require 'validation/rule/stretchy_rule'
7
+ require 'validation/rule/min_should_match'
6
8
  require 'validation/rule/required'
7
9
  require 'validation/rule/responds_to'
8
10
  require 'validation/rule/decay'
@@ -1,6 +1,6 @@
1
1
  module Validation
2
2
  module Rule
3
- class Decay
3
+ class Decay < StretchyRule
4
4
  DECAY_FUNCTIONS = [:gauss, :linear, :exp]
5
5
 
6
6
  def error_key
@@ -10,11 +10,6 @@ module Validation
10
10
  def valid_value?(value)
11
11
  DECAY_FUNCTIONS.any?{|f| f == value || f.to_s == value }
12
12
  end
13
-
14
- def params
15
- {}
16
- end
17
-
18
13
  end
19
14
  end
20
15
  end
@@ -1,6 +1,6 @@
1
1
  module Validation
2
2
  module Rule
3
- class Distance
3
+ class Distance < StretchyRule
4
4
  DISTANCE_FORMAT = /^(\d+)(km|mi)$/
5
5
 
6
6
  def error_key
@@ -10,11 +10,6 @@ module Validation
10
10
  def valid_value?(value)
11
11
  !!(value.to_s =~ DISTANCE_FORMAT)
12
12
  end
13
-
14
- def params
15
- {}
16
- end
17
-
18
13
  end
19
14
  end
20
15
  end
@@ -1,22 +1,23 @@
1
1
  module Validation
2
2
  module Rule
3
- class Field
3
+ class Field < StretchyRule
4
4
 
5
5
  def error_key
6
6
  :field
7
7
  end
8
8
 
9
9
  def valid_value?(value)
10
- valid = true
11
- valid = false unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Numeric)
12
- valid = false if value.to_s.empty?
13
- valid
10
+ return true if empty_ok?(value)
11
+ if params[:array]
12
+ value.all? {|v| valid_class?(v) && !is_empty?(v) }
13
+ else
14
+ valid_class?(value) && !is_empty?(value)
15
+ end
14
16
  end
15
17
 
16
- def params
17
- {}
18
+ def valid_class?(value)
19
+ [String, Symbol, Numeric].any?{|c| value.is_a?(c) }
18
20
  end
19
-
20
21
  end
21
22
  end
22
23
  end
@@ -1,17 +1,13 @@
1
1
  module Validation
2
2
  module Rule
3
- class Inclusion
4
-
5
- def initialize(params = {})
6
- @params = params
7
- end
3
+ class Inclusion < StretchyRule
8
4
 
9
5
  def error_key
10
6
  :inclusion
11
7
  end
12
8
 
13
9
  def valid_value?(value)
14
- return true if value.nil? && !params[:required]
10
+ return true if empty_ok?(value)
15
11
  within.any? do |allowed_value|
16
12
  if value.respond_to?(:eql?)
17
13
  value.eql?(allowed_value)
@@ -22,11 +18,7 @@ module Validation
22
18
  end
23
19
 
24
20
  def within
25
- @params[:in] || @params[:within]
26
- end
27
-
28
- def params
29
- @params
21
+ params[:in] || params[:within]
30
22
  end
31
23
  end
32
24
  end
@@ -1,6 +1,6 @@
1
1
  module Validation
2
2
  module Rule
3
- class Latitude
3
+ class Latitude < StretchyRule
4
4
 
5
5
  def error_key
6
6
  :latitude
@@ -12,10 +12,6 @@ module Validation
12
12
  valid = false unless value && value.to_f <= 90 && value.to_f >= -90
13
13
  valid
14
14
  end
15
-
16
- def params
17
- {}
18
- end
19
15
  end
20
16
  end
21
17
  end
@@ -1,6 +1,6 @@
1
1
  module Validation
2
2
  module Rule
3
- class Longitude
3
+ class Longitude < StretchyRule
4
4
 
5
5
  def error_key
6
6
  :longitude
@@ -12,11 +12,6 @@ module Validation
12
12
  valid = false unless value && value <= 180 && value >= -180
13
13
  valid
14
14
  end
15
-
16
- def params
17
- {}
18
- end
19
-
20
15
  end
21
16
  end
22
17
  end
@@ -0,0 +1,18 @@
1
+ module Validation
2
+ module Rule
3
+ class MinShouldMatch < StretchyRule
4
+
5
+ FORMAT_REGEX = /^-?\d+([<>]-?\d+)?%?$/
6
+
7
+ def error_key
8
+ :field
9
+ end
10
+
11
+ def valid_value?(value)
12
+ return true if empty_ok?(value)
13
+ value.to_s =~ FORMAT_REGEX
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -1,26 +1,14 @@
1
1
  module Validation
2
2
  module Rule
3
- class Required
3
+ class Required < StretchyRule
4
4
 
5
5
  def error_key
6
6
  :required
7
7
  end
8
8
 
9
9
  def valid_value?(value)
10
- case value
11
- when nil
12
- false
13
- when String, Array, Hash
14
- !value.empty?
15
- else
16
- true
17
- end
10
+ !is_empty?(value)
18
11
  end
19
-
20
- def params
21
- {}
22
- end
23
-
24
12
  end
25
13
  end
26
14
  end
@@ -1,10 +1,6 @@
1
1
  module Validation
2
2
  module Rule
3
- class RespondsTo
4
-
5
- def initialize(params)
6
- @params = params
7
- end
3
+ class RespondsTo < StretchyRule
8
4
 
9
5
  def error_key
10
6
  :responds_to
@@ -13,11 +9,6 @@ module Validation
13
9
  def valid_value?(value)
14
10
  value.respond_to?(params[:method])
15
11
  end
16
-
17
- def params
18
- @params
19
- end
20
-
21
12
  end
22
13
  end
23
14
  end
@@ -0,0 +1,38 @@
1
+ module Validation
2
+ module Rule
3
+ class StretchyRule
4
+
5
+ def initialize(params = {})
6
+ @params = params
7
+ end
8
+
9
+ def error_key
10
+ raise "Override in subclass"
11
+ end
12
+
13
+ def valid_value?(value)
14
+ raise "Override in subclass"
15
+ end
16
+
17
+ def empty_ok?(value)
18
+ !required? && is_empty?(value)
19
+ end
20
+
21
+ def invalid_empty?(value)
22
+ required? && is_empty?(value)
23
+ end
24
+
25
+ def is_empty?(value)
26
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
27
+ end
28
+
29
+ def required?
30
+ !!(params.is_a?(Hash) && params[:required])
31
+ end
32
+
33
+ def params
34
+ @params
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,12 +1,13 @@
1
1
  module Validation
2
2
  module Rule
3
- class Type
3
+ class Type < StretchyRule
4
4
 
5
5
  def initialize(params = {})
6
6
  if params.is_a?(Hash)
7
7
  @params = params
8
+ @params[:classes] = Array(params[:classes])
8
9
  else
9
- @params = { classes: params }
10
+ @params = { classes: Array(params) }
10
11
  end
11
12
  end
12
13
 
@@ -16,29 +17,15 @@ module Validation
16
17
 
17
18
  def valid_value?(value)
18
19
  return true if value.nil? && !params[:required]
19
-
20
- valid = true
21
20
  if params[:array]
22
- valid = false unless value.all? {|v| validate_type(v) }
21
+ value.all? {|v| valid_type?(v) }
23
22
  else
24
- valid = false unless validate_type(value)
23
+ valid_type?(value)
25
24
  end
26
- valid
27
25
  end
28
26
 
29
- def validate_type(value)
30
- valid = true
31
- case params[:classes]
32
- when Array
33
- valid = false unless params[:classes].any? {|type| value.is_a?(type) }
34
- else
35
- valid = false unless value.is_a?(params[:classes])
36
- end
37
- valid
38
- end
39
-
40
- def params
41
- @params
27
+ def valid_type?(value)
28
+ params[:classes].any? {|type| value.is_a?(type) }
42
29
  end
43
30
  end
44
31
  end
data/solano.yml ADDED
@@ -0,0 +1,9 @@
1
+ ---
2
+ ruby_version: ruby-2.2.2
3
+ bundler_version: 1.9.4
4
+ test_pattern:
5
+ - spec/**/*_spec.rb
6
+ elasticsearch:
7
+ version: '1.6'
8
+ java:
9
+ java_version: java-7-openjdk
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stretchy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - agius
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-06-08 00:00:00.000000000 Z
11
+ date: 2015-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elasticsearch
@@ -173,7 +173,6 @@ extra_rdoc_files: []
173
173
  files:
174
174
  - ".gitignore"
175
175
  - ".ruby-version"
176
- - ".travis.yml"
177
176
  - ".yardopts"
178
177
  - Gemfile
179
178
  - README.md
@@ -222,6 +221,7 @@ files:
222
221
  - lib/stretchy/queries/function_score_query.rb
223
222
  - lib/stretchy/queries/match_all_query.rb
224
223
  - lib/stretchy/queries/match_query.rb
224
+ - lib/stretchy/queries/more_like_this_query.rb
225
225
  - lib/stretchy/results.rb
226
226
  - lib/stretchy/results/base.rb
227
227
  - lib/stretchy/results/null_results.rb
@@ -244,10 +244,12 @@ files:
244
244
  - lib/validation/rule/inclusion.rb
245
245
  - lib/validation/rule/latitude.rb
246
246
  - lib/validation/rule/longitude.rb
247
- - lib/validation/rule/one_of.rb
247
+ - lib/validation/rule/min_should_match.rb
248
248
  - lib/validation/rule/required.rb
249
249
  - lib/validation/rule/responds_to.rb
250
+ - lib/validation/rule/stretchy_rule.rb
250
251
  - lib/validation/rule/type.rb
252
+ - solano.yml
251
253
  - stretchy.gemspec
252
254
  homepage: https://github.com/hired/stretchy
253
255
  licenses:
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.1.5
4
- before_install: gem update bundler
5
- services:
6
- - elasticsearch
@@ -1,22 +0,0 @@
1
- module Validation
2
- module Rule
3
- class OneOf
4
-
5
- def initialize(params)
6
- @params = params
7
- end
8
-
9
- def error_key
10
- :one_of
11
- end
12
-
13
- def valid_value?(value)
14
-
15
- end
16
-
17
- def params
18
- @params
19
- end
20
- end
21
- end
22
- end