stretchy 0.4.0 → 0.4.1

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