stretchy 0.4.3 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
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