Class: Sparkql::FunctionResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/sparkql/function_resolver.rb

Overview

Binding class to all supported function calls in the parser. Current support requires that the resolution of function calls to happen on the fly at parsing time at which point a value and value type is required, just as literals would be returned to the expression tokenization level.

Name and argument requirements for the function should match the function declaration in SUPPORTED_FUNCTIONS which will run validation on the function syntax prior to execution.

Constant Summary collapse

SECONDS_IN_DAY =
60 * 60 * 24
STRFTIME_DATE_FORMAT =
'%Y-%m-%d'
STRFTIME_TIME_FORMAT =
'%H:%M:%S.%N'
VALID_REGEX_FLAGS =
["", "i"]
SUPPORTED_FUNCTIONS =
{
  :polygon => {
    :args => [:character],
    :return_type => :shape
  }, 
  :rectangle => {
    :args => [:character],
    :return_type => :shape
  }, 
  :radius => {
    :args => [:character, :decimal],
    :return_type => :shape
  },
  :regex => {
    :args => [:character],
    :opt_args => [{
      :type => :character,
      :default => ''
    }],
    :return_type => :character
  },
  :linestring => {
    :args => [:character],
    :return_type => :shape
  },
  :days => {
    :args => [:integer],
    :return_type => :datetime
  }, 
  :months => {
    :args => [:integer],
    :return_type => :datetime
  },
  :years => {
    :args => [:integer],
    :return_type => :datetime
  },
  :now => { 
    :args => [],
    :return_type => :datetime
  },
  :date => { 
    :args => [[:field,:datetime]],
    :resolve_for_type => true,
    :return_type => :date
  },
  :time => { 
    :args => [[:field,:datetime]],
    :resolve_for_type => true,
    :return_type => :time
  },
  :range => {
    :args => [:character, :character],
    :return_type => :character
  }
}

Instance Method Summary collapse

Constructor Details

#initialize(name, args) ⇒ FunctionResolver

Construct a resolver instance for a function name: function name (String) args: array of literal hashes of the format :value=><escaped_literal_value>.

Empty arry for functions that have no arguments.


77
78
79
80
81
# File 'lib/sparkql/function_resolver.rb', line 77

def initialize(name, args)
  @name = name
  @args = args
  @errors = []
end

Instance Method Details

#callObject

Execute the function



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/sparkql/function_resolver.rb', line 132

def call()
  real_vals = @args.map { |i| i[:value]}
  name = @name.to_sym

  required_args = support[name][:args]
  total_args = required_args + Array(support[name][:opt_args]).collect {|args| args[:default]}
  fill_in_optional_args = total_args.drop(real_vals.length)

  fill_in_optional_args.each do |default|
    real_vals << default
  end
  method = name
  if support[name][:resolve_for_type]
    method_type =  @args.first[:type]
    method = "#{method}_#{method_type}"
  end
  v = self.send(method, *real_vals)

  unless v.nil?
    v[:function_name] = @name
    v[:function_parameters] = real_vals
  end

  v
end

#errorsObject



119
120
121
# File 'lib/sparkql/function_resolver.rb', line 119

def errors
  @errors
end

#errors?Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/sparkql/function_resolver.rb', line 123

def errors?
  @errors.size > 0
end

#return_typeObject



115
116
117
# File 'lib/sparkql/function_resolver.rb', line 115

def return_type
  support[@name.to_sym][:return_type]
end

#supportObject



127
128
129
# File 'lib/sparkql/function_resolver.rb', line 127

def support
  SUPPORTED_FUNCTIONS
end

#validateObject

Validate the function instance prior to calling it. All validation failures will show up in the errors array.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/sparkql/function_resolver.rb', line 85

def validate()
  name = @name.to_sym
  unless support.has_key?(name)
    @errors << Sparkql::ParserError.new(:token => @name, 
      :message => "Unsupported function call '#{@name}' for expression",
      :status => :fatal )
    return
  end

  required_args = support[name][:args]
  total_args = required_args + Array(support[name][:opt_args]).collect {|args| args[:type]}

  if @args.size < required_args.size || @args.size > total_args.size
    @errors << Sparkql::ParserError.new(:token => @name, 
      :message => "Function call '#{@name}' requires #{required_args.size} arguments",
      :status => :fatal )
    return
  end
  
  count = 0
  @args.each do |arg|
    unless Array(total_args[count]).include?(arg[:type])
      @errors << Sparkql::ParserError.new(:token => @name, 
        :message => "Function call '#{@name}' has an invalid argument at #{arg[:value]}",
        :status => :fatal )
    end
    count +=1
  end
end