Class: Proc
- Includes:
- MethodAndProcExtensions
- Defined in:
- lib/abstractivator/proc_ext.rb
Defined Under Namespace
Classes: LooseCallInfo
Constant Summary
Constants included from MethodAndProcExtensions
MethodAndProcExtensions::KEYWORD_PARAMETER_TYPES
Class Method Summary collapse
-
.compose(*procs) ⇒ Object
composes procedures.
-
.identity ⇒ Object
returns the identity function.
-
.loose_call(x, args, kws = {}, &block) ⇒ Object
Tries to coerce x into a procedure, then calls it with the given argument list.
- .loosen_varargs!(args) ⇒ Object
-
.pipe(*procs) ⇒ Object
composes procedures in reverse order.
-
.pipe_value(value, *procs) ⇒ Object
makes a pipeline transform as with Proc::pipe and applies it to the given value.
Instance Method Summary collapse
-
#compose(other) ⇒ Object
composes this procedure with another procedure f.compose(g) ==> proc { |x| f.call(g.call(x)) }.
- #proxy_call(*args, **kws, &block) ⇒ Object
-
#reverse_args ⇒ Object
returns a version of the procedure with the argument list reversed.
Methods included from MethodAndProcExtensions
#accepts_keywords, #loosen_args
Class Method Details
.compose(*procs) ⇒ Object
composes procedures. compose(f, g, h) returns the procedure proc { |x| f.call(g.call(h.call(x))) }
31 32 33 |
# File 'lib/abstractivator/proc_ext.rb', line 31 def self.compose(*procs) procs.map(&:to_proc).inject_right(identity) { |inner, p| p.compose(inner) } end |
.identity ⇒ Object
returns the identity function
50 51 52 |
# File 'lib/abstractivator/proc_ext.rb', line 50 def self.identity proc {|x| x} end |
.loose_call(x, args, kws = {}, &block) ⇒ Object
Tries to coerce x into a procedure, then calls it with the given argument list. If x cannot be coerced into a procedure, returns x. This method is optimized for use cases typically found in tight loops, namely where x is either a symbol or a keyword-less fixed-arity proc. It attempts to minimize the number of intermediate arrays created for these cases (as would be produced by calls to #map, #select, #take, #pad_right, etc.) CPU overhead created by loose_call is bad, but unexpected memory consumption would be worse, considering Proc#call has zero memory footprint. These optimizations produce a ~5x speedup, which is still 2-4x slower than regular Proc#call.
84 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 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/abstractivator/proc_ext.rb', line 84 def self.loose_call(x, args, kws={}, &block) return x.to_proc.call(*args) if x.is_a?(Symbol) # optimization for a typical use case x = x.to_proc if x.respond_to?(:to_proc) return x unless x.callable? # cache proc info for performance info = x.instance_variable_get(:@loose_call_info) unless info params = x.parameters info = LooseCallInfo.new info.params = params info.req_arity = params.count { |p| p.first == :req } info.total_arity = info.req_arity + params.count { |p| p.first == :opt } info.accepts_arg_splat = params.any? { |p| p.first == :rest } accepts_kw_splat = params.any? { |p| p.first == :keyrest } has_kw_args = params.any? { |(type, name)| (type == :key || type == :keyreq) && !name.nil? } info.requires_kw_customization = (has_kw_args || kws.any?) && !accepts_kw_splat if info.requires_kw_customization opt_key_names = info.params.select { |(type, name)| type == :key && !name.nil? }.map(&:value) req_key_names = info.params.select { |(type, name)| type == :keyreq && !name.nil? }.map(&:value) info.all_key_names = opt_key_names + req_key_names info.kw_padding = req_key_names.hash_map { nil } end x.instance_variable_set(:@loose_call_info, info) end # customize args unless info.accepts_arg_splat args = args.take(info.total_arity) if args.size > info.total_arity args = args.pad_right(info.req_arity) if args.size < info.req_arity end # customize keywords if info.requires_kw_customization kws = info.kw_padding.merge(kws.select { |k| info.all_key_names.include?(k) }) end if kws.any? x.call(*args, **kws, &block) else x.call(*args, &block) end end |
.loosen_varargs!(args) ⇒ Object
128 129 130 131 132 133 134 135 |
# File 'lib/abstractivator/proc_ext.rb', line 128 def self.loosen_varargs!(args) if args.size == 1 && args.first.is_a?(Array) real_args = args.first args.clear args.concat(real_args) nil end end |
Instance Method Details
#compose(other) ⇒ Object
composes this procedure with another procedure f.compose(g) ==> proc { |x| f.call(g.call(x)) }
24 25 26 |
# File 'lib/abstractivator/proc_ext.rb', line 24 def compose(other) proc{|x| self.call(other.call(x))} end |
#proxy_call(*args, **kws, &block) ⇒ Object
61 62 63 64 65 66 67 68 69 |
# File 'lib/abstractivator/proc_ext.rb', line 61 def proxy_call(*args, **kws, &block) if accepts_keywords call(*args, **kws, &block) elsif kws.any? call(*(args + [kws]), &block) else call(*args, &block) end end |
#reverse_args ⇒ Object
returns a version of the procedure with the argument list reversed
55 56 57 58 59 |
# File 'lib/abstractivator/proc_ext.rb', line 55 def reverse_args proc do |*args, &block| self.call(*args.reverse, &block) end end |