lib/delorean/base.rb in delorean_lang-0.3.24 vs lib/delorean/base.rb in delorean_lang-0.3.25
- old
+ new
@@ -2,25 +2,26 @@
require 'active_record'
require 'bigdecimal'
module Delorean
- # FIXME: add string.gsub; Also, should be able to index regex
- # matches. Need string.length.
+ DT_TYPES = [Date, Time, ActiveSupport::TimeWithZone]
+ NUM_OR_STR = [Numeric, String]
+ NUM_OR_NIL = [nil, Fixnum]
# FIXME: the whitelist is quite hacky. It's currently difficult to
# override it. A user will likely want to directly modify this
# hash. The whole whitelist mechanism should be eventually
# rethought.
RUBY_WHITELIST = {
attributes: [ActiveRecord::Base],
- between?: [[Numeric, String],[Numeric, String],[Numeric, String]],
+ between?: [NUM_OR_STR, NUM_OR_STR, NUM_OR_STR],
between: "between?",
compact: [Array],
to_set: [Array],
- flatten: [Array, [Fixnum, nil]],
- length: [Enumerable],
+ flatten: [Array, NUM_OR_NIL],
+ length: [[String, Enumerable]],
max: [Array],
member: "member?",
member?: [Enumerable, [Object]],
empty: "empty?",
empty?: [Enumerable],
@@ -31,55 +32,46 @@
split: [String, String],
uniq: [Array],
sum: [Array],
transpose: [Array],
join: [Array, String],
- zip: [Array, [Array, Array, Array]],
+ zip: [Array, Array, [Array, nil], [Array, nil]],
index: [Array, [Object]],
product: [Array, Array],
- first: [[ActiveRecord::Relation, Enumerable], [nil, Fixnum]],
- last: [[ActiveRecord::Relation, Enumerable], [nil, Fixnum]],
+ first: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
+ last: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
intersection: [Set, Enumerable],
union: [Set, Enumerable],
keys: [Hash],
values: [Hash],
upcase: [String],
downcase: [String],
- match: [String, [String], [nil, Fixnum]],
+ match: [String, [String], NUM_OR_NIL],
- iso8601: [[Date, Time, ActiveSupport::TimeWithZone]],
- hour: [[Date, Time, ActiveSupport::TimeWithZone]],
- min: [[Date, Time, ActiveSupport::TimeWithZone, Array]],
- sec: [[Date, Time, ActiveSupport::TimeWithZone]],
- to_date: [[Date, Time, ActiveSupport::TimeWithZone, String]],
+ iso8601: [DT_TYPES],
+ hour: [DT_TYPES],
+ min: [DT_TYPES+[Array]],
+ sec: [DT_TYPES],
+ to_date: [DT_TYPES+[String]],
- month: [[Date, Time, ActiveSupport::TimeWithZone]],
- day: [[Date, Time, ActiveSupport::TimeWithZone]],
- year: [[Date, Time, ActiveSupport::TimeWithZone]],
+ month: [DT_TYPES],
+ day: [DT_TYPES],
+ year: [DT_TYPES],
- next_month: [[Date, Time, ActiveSupport::TimeWithZone],
- [nil, Fixnum],
- ],
- prev_month: [[Date, Time, ActiveSupport::TimeWithZone],
- [nil, Fixnum],
- ],
+ next_month: [DT_TYPES, NUM_OR_NIL],
+ prev_month: [DT_TYPES, NUM_OR_NIL],
- beginning_of_month: [[Date, Time, ActiveSupport::TimeWithZone]],
+ beginning_of_month: [DT_TYPES],
+ end_of_month: [DT_TYPES],
- end_of_month: [[Date, Time, ActiveSupport::TimeWithZone]],
+ next_day: [DT_TYPES, NUM_OR_NIL],
+ prev_day: [DT_TYPES, NUM_OR_NIL],
- next_day: [[Date, Time, ActiveSupport::TimeWithZone],
- [nil, Fixnum],
- ],
- prev_day: [[Date, Time, ActiveSupport::TimeWithZone],
- [nil, Fixnum],
- ],
-
- to_i: [[Numeric, String]],
- to_f: [[Numeric, String]],
- to_d: [[Numeric, String]],
+ to_i: [NUM_OR_STR],
+ to_f: [NUM_OR_STR],
+ to_d: [NUM_OR_STR],
to_s: [Object],
to_a: [Object],
to_json: [Object],
abs: [Numeric],
round: [Numeric, [nil, Integer]],
@@ -95,19 +87,18 @@
# This whole thing needs to be redone.
engine.evaluate(node, attr, params.clone)
end
def /(args)
- raise "non-array/string arg to /" unless
- args.is_a?(Array) || args.is_a?(String)
-
begin
case args
when Array
engine.eval_to_hash(node, args, params.clone)
when String
engine.evaluate(node, args, params.clone)
+ else
+ raise "non-array/string arg to /"
end
rescue => exc
Delorean::Engine.grok_runtime_exception(exc)
end
end
@@ -132,34 +123,29 @@
end
end
class BaseClass
def self._get_attr(obj, attr, _e)
- # FIXME: even Javascript which is superpermissive raises an
- # exception on null getattr.
- return nil if obj.nil?
-
# NOTE: should keep this function consistent with _index
-
- if obj.kind_of? ActiveRecord::Base
- klass = obj.class
-
- return obj.read_attribute(attr) if
- klass.attribute_names.member? attr
-
- return obj.send(attr.to_sym) if
- klass.reflect_on_all_associations.map(&:name).member? attr.to_sym
- elsif obj.instance_of?(NodeCall)
+ case obj
+ when nil
+ # FIXME: even Javascript which is superpermissive raises an
+ # exception on null getattr.
+ return nil
+ when ActiveRecord::Base
+ return obj.read_attribute(attr) if obj.has_attribute?(attr)
+ return obj.send(attr.to_sym) if obj.class.reflections[attr]
+ when NodeCall
return obj.evaluate(attr)
- elsif obj.instance_of?(Hash)
+ when Hash
# FIXME: this implementation doesn't handle something like
# {}.length. i.e. length is a whitelisted function, but not
# an attr. This implementation returns nil instead of 0.
return obj[attr] if obj.member?(attr)
return attr.is_a?(String) ? obj[attr.to_sym] : nil
- elsif obj.instance_of?(Class) && (obj < BaseClass)
- return obj.send((attr + POST).to_sym, _e)
+ when Class
+ return obj.send((attr + POST).to_sym, _e) if obj < BaseClass
end
begin
return _instance_call(obj, attr, [], _e)
rescue => exc
@@ -169,22 +155,23 @@
end
######################################################################
def self._index(obj, args, _e)
- return nil if obj.nil?
-
# NOTE: should keep this function consistent with _get_attr
-
- if obj.instance_of?(Hash) || obj.kind_of?(ActiveRecord::Base) ||
- obj.instance_of?(NodeCall) || obj.instance_of?(Class)
+ case obj
+ when nil
+ # FIXME: even Javascript which is superpermissive raises an
+ # exception on null getattr.
+ return nil
+ when Hash, ActiveRecord::Base, NodeCall, Class
raise InvalidIndex unless args.length == 1
_get_attr(obj, args[0], _e)
- elsif obj.instance_of?(Array) || obj.instance_of?(String)
- raise InvalidIndex unless args.length <= 2
- raise InvalidIndex unless
- args[0].is_a?(Fixnum) && (!args[1] || args[1].is_a?(Fixnum))
+ when Array, String, MatchData
+ raise InvalidIndex unless args.length <= 2 &&
+ args[0].is_a?(Fixnum) &&
+ (args[1].nil? || args[1].is_a?(Fixnum))
obj[*args]
else
raise InvalidIndex
end
end
@@ -232,37 +219,31 @@
if obj.is_a?(Class)
_e[:_engine].parse_check_call_fn(method, args.count, obj)
return obj.send(msg, *args)
end
- if obj.class.include?(Delorean::Model)
- sig = obj.class.delorean_instance_methods[msg]
- end
+ cls = obj.class
+ sig = (cls < Delorean::Model && cls.delorean_instance_methods[msg]) ||
+ RUBY_WHITELIST[msg]
- sig = RUBY_WHITELIST[msg] unless sig
-
raise "no such method #{method}" unless sig
# if sig is a string, then method mapped to another name
return _instance_call(obj, sig, args, _e) if sig.is_a? String
raise "too many args to #{method}" if args.length>(sig.length-1)
arglist = [obj] + args
- sig.each_with_index { |s, i|
+ sig.each_with_index do |s, i|
s = [s] unless s.is_a?(Array)
- ok, ai = false, arglist[i]
+ ai = arglist[i]
- s.each { |sc|
- if (sc.nil? && i>=arglist.length) || (sc && ai.class <= sc)
- ok = true
- break
- end
- }
- raise "bad arg #{i}, method #{method}: #{ai}/#{ai.class} #{s}" if !ok
- }
+ raise "bad arg #{i}, method #{method}: #{ai}/#{ai.class} #{s}" unless
+ (s.member?(nil) && i>=arglist.length) ||
+ s.detect {|sc| sc && ai.class <= sc}
+ end
res = obj.send(msg, *args)
# FIXME: can't freeze AR relations since then we can't chain
# calls (e.g. the chaining modifies the relation object. Not
# sure what this side-effect means. Delorean code which