sparkql 1.2.2 → 1.2.7

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.
data/lib/sparkql/parser.y CHANGED
@@ -98,6 +98,8 @@ rule
98
98
  | field_arithmetic_expression MUL field_arithmetic_expression { result = tokenize_arithmetic(val[0], val[1], val[2]) }
99
99
  | field_arithmetic_expression DIV field_arithmetic_expression { result = tokenize_arithmetic(val[0], val[1], val[2]) }
100
100
  | field_arithmetic_expression MOD field_arithmetic_expression { result = tokenize_arithmetic(val[0], val[1], val[2]) }
101
+ | LPAREN field_arithmetic_expression RPAREN { result = tokenize_arithmetic_group(val[1]) }
102
+ | UMINUS field_arithmetic_expression { result = tokenize_arithmetic_negation(val[1]) }
101
103
  | literals
102
104
  | field_function_expression
103
105
  ;
@@ -117,6 +119,8 @@ rule
117
119
  : arithmetic_condition
118
120
  | literal_list { result = tokenize_list(val[0]) }
119
121
  | literal
122
+ | LPAREN condition RPAREN { result = group_fold(val[1]) }
123
+ | UMINUS condition { result = tokenize_literal_negation(val[1]) }
120
124
  ;
121
125
 
122
126
  arithmetic_condition
@@ -194,8 +198,6 @@ rule
194
198
  : INTEGER
195
199
  | DECIMAL
196
200
  | CHARACTER
197
- | LPAREN literals RPAREN { result = val[1] }
198
- | UMINUS literals { result = tokenize_literal_negation(val[1]) }
199
201
  ;
200
202
 
201
203
  ##### Literal
@@ -85,7 +85,7 @@ module Sparkql::ParserCompatibility
85
85
 
86
86
  # Delegator for methods to process the error list.
87
87
  def process_errors
88
- Sparkql::ErrorsProcessor.new(@errors)
88
+ Sparkql::ErrorsProcessor.new(errors)
89
89
  end
90
90
 
91
91
  # delegate :errors?, :fatal_errors?, :dropped_errors?, :recovered_errors?, :to => :process_errors
@@ -161,7 +161,7 @@ module Sparkql::ParserCompatibility
161
161
  def datetime_escape(string)
162
162
  DateTime.parse(string)
163
163
  end
164
-
164
+
165
165
  def time_escape(string)
166
166
  DateTime.parse(string)
167
167
  end
@@ -214,7 +214,8 @@ module Sparkql::ParserCompatibility
214
214
 
215
215
  # Checks the type of an expression with what is expected.
216
216
  def check_type!(expression, expected, supports_nulls = true)
217
- if expected == expression[:type] || check_function_type?(expression, expected) ||
217
+ if (expected == expression[:type] && !expression.key?(:field_manipulations)) ||
218
+ (expression.key?(:field_manipulations) && check_function_type?(expression, expected)) ||
218
219
  (supports_nulls && expression[:type] == :null)
219
220
  return true
220
221
  # If the field will be passed into a function,
@@ -230,7 +231,7 @@ module Sparkql::ParserCompatibility
230
231
  expression[:type] = :datetime
231
232
  expression[:cast] = :date
232
233
  return true
233
- elsif expected == :date && expression[:type] == :datetime
234
+ elsif expected == :date && expression[:type] == :datetime
234
235
  expression[:type] = :date
235
236
  expression[:cast] = :datetime
236
237
  if multiple_values?(expression[:value])
@@ -253,26 +254,44 @@ module Sparkql::ParserCompatibility
253
254
  :message => "expected #{expected} but found #{expression[:type]}",
254
255
  :status => :fatal )
255
256
  end
256
-
257
+
257
258
  # If a function is being applied to a field, we check that the return type of
258
259
  # the function matches what is expected, and that the function supports the
259
260
  # field type as the first argument.
260
261
  def check_function_type?(expression, expected)
261
- return false unless expression.key?(:field_manipulations) && expression[:field_manipulations][:return_type] == expression[:type]
262
- # Lookup the function arguments
263
- function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[deepest_function(expression[:field_manipulations])[:function_name].to_sym]
264
- return false if function.nil?
265
-
266
- Array(function[:args].first).include?(expected)
262
+ validate_manipulation_types(expression[:field_manipulations], expected)
267
263
  end
268
264
 
265
+ def validate_manipulation_types(field_manipulations, expected)
266
+ if field_manipulations[:type] == :function
267
+ function = Sparkql::FunctionResolver::SUPPORTED_FUNCTIONS[field_manipulations[:function_name].to_sym]
268
+ return false if function.nil?
269
+ field_manipulations[:args].each_with_index do |arg, index|
270
+ if arg[:type] == :field
271
+ return false unless function[:args][index].include?(:field)
272
+ end
273
+ end
274
+ elsif field_manipulations[:type] == :arithmetic
275
+ lhs = field_manipulations[:lhs]
276
+ return false unless validate_side(lhs, expected)
269
277
 
270
- def deepest_function(function)
271
- if function[:args].first[:type] == :function
272
- deepest_function(function[:args].first)
273
- else
274
- function
278
+ rhs = field_manipulations[:rhs]
279
+ return false unless rhs.nil? || validate_side(rhs, expected)
280
+ end
281
+ true
282
+ end
283
+
284
+ def validate_side(side, expected)
285
+ if side[:type] == :arithmetic
286
+ return validate_manipulation_types(side, expected)
287
+ elsif side[:type] == :field
288
+ return false unless [:decimal, :integer].include?(expected)
289
+ elsif side[:type] == :function
290
+ return false unless [:decimal, :integer].include?(side[:return_type])
291
+ elsif ![:decimal, :integer].include?(side[:type])
292
+ return false
275
293
  end
294
+ true
276
295
  end
277
296
 
278
297
  # Builds the correct operator based on the type and the value.
@@ -305,7 +324,7 @@ module Sparkql::ParserCompatibility
305
324
  def operator_supports_multiples?(operator)
306
325
  OPERATORS_SUPPORTING_MULTIPLES.include?(operator)
307
326
  end
308
-
327
+
309
328
  def coerce_datetime datetime
310
329
  if datestr = datetime.match(/^(\d{4}-\d{2}-\d{2})/)
311
330
  datestr[0]
@@ -1,10 +1,14 @@
1
1
  # This is the guts of the parser internals and is mixed into the parser for organization.
2
+ require 'bigdecimal'
3
+
2
4
  module Sparkql::ParserTools
3
5
 
4
6
  # Coercible types from highest precision to lowest
5
7
  DATE_TYPES = [:datetime, :date]
6
8
  NUMBER_TYPES = [:decimal, :integer]
7
9
  ARITHMETIC_TYPES = [:decimal, :integer, :field, :arithmetic]
10
+ GROUP = 'Group'.freeze
11
+ NEGATION = 'Negation'.freeze
8
12
 
9
13
  def parse(str)
10
14
  @lexer = Sparkql::Lexer.new(str)
@@ -24,24 +28,16 @@ module Sparkql::ParserTools
24
28
  end
25
29
 
26
30
  def arithmetic_field(nested_representation)
27
- lhs = nested_representation[:lhs]
28
- rhs = nested_representation[:rhs]
29
-
30
- if lhs[:type] == :field
31
- lhs[:value]
32
- elsif rhs[:type] == :field
33
- rhs[:value]
34
- elsif lhs.key?(:field)
35
- lhs[:field]
36
- elsif rhs.key?(:field)
37
- rhs[:field]
38
- elsif lhs[:type] == :arithmetic
39
- arithmetic_field(lhs)
40
- elsif rhs[:type] == :arithmetic
41
- arithmetic_field(rhs)
42
- else
43
- nil
44
- end
31
+ return if nested_representation.nil?
32
+
33
+ return nested_representation[:value] if nested_representation[:type] == :field
34
+ return nested_representation[:field] if nested_representation.key?(:field)
35
+
36
+ field = arithmetic_field(nested_representation[:lhs])
37
+ return field unless field.nil?
38
+
39
+ field = arithmetic_field(nested_representation[:rhs])
40
+ return field unless field.nil?
45
41
  end
46
42
 
47
43
  def no_field_error(field, operator)
@@ -129,6 +125,26 @@ module Sparkql::ParserTools
129
125
  expressions
130
126
  end
131
127
 
128
+ def tokenize_arithmetic_group(lhs)
129
+ @lexer.leveldown
130
+ @lexer.block_group_identifier -= 1
131
+ lhs = {type: :field, value: lhs} if lhs.is_a?(String)
132
+ {
133
+ type: :arithmetic,
134
+ op: GROUP,
135
+ lhs: lhs
136
+ }
137
+ end
138
+
139
+ def tokenize_arithmetic_negation(lhs)
140
+ lhs = {type: :field, value: lhs} if lhs.is_a?(String)
141
+ {
142
+ type: :arithmetic,
143
+ op: NEGATION,
144
+ lhs: lhs
145
+ }
146
+ end
147
+
132
148
  def tokenize_list(list)
133
149
  return if list.nil?
134
150
  validate_multiple_values list[:value]
@@ -245,22 +261,31 @@ module Sparkql::ParserTools
245
261
  true
246
262
  end
247
263
 
264
+ def group_fold(exp)
265
+ @lexer.leveldown
266
+ @lexer.block_group_identifier -= 1
267
+ exp
268
+ end
269
+
248
270
  def add_fold(n1, n2)
249
271
  return if arithmetic_error?(n1) || arithmetic_error?(n2)
250
272
 
251
- { type: arithmetic_type(n1, n2), value: (escape_value(n1) + escape_value(n2)).to_s }
273
+ value = escape_arithmetic_value(n1) + escape_arithmetic_value(n2)
274
+ { type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
252
275
  end
253
276
 
254
277
  def sub_fold(n1, n2)
255
278
  return if arithmetic_error?(n1) || arithmetic_error?(n2)
256
279
 
257
- { type: arithmetic_type(n1, n2), value: (escape_value(n1) - escape_value(n2)).to_s }
280
+ value = escape_arithmetic_value(n1) - escape_arithmetic_value(n2)
281
+ { type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
258
282
  end
259
283
 
260
284
  def mul_fold(n1, n2)
261
285
  return if arithmetic_error?(n1) || arithmetic_error?(n2)
262
286
 
263
- { type: arithmetic_type(n1, n2), value: (escape_value(n1) * escape_value(n2)).to_s }
287
+ value = escape_arithmetic_value(n1) * escape_arithmetic_value(n2)
288
+ { type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
264
289
  end
265
290
 
266
291
  def div_fold(n1, n2)
@@ -268,7 +293,8 @@ module Sparkql::ParserTools
268
293
  arithmetic_error?(n2) ||
269
294
  zero_error?(n2)
270
295
 
271
- { type: arithmetic_type(n1, n2), value: (escape_value(n1) / escape_value(n2)).to_s }
296
+ value = escape_arithmetic_value(n1) / escape_arithmetic_value(n2)
297
+ { type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
272
298
  end
273
299
 
274
300
  def mod_fold(n1, n2)
@@ -276,7 +302,8 @@ module Sparkql::ParserTools
276
302
  arithmetic_error?(n2) ||
277
303
  zero_error?(n2)
278
304
 
279
- { type: arithmetic_type(n1, n2), value: (escape_value(n1) % escape_value(n2)).to_s }
305
+ value = escape_arithmetic_value(n1) % escape_arithmetic_value(n2)
306
+ { type: arithmetic_type(n1, n2), value: unescape_arithmetic(value) }
280
307
  end
281
308
 
282
309
  def arithmetic_type(num1, num2)
@@ -287,6 +314,23 @@ module Sparkql::ParserTools
287
314
  end
288
315
  end
289
316
 
317
+ def escape_arithmetic_value(expression)
318
+ case expression[:type]
319
+ when :decimal
320
+ BigDecimal.new(expression[:value])
321
+ else
322
+ escape_value(expression)
323
+ end
324
+ end
325
+
326
+ def unescape_arithmetic(value)
327
+ if value.is_a?(BigDecimal)
328
+ value.round(20).to_s('F')
329
+ else
330
+ value.to_s
331
+ end
332
+ end
333
+
290
334
  def zero_error?(number)
291
335
  return unless escape_value(number) == 0
292
336
 
data/sparkql.gemspec CHANGED
@@ -1,30 +1,32 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "sparkql/version"
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
4
+ require 'sparkql/version'
4
5
 
5
6
  Gem::Specification.new do |s|
6
- s.name = "sparkql"
7
+ s.name = 'sparkql'
7
8
  s.version = Sparkql::VERSION
8
- s.authors = ["Wade McEwen"]
9
- s.email = ["wade@fbsdata.com"]
10
- s.homepage = ""
11
- s.summary = %q{API Parser engine for filter searching}
12
- s.description = %q{Specification and base implementation of the Spark API parsing system.}
9
+ s.authors = ['Wade McEwen']
10
+ s.email = ['wade@fbsdata.com']
11
+ s.homepage = ''
12
+ s.summary = 'API Parser engine for filter searching'
13
+ s.description = 'Specification and base implementation of the Spark API parsing system.'
14
+
15
+ s.rubyforge_project = 'sparkql'
13
16
 
14
- s.rubyforge_project = "sparkql"
15
-
16
17
  s.license = 'Apache 2.0'
17
-
18
+
18
19
  s.files = `git ls-files`.split("\n")
19
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
- s.require_paths = ["lib"]
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
22
+ s.require_paths = ['lib']
23
+
24
+ s.required_ruby_version = '>= 2.3'
22
25
 
23
26
  s.add_dependency 'georuby', '~> 2.0'
27
+ s.add_development_dependency 'ci_reporter', '~> 1.6'
28
+ s.add_development_dependency 'mocha', '~> 0.12.0'
24
29
  s.add_development_dependency 'racc', '~> 1.4.8'
25
30
  s.add_development_dependency 'rake', '~> 0.9.2'
26
31
  s.add_development_dependency 'test-unit', '~> 2.1.0'
27
- s.add_development_dependency 'ci_reporter', '~> 1.6'
28
- s.add_development_dependency 'mocha', '~> 0.12.0'
29
-
30
32
  end
@@ -1,21 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'test_helper'
2
4
  require 'sparkql/geo'
3
5
 
4
6
  class FunctionResolverTest < Test::Unit::TestCase
5
7
  include Sparkql
6
-
7
- EXAMPLE_DATE = DateTime.parse("2013-07-26T10:22:15.422804")
8
8
 
9
- test "function parameters and name preserved" do
10
- f = FunctionResolver.new('radius', [{:type => :character,
11
- :value => "35.12 -68.33"},{:type => :decimal, :value => 1.0}])
9
+ EXAMPLE_DATE = DateTime.parse('2013-07-26T10:22:15.422804')
10
+
11
+ test 'function parameters and name preserved' do
12
+ f = FunctionResolver.new('radius', [{ type: :character,
13
+ value: '35.12 -68.33' }, { type: :decimal, value: 1.0 }])
12
14
  value = f.call
13
15
  assert_equal 'radius', value[:function_name]
14
- assert_equal(["35.12 -68.33", 1.0], value[:function_parameters])
16
+ assert_equal(['35.12 -68.33', 1.0], value[:function_parameters])
15
17
  end
16
18
 
17
- test "round(float)" do
18
- f = FunctionResolver.new('round', [{:type => :decimal, :value => 0.5}])
19
+ test 'round(float)' do
20
+ f = FunctionResolver.new('round', [{ type: :decimal, value: 0.5 }])
19
21
  f.validate
20
22
  assert !f.errors?, "Errors #{f.errors.inspect}"
21
23
  value = f.call
@@ -23,22 +25,22 @@ class FunctionResolverTest < Test::Unit::TestCase
23
25
  assert_equal '1', value[:value]
24
26
  end
25
27
 
26
- test "round(Field)" do
27
- f = FunctionResolver.new('round', [{:type => :field, :value => 'ListPrice'}])
28
+ test 'round(Field)' do
29
+ f = FunctionResolver.new('round', [{ type: :field, value: 'ListPrice' }])
28
30
  f.validate
29
31
  assert !f.errors?, "Errors #{f.errors.inspect}"
30
32
  value = f.call
31
33
 
32
34
  assert_equal :function, value[:type]
33
35
  assert_equal 'round', value[:value]
34
- assert_equal "ListPrice", value[:args].first[:value]
36
+ assert_equal 'ListPrice', value[:args].first[:value]
35
37
  end
36
38
 
37
- test "substring character one index" do
39
+ test 'substring character one index' do
38
40
  f = FunctionResolver.new('substring', [
39
- {:type => :character, :value => 'ListPrice'},
40
- {:type => :integer, :value => 1}
41
- ])
41
+ { type: :character, value: 'ListPrice' },
42
+ { type: :integer, value: 1 }
43
+ ])
42
44
 
43
45
  f.validate
44
46
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -48,26 +50,26 @@ class FunctionResolverTest < Test::Unit::TestCase
48
50
  assert_equal 'istPrice', value[:value]
49
51
  end
50
52
 
51
- test "substring character two indexes" do
53
+ test 'substring character two indexes' do
52
54
  f = FunctionResolver.new('substring', [
53
- {:type => :character, :value => 'alfb'},
54
- {:type => :integer, :value => 1},
55
- {:type => :integer, :value => 2}
56
- ])
55
+ { type: :character, value: 'alfb' },
56
+ { type: :integer, value: 1 },
57
+ { type: :integer, value: 2 }
58
+ ])
57
59
 
58
60
  f.validate
59
61
  assert !f.errors?, "Errors #{f.errors.inspect}"
60
62
  value = f.call
61
63
 
62
64
  assert_equal :character, value[:type]
63
- assert_equal "lf", value[:value]
65
+ assert_equal 'lf', value[:value]
64
66
  end
65
67
 
66
- test "substring character large first index" do
68
+ test 'substring character large first index' do
67
69
  f = FunctionResolver.new('substring', [
68
- {:type => :character, :value => 'ListPrice'},
69
- {:type => :integer, :value => 10}
70
- ])
70
+ { type: :character, value: 'ListPrice' },
71
+ { type: :integer, value: 10 }
72
+ ])
71
73
 
72
74
  f.validate
73
75
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -77,11 +79,11 @@ class FunctionResolverTest < Test::Unit::TestCase
77
79
  assert_equal '', value[:value]
78
80
  end
79
81
 
80
- test "substring field one index" do
82
+ test 'substring field one index' do
81
83
  f = FunctionResolver.new('substring', [
82
- {:type => :field, :value => 'ListPrice'},
83
- {:type => :integer, :value => 1}
84
- ])
84
+ { type: :field, value: 'ListPrice' },
85
+ { type: :integer, value: 1 }
86
+ ])
85
87
 
86
88
  f.validate
87
89
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -89,16 +91,16 @@ class FunctionResolverTest < Test::Unit::TestCase
89
91
 
90
92
  assert_equal :function, value[:type]
91
93
  assert_equal 'substring', value[:value]
92
- assert_equal "ListPrice", value[:args].first[:value]
94
+ assert_equal 'ListPrice', value[:args].first[:value]
93
95
  assert_equal 2, value[:args].size
94
96
  end
95
97
 
96
- test "substring field two indexes" do
98
+ test 'substring field two indexes' do
97
99
  f = FunctionResolver.new('substring', [
98
- {:type => :field, :value => 'ListPrice'},
99
- {:type => :integer, :value => 1},
100
- {:type => :integer, :value => 2}
101
- ])
100
+ { type: :field, value: 'ListPrice' },
101
+ { type: :integer, value: 1 },
102
+ { type: :integer, value: 2 }
103
+ ])
102
104
 
103
105
  f.validate
104
106
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -106,28 +108,28 @@ class FunctionResolverTest < Test::Unit::TestCase
106
108
 
107
109
  assert_equal :function, value[:type]
108
110
  assert_equal 'substring', value[:value]
109
- assert_equal "ListPrice", value[:args].first[:value]
111
+ assert_equal 'ListPrice', value[:args].first[:value]
110
112
  assert_equal 2, value[:args].last[:value]
111
113
  end
112
114
 
113
- test "substring with negative M is a parse error" do
115
+ test 'substring with negative M is a parse error' do
114
116
  f = FunctionResolver.new('substring', [
115
- {:type => :field, :value => 'ListPrice'},
116
- {:type => :integer, :value => 1},
117
- {:type => :integer, :value => -5}
118
- ])
117
+ { type: :field, value: 'ListPrice' },
118
+ { type: :integer, value: 1 },
119
+ { type: :integer, value: -5 }
120
+ ])
119
121
 
120
122
  f.validate
121
123
  f.call
122
124
  assert f.errors?
123
125
  end
124
126
 
125
- test "character substring with negative M is a parse error" do
127
+ test 'character substring with negative M is a parse error' do
126
128
  f = FunctionResolver.new('substring', [
127
- {:type => :character, :value => 'ListPrice'},
128
- {:type => :integer, :value => 1},
129
- {:type => :integer, :value => -5}
130
- ])
129
+ { type: :character, value: 'ListPrice' },
130
+ { type: :integer, value: 1 },
131
+ { type: :integer, value: -5 }
132
+ ])
131
133
 
132
134
  f.validate
133
135
  f.call
@@ -136,8 +138,8 @@ class FunctionResolverTest < Test::Unit::TestCase
136
138
 
137
139
  test 'trim with field' do
138
140
  f = FunctionResolver.new('trim', [
139
- {:type => :field, :value => 'Name'}
140
- ])
141
+ { type: :field, value: 'Name' }
142
+ ])
141
143
 
142
144
  f.validate
143
145
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -145,13 +147,13 @@ class FunctionResolverTest < Test::Unit::TestCase
145
147
 
146
148
  assert_equal :function, value[:type]
147
149
  assert_equal 'trim', value[:value]
148
- assert_equal "Name", value[:args].first[:value]
150
+ assert_equal 'Name', value[:args].first[:value]
149
151
  end
150
152
 
151
153
  test 'trim with character' do
152
154
  f = FunctionResolver.new('trim', [
153
- {:type => :character, :value => ' val '}
154
- ])
155
+ { type: :character, value: ' val ' }
156
+ ])
155
157
 
156
158
  f.validate
157
159
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -162,7 +164,7 @@ class FunctionResolverTest < Test::Unit::TestCase
162
164
  end
163
165
 
164
166
  test "tolower('string')" do
165
- f = FunctionResolver.new('tolower', [{:type => :character, :value => "STRING"}])
167
+ f = FunctionResolver.new('tolower', [{ type: :character, value: 'STRING' }])
166
168
  f.validate
167
169
  assert !f.errors?, "Errors #{f.errors.inspect}"
168
170
  value = f.call
@@ -170,18 +172,18 @@ class FunctionResolverTest < Test::Unit::TestCase
170
172
  assert_equal "'string'", value[:value]
171
173
  end
172
174
 
173
- test "toupper(SomeField)" do
174
- f = FunctionResolver.new('toupper', [{:type => :field, :value => "City"}])
175
+ test 'toupper(SomeField)' do
176
+ f = FunctionResolver.new('toupper', [{ type: :field, value: 'City' }])
175
177
  f.validate
176
178
  assert !f.errors?, "Errors #{f.errors.inspect}"
177
179
  value = f.call
178
180
  assert_equal :function, value[:type]
179
181
  assert_equal 'toupper', value[:value]
180
- assert_equal "City", value[:args].first[:value]
182
+ assert_equal 'City', value[:args].first[:value]
181
183
  end
182
184
 
183
185
  test "toupper('string')" do
184
- f = FunctionResolver.new('toupper', [{:type => :character, :value => "string"}])
186
+ f = FunctionResolver.new('toupper', [{ type: :character, value: 'string' }])
185
187
  f.validate
186
188
  assert !f.errors?, "Errors #{f.errors.inspect}"
187
189
  value = f.call
@@ -189,18 +191,18 @@ class FunctionResolverTest < Test::Unit::TestCase
189
191
  assert_equal "'STRING'", value[:value]
190
192
  end
191
193
 
192
- test "length(SomeField)" do
193
- f = FunctionResolver.new('length', [{:type => :field, :value => "City"}])
194
+ test 'length(SomeField)' do
195
+ f = FunctionResolver.new('length', [{ type: :field, value: 'City' }])
194
196
  f.validate
195
197
  assert !f.errors?, "Errors #{f.errors.inspect}"
196
198
  value = f.call
197
199
  assert_equal :function, value[:type]
198
200
  assert_equal 'length', value[:value]
199
- assert_equal "City", value[:args].first[:value]
201
+ assert_equal 'City', value[:args].first[:value]
200
202
  end
201
203
 
202
204
  test "length('string')" do
203
- f = FunctionResolver.new('length', [{:type => :character, :value => "string"}])
205
+ f = FunctionResolver.new('length', [{ type: :character, value: 'string' }])
204
206
  f.validate
205
207
  assert !f.errors?, "Errors #{f.errors.inspect}"
206
208
  value = f.call
@@ -208,7 +210,7 @@ class FunctionResolverTest < Test::Unit::TestCase
208
210
  assert_equal '6', value[:value]
209
211
  end
210
212
 
211
- test "now()" do
213
+ test 'now()' do
212
214
  start = Time.now
213
215
  f = FunctionResolver.new('now', [])
214
216
  f.validate
@@ -216,10 +218,11 @@ class FunctionResolverTest < Test::Unit::TestCase
216
218
  value = f.call
217
219
  assert_equal :datetime, value[:type]
218
220
  test_time = Time.parse(value[:value])
219
- assert (-5 < test_time - start && 5 > test_time - start), "Time range off by more than five seconds #{test_time - start} '#{test_time} - #{start}'"
221
+ assert (test_time - start > -5 && test_time - start < 5),
222
+ "Time range off by more than five seconds #{test_time - start} '#{test_time} - #{start}'"
220
223
  end
221
224
 
222
- test "mindatetime()" do
225
+ test 'mindatetime()' do
223
226
  f = FunctionResolver.new('mindatetime', [])
224
227
  f.validate
225
228
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -229,7 +232,7 @@ class FunctionResolverTest < Test::Unit::TestCase
229
232
  assert_equal '1970-01-01T00:00:00+00:00', value[:value]
230
233
  end
231
234
 
232
- test "maxdatetime()" do
235
+ test 'maxdatetime()' do
233
236
  f = FunctionResolver.new('maxdatetime', [])
234
237
  f.validate
235
238
  assert !f.errors?, "Errors #{f.errors.inspect}"
@@ -239,8 +242,8 @@ class FunctionResolverTest < Test::Unit::TestCase
239
242
  assert_equal '9999-12-31T23:59:59+00:00', value[:value]
240
243
  end
241
244
 
242
- test "floor(float)" do
243
- f = FunctionResolver.new('floor', [{:type => :decimal, :value => 0.5}])
245
+ test 'floor(float)' do
246
+ f = FunctionResolver.new('floor', [{ type: :decimal, value: 0.5 }])
244
247
  f.validate
245
248
  assert !f.errors?, "Errors #{f.errors.inspect}"
246
249
  value = f.call
@@ -248,19 +251,19 @@ class FunctionResolverTest < Test::Unit::TestCase
248
251
  assert_equal '0', value[:value]
249
252
  end
250
253
 
251
- test "floor(Field)" do
252
- f = FunctionResolver.new('floor', [{:type => :field, :value => 'ListPrice'}])
254
+ test 'floor(Field)' do
255
+ f = FunctionResolver.new('floor', [{ type: :field, value: 'ListPrice' }])
253
256
  f.validate
254
257
  assert !f.errors?, "Errors #{f.errors.inspect}"
255
258
  value = f.call
256
259
 
257
260
  assert_equal :function, value[:type]
258
261
  assert_equal 'floor', value[:value]
259
- assert_equal "ListPrice", value[:args].first[:value]
262
+ assert_equal 'ListPrice', value[:args].first[:value]
260
263
  end
261
264
 
262
- test "ceiling(float)" do
263
- f = FunctionResolver.new('ceiling', [{:type => :decimal, :value => 0.5}])
265
+ test 'ceiling(float)' do
266
+ f = FunctionResolver.new('ceiling', [{ type: :decimal, value: 0.5 }])
264
267
  f.validate
265
268
  assert !f.errors?, "Errors #{f.errors.inspect}"
266
269
  value = f.call
@@ -268,93 +271,344 @@ class FunctionResolverTest < Test::Unit::TestCase
268
271
  assert_equal '1', value[:value]
269
272
  end
270
273
 
271
- test "ceiling(Field)" do
272
- f = FunctionResolver.new('ceiling', [{:type => :field, :value => 'ListPrice'}])
274
+ test 'ceiling(Field)' do
275
+ f = FunctionResolver.new('ceiling', [{ type: :field, value: 'ListPrice' }])
273
276
  f.validate
274
277
  assert !f.errors?, "Errors #{f.errors.inspect}"
275
278
  value = f.call
276
279
 
277
280
  assert_equal :function, value[:type]
278
281
  assert_equal 'ceiling', value[:value]
279
- assert_equal "ListPrice", value[:args].first[:value]
282
+ assert_equal 'ListPrice', value[:args].first[:value]
283
+ end
284
+
285
+ test 'seconds()' do
286
+ test_time = Time.new(2019, 4, 1, 8, 30, 20, 0)
287
+
288
+ f = FunctionResolver.new('seconds', [{ type: :integer, value: 7 }])
289
+ f.expects(:current_time).returns(test_time)
290
+ f.validate
291
+ assert !f.errors?, "Errors #{f.errors.inspect}"
292
+ value = f.call
293
+
294
+ assert_equal :datetime, value[:type]
295
+ d = DateTime.parse(value[:value])
296
+ assert_equal test_time.year, d.year
297
+ assert_equal test_time.month, d.month
298
+ assert_equal test_time.mday, d.mday
299
+ assert_equal test_time.hour, d.hour
300
+ assert_equal test_time.min, d.min
301
+ assert_equal test_time.sec + 7, d.sec
302
+
303
+ f = FunctionResolver.new('seconds', [{ type: :integer, value: -21 }])
304
+ f.expects(:current_time).returns(test_time)
305
+ f.validate
306
+ assert !f.errors?, "Errors #{f.errors.inspect}"
307
+ value = f.call
308
+
309
+ assert_equal :datetime, value[:type]
310
+ d = DateTime.parse(value[:value])
311
+ assert_equal test_time.year, d.year
312
+ assert_equal test_time.month, d.month
313
+ assert_equal test_time.mday, d.mday
314
+ assert_equal test_time.hour, d.hour
315
+ assert_equal test_time.min - 1, d.min
316
+ assert_equal 29, d.min
317
+ assert_equal 59, d.sec
318
+
319
+ f = FunctionResolver.new('seconds', [{ type: :integer,
320
+ value: -Sparkql::FunctionResolver::SECONDS_IN_DAY }])
321
+ f.expects(:current_time).returns(test_time)
322
+ f.validate
323
+ assert !f.errors?, "Errors #{f.errors.inspect}"
324
+ value = f.call
325
+
326
+ assert_equal :datetime, value[:type]
327
+ d = DateTime.parse(value[:value])
328
+ assert_equal test_time.year, d.year
329
+ assert_equal test_time.month - 1, d.month
330
+ assert_equal 31, d.mday
331
+ assert_equal test_time.hour, d.hour
332
+ assert_equal test_time.min, d.min
333
+ assert_equal test_time.min, d.min
334
+ assert_equal test_time.sec, d.sec
280
335
  end
281
336
 
282
- test "days()" do
283
- d = Date.new(2012,10,20)
284
- Date.expects(:today).returns(d)
285
- dt = DateTime.new(d.year, d.month,d.day, 0,0,0, DateTime.now.offset)
286
- start = Time.parse(dt.to_s)
287
- f = FunctionResolver.new('days', [{:type=>:integer, :value =>7}])
337
+ test 'minutes()' do
338
+ test_time = Time.new(2019, 4, 1, 8, 30, 20, 0)
339
+
340
+ f = FunctionResolver.new('minutes', [{ type: :integer, value: 7 }])
341
+ f.expects(:current_time).returns(test_time)
342
+ f.validate
343
+ assert !f.errors?, "Errors #{f.errors.inspect}"
344
+ value = f.call
345
+
346
+ assert_equal :datetime, value[:type]
347
+ d = DateTime.parse(value[:value])
348
+ assert_equal test_time.year, d.year
349
+ assert_equal test_time.month, d.month
350
+ assert_equal test_time.mday, d.mday
351
+ assert_equal test_time.hour, d.hour
352
+ assert_equal test_time.min + 7, d.min
353
+ assert_equal test_time.sec, d.sec
354
+
355
+ f = FunctionResolver.new('minutes', [{ type: :integer, value: -37 }])
356
+ f.expects(:current_time).returns(test_time)
357
+ f.validate
358
+ assert !f.errors?, "Errors #{f.errors.inspect}"
359
+ value = f.call
360
+
361
+ assert_equal :datetime, value[:type]
362
+ d = DateTime.parse(value[:value])
363
+ assert_equal test_time.year, d.year
364
+ assert_equal test_time.month, d.month
365
+ assert_equal test_time.mday, d.mday
366
+ assert_equal test_time.hour - 1, d.hour
367
+ assert_equal 53, d.min
368
+
369
+ f = FunctionResolver.new('minutes', [{ type: :integer,
370
+ value: -1440 }])
371
+ f.expects(:current_time).returns(test_time)
372
+ f.validate
373
+ assert !f.errors?, "Errors #{f.errors.inspect}"
374
+ value = f.call
375
+
376
+ assert_equal :datetime, value[:type]
377
+ d = DateTime.parse(value[:value])
378
+ assert_equal test_time.year, d.year
379
+ assert_equal test_time.month - 1, d.month
380
+ assert_equal 31, d.mday
381
+ assert_equal test_time.hour, d.hour
382
+ assert_equal test_time.min, d.min
383
+ end
384
+
385
+ test 'hours(), same day' do
386
+ test_time = Time.new(2019, 4, 1, 8, 30, 20, 0)
387
+ tests = [1, -1, 5, -5, 12]
388
+
389
+ tests.each do |offset|
390
+ f = FunctionResolver.new('hours', [{ type: :integer,
391
+ value: offset }])
392
+ f.expects(:current_time).returns(test_time)
393
+ f.validate
394
+ assert !f.errors?, "Errors #{f.errors.inspect}"
395
+ value = f.call
396
+
397
+ assert_equal :datetime, value[:type]
398
+ d = DateTime.parse(value[:value])
399
+ assert_equal test_time.year, d.year
400
+ assert_equal test_time.month, d.month
401
+ assert_equal test_time.mday, d.mday
402
+ assert_equal test_time.hour + offset, d.hour
403
+ assert_equal test_time.min, d.min
404
+ assert_equal test_time.sec, d.sec
405
+ end
406
+ end
407
+
408
+ test 'hours(), wrap day' do
409
+ test_time = Time.new(2019, 4, 1, 8, 30, 20, 0)
410
+
411
+ # Jump forward a few days, and a few hours.
412
+ f = FunctionResolver.new('hours', [{ type: :integer, value: 52 }])
413
+ f.expects(:current_time).returns(test_time)
414
+ f.validate
415
+ assert !f.errors?, "Errors #{f.errors.inspect}"
416
+ value = f.call
417
+
418
+ assert_equal :datetime, value[:type]
419
+ d = DateTime.parse(value[:value])
420
+ assert_equal test_time.year, d.year
421
+ assert_equal test_time.month, d.month
422
+ assert_equal test_time.mday + 2, d.mday
423
+ assert_equal test_time.hour + 4, d.hour
424
+ assert_equal test_time.min, d.min
425
+
426
+ # Drop back to the previous day, which'll also hit the previous month
427
+ f = FunctionResolver.new('hours', [{ type: :integer, value: -24 }])
428
+ f.expects(:current_time).returns(test_time)
429
+ f.validate
430
+ assert !f.errors?, "Errors #{f.errors.inspect}"
431
+ value = f.call
432
+
433
+ assert_equal :datetime, value[:type]
434
+ d = DateTime.parse(value[:value])
435
+ assert_equal test_time.year, d.year
436
+ assert_equal test_time.month - 1, d.month
437
+ assert_equal 31, d.mday
438
+ assert_equal test_time.hour, d.hour
439
+ assert_equal test_time.min, d.min
440
+
441
+ # Drop back one full year's worth of hours.
442
+ f = FunctionResolver.new('hours', [{ type: :integer, value: -8760 }])
443
+ f.expects(:current_time).returns(test_time)
444
+ f.validate
445
+ assert !f.errors?, "Errors #{f.errors.inspect}"
446
+ value = f.call
447
+
448
+ assert_equal :datetime, value[:type]
449
+ d = DateTime.parse(value[:value])
450
+ assert_equal test_time.year - 1, d.year
451
+ assert_equal test_time.month, d.month
452
+ assert_equal test_time.mday, d.mday
453
+ assert_equal test_time.hour, d.hour
454
+ assert_equal test_time.min, d.min
455
+ end
456
+
457
+ test 'days()' do
458
+ test_date = Date.new(2012, 10, 20) # Sat, 20 Oct 2012 00:00:00 GMT
459
+ f = FunctionResolver.new('days', [{ type: :integer, value: 7 }])
460
+ f.expects(:current_date).returns(test_date)
288
461
  f.validate
289
462
  assert !f.errors?, "Errors #{f.errors.inspect}"
290
463
  value = f.call
291
464
  assert_equal :date, value[:type]
292
- test_time = Time.parse(value[:value])
293
- assert (615000 > test_time - start && 600000 < test_time - start), "Time range off by more than five seconds #{test_time - start} '#{test_time} - #{start}'"
465
+ assert_equal '2012-10-27', value[:value]
466
+ end
467
+
468
+ test 'weekdays()' do
469
+ friday = Date.new(2012, 10, 19)
470
+ saturday = Date.new(2012, 10, 20)
471
+ sunday = Date.new(2012, 10, 21)
472
+ monday = Date.new(2012, 10, 22)
473
+ {
474
+ friday => [
475
+ [-5, '2012-10-12'],
476
+ [-4, '2012-10-15'],
477
+ [-1, '2012-10-18'],
478
+ [0, '2012-10-19'],
479
+ [1, '2012-10-22'],
480
+ [2, '2012-10-23'],
481
+ [5, '2012-10-26'],
482
+ [6, '2012-10-29'],
483
+ [7, '2012-10-30'],
484
+ [31, '2012-12-03']
485
+ ],
486
+ saturday => [
487
+ [-6, '2012-10-12'],
488
+ [-5, '2012-10-15'],
489
+ [-1, '2012-10-19'],
490
+ [0, '2012-10-22'],
491
+ [1, '2012-10-22'],
492
+ [2, '2012-10-23'],
493
+ [3, '2012-10-24'],
494
+ [4, '2012-10-25'],
495
+ [5, '2012-10-26'],
496
+ [6, '2012-10-29'],
497
+ [7, '2012-10-30'],
498
+ [31, '2012-12-03']
499
+ ],
500
+ sunday => [
501
+ [-6, '2012-10-12'],
502
+ [-5, '2012-10-15'],
503
+ [-1, '2012-10-19'],
504
+ [0, '2012-10-22'],
505
+ [1, '2012-10-22'],
506
+ [2, '2012-10-23'],
507
+ [5, '2012-10-26'],
508
+ [6, '2012-10-29'],
509
+ [7, '2012-10-30'],
510
+ [31, '2012-12-03']
511
+ ],
512
+ monday => [
513
+ [-6, '2012-10-12'],
514
+ [-5, '2012-10-15'],
515
+ [-1, '2012-10-19'],
516
+ [0, '2012-10-22'],
517
+ [1, '2012-10-23'],
518
+ [2, '2012-10-24'],
519
+ [5, '2012-10-29'],
520
+ [6, '2012-10-30'],
521
+ [7, '2012-10-31'],
522
+ [31, '2012-12-04']
523
+ ]
524
+ }.each do |test_date, weekday_tests|
525
+ weekday_tests.each do |days, expected_value|
526
+ f = FunctionResolver.new('weekdays', [{ type: :integer, value: days }])
527
+ f.expects(:current_date).returns(test_date)
528
+ f.validate
529
+ assert !f.errors?, "#{test_date}: #{days} = #{expected_value}"
530
+ value = f.call
531
+ assert_equal :date, value[:type]
532
+ assert_equal expected_value, value[:value], "#{test_date}: #{days} = #{expected_value}"
533
+ end
534
+ end
294
535
  end
295
536
 
296
- test "months()" do
537
+ test 'months()' do
297
538
  dt = DateTime.new(2014, 1, 6, 0, 0, 0, 0)
298
539
  DateTime.expects(:now).once.returns(dt)
299
540
 
300
- f = FunctionResolver.new('months', [{:type=>:integer, :value =>3}])
541
+ f = FunctionResolver.new('months', [{ type: :integer, value: 3 }])
301
542
  f.validate
302
543
  assert !f.errors?, "Errors resolving months(): #{f.errors.inspect}"
303
544
  value = f.call
304
545
  assert_equal :date, value[:type]
305
546
 
306
- assert_equal "2014-04-06", value[:value]
547
+ assert_equal '2014-04-06', value[:value]
307
548
  end
308
-
309
- test "years()" do
549
+
550
+ test 'years()' do
310
551
  dt = DateTime.new(2014, 1, 6, 0, 0, 0, 0)
311
552
  DateTime.expects(:now).once.returns(dt)
312
- f = FunctionResolver.new('years', [{:type=>:integer, :value =>-4}])
553
+ f = FunctionResolver.new('years', [{ type: :integer, value: -4 }])
313
554
  f.validate
314
555
  assert !f.errors?, "Errors resolving years(): #{f.errors.inspect}"
315
556
  value = f.call
316
557
  assert_equal :date, value[:type]
317
- assert_equal '2010-01-06', value[:value], "negative values should go back in time"
558
+ assert_equal '2010-01-06', value[:value], 'negative values should go back in time'
318
559
  end
319
560
 
320
- test "year(), month(), and day()" do
321
- ['year', 'month', 'day'].each do |function|
322
- f = FunctionResolver.new(function, [{:type => :field, :value => "OriginalEntryTimestamp"}])
561
+ test 'year(), month(), and day()' do
562
+ %w[year month day].each do |function|
563
+ f = FunctionResolver.new(function, [{ type: :field, value: 'OriginalEntryTimestamp' }])
323
564
  f.validate
324
565
  assert !f.errors?, "Errors #{f.errors.inspect}"
325
566
  value = f.call
326
567
  assert_equal :function, value[:type]
327
568
  assert_equal function, value[:value]
328
- assert_equal "OriginalEntryTimestamp", value[:args].first[:value]
569
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
329
570
  end
330
571
  end
331
572
 
332
- test "hour(), minute(), and second()" do
333
- ['year', 'month', 'day'].each do |function|
334
- f = FunctionResolver.new(function, [{:type => :field, :value => "OriginalEntryTimestamp"}])
573
+ test 'hour(), minute(), and second()' do
574
+ %w[year month day].each do |function|
575
+ f = FunctionResolver.new(function, [{ type: :field, value: 'OriginalEntryTimestamp' }])
335
576
  f.validate
336
577
  assert !f.errors?, "Errors #{f.errors.inspect}"
337
578
  value = f.call
338
579
  assert_equal :function, value[:type]
339
580
  assert_equal function, value[:value]
340
- assert_equal "OriginalEntryTimestamp", value[:args].first[:value]
581
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
341
582
  end
342
583
  end
343
584
 
344
- test "fractionalseconds()" do
345
- f = FunctionResolver.new('fractionalseconds', [{:type => :field, :value => "OriginalEntryTimestamp"}])
585
+ test 'day of week and day of year parse' do
586
+ %w[dayofyear dayofweek].each do |function|
587
+ f = FunctionResolver.new(function, [{ type: :field, value: 'OriginalEntryTimestamp' }])
588
+ f.validate
589
+ assert !f.errors?, "Errors #{f.errors.inspect}"
590
+ value = f.call
591
+ assert_equal :function, value[:type]
592
+ assert_equal function, value[:value]
593
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
594
+ end
595
+ end
596
+
597
+ test 'fractionalseconds()' do
598
+ f = FunctionResolver.new('fractionalseconds', [{ type: :field, value: 'OriginalEntryTimestamp' }])
346
599
  f.validate
347
600
  assert !f.errors?, "Errors #{f.errors.inspect}"
348
601
  value = f.call
349
602
  assert_equal :function, value[:type]
350
603
  assert_equal 'fractionalseconds', value[:value]
351
- assert_equal "OriginalEntryTimestamp", value[:args].first[:value]
604
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
352
605
  end
353
606
 
354
607
  # Polygon searches
355
-
356
- test "radius()" do
357
- f = FunctionResolver.new('radius', [{:type => :character, :value => "35.12 -68.33"},{:type => :decimal, :value => 1.0}])
608
+
609
+ test 'radius()' do
610
+ f = FunctionResolver.new('radius',
611
+ [{ type: :character, value: '35.12 -68.33' }, { type: :decimal, value: 1.0 }])
358
612
  f.validate
359
613
  assert !f.errors?, "Errors #{f.errors.inspect}"
360
614
  value = f.call
@@ -364,38 +618,41 @@ class FunctionResolverTest < Test::Unit::TestCase
364
618
  assert_equal 1.0, value[:value].radius, "#{value[:value].inspect} "
365
619
  end
366
620
 
367
- test "radius() can be overloaded with a ListingKey" do
368
- f = FunctionResolver.new('radius', [{:type => :character, :value => "20100000000000000000000000"},
369
- {:type => :decimal, :value => 1.0}])
621
+ test 'radius() can be overloaded with a ListingKey' do
622
+ f = FunctionResolver.new('radius', [{ type: :character, value: '20100000000000000000000000' },
623
+ { type: :decimal, value: 1.0 }])
370
624
  f.validate
371
625
  assert !f.errors?, "Errors #{f.errors.inspect}"
372
626
  value = f.call
373
627
  assert_equal :shape, value[:type]
374
628
  assert_equal Sparkql::Geo::RecordRadius, value[:value].class
375
- assert_equal "20100000000000000000000000", value[:value].record_id, "#{value[:value].inspect} "
629
+ assert_equal '20100000000000000000000000', value[:value].record_id, "#{value[:value].inspect} "
376
630
  assert_equal 1.0, value[:value].radius, "#{value[:value].inspect} "
377
631
  end
378
632
 
379
- test "radius() fails if not given coords or a flex ID" do
380
- f = FunctionResolver.new('radius', [{:type => :character, :value => "35.12,-68.33"},
381
- {:type => :decimal, :value => 1.0}])
633
+ test 'radius() fails if not given coords or a flex ID' do
634
+ f = FunctionResolver.new('radius', [{ type: :character, value: '35.12,-68.33' },
635
+ { type: :decimal, value: 1.0 }])
382
636
  f.validate
383
637
  f.call
384
638
  assert f.errors?
385
639
  end
386
640
 
387
- test "polygon()" do
388
- f = FunctionResolver.new('polygon', [{:type => :character, :value => "35.12 -68.33,35.12 -68.32, 35.13 -68.32,35.13 -68.33"}])
641
+ test 'polygon()' do
642
+ f = FunctionResolver.new('polygon',
643
+ [{ type: :character,
644
+ value: '35.12 -68.33,35.12 -68.32, 35.13 -68.32,35.13 -68.33' }])
389
645
  f.validate
390
646
  assert !f.errors?, "Errors #{f.errors.inspect}"
391
647
  value = f.call
392
648
  assert_equal :shape, value[:type]
393
649
  assert_equal GeoRuby::SimpleFeatures::Polygon, value[:value].class
394
- assert_equal [[-68.33, 35.12], [-68.32, 35.12], [-68.32, 35.13], [-68.33, 35.13], [-68.33, 35.12]], value[:value].to_coordinates.first, "#{value[:value].inspect} "
650
+ assert_equal [[-68.33, 35.12], [-68.32, 35.12], [-68.32, 35.13], [-68.33, 35.13], [-68.33, 35.12]],
651
+ value[:value].to_coordinates.first, "#{value[:value].inspect} "
395
652
  end
396
-
397
- test "linestring()" do
398
- f = FunctionResolver.new('linestring', [{:type => :character, :value => "35.12 -68.33,35.12 -68.32"}])
653
+
654
+ test 'linestring()' do
655
+ f = FunctionResolver.new('linestring', [{ type: :character, value: '35.12 -68.33,35.12 -68.32' }])
399
656
  f.validate
400
657
  assert !f.errors?, "Errors #{f.errors.inspect}"
401
658
  value = f.call
@@ -404,70 +661,72 @@ class FunctionResolverTest < Test::Unit::TestCase
404
661
  assert_equal [[-68.33, 35.12], [-68.32, 35.12]], value[:value].to_coordinates, "#{value[:value].inspect} "
405
662
  end
406
663
 
407
- test "rectangle()" do
408
- f = FunctionResolver.new('rectangle', [{:type => :character, :value => "35.12 -68.33, 35.13 -68.32"}])
664
+ test 'rectangle()' do
665
+ f = FunctionResolver.new('rectangle', [{ type: :character, value: '35.12 -68.33, 35.13 -68.32' }])
409
666
  f.validate
410
667
  assert !f.errors?, "Errors #{f.errors.inspect}"
411
668
  value = f.call
412
669
  assert_equal :shape, value[:type]
413
670
  assert_equal GeoRuby::SimpleFeatures::Polygon, value[:value].class
414
- assert_equal [[-68.33,35.12], [-68.32,35.12], [-68.32,35.13], [-68.33,35.13], [-68.33,35.12]], value[:value].to_coordinates.first, "#{value[:value].inspect} "
671
+ assert_equal [[-68.33, 35.12], [-68.32, 35.12], [-68.32, 35.13], [-68.33, 35.13], [-68.33, 35.12]],
672
+ value[:value].to_coordinates.first, "#{value[:value].inspect} "
415
673
  end
416
674
 
417
- test "range()" do
418
- f = FunctionResolver.new('range', [{:type => :character, :value => "M01"},
419
- {:type => :character, :value => "M05"}])
675
+ test 'range()' do
676
+ f = FunctionResolver.new('range', [{ type: :character, value: 'M01' },
677
+ { type: :character, value: 'M05' }])
420
678
  f.validate
421
679
  assert !f.errors?, "Errors #{f.errors.inspect}"
422
680
  value = f.call
423
681
  assert_equal :character, value[:type]
424
- assert_equal ["M01", "M05"], value[:value]
682
+ assert_equal %w[M01 M05], value[:value]
425
683
  end
426
-
427
- test "invalid params" do
428
- f = FunctionResolver.new('now', [{:type => :character, :value=>'bad value'}])
684
+
685
+ test 'invalid params' do
686
+ f = FunctionResolver.new('now', [{ type: :character, value: 'bad value' }])
429
687
  f.validate
430
688
  assert f.errors?, "'now' function does not support parameters"
431
-
689
+
432
690
  f = FunctionResolver.new('days', [])
433
691
  f.validate
434
692
  assert f.errors?, "'days' function requires one parameter"
435
-
436
- f = FunctionResolver.new('days', [{:type => :character, :value=>'bad value'}])
693
+
694
+ f = FunctionResolver.new('days', [{ type: :character, value: 'bad value' }])
437
695
  f.validate
438
696
  assert f.errors?, "'days' function needs integer parameter"
439
697
  end
440
698
 
441
- test "assert nil returned when function called with errors" do
442
- f = FunctionResolver.new('radius', [{:type => :character,
443
- :value => "35.12 -68.33, 35.13 -68.34"},{:type => :decimal,
444
- :value => 1.0}])
699
+ test 'assert nil returned when function called with errors' do
700
+ f = FunctionResolver.new('radius', [{ type: :character,
701
+ value: '35.12 -68.33, 35.13 -68.34' }, { type: :decimal,
702
+ value: 1.0 }])
445
703
  assert_nil f.call
446
704
  end
447
-
448
- test "return_type" do
449
- f = FunctionResolver.new('radius', [{:type => :character,
450
- :value => "35.12 -68.33, 35.13 -68.34"},{:type => :decimal,
451
- :value => 1.0}])
705
+
706
+ test 'return_type' do
707
+ f = FunctionResolver.new('radius', [{ type: :character,
708
+ value: '35.12 -68.33, 35.13 -68.34' }, { type: :decimal,
709
+ value: 1.0 }])
452
710
  assert_equal :shape, f.return_type
453
711
  end
454
712
 
455
713
  test 'return_type for cast()' do
456
- f = FunctionResolver.new('cast', [{:type => :character,
457
- :value => "1"},{:type => :character,
458
- :value => 'decimal'}])
714
+ f = FunctionResolver.new('cast', [{ type: :character,
715
+ value: '1' }, { type: :character,
716
+ value: 'decimal' }])
459
717
 
460
718
  assert_equal :decimal, f.return_type
461
719
 
462
- f = FunctionResolver.new('cast', [{:type => :character,
463
- :value => "1"},{:type => :character,
464
- :value => 'integer'}])
720
+ f = FunctionResolver.new('cast', [{ type: :character,
721
+ value: '1' }, { type: :character,
722
+ value: 'integer' }])
465
723
 
466
724
  assert_equal :integer, f.return_type
467
725
  end
468
726
 
469
- test "cast() decimal to integer" do
470
- f = FunctionResolver.new('cast', [{:type => :decimal, :value => '1.2'}, {type: :character, :value => 'integer'}])
727
+ test 'cast() decimal to integer' do
728
+ f = FunctionResolver.new('cast',
729
+ [{ type: :decimal, value: '1.2' }, { type: :character, value: 'integer' }])
471
730
  f.validate
472
731
  assert !f.errors?, "Errors #{f.errors.inspect}"
473
732
  value = f.call
@@ -476,8 +735,8 @@ class FunctionResolverTest < Test::Unit::TestCase
476
735
  assert_equal '1', value[:value]
477
736
  end
478
737
 
479
- test "cast() integer to decimal" do
480
- f = FunctionResolver.new('cast', [{:type => :decimal, :value => '1'}, {type: :character, :value => 'decimal'}])
738
+ test 'cast() integer to decimal' do
739
+ f = FunctionResolver.new('cast', [{ type: :decimal, value: '1' }, { type: :character, value: 'decimal' }])
481
740
  f.validate
482
741
  assert !f.errors?, "Errors #{f.errors.inspect}"
483
742
  value = f.call
@@ -486,8 +745,8 @@ class FunctionResolverTest < Test::Unit::TestCase
486
745
  assert_equal '1.0', value[:value]
487
746
  end
488
747
 
489
- test "cast() nil to integer" do
490
- f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'integer'}])
748
+ test 'cast() nil to integer' do
749
+ f = FunctionResolver.new('cast', [{ type: :null, value: 'NULL' }, { type: :character, value: 'integer' }])
491
750
  f.validate
492
751
  assert !f.errors?, "Errors #{f.errors.inspect}"
493
752
  value = f.call
@@ -496,8 +755,8 @@ class FunctionResolverTest < Test::Unit::TestCase
496
755
  assert_equal '0', value[:value]
497
756
  end
498
757
 
499
- test "cast() nil to decimal" do
500
- f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'decimal'}])
758
+ test 'cast() nil to decimal' do
759
+ f = FunctionResolver.new('cast', [{ type: :null, value: 'NULL' }, { type: :character, value: 'decimal' }])
501
760
  f.validate
502
761
  assert !f.errors?, "Errors #{f.errors.inspect}"
503
762
  value = f.call
@@ -506,8 +765,9 @@ class FunctionResolverTest < Test::Unit::TestCase
506
765
  assert_equal '0.0', value[:value]
507
766
  end
508
767
 
509
- test "cast() nil to character" do
510
- f = FunctionResolver.new('cast', [{:type => :null, :value => 'NULL'}, {type: :character, :value => 'character'}])
768
+ test 'cast() nil to character' do
769
+ f = FunctionResolver.new('cast',
770
+ [{ type: :null, value: 'NULL' }, { type: :character, value: 'character' }])
511
771
  f.validate
512
772
  assert !f.errors?, "Errors #{f.errors.inspect}"
513
773
  value = f.call
@@ -516,39 +776,43 @@ class FunctionResolverTest < Test::Unit::TestCase
516
776
  assert_equal "''", value[:value]
517
777
  end
518
778
 
519
- test "cast() character to decimal" do
520
- f = FunctionResolver.new('cast', [{:type => :character, :value => "1.1"}, {type: :character, :value => 'decimal'}])
779
+ test 'cast() character to decimal' do
780
+ f = FunctionResolver.new('cast',
781
+ [{ type: :character, value: '1.1' }, { type: :character, value: 'decimal' }])
521
782
  f.validate
522
783
  assert !f.errors?, "Errors #{f.errors.inspect}"
523
784
  value = f.call
524
785
 
525
786
  assert_equal :decimal, value[:type]
526
- assert_equal "1.1", value[:value]
787
+ assert_equal '1.1', value[:value]
527
788
  end
528
789
 
529
- test "cast() character to integer" do
530
- f = FunctionResolver.new('cast', [{:type => :character, :value => "1"}, {type: :character, :value => 'integer'}])
790
+ test 'cast() character to integer' do
791
+ f = FunctionResolver.new('cast',
792
+ [{ type: :character, value: '1' }, { type: :character, value: 'integer' }])
531
793
  f.validate
532
794
  assert !f.errors?, "Errors #{f.errors.inspect}"
533
795
  value = f.call
534
796
 
535
797
  assert_equal :integer, value[:type]
536
- assert_equal "1", value[:value]
798
+ assert_equal '1', value[:value]
537
799
  end
538
800
 
539
- test "cast() Field" do
540
- f = FunctionResolver.new('cast', [{:type => :field, :value => 'Bedrooms'}, {type: :character, :value => 'character'}])
801
+ test 'cast() Field' do
802
+ f = FunctionResolver.new('cast',
803
+ [{ type: :field, value: 'Bedrooms' }, { type: :character, value: 'character' }])
541
804
  f.validate
542
805
  assert !f.errors?, "Errors #{f.errors.inspect}"
543
806
  value = f.call
544
807
 
545
808
  assert_equal :function, value[:type]
546
809
  assert_equal 'cast', value[:value]
547
- assert_equal ['Bedrooms', 'character'], value[:args].map { |v| v[:value]}
810
+ assert_equal(%w[Bedrooms character], value[:args].map{ |v| v[:value] })
548
811
  end
549
812
 
550
- test "invalid cast returns null" do
551
- f = FunctionResolver.new('cast', [{:type => :character, :value => '1.1.1'}, {type: :character, :value => 'integer'}])
813
+ test 'invalid cast returns null' do
814
+ f = FunctionResolver.new('cast',
815
+ [{ type: :character, value: '1.1.1' }, { type: :character, value: 'integer' }])
552
816
  f.validate
553
817
  assert !f.errors?, "Errors #{f.errors.inspect}"
554
818
  value = f.call
@@ -557,14 +821,14 @@ class FunctionResolverTest < Test::Unit::TestCase
557
821
  assert_equal 'NULL', value[:value]
558
822
  end
559
823
 
560
- test "invalid function" do
824
+ test 'invalid function' do
561
825
  f = FunctionResolver.new('then', [])
562
826
  f.validate
563
827
  assert f.errors?, "'then' is not a function"
564
828
  end
565
829
 
566
- test "time(datetime)" do
567
- f = FunctionResolver.new('time', [{:type => :datetime, :value => EXAMPLE_DATE}])
830
+ test 'time(datetime)' do
831
+ f = FunctionResolver.new('time', [{ type: :datetime, value: EXAMPLE_DATE }])
568
832
  f.validate
569
833
  assert !f.errors?, "Errors #{f.errors.inspect}"
570
834
  value = f.call
@@ -572,8 +836,8 @@ class FunctionResolverTest < Test::Unit::TestCase
572
836
  assert_equal '10:22:15.422804000', value[:value]
573
837
  end
574
838
 
575
- test "date(datetime)" do
576
- f = FunctionResolver.new('date', [{:type => :datetime, :value => EXAMPLE_DATE}])
839
+ test 'date(datetime)' do
840
+ f = FunctionResolver.new('date', [{ type: :datetime, value: EXAMPLE_DATE }])
577
841
  f.validate
578
842
  assert !f.errors?, "Errors #{f.errors.inspect}"
579
843
  value = f.call
@@ -581,39 +845,39 @@ class FunctionResolverTest < Test::Unit::TestCase
581
845
  assert_equal '2013-07-26', value[:value]
582
846
  end
583
847
 
584
- ###
585
- # Delayed functions. These functions don't get run immediately and require
586
- # resolution by the backing system
587
- ###
588
-
589
- test "time(field)" do
590
- f = FunctionResolver.new('time', [{:type => :field, :value => "OriginalEntryTimestamp"}])
848
+ ###
849
+ # Delayed functions. These functions don't get run immediately and require
850
+ # resolution by the backing system
851
+ ###
852
+
853
+ test 'time(field)' do
854
+ f = FunctionResolver.new('time', [{ type: :field, value: 'OriginalEntryTimestamp' }])
591
855
  f.validate
592
856
  assert !f.errors?, "Errors #{f.errors.inspect}"
593
857
  value = f.call
594
858
  assert_equal :function, value[:type]
595
859
  assert_equal 'time', value[:value]
596
- assert_equal "OriginalEntryTimestamp", value[:args].first[:value]
860
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
597
861
  end
598
-
599
- test "date(field)" do
600
- f = FunctionResolver.new('date', [{:type => :field, :value => "OriginalEntryTimestamp"}])
862
+
863
+ test 'date(field)' do
864
+ f = FunctionResolver.new('date', [{ type: :field, value: 'OriginalEntryTimestamp' }])
601
865
  f.validate
602
866
  assert !f.errors?, "Errors #{f.errors.inspect}"
603
867
  value = f.call
604
868
  assert_equal :function, value[:type]
605
869
  assert_equal 'date', value[:value]
606
- assert_equal "OriginalEntryTimestamp", value[:args].first[:value]
870
+ assert_equal 'OriginalEntryTimestamp', value[:args].first[:value]
607
871
  end
608
872
 
609
- test "startswith(), endswith() and contains()" do
610
- [{'startswith' => "^far"},
611
- {'endswith' => "far$"},
612
- {'contains' => "far"}].each do |test_case|
873
+ test 'startswith(), endswith() and contains()' do
874
+ [{ 'startswith' => '^far' },
875
+ { 'endswith' => 'far$' },
876
+ { 'contains' => 'far' }].each do |test_case|
613
877
  function = test_case.keys.first
614
878
  expected_value = test_case[function]
615
879
 
616
- f = FunctionResolver.new(function, [{:type => :character, :value => "far"}])
880
+ f = FunctionResolver.new(function, [{ type: :character, value: 'far' }])
617
881
  f.validate
618
882
  assert !f.errors?, "Errors #{f.errors.inspect}"
619
883
  value = f.call
@@ -626,8 +890,8 @@ class FunctionResolverTest < Test::Unit::TestCase
626
890
 
627
891
  test 'wkt()' do
628
892
  f = FunctionResolver.new('wkt',
629
- [{:type => :character,
630
- :value => "SRID=12345;POLYGON((-127.89734578345 45.234534534,-127.89734578345 45.234534534,-127.89734578345 45.234534534,-127.89734578345 45.234534534))"}])
893
+ [{ type: :character,
894
+ value: 'SRID=12345;POLYGON((-127.89734578345 45.234534534,-127.89734578345 45.234534534,-127.89734578345 45.234534534,-127.89734578345 45.234534534))' }])
631
895
  f.validate
632
896
  assert !f.errors?, "Errors #{f.errors.inspect}"
633
897
  value = f.call
@@ -636,8 +900,8 @@ class FunctionResolverTest < Test::Unit::TestCase
636
900
 
637
901
  test 'wkt() invalid params' do
638
902
  f = FunctionResolver.new('wkt',
639
- [{:type => :character,
640
- :value => "POLYGON((45.234534534))"}])
903
+ [{ type: :character,
904
+ value: 'POLYGON((45.234534534))' }])
641
905
  f.validate
642
906
  f.call
643
907
  assert f.errors?