stretchy 0.4.0 → 0.4.1

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.ruby-version +1 -0
  4. data/lib/stretchy/boosts/base.rb +2 -3
  5. data/lib/stretchy/boosts/field_decay_boost.rb +23 -19
  6. data/lib/stretchy/boosts/filter_boost.rb +6 -3
  7. data/lib/stretchy/boosts/random_boost.rb +7 -4
  8. data/lib/stretchy/builders/boost_builder.rb +1 -1
  9. data/lib/stretchy/builders/filter_builder.rb +55 -0
  10. data/lib/stretchy/builders/match_builder.rb +41 -43
  11. data/lib/stretchy/builders/query_builder.rb +37 -0
  12. data/lib/stretchy/builders/shell_builder.rb +53 -0
  13. data/lib/stretchy/builders/where_builder.rb +88 -159
  14. data/lib/stretchy/builders.rb +3 -0
  15. data/lib/stretchy/clauses/base.rb +33 -78
  16. data/lib/stretchy/clauses/boost_clause.rb +10 -10
  17. data/lib/stretchy/clauses/boost_match_clause.rb +3 -3
  18. data/lib/stretchy/clauses/boost_where_clause.rb +5 -5
  19. data/lib/stretchy/clauses/match_clause.rb +5 -24
  20. data/lib/stretchy/clauses/where_clause.rb +23 -42
  21. data/lib/stretchy/errors/validation_error.rb +17 -0
  22. data/lib/stretchy/errors.rb +1 -1
  23. data/lib/stretchy/filters/and_filter.rb +5 -1
  24. data/lib/stretchy/filters/base.rb +2 -2
  25. data/lib/stretchy/filters/bool_filter.rb +10 -4
  26. data/lib/stretchy/filters/exists_filter.rb +5 -1
  27. data/lib/stretchy/filters/geo_filter.rb +13 -7
  28. data/lib/stretchy/filters/not_filter.rb +5 -2
  29. data/lib/stretchy/filters/or_filter.rb +4 -1
  30. data/lib/stretchy/filters/query_filter.rb +5 -1
  31. data/lib/stretchy/filters/range_filter.rb +10 -5
  32. data/lib/stretchy/filters/terms_filter.rb +8 -2
  33. data/lib/stretchy/queries/base.rb +2 -2
  34. data/lib/stretchy/queries/bool_query.rb +12 -0
  35. data/lib/stretchy/queries/filtered_query.rb +8 -3
  36. data/lib/stretchy/queries/function_score_query.rb +25 -32
  37. data/lib/stretchy/queries/match_query.rb +10 -3
  38. data/lib/stretchy/results/base.rb +9 -23
  39. data/lib/stretchy/types/base.rb +2 -2
  40. data/lib/stretchy/types/geo_point.rb +15 -6
  41. data/lib/stretchy/types/range.rb +24 -6
  42. data/lib/stretchy/utils/logger.rb +10 -5
  43. data/lib/stretchy/utils/validation.rb +77 -0
  44. data/lib/stretchy/utils.rb +1 -1
  45. data/lib/stretchy/version.rb +1 -1
  46. data/lib/stretchy.rb +3 -0
  47. data/lib/stretchy_validations.rb +10 -0
  48. data/lib/validation/rule/decay.rb +20 -0
  49. data/lib/validation/rule/distance.rb +20 -0
  50. data/lib/validation/rule/field.rb +22 -0
  51. data/lib/validation/rule/inclusion.rb +33 -0
  52. data/lib/validation/rule/latitude.rb +21 -0
  53. data/lib/validation/rule/longitude.rb +22 -0
  54. data/lib/validation/rule/one_of.rb +22 -0
  55. data/lib/validation/rule/required.rb +26 -0
  56. data/lib/validation/rule/responds_to.rb +23 -0
  57. data/lib/validation/rule/type.rb +41 -0
  58. data/stretchy.gemspec +2 -0
  59. metadata +48 -5
  60. data/lib/stretchy/errors/contract_error.rb +0 -5
  61. data/lib/stretchy/utils/contract.rb +0 -120
@@ -22,9 +22,9 @@ module Stretchy
22
22
  # @return [MatchClause] Temporary clause outside current state
23
23
  def self.tmp(options = {})
24
24
  if options.delete(:inverse)
25
- self.new(Base.new).not(options)
25
+ self.new(Builders::ShellBuilder.new).not(options)
26
26
  else
27
- self.new(Base.new, options)
27
+ self.new(Builders::ShellBuilder.new, options)
28
28
  end
29
29
  end
30
30
 
@@ -52,12 +52,8 @@ module Stretchy
52
52
  def initialize(base, opts_or_str = {}, options = {})
53
53
  super(base)
54
54
  if opts_or_str.is_a?(Hash)
55
- @inverse = opts_or_str.delete(:inverse) || options.delete(:inverse)
56
- @should = opts_or_str.delete(:should) || options.delete(:should)
57
55
  add_params(options.merge(opts_or_str))
58
56
  else
59
- @inverse = options.delete(:inverse)
60
- @should = options.delete(:should)
61
57
  add_params(options.merge('_all' => opts_or_str))
62
58
  end
63
59
  end
@@ -136,9 +132,10 @@ module Stretchy
136
132
  # @return [Stretchy::Boosts::FilterBoost] boost containing these match parameters
137
133
  def to_boost(weight = nil)
138
134
  weight ||= Stretchy::Boosts::FilterBoost::DEFAULT_WEIGHT
135
+
139
136
  Stretchy::Boosts::FilterBoost.new(
140
137
  filter: Stretchy::Filters::QueryFilter.new(
141
- @match_builder.build
138
+ base.match_builder.to_query
142
139
  ),
143
140
  weight: weight
144
141
  )
@@ -154,22 +151,6 @@ module Stretchy
154
151
 
155
152
  private
156
153
 
157
- def get_storage
158
- if inverse?
159
- if should?
160
- @match_builder.shouldnotmatches
161
- else
162
- @match_builder.antimatches
163
- end
164
- else
165
- if should?
166
- @match_builder.shouldmatches
167
- else
168
- @match_builder.matches
169
- end
170
- end
171
- end
172
-
173
154
  def add_params(params = {})
174
155
  case params
175
156
  when Hash
@@ -182,7 +163,7 @@ module Stretchy
182
163
  end
183
164
 
184
165
  def add_param(field, param)
185
- get_storage[field] += Array(param)
166
+ base.match_builder.add_matches(field, param, inverse: inverse?, should: should?)
186
167
  end
187
168
 
188
169
  end
@@ -32,9 +32,9 @@ module Stretchy
32
32
  # @return [WhereClause] A clause outside the main query context
33
33
  def self.tmp(options = {})
34
34
  if options.delete(:inverse)
35
- self.new(Base.new).not(options)
35
+ self.new(Builders::ShellBuilder.new).not(options)
36
36
  else
37
- self.new(Base.new, options)
37
+ self.new(Builders::ShellBuilder.new, options)
38
38
  end
39
39
  end
40
40
 
@@ -62,7 +62,7 @@ module Stretchy
62
62
  #
63
63
  # @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-range-filter.html Elastic Docs - Range Filter
64
64
  #
65
- def initialize(base, options = {})
65
+ def initialize(base = nil, options = {})
66
66
  super(base)
67
67
  add_params(options)
68
68
  end
@@ -98,7 +98,7 @@ module Stretchy
98
98
  # exclusive: true
99
99
  # )
100
100
  def range(field, options = {})
101
- get_storage(:ranges)[field] = Stretchy::Types::Range.new(options)
101
+ base.where_builder.add_range(field, options.merge(inverse: inverse?, should: should?))
102
102
  self
103
103
  end
104
104
 
@@ -124,10 +124,9 @@ module Stretchy
124
124
  # lng: 29.2
125
125
  # )
126
126
  def geo(field, options = {})
127
- get_storage(:geos)[field] = {
128
- distance: options[:distance],
129
- geo_point: Stretchy::Types::GeoPoint.new(options)
130
- }
127
+ distance = options[:distance]
128
+ opts = options.merge(inverse: inverse?, should: should?)
129
+ base.where_builder.add_geo(field, distance, opts)
131
130
  self
132
131
  end
133
132
 
@@ -198,54 +197,34 @@ module Stretchy
198
197
  def to_boost(weight = nil)
199
198
  weight ||= Stretchy::Boosts::FilterBoost::DEFAULT_WEIGHT
200
199
 
201
- if @match_builder.any? && @where_builder.any?
200
+ if base.match_builder.any? && base.where_builder.any?
202
201
  Stretchy::Boosts::FilterBoost.new(
203
202
  filter: Stretchy::Filters::QueryFilter.new(
204
203
  Stretchy::Queries::FilteredQuery.new(
205
- query: @match_builder.build,
206
- filter: @where_builder.build
204
+ query: base.match_builder.to_query,
205
+ filter: base.where_builder.to_filter
207
206
  )
208
207
  ),
209
208
  weight: weight
210
209
  )
211
210
 
212
- elsif @match_builder.any?
211
+ elsif base.match_builder.any?
213
212
  Stretchy::Boosts::FilterBoost.new(
214
213
  filter: Stretchy::Filters::QueryFilter.new(
215
- @match_builder.build
214
+ base.match_builder.to_query
216
215
  ),
217
216
  weight: weight
218
217
  )
219
218
 
220
- elsif @where_builder.any?
219
+ elsif base.where_builder.any?
221
220
  Stretchy::Boosts::FilterBoost.new(
222
- filter: @where_builder.build,
221
+ filter: base.where_builder.to_filter,
223
222
  weight: weight
224
223
  )
225
224
  end
226
225
  end
227
226
 
228
227
  private
229
-
230
- def get_storage(builder_field, is_inverse = nil)
231
- is_inverse = inverse? if is_inverse.nil?
232
- field = builder_field.to_s
233
- if inverse? || is_inverse
234
- if should?
235
- field = "shouldnot#{field}"
236
- else
237
- field = "anti#{field}"
238
- end
239
- else
240
- field = "should#{field}" if should?
241
- end
242
-
243
- if field =~ /match/
244
- @match_builder.send(field)
245
- else
246
- @where_builder.send(field)
247
- end
248
- end
249
228
 
250
229
  def add_params(options = {})
251
230
  options.each do |field, param|
@@ -263,15 +242,17 @@ module Stretchy
263
242
 
264
243
  def add_param(field, param)
265
244
  case param
266
- when nil
267
- get_storage(:exists, true) << field
268
245
  when String, Symbol
269
- get_storage(:matches)[field] += Array(param)
270
- get_storage(:matchops)[field] = 'or'
271
- when Range
272
- get_storage(:ranges)[field] = Stretchy::Types::Range.new(param)
246
+ base.match_builder.add_matches(field, param,
247
+ inverse: inverse?,
248
+ should: should?,
249
+ or: true
250
+ )
273
251
  else
274
- get_storage(:terms)[field] += Array(param)
252
+ base.where_builder.add_param(field, param,
253
+ inverse: inverse?,
254
+ should: should?
255
+ )
275
256
  end
276
257
  end
277
258
 
@@ -0,0 +1,17 @@
1
+ module Stretchy
2
+ module Errors
3
+ class ValidationError < StandardError
4
+
5
+ def initialize(errors)
6
+ @errors = errors
7
+ end
8
+
9
+ def message
10
+ @errors.map do |key, err|
11
+ "Attribute #{key} violated rule #{err[:rule]}"
12
+ end.join("\n")
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -1 +1 @@
1
- require 'stretchy/errors/contract_error.rb'
1
+ require 'stretchy/errors/validation_error.rb'
@@ -4,7 +4,11 @@ module Stretchy
4
4
  module Filters
5
5
  class AndFilter < Base
6
6
 
7
- contract filters: {type: Stretchy::Filters::Base, array: true}
7
+ attribute :filters
8
+ validations do
9
+ rule :filters, :not_empty
10
+ rule :filters, type: {classes: Filters::Base, array: true}
11
+ end
8
12
 
9
13
  def initialize(*args)
10
14
  @filters = args.flatten
@@ -1,10 +1,10 @@
1
- require 'stretchy/utils/contract'
1
+ require 'stretchy/utils/validation'
2
2
 
3
3
  module Stretchy
4
4
  module Filters
5
5
  class Base
6
6
 
7
- include Stretchy::Utils::Contract
7
+ include Utils::Validation
8
8
 
9
9
  def initialize
10
10
  raise "Override this in subclass"
@@ -4,16 +4,22 @@ module Stretchy
4
4
  module Filters
5
5
  class BoolFilter < Base
6
6
 
7
- contract must: {type: Base, array: true},
8
- must_not: {type: Base, array: true},
9
- should: {type: Base, array: true}
7
+ attribute :must, Array[Base]
8
+ attribute :must_not, Array[Base]
9
+ attribute :should, Array[Base]
10
+
11
+ validations do
12
+ rule :must, type: {classes: Base, array: true}
13
+ rule :must_not, type: {classes: Base, array: true}
14
+ rule :should, type: {classes: Base, array: true}
15
+ end
10
16
 
11
17
  def initialize(options = {})
12
18
  @must = Array(options[:must])
13
19
  @must_not = Array(options[:must_not])
14
20
  @should = Array(options[:should])
21
+ require_one!(:must, :must_not, :should)
15
22
  validate!
16
- require_one(must: @must, must_not: @must_not, should: @should)
17
23
  end
18
24
 
19
25
  def to_search
@@ -4,7 +4,11 @@ module Stretchy
4
4
  module Filters
5
5
  class ExistsFilter < Base
6
6
 
7
- contract field: {type: :field, required: true}
7
+ attribute :field
8
+
9
+ validations do
10
+ rule :field, :field
11
+ end
8
12
 
9
13
  # CAUTION: this will match empty strings
10
14
  # see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-exists-filter.html
@@ -5,14 +5,20 @@ module Stretchy
5
5
  module Filters
6
6
  class GeoFilter < Base
7
7
 
8
- contract distance: {type: :distance, required: true},
9
- geo_point: {type: Stretchy::Types::GeoPoint, required: true},
10
- field: {type: :field, required: true}
8
+ attribute :field
9
+ attribute :distance
10
+ attribute :geo_point
11
11
 
12
- def initialize(options = {})
13
- @field = options[:field]
14
- @distance = options[:distance]
15
- @geo_point = options[:geo_point] || Stretchy::Types::GeoPoint.new(options)
12
+ validations do
13
+ rule :field, :field
14
+ rule :geo_point, type: {classes: Types::GeoPoint}
15
+ rule :distance, :distance
16
+ end
17
+
18
+ def initialize(field, distance, geo_point)
19
+ @field = field
20
+ @distance = distance
21
+ @geo_point = Types::GeoPoint.new(geo_point)
16
22
  validate!
17
23
  end
18
24
 
@@ -4,9 +4,12 @@ module Stretchy
4
4
  module Filters
5
5
  class NotFilter < Base
6
6
 
7
- attr_reader :filters
7
+ attribute :filters, Array[Base]
8
8
 
9
- contract :filters, { type: Base, array: true, required: true }
9
+ validations do
10
+ rule :filters, type: { classes: Base, array: true }
11
+ rule :filters, :not_empty
12
+ end
10
13
 
11
14
  def initialize(*filters)
12
15
  @filters = Array(filters).flatten
@@ -4,7 +4,10 @@ module Stretchy
4
4
  module Filters
5
5
  class OrFilter < Base
6
6
 
7
- contract filters: {type: Base, array: true}
7
+ attribute :filters, Array[Base]
8
+ validations do
9
+ rule :filters, type: {classes: Base, array: true}
10
+ end
8
11
 
9
12
  def initialize(*args)
10
13
  @filters = args.flatten
@@ -5,7 +5,11 @@ module Stretchy
5
5
  module Filters
6
6
  class QueryFilter < Base
7
7
 
8
- contract :query, type: Stretchy::Queries::Base
8
+ attribute :query, Queries::Base
9
+
10
+ validations do
11
+ rule :query, type: {classes: Queries::Base}
12
+ end
9
13
 
10
14
  def initialize(query)
11
15
  @query = query
@@ -5,12 +5,17 @@ module Stretchy
5
5
  module Filters
6
6
  class RangeFilter < Base
7
7
 
8
- contract field: {type: :field, required: true},
9
- range: {type: Stretchy::Types::Range, required: true}
8
+ attribute :field
9
+ attribute :range, Types::Range
10
10
 
11
- def initialize(options = {})
12
- @field = options[:field]
13
- @range = options[:stretchy_range] || Stretchy::Types::Range.new(options)
11
+ validations do
12
+ rule :field, :field
13
+ rule :range, type: {classes: Types::Range}
14
+ end
15
+
16
+ def initialize(field, range_options)
17
+ @field = field
18
+ @range = Types::Range.new(range_options)
14
19
  validate!
15
20
  end
16
21
 
@@ -4,8 +4,14 @@ module Stretchy
4
4
  module Filters
5
5
  class TermsFilter < Base
6
6
 
7
- contract field: {type: :field, required: true},
8
- terms: {type: [Numeric, Time, String, Symbol], array: true, required: true}
7
+ attribute :field
8
+ attribute :terms
9
+
10
+ validations do
11
+ rule :field, :field
12
+ rule :terms, type: {classes: [Numeric, Time, String, Symbol], array: true}
13
+ rule :terms, :not_empty
14
+ end
9
15
 
10
16
  def initialize(field, terms)
11
17
  @field = field
@@ -1,10 +1,10 @@
1
- require 'stretchy/utils/contract'
1
+ require 'stretchy/utils/validation'
2
2
 
3
3
  module Stretchy
4
4
  module Queries
5
5
  class Base
6
6
 
7
- include Stretchy::Utils::Contract
7
+ include Stretchy::Utils::Validation
8
8
 
9
9
  def initialize
10
10
  raise "Override this in subclass"
@@ -4,10 +4,22 @@ module Stretchy
4
4
  module Queries
5
5
  class BoolQuery < Base
6
6
 
7
+ attribute :must, Array
8
+ attribute :must_not, Array
9
+ attribute :should, Array
10
+
11
+ validations do
12
+ rule :must, type: {classes: Base, array: true}
13
+ rule :must_not, type: {classes: Base, array: true}
14
+ rule :should, type: {classes: Base, array: true}
15
+ end
16
+
7
17
  def initialize(options = {})
8
18
  @must = Array(options[:must])
9
19
  @must_not = Array(options[:must_not])
10
20
  @should = Array(options[:should])
21
+ require_one! :must, :must_not, :should
22
+ validate!
11
23
  end
12
24
 
13
25
  def to_search
@@ -5,14 +5,19 @@ module Stretchy
5
5
  module Queries
6
6
  class FilteredQuery < Base
7
7
 
8
- contract query: {type: Base},
9
- filter: {type: Stretchy::Filters::Base}
8
+ attribute :query, Base
9
+ attribute :filter, Filters::Base
10
+
11
+ validations do
12
+ rule :query, type: {classes: Base}
13
+ rule :filter, type: {classes: Filters::Base}
14
+ end
10
15
 
11
16
  def initialize(options = {})
12
17
  @query = options[:query]
13
18
  @filter = options[:filter]
19
+ require_one! :query, :filter
14
20
  validate!
15
- require_one(query: @query, filter: @filter)
16
21
  end
17
22
 
18
23
  def to_search
@@ -8,39 +8,38 @@ module Stretchy
8
8
  SCORE_MODES = %w(multiply sum avg first max min)
9
9
  BOOST_MODES = %w(multiply replace sum avg max min)
10
10
 
11
- contract functions: {type: Stretchy::Boosts::Base, array: true},
12
- query: {type: Base},
13
- filter: {type: Stretchy::Filters::Base},
14
- score_mode: {type: String, in: SCORE_MODES},
15
- boost_mode: {type: String, in: BOOST_MODES},
16
- min_score: {type: Numeric},
17
- max_boost: {type: Numeric},
18
- boost: {type: Numeric}
11
+ attribute :functions, Array[Boosts::Base]
12
+ attribute :query, Base
13
+ attribute :filter, Filters::Base
14
+ attribute :score_mode
15
+ attribute :boost_mode
16
+ attribute :min_score
17
+ attribute :max_boost
18
+ attribute :boost
19
+
20
+ validations do
21
+ rule :functions, type: {classes: Boosts::Base, array: true}
22
+ rule :query, type: {classes: Base}
23
+ rule :filter, type: {classes: Filters::Base}
24
+ rule :score_mode, inclusion: {in: SCORE_MODES}
25
+ rule :boost_mode, inclusion: {in: BOOST_MODES}
26
+ rule :min_score, type: {classes: Numeric}
27
+ rule :max_boost, type: {classes: Numeric}
28
+ rule :boost, type: {classes: Numeric}
29
+ end
19
30
 
20
31
  def initialize(options = {})
21
- @functions = Array(options[:functions])
22
- @query = options[:query]
23
- @filter = options[:filter]
24
-
25
- self.class.attributes.map do |field|
26
- instance_variable_set("@#{field}", options[field])
27
- end
32
+ self.class.attribute_set.set(self, options) if options
33
+ set_default_attributes
34
+ require_only_one! :query, :filter
28
35
  validate!
29
- validate_query_or_filter
30
- end
31
-
32
- def self.attributes
33
- [:boost, :max_boost, :score_mode, :boost_mode, :min_score]
34
- end
35
-
36
- def validate_query_or_filter
37
- if @query && @filter
38
- raise Stretchy::Errors::ContractError.new "Cannot have both query and filter -- combine using a FilteredQuery"
39
- end
40
36
  end
41
37
 
42
38
  def to_search
43
39
  json = {}
40
+ attributes.each do |field, value|
41
+ json[field] = value if value
42
+ end
44
43
  json[:functions] = @functions.map(&:to_search)
45
44
  if @query
46
45
  json[:query] = @query.to_search
@@ -50,12 +49,6 @@ module Stretchy
50
49
  json[:query] = Stretchy::Queries::MatchAllQuery.new.to_search
51
50
  end
52
51
 
53
- self.class.attributes.reduce(json) do |body, field|
54
- ivar = instance_variable_get("@#{field}")
55
- body[field] = ivar if ivar
56
- body
57
- end
58
-
59
52
  { function_score: json }
60
53
  end
61
54
  end
@@ -6,9 +6,16 @@ module Stretchy
6
6
 
7
7
  OPERATORS = ['and', 'or']
8
8
 
9
- contract field: {type: :field},
10
- operator: {type: String, in: OPERATORS},
11
- string: {type: String}
9
+ attribute :field
10
+ attribute :string
11
+ attribute :operator
12
+
13
+ validations do
14
+ rule :field, :field
15
+ rule :operator, inclusion: {in: OPERATORS}
16
+ rule :string, type: {classes: String}
17
+ rule :string, :required
18
+ end
12
19
 
13
20
  def initialize(options = {})
14
21
  case options
@@ -4,41 +4,27 @@ module Stretchy
4
4
 
5
5
  extend Forwardable
6
6
 
7
- attr_reader :clause, :index_name
7
+ attr_reader :base, :index_name
8
8
 
9
- delegate [:type, :current_page, :limit_value] => :clause
9
+ delegate [:type, :current_page, :fields, :offset, :limit,
10
+ :aggregations] => :base
10
11
 
11
- def initialize(clause)
12
- @clause = clause
13
- @index_name = clause.index_name || Stretchy.index_name
12
+ def initialize(base)
13
+ @base = base
14
+ @index_name = base.index || Stretchy.index_name
14
15
  end
15
16
 
16
- def limit
17
- clause.get_limit
18
- end
19
17
  alias :per_page :limit
20
18
  alias :limit_value :limit
21
19
 
22
- def fields
23
- clause.get_fields
24
- end
25
-
26
- def offset
27
- clause.get_offset
28
- end
29
-
30
- def page
31
- clause.get_page
32
- end
33
-
34
20
  def total_pages
35
21
  [(total.to_f / limit).ceil, 1].max
36
22
  end
37
23
 
38
24
  def request
39
25
  return @request if @request
40
- @request = {query: clause.to_search}
41
- @request[:aggs] = clause.get_aggregations if clause.get_aggregations.any?
26
+ @request = {query: base.to_search}
27
+ @request[:aggs] = base.aggregate_builder if base.aggregate_builder.any?
42
28
  @request
43
29
  end
44
30
 
@@ -50,7 +36,7 @@ module Stretchy
50
36
  size: limit
51
37
  }
52
38
  params[:fields] = fields if fields
53
- params[:explain] = true if clause.get_explain
39
+ params[:explain] = true if base.explain
54
40
  @response ||= Stretchy.search(params)
55
41
  end
56
42
 
@@ -1,10 +1,10 @@
1
- require 'stretchy/utils/contract'
1
+ require 'stretchy/utils/validation'
2
2
 
3
3
  module Stretchy
4
4
  module Types
5
5
  class Base
6
6
 
7
- include Stretchy::Utils::Contract
7
+ include Utils::Validation
8
8
 
9
9
  def initialize
10
10
  raise "Override this in subclass"
@@ -4,16 +4,25 @@ module Stretchy
4
4
  module Types
5
5
  class GeoPoint < Base
6
6
 
7
- attr_reader :lat, :lon
7
+ attribute :lat
8
+ attribute :lon
8
9
 
9
- contract lat: { type: :lat, required: true },
10
- lon: { type: :lng, required: true }
10
+ validations do
11
+ rule :lat, :latitude
12
+ rule :lon, :longitude
13
+ end
11
14
 
15
+ attr_reader :lat, :lon
12
16
 
13
17
  def initialize(options = {})
14
- @lat = options[:lat] || options[:latitude]
15
- @lon = options[:lng] || options[:lon] ||
16
- options[:longitude]
18
+ if options.is_a?(self.class)
19
+ @lat = options.lat
20
+ @lon = options.lon
21
+ else
22
+ @lat = options[:lat] || options[:latitude]
23
+ @lon = options[:lng] || options[:lon] ||
24
+ options[:longitude]
25
+ end
17
26
 
18
27
  validate!
19
28
  end