Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
11807 lines (9455 sloc) 308 KB
###
RubyJS Alpha 0.8.0-beta1
Copyright (c) 2012 Sebastian Burkhard
All rights reserved.
http://www.rubyjs.org/LICENSE.txt
###
root = global ? window
# TODO: link rubyjs/_r directly to RubyJS.RubyJS.prototype.box
# this is a suboptimal solution as of now.
root.RubyJS = (obj, recursive, block) ->
RubyJS.box(obj, recursive, block)
RubyJS.VERSION = '0.8.0-beta1'
# noConflict mode for R
previousR = root.R if root.R?
RubyJS.noConflict = ->
root.R = previousR
RubyJS
# Alias to RubyJS
root.R = RubyJS
RubyJS.extend = (obj, mixin) ->
obj[name] = method for name, method of mixin
obj
RubyJS.include = (mixin, replace = false) ->
for name, method of mixin.prototype
if replace
@prototype[name] = method
else
@prototype[name] = method unless @prototype[name]
mixin
if typeof(exports) != 'undefined'
exports.R = R
exports.RubyJS = RubyJS
########################################
# Private methods of RubyJS base object
########################################
# helper method to get an arguments object
#
# @return [Arguments]
# @private
#
RubyJS.argify = -> arguments
# Creates a wrapper method that calls a functional style
# method with this as the first arguments. Tries to avoid apply.
#
# callFunctionWithThis(_s.ljust)
# // creates a function similar to this:
# // function (len, pad) {
# // return _s.ljust(this, len, pad)
# // }
#
# This can be used to extend native classes/prototypes with functional
# methods.
#
# String.prototype.capitalize = callFunctionWithThis(_s.capitalize)
# "foo".capitalize() // => "Foo"
#
# @private
#
RubyJS.callFunctionWithThis = callFunctionWithThis = (func) ->
() ->
a = arguments
switch arguments.length
when 0 then func(this)
when 1 then func(this, a[0])
when 2 then func(this, a[0], a[1])
when 3 then func(this, a[0], a[1], a[2])
when 4 then func(this, a[0], a[1], a[2], a[3])
when 5 then func(this, a[0], a[1], a[2], a[3], a[4])
when 6 then func(this, a[0], a[1], a[2], a[3], a[4], a[5])
# Slow fallback when passed more than 6 arguments.
else func.apply(null, [this].concat(nativeSlice.call(arguments, 0)))
# RubyJS specific helper methods
# @private
RubyJS.ensure_args_length = __ensure_args_length = (args, length) ->
throw R.ArgumentError.new() unless args.length is length
# Finds, removes and returns the last block/function in arguments list.
# This is a destructive method.
#
# @example Use like this
# foo = (args...) ->
# console.log( args.length ) # => 2
# block = __extract_block(args)
# console.log( args.length ) # => 1
# other = args[0]
#
# @private
#
RubyJS.extract_block = __extract_block = (args) ->
idx = args.length
while --idx >= 0
return args.pop() if args[idx]?.call?
null
# Native classes, to avoid naming conflicts inside RubyJS classes.
nativeArray = Array
nativeNumber = Number
nativeObject = Object
nativeRegExp = RegExp
nativeString = String
ObjProto = Object.prototype
StrProto = String.prototype
ArrProto = Array.prototype
nativeToString = ObjProto.toString
nativeStrSlice = StrProto.slice
nativeStrMatch = StrProto.match
nativeJoin = ArrProto.join
nativeSort = ArrProto.sort
nativeSlice = ArrProto.slice
nativeUnshift = ArrProto.unshift
nativePush = ArrProto.push
# TODO: create BlockNone class that coerces multiple yield arguments into array.
# @abstract
class Block
# Block.create returns a different implementation of Block (BlockMulti,
# BlockSingle) depending on the arity of block.
#
# If no block is given returns a BlockArgs callback, that returns
# a single block argument.
#
@create: (block, thisArg) ->
if block?.call? #block_given
if block.length != 1
new BlockMulti(block, thisArg)
else
new BlockSingle(block, thisArg)
else
new BlockArgs(block, thisArg)
# handles block argument splatting
# reverse_each([[1,2]], (a,b) -> )
# if block has multiple arguments, returns a wrapper
# function that applies arguments to block instead of passing.
# Otherwise it returns the block itself.
@splat_arguments: (block) ->
if block.length > 1
(item) ->
if typeof item is 'object' && __isArr(item)
block.apply(null, item)
else
block(item)
else
block
# @private
class BlockArgs
constructor: (@block, @thisArg) ->
invoke: (args) ->
RCoerce.single_block_args(args, @block)
# @private
class BlockMulti
constructor: (@block, @thisArg) ->
args: (args) ->
if args.length > 1 then nativeSlice.call(args) else args[0]
# @param args array or arguments
invoke: (args) ->
if args.length > 1
@block.apply(@thisArg, args)
else
arg = args[0]
if typeof arg is 'object' && __isArr(arg)
@block.apply(@thisArg, arg)
else
@block.call(@thisArg, arg)
invokeSplat: ->
@block.apply(@thisArg, arguments)
# for blocks with arity 1
# @private
class BlockSingle
constructor: (@block, @thisArg) ->
args: (args) ->
args[0]
invoke: (args) ->
@block.call(@thisArg, args[0])
invokeSplat: ->
@block.apply(@thisArg, arguments)
R.Block = Block
R.blockify = __blockify = Block.create
# Breaker is a class for adding support to breaking out of functions
# that act like loops. Because we mimick ruby block/procs/lambdas by passing
# functions, so neither break nor return would work in JS.
#
# @see RubyJS.Kernel#catch_break
#
# @example Breaking loops
# sum = R('')
# R.catch_break( breaker ) -> # breaker is a new Breaker instance
# R('a').upto('f') (chr) ->
# breaker.break() if chr.equals('d')
# sum.append(chr)
# # => 'abc'
#
# @example Breaking out and return a value
# R.catch_break( breaker ) -> # breaker is a new Breaker instance
# R(1).upto(100) (i) ->
# breaker.break('foo') if i.equals(81)
# # => 'foo'
#
#
#
class RubyJS.Breaker
constructor: (@return_value = null) ->
# Breaks out of the loop by throwing itself. Accepts a return value.
#
# @example Breaking out and return a value
# R.catch_break( breaker )
# breaker.break('foo')
# # => 'foo'
#
# @param value Return value
#
break: (return_value) ->
@return_value = return_value
throw this
handle_break: (e) ->
if this is e
return (e.return_value)
else
throw e
# methods are included in RubyJS classes. Most notably the Base object R.
class RubyJS.Kernel
# A method to easily check whether an object is a RubyJS object with CoffeeScript.
#
# foo.rubyjs?
#
rubyjs: -> true
# TODO: find a better name for box.
# TODO: handle the case when calling R(true, -> ), R({}, -> )
box: (obj, recursive, block) ->
# R(null) should simply return null. At a later point maybe an
# instance of NilClass
return obj unless obj?
# typeof with JS primitive is very fast. Handle primitives first
# for performance reasons.
if typeof obj is 'object'
# Is obj already a RubyJS object? Check if rubyjs method exists.
# has to be after typeof, checking for a member/method on a primitive
# will "convert it to an object", what makes it slow.
return obj if obj.rubyjs?
_v = obj.valueOf()
if typeof _v isnt 'object'
# take care of [object String] and [object Number]
obj = _v
else
if R.Array.isNativeArray(obj)
# Small performance improvement. which probably should be somewhere else.
object_type = '[object Array]'
else
object_type = nativeToString.call(obj)
# check primitives first
if typeof obj is 'number'
# Numeric.typecast returns a float or fixnum.
obj = RubyJS.Numeric.typecast(obj)
else if typeof obj is 'string'
obj = new R.String(obj)
# To stay lean we do not wrap booleans for now:
# else if primitive_type is 'boolean'
# Array and Regexp should be the first to be checked. String and
# Numbers are in most cases already taken care for, but have to be checked
# again in case a primitive wrappers are passed.
else if object_type is '[object Array]'
obj = new R.Array(obj, recursive is true)
else if object_type is '[object RegExp]'
obj = R.Regexp.try_convert(obj)
# TODO: if at this point obj is not rubyjs, raise error.
# Handles the case R(1, -> )
if typeof recursive is 'function'
block = recursive
recursive = false
if typeof block is 'function'
# Call the block with obj as receiver, so that the block has context
# of the just converted obj. R('a', -> @capitalize()).
obj = block.call(obj)
if obj is null or obj is undefined
# specifically convert undefined to null
obj = null
else if obj.to_native?
obj = obj.to_native(true)
# else it is a native object or primitive, and should be left alone.
return obj
# Equivalent to %w[] in Ruby
#
# Creates an R.Array of R.String for every word separated by space.
#
# @example:
# R.w('foo bar baz') # => ['foo', 'bar', 'baz']
# R.w('foo\nbar') # => ['foo\nbar']
# R.w('') # => ['']
#
w: (str) ->
new R.String(str).split(/\s+/)
# Shortcut for creating a R.Range.
#
# @example
# R.r(0,4) # => (0..4)
# R.r(0,4, true) # => (0...4)
#
# @alias #rng
#
r: (a,b,excluding) ->
if excluding is true # note: true not truethy
R.Range.new(a,b, true)
else
R.Range.new(a,b)
# Shortcut for creating floats
f: (flt) ->
new R.Float(flt)
# Shortcut for creating Ranges
rng: @prototype.r
catch_break: (block, context = this) ->
breaker = new R.Breaker()
try
return block.call(context, breaker)
catch e
return breaker.handle_break(e)
$Array: (obj, recursive = false) ->
if recursive is true
R.Array.new( @box(e) for e in obj )
else
R.Array.new(obj)
arr_r: (obj) ->
new RArray(R(e) for e in obj)
# TODO: Remove from code
$Array_r: (obj) ->
@$Array(obj, true)
$Float: (obj) ->
obj = @box(obj)
throw R.TypeError.new() if obj == null
throw R.TypeError.new() unless obj.to_f?
if obj.is_float?
obj
else if obj.is_string?
stripped = obj.strip()
if stripped.valid_float()
new R.Float(+stripped.to_native().replace(/_/g, ''))
else
throw R.ArgumentError.new()
else if obj.rubyjs?
new R.Float(obj.to_native())
else # is not a R object
new R.Float(obj)
$Integer: (obj) ->
obj = R(obj)
throw R.TypeError.new() unless obj?
# throw R.TypeError.new() unless obj.to_i?
if obj.is_integer?
obj
else if obj.is_string?
stripped = obj.strip()
if stripped.valid_float()
new R.Fixnum(Math.floor(+stripped.to_native().replace(/_/g, '')))
else
throw R.ArgumentError.new()
else if obj.rubyjs?
# throw R.TypeError.new() unless obj.to_i?
new R.Fixnum(Math.floor(obj.to_native()))
else # is not a R object
new R.Fixnum(Math.floor(obj))
$Integer: @prototype.$Integer
$String: (obj) -> R.String.try_convert(obj) or throw(R.TypeError.new())
$Range: (start,end,exclusive) ->
R.Range.new(start,end,exclusive)
puts: (obj) ->
console.log(obj.valueOf())
rand: (limit) ->
r = R(Math.random())
if limit then r.multiply(limit).to_i() else r
__enumerate = (func, args) ->
ary = []
args.push () ->
if arguments.length > 1
ary.push(nativeSlice.call(arguments, 0))
else
ary.push(arguments[0])
func.apply(null, args)
ary
__random = (limit) ->
Math.floor( Math.random() * (limit + 1) )
__rand = RubyJS.Kernel.prototype.rand
########################################
# Public methods of RubyJS base object
########################################
# adds all methods to the global R object
for own name, method of RubyJS.Kernel.prototype
RubyJS[name] = method
#
RubyJS['$~'] = null
#
RubyJS['$,'] = null
#
RubyJS['$;'] = "\n"
#
RubyJS['$/'] = "\n"
RubyJS.inspect = (obj) ->
if obj is null or obj is 'undefined'
'null'
else if obj.inspect?
obj.inspect()
else if _a.isArray(obj)
"[#{obj}]"
else
obj
# Adds useful methods to the global namespace.
#
# e.g. _proc, _puts, _truthy, _inspect, _falsey
#
RubyJS.pollute_global_with_kernel = (prefix = "_") ->
args = [
'w', 'fn', 'proc', 'puts', 'truthy', 'falsey', 'inspect'
]
for name in args
root[prefix + name] = R[name]
null
# Adds the _a, _n, etc shortcuts to the global namespace.
#
RubyJS.pollute_global_with_shortcuts = (prefix = "_") ->
shortcuts =
_arr: 'a'
_num: 'n'
_str: 's'
_itr: 'i'
_enum: 'e'
_hsh: 'h'
_time: 't'
for k,v of shortcuts
R[prefix + v] = R[k]
root[prefix + v] = R[k]
null
# Adds RubyJS methods to JS native classes.
#
# RubyJS.i_am_feeling_evil()
# ['foo', 'bar'].rb_map(proc('rb_reverse')).rb_sort()
# # =>['oof', 'rab']
#
RubyJS.god_mode = (prefix = 'rb_') ->
overwrites = [
[Array.prototype, _arr],
[Number.prototype, _num],
[String.prototype, _str],
[Date.prototype, _time]
]
for [proto, methods] in overwrites
for name, func of methods
new_name = prefix + name
if typeof func == 'function'
if proto[new_name] is undefined
do (new_name, func) ->
# The following is 100x faster than slicing.
proto[new_name] = callFunctionWithThis(func)
else if prefix == '' && proto['rb_'+new_name]
console.log("#{proto}.#{new_name} exists. skipped.")
true
# proc() is the equivalent to symbol to proc functionality of Ruby.
#
# proc accepts additional arguments which are passed to the block.
#
# @note proc() calls methods and not properties
#
# @example
#
# R.w('foo bar').map( R.proc('capitalize') )
# R.w('foo bar').map( R.proc('ljust', 10) )
#
RubyJS.proc = (key) ->
if arguments.length == 1
# Wrapper block doesnt need to mangle arguments
(el) ->
fn = el[key]
if typeof fn is 'function'
fn.call(el)
else if fn is undefined
# RELOADED: dont use R()
R(el)[key]().valueOf()
else
fn
else
args = nativeSlice.call(arguments, 1)
# Wrapper block that mangles arguments
(el) ->
fn = el[key]
if typeof fn is 'function'
el[key].apply(el, args)
else
# no method found, now check if it exists in rubyjs equivalent
el = R(el)
el[key].apply(el, args).valueOf()
RubyJS.fn = (func) ->
(el) ->
arguments[0] = el
func.apply(null, arguments)
# Check wether an obj is falsey according to Ruby
#
# RubyJS.falsey(null) // => true
# RubyJS.falsey(false) // => true
# RubyJS.falsey(undefined) // => true
# RubyJS.falsey(0) // => false
# RubyJS.falsey("0") // => false
# RubyJS.falsey(-1) // => false
#
RubyJS.falsey = (obj) ->
obj is false or obj is null or obj is undefined
# Check wether an obj is truthy according to Ruby
#
# RubyJS.truthy(null) // => false
# RubyJS.truthy(false) // => false
# RubyJS.truthy(undefined) // => false
# RubyJS.truthy(0) // => true
# RubyJS.truthy("0") // => true
# RubyJS.truthy(-1) // => true
#
RubyJS.truthy = (obj) ->
!__falsey(obj)
RubyJS.respond_to = (obj, function_name) ->
obj[function_name] != undefined
# Compares to objects.
#
# // => true
# R.is_equal(1,1)
# R.is_equal(1, new Number(1))
# R.is_equal(1, {valueOf: function () {return 1;}})
# R.is_equal(1, {equals: function (n) {return n === 1;}})
#
RubyJS.is_equal = (a, b) ->
return true if a is b
if typeof a is 'object'
if a.equals?
a.equals(b)
else if __isArr(a)
_arr.equals(a,b)
else if a.valueOf?
a.valueOf() is b.valueOf()
else
false
else if typeof b is 'object'
if b.equals?
b.equals(a)
else if __isArr(b)
_arr.equals(a,b)
else if b.valueOf?
b.valueOf() is a.valueOf()
else
false
else
# for elements that are literals
a is b
RubyJS.is_eql = (a, b) ->
if typeof a is 'object'
a.eql(b)
else if typeof b is 'object'
b.eql(a)
else
a is b
__falsey = R.falsey
__truthy = R.truthy
__equals = R.is_equal
errors = [
'ArgumentError'
'RegexpError'
'TypeError'
'KeyError'
'IndexError'
'FloatDomainError'
'RangeError'
'StandardError'
'ZeroDivisionError'
'NotSupportedError'
'NotImplementedError'
]
for error in errors
do (error) ->
errorClass = class extends Error
errorClass.new = -> new RubyJS[error](error)
RubyJS[error] = this["R"+error] = errorClass
# Singleton class for type coercion inside RubyJS.
#
# @private
RubyJS.coerce = _coerce =
native: (obj) ->
if typeof obj != 'object'
obj
else
obj.valueOf()
str: (obj) ->
_err.throw_type() if obj == null
obj = obj.valueOf() if typeof obj is 'object'
# throw new R.TypeError("#{obj} is not a valid string") unless typeof obj is 'string'
_err.throw_type() unless typeof obj is 'string'
obj
try_str: (obj) ->
return obj if typeof obj is 'string'
obj = obj.valueOf() if obj isnt null
return obj if typeof obj is 'string'
null
num: (obj) ->
_err.throw_type() if obj == null
obj = obj.valueOf() if typeof obj is 'object'
# throw new R.TypeError("#{obj} is not a valid num") unless typeof obj is 'number'
_err.throw_type() unless typeof obj is 'number'
obj
int: (obj) ->
_err.throw_type() if obj == null
obj = obj.valueOf() if typeof obj is 'object'
# throw new R.TypeError("#{obj} is not a valid int") unless typeof obj is 'number'
_err.throw_type() unless typeof obj is 'number'
Math.floor(obj)
isArray: nativeArray.isArray or (obj) ->
nativeToString.call(obj) is '[object Array]'
is_arr: (obj) ->
typeof obj is 'object' && obj != null && _coerce.isArray(obj.valueOf())
is_str: (obj) ->
return true if typeof obj is 'string'
typeof obj is 'object' && obj != null && typeof obj.valueOf() is 'string'
is_rgx: (obj) ->
return false unless obj?
nativeToString.call(obj.valueOf()) is '[object RegExp]'
arr: (obj) ->
_err.throw_type() if obj == null
_err.throw_type() if typeof obj != 'object'
obj = obj.valueOf()
_err.throw_type() unless _coerce.isArray(obj)
obj
split_args: (args, offset) ->
arg_len = args.length
ary = []
idx = offset
while idx < arg_len
el = args[idx]
ary.push(el) unless el is undefined
idx += 1
ary
# Use call_with when you want to delegate a method call to a functional one
# when the original method has flexible length of arguments.
#
# @example
# class RString
# # new RString("foo").count('o')
# # new RString("foo").count('o', 'of')
# count: () ->
# __call( _str.count, @__native__, arguments)
#
call_with: (func, thisArg, args) ->
a = args
switch args.length
when 0 then func(thisArg)
when 1 then func(thisArg, a[0])
when 2 then func(thisArg, a[0], a[1])
when 3 then func(thisArg, a[0], a[1], a[2])
when 4 then func(thisArg, a[0], a[1], a[2], a[3])
when 5 then func(thisArg, a[0], a[1], a[2], a[3], a[4])
when 6 then func(thisArg, a[0], a[1], a[2], a[3], a[4], a[5])
# Slow fallback when passed more than 6 arguments.
else func.apply(null, [thisArg].concat(nativeSlice.call(args, 0)))
cmp: (a, b) ->
if typeof a isnt 'object' and typeof a is typeof b
if a is b
0
else
if a < b then -1 else 1
else
if __isArr(a)
_arr.cmp(a, b)
else
a = R(a)
throw 'NoMethodError' unless a.cmp?
a.cmp(b)
cmpstrict: (a, b) ->
if typeof a is typeof b and typeof a isnt 'object'
if a is b
0
else
if a < b then -1 else 1
else
a = R(a)
throw 'NoMethodError' unless a.cmp?
cmp = a.cmp(b)
_err.throw_argument() if cmp is null
cmp
__str = _coerce.str
__int = _coerce.int
__num = _coerce.num
__arr = _coerce.arr
__isArr = _coerce.is_arr
__isStr = _coerce.is_str
__isRgx = _coerce.is_rgx
__call = _coerce.call_with
__cmp = _coerce.cmp
__cmpstrict = _coerce.cmpstrict
__try_str = _coerce.try_str
_err =
throw_argument: (msg) ->
throw RArgumentError.new(msg)
throw_type: (msg) ->
throw RTypeError.new(msg)
throw_index: (msg) ->
throw RIndexError.new(msg)
throw_not_implemented: (msg) ->
throw RNotImplementedError.new(msg)
throw_key: (msg) ->
throw RKeyError.new(msg)
class NumericMethods
cmp: (num, other) ->
if num is other then 0 else null
# Returns true if num is NaN.
#
# @example
# _n.nan(2) // => false
# _n.nan('test') // => true
# _n.nan(true) // => false
# _n.nan(NaN) // => true
#
# @return [Boolean]
#
nan: (num) ->
isNaN(num)
# Returns the absolute value of num.
#
# @example
# _n.abs(12) // => 12
# _n.abs(-34.56) // => 34.56
#
# @return [Number]
#
abs: (num) ->
if num < 0 then (- num) else num
# Returns square of num.
#
# @example
# _n.abs2(2) // => 4
# _n.abs2(-4) // => 16
#
# @return [Number]
#
abs2: (num) ->
return num if _num.nan(num)
Math.pow(num, 2)
# Returns the smallest Integer greater than or equal to num. Class Numeric achieves this by converting itself to
# a Float then invoking Float#ceil.
#
# @example
# _n.ceil(1) // => 1
# _n.ceil(1.2) // => 2
# _n.ceil(-1.2) // => -1
# _n.ceil(-1) // => -1
#
# @return [Number]
#
ceil: (num) ->
Math.ceil(num)
# Returns an array with quotient and modulus as a result of division num by other.
#
# @example
# _n.divmod(8, 4) // => [2, 0]
# _n.divmod(13, 4) // => [3, 1]
# _n.divmod(-8.5, -4) // => [2, -0.5]
#
# @return [Array]
#
divmod: (num, other) ->
quotient = Math.floor(num / other)
modulus = num % other
[quotient, modulus]
# Returns array from num to stop (inclusive) when passed no block.
# When passed a block, iterates block by passing decreasing values from num to stop (inclusive).
#
# @example
# var print = function(i) { console.log(i);}
#
# _n.downto(3, 1, print) // => 3\n 2\n 1\n 3
# _n.downto(3, 1) // => [3, 2, 1]
#
# @return [Array] or Number
#
downto: (num, stop, block) ->
return __enumerate(_num.downto, [num, stop]) unless block?.call?
stop = Math.ceil(stop)
idx = num
while idx >= stop
block( idx )
idx -= 1
num
# Returns true if num and other are the same type (or can be converted to the same type) and have equal values.
#
# @example
# _n.eql(1, 1.0) // => true
# _n.eql(2, 1) // => false
# _n.eql(3.5, 2) // => false
#
# @return [Boolean]
#
eql: (num, other) ->
num == other
# Returns the largest integer less than or equal to num.
#
# @example
# _n.floor(1.5) // => 1
# _n.floor(-1) // => -1
# _n.floor(-2.5) // => -3
#
# @return [Number]
#
floor: (num) ->
Math.floor(num)
# Returns num if num is not zero, null otherwise. This behavior is useful
# when chaining comparisons:
#
# @example
# _n.nonzero(1) // => 1
# _n.nonzero(0) // => null
#
# @return [Number] or null
#
nonzero: (num) ->
if num is 0 then null else num
# remainder: (other) ->
# other = @box(other)
# mod = @['%'](other)
# if !mod.equals(0) and ((@lt(0) && other.gt(0)) or (@gt(0) && other['lt'](0)))
# mod.minus(other)
# else
# mod
# When passed a block, invokes it with the sequence of numbers
# from num to limit that are incremented/decremented by step.
# Default step value is 1.
# If step is negative then the sequence of numbers from num to limit
# will be decremented by step.
# When no block is given, an enumerator is returned instead.
#
# @example
# _n.step(5, 3, -1) // => [5, 4, 3]
# _n.step(3, 5, 1) // => [3, 4, 5]
# _n.step(3, 5, 0.5) // => [3, 3.5, 4, 4.5, 5]
#
# @return [this] or [Array]
#
step: (num, limit, step = 1, block) ->
unless block?.call? or step?.call?
return __enumerate(_num.step, [num, limit, step])
unless block?.call?
block = step
step = 1
if step is 0
_err.throw_argument()
float_mode = num % 1 is 0 or limit % 1 is 0 or step % 1 is 0
# eps = 0.0000000000000002220446049250313080847263336181640625
if float_mode
# For some reason the following ported code is not needed.
# it appears to work properly in js withouth the Float::EPSILON
# err = (num.abs().plus(limit.abs()).plus(limit.minus(num).abs()).divide(step.abs())).multiply(eps)
# err = 0.5 if err.gt(0.5)
# n = (limit.minus(num)).divide(step.plus(err)).floor()
n = (limit - num) / step
i = 0
if step > 0
while i <= n
d = i * step + num
d = limit if limit < d
block(d)
i += 1
else
while i <= n
d = i * step + num
d = limit if limit > d
block(d)
i += 1
else
if step > 0
until num > limit
block(num)
num += step
else
until num < limit
block(num)
num += step
this
# truncate: (num) ->
# @to_f().truncate()
# Returns array of numbers from num to stop (inclusive) when passed no block.
# When passed a block, iterates block by passing increasing values from num to stop (inclusive).
#
# @example
# var print = function(i) { console.log(i);}
#
# _n.upto(1, 3, print) // => 1\n 2\n 3\n 1
# _n.upto(1, 3) // => [1, 2, 3]
#
# @return [Array] or [Number]
#
upto: (num, stop, block) ->
return __enumerate(_num.upto, [num, stop]) unless block?.call?
stop = Math.floor(stop)
idx = num
while idx <= stop
block( idx )
idx += 1
num
# Returns true if num has a zero value.
#
# @example
# _n.zero(0) // => true
# _n.zero(1) // => false
#
# @return [Boolean]
#
zero: (num) ->
num is 0
# Returns true if num is even number. Returns false otherwise.
#
# @example
# _n.even(2) // => true
# _n.even(3) // => false
#
# @return [Boolean]
#
even: (num) ->
num % 2 == 0
# Returns number that is greatest common divisor of num and other.
#
# @example
# _n.gcd(4, 2) // => 2
# _n.gcd(21, 14) // => 7
#
# @return [Number]
#
gcd: (num, other) ->
t = null
other = __int(other)
while (other != 0)
t = other
other = num % other
num = t
if num < 0 then (- num) else num
# Returns an array; [int.gcd(int2), int.lcm(int2)].
#
# @example
# _n.gcdlcm(2, 2) // => [2, 2]
# _n.gcdlcm(3, -7) // => [1, 21]
# _n.gcdlcm((1<<31)-1, (1<<61)-1) // => [1, 4951760154835678088235319297]
#
# @return [Array<Number, Number>]
#
gcdlcm: (num, other) ->
other = __int(other)
[_num.gcd(num, other), _num.lcm(num, other)]
# Returns the least common multiple (always positive). 0.lcm(x) and x.lcm(0) return zero.
#
# @example
# _n.lcm(2, 2) // => 2
# _n.lcm(3, -7) // => 21
# _n.lcm((1<<31)-1, (1<<61)-1) // => 4951760154835678088235319297
#
# @return [Number]
#
lcm: (num, other) ->
other = __int(other)
lcm = num * other / _num.gcd(num, other)
_num.numerator(lcm)
# Returns int if num is positive number.
# Returns positive int if num is negative number.
#
# @example
# _n.numerator(2) // => 2
# _n.numerator(-22) // => 22
#
# @return [Number]
numerator: (num) ->
if num < 0 then (- num) else num
# Returns true if int is an odd number.
#
# @return [Boolean]
#
odd: (num) ->
num % 2 == 1
# Returns the int itself.
#
# a.ord // => 97
#
# This method is intended for compatibility to character constant in Ruby
# 1.9. For example, ?a.ord returns 97 both in 1.8 and 1.9.
#
# @return [this]
#
ord: ->
this
# Returns Number equal to num + 1.
#
# @example
# _n.next(0) // => 1
# _n.next(5) // => 6
# _n.next(-3) // => -4
#
# @return [Number]
# @alias #succ
#
next: (num) ->
num + 1
# Returns Number equal to num - 1.
#
# @example
# _n.pred(0) // => -1
# _n.pred(9) // => 8
# _n.pred(-3) // => -4
# _n.pred(-5.5) // => -6.5
#
# @return [Number]
#
pred: (num) ->
num - 1
# Rounds num to a given precision n in decimal digits and returns Number.
# When given precision n is negative returns 0.
#
# @example
# _n.round(3, 2) // => 3
# _n.round(3.12, 0) // => 3
# _n.round(3.123, 2) // => 3.12
# _n.round(3.12, -2) // => 0
# _n.round(-3.12, 1) // => -3.1
#
# @return [Number]
#
round: (num, n) ->
return num if n is undefined
multiplier = Math.pow(10, __int(n))
Math.round(num * multiplier) / multiplier
# Iterates block int times, passing in values from zero to int - 1.
#
# If no block is given, an enumerator is returned instead.
#
# @example
# _n.times(5, function(i) { console.log(i) }) // => 0 1 2 3 4
# _n.times(3) // => [0, 1, 2]
#
# @return [this] or [Array]
#
times: (num, block) ->
return __enumerate(_num.times, [num]) unless block?.call?
if num > 0
idx = 0
while idx < num
block( idx )
idx = idx + 1
num
else
num
magnitude: @prototype.abs
succ: @prototype.next
_num = R._num = (arr) ->
new Chain(arr, _num)
R.extend(_num, new NumericMethods())
# EnumerableMethods work with Array, Hash/Objects and every object that
# implements a #each method.
#
# EnumerableMethods are included in ArrayMethods and HashMethods.
#
# @example
# _e.each([1], function(key) { _puts(key)} ) // > 1
# _e.each({a: 1}, function(key,val) { _puts(key+val)} ) // > 'a1'
# _e.each("g", function(key,val) { _puts(key+val)} ) // > '0g'
# _e.each([1], _puts ) // > 1
# // works with arguments as well
# function foo() { _e.each(arguments, ... )}
#
class EnumerableMethods
catch_break: R.Kernel.prototype.catch_break
each: (coll, block) ->
if coll.each?
coll.each(block)
else if __isArr(coll)
_arr.each(coll, block)
else
for own k,v of coll
block(k,v)
coll
all: (coll, block) ->
_itr.catch_break (breaker) ->
callback = __blockify(block, coll)
_itr.each coll, ->
result = callback.invoke(arguments)
breaker.break(false) if __falsey(result)
true
any: (coll, block) ->
_itr.catch_break (breaker) ->
callback = __blockify(block, coll)
_itr.each coll, ->
result = callback.invoke(arguments)
breaker.break(true) unless __falsey( result )
false
collect_concat: (coll, block = null) ->
callback = __blockify(block, this)
ary = []
_itr.each coll, ->
ary.push(callback.invoke(arguments))
_arr.flatten(ary, 1)
flat_map: @collect_concat
count: (coll, block) ->
counter = 0
if block is undefined
_itr.each coll, -> counter += 1
else if block is null
_itr.each coll, (el) -> counter += 1 if el is null
else if block.call?
callback = __blockify(block, coll)
_itr.each coll, ->
result = callback.invoke(arguments)
counter += 1 unless __falsey(result)
else
countable = block
_itr.each coll, (el) ->
counter += 1 if __equals(countable, el)
counter
cycle: (coll, n, block) ->
unless block
if n && n.call?
block = n
n = null
if !(n is null or n is undefined)
many = __int(n)
return null if many <= 0
else
many = null
return __enumerate(_itr.cycle, [n]) unless block
callback = __blockify(block, coll)
cache = []
_itr.each coll, ->
cache.push(callback.args(arguments))
callback.invoke(arguments)
return null if cache.length == 0
if many > 0 # cycle(2, () -> ... )
i = 0
many -= 1
while many > i
# OPTIMIZE use normal arrays and for el in cache
_arr.each cache, ->
callback.invoke(arguments)
i += 1
else
while true # cycle(() -> ... )
_arr.each cache, ->
callback.invoke(arguments)
drop: (coll, n) ->
# TODO use splice when implemented
ary = []
_itr.each_with_index coll, (el, idx) ->
ary.push(el) if n <= idx
ary
drop_while: (coll, block) ->
callback = __blockify(block, coll)
ary = []
dropping = true
_itr.each coll, ->
unless dropping && callback.invoke(arguments)
dropping = false
ary.push(callback.args(arguments))
ary
each_cons: (coll, n, block) ->
# TODO: use callback
callback = __blockify(block, coll)
len = block.length
ary = []
_itr.each coll, ->
ary.push(BlockMulti.prototype.args(arguments))
ary.shift() if ary.length > n
if ary.length is n
if len > 1
block.apply(coll, ary.slice(0))
else
block.call(coll, ary.slice(0))
null
each_entry: (coll, block) ->
# hard code BlockMulti because each_entry converts multiple
# yields into an array
callback = new BlockMulti(block, coll)
len = block.length
_itr.each coll, ->
args = callback.args(arguments)
if len > 1 and __isArr(args)
block.apply(coll, args)
else
block.call(coll, args)
coll
each_slice: (coll, n, block) ->
callback = __blockify(block, coll)
len = block.length
ary = []
_itr.each coll, ->
ary.push( BlockMulti.prototype.args(arguments) )
if ary.length == n
args = ary.slice(0)
if len > 1
block.apply(coll, args)
else
block.call(coll, args)
ary = []
unless ary.length == 0
args = ary.slice(0)
if len > 1
block.apply(coll, args)
else
block.call(coll, args)
null
each_with_index: (coll, block) ->
unless block?.call?
return __enumerate(_itr.each_with_index, [coll])
callback = __blockify(block, coll)
idx = 0
_itr.each coll, ->
val = callback.invokeSplat(callback.args(arguments), idx)
idx += 1
val
coll
each_with_object: (coll, obj, block) ->
unless block?.call?
return __enumerate(_itr.each_with_object, [coll, obj])
callback = __blockify(block, coll)
_itr.each coll, ->
args = BlockMulti.prototype.args(arguments)
callback.invokeSplat(args, obj)
obj
find: (coll, ifnone, block = null) ->
if block == null
block = ifnone
ifnone = null
callback = __blockify(block, this)
_itr.catch_break (breaker) ->
_itr.each coll, ->
unless __falsey(callback.invoke(arguments))
breaker.break(callback.args(arguments))
ifnone?()
find_all: (coll, block) ->
ary = []
callback = __blockify(block, coll)
_itr.each coll, ->
unless __falsey(callback.invoke(arguments))
ary.push(callback.args(arguments))
ary
find_index: (coll, value) ->
if value.call?
block = value
else
block = (el) -> __equals(value, el)
_itr.catch_break (breaker) ->
idx = 0
callback = __blockify(block, coll)
_itr.each coll, ->
breaker.break(idx) if callback.invoke(arguments)
idx += 1
null
first: (coll, n = null) ->
if n != null
_err.throw_argument() if n < 0
_itr.take(coll, n)
else
_itr.take(coll, 1)[0]
include: (coll, other) ->
_itr.catch_break (breaker) ->
_itr.each coll, (el) ->
breaker.break(true) if __equals(other, el)
false
# @private
__inject_args__: (initial, sym, block) ->
if sym?.call?
block = sym
else if sym
# for [1,2,3].inject(5, (memo, i) -> )
block = (memo, el) -> memo[sym](el)
else if R(initial)?.is_string?
# for [1,2,3].inject('-')
block = (memo, el) -> memo["#{initial}"](el)
initial = undefined
else if initial.call?
block = initial
initial = undefined
[initial, sym, block]
inject: (coll, init, sym, block) ->
[init, sym, block] = _itr.__inject_args__(init, sym, block)
callback = __blockify(block, coll)
_itr.each coll, ->
if init is undefined
init = callback.args(arguments)
else
args = BlockMulti.prototype.args(arguments)
init = callback.invokeSplat(init, args)
init
grep: (coll, pattern, block) ->
ary = []
pattern = R(pattern)
callback = __blockify(block, coll)
if block
_itr.each coll, (el) ->
if pattern['==='](el)
ary.push(callback.invoke(arguments))
else
_itr.each coll, (el) ->
ary.push(el) if pattern['==='](el)
ary
group_by: (coll, block) ->
callback = __blockify(block, coll)
h = {}
_itr.each coll, ->
args = callback.args(arguments)
key = callback.invoke(arguments)
h[key] ||= []
h[key].push(args)
h
map: (coll, block) ->
callback = __blockify(block, coll)
arr = []
_itr.each coll, ->
arr.push(callback.invoke(arguments))
arr
max: (coll, block) ->
max = undefined
block ||= __cmp
_itr.each coll, (item) ->
if max is undefined
max = item
else
comp = block(item, max)
_err.throw_argument() if comp is null
max = item if comp > 0
return null if max is undefined
max
max_by: (coll, block) ->
max = undefined
# OPTIMIZE: use sorted element
_itr.each coll, (item) ->
if max is undefined
max = item
else
cmp = __cmpstrict(block(item), block(max))
max = item if cmp > 0
return null if max is undefined
max
min: (coll, block) ->
min = undefined
block ||= __cmp
# Following Optimization won't complain if:
# [1,2,'3']
#
# optimization for elements that are arrays
_itr.each coll, (item) ->
if min is undefined
min = item
else
comp = block.call(this, item, min)
_err.throw_argument() if comp is null
min = item if comp < 0
return null if min is undefined
min
min_by: (coll, block) ->
min = undefined
# OPTIMIZE: use sorted element
_itr.each coll, (item) ->
if min is undefined
min = item
else
cmp = __cmpstrict(block(item), block(min))
min = item if cmp < 0
return null if min is undefined
min
minmax: (coll, block) ->
# TODO: optimize
[_itr.min(coll, block), _itr.max(coll, block)]
minmax_by: (coll, block) ->
[_itr.min_by(coll, block), _itr.max_by(coll, block)]
none: (coll, block) ->
_itr.catch_break (breaker) ->
callback = __blockify(block, coll)
_itr.each coll, (args) ->
result = callback.invoke(arguments)
breaker.break(false) unless __falsey(result)
true
one: (coll, block) ->
counter = 0
_itr.catch_break (breaker) ->
callback = __blockify(block, coll)
_itr.each coll, (args) ->
result = callback.invoke(arguments)
counter += 1 unless __falsey(result)
breaker.break(false) if counter > 1
counter is 1
partition: (coll, block) ->
left = []
right = []
callback = __blockify(block, coll)
_itr.each coll, ->
args = BlockMulti.prototype.args(arguments)
if callback.invokeSplat(args)
left.push(args)
else
right.push(args)
[left, right]
reject: (coll, block) ->
callback = __blockify(block, coll)
ary = []
_itr.each coll, ->
if __falsey(callback.invoke(arguments))
ary.push(callback.args(arguments))
ary
reverse_each: (coll, block) ->
# There is no other way then to convert to an array first.
# Because Enumerable depends only on #each (through #to_a)
_arr.reverse_each(_itr.to_a(coll), block )
coll
slice_before: (args...) ->
# TODO
# block = __extract_block(args)
# # _err.throw_argument() if args.length == 1
# arg = R(args[0])
# if block
# has_init = !(arg is undefined)
# else
# block = (elem) -> arg['==='] elem
# self = this
# R.Enumerator.create (yielder) ->
# accumulator = null
# self.each (elem) ->
# start_new = if has_init then block(elem, arg.dup()) else block(elem)
# if start_new
# yielder.yield accumulator if accumulator
# accumulator = R([elem])
# else
# accumulator ||= new RArray([])
# accumulator.append elem
# yielder.yield accumulator if accumulator
sort: (coll, block) ->
# TODO: throw Error when comparing different values.
block ||= __cmpstrict
coll = coll.to_native() if coll.to_native?
nativeSort.call(coll, block)
sort_by: (coll, block) ->
callback = __blockify(block, coll)
ary = []
_itr.each coll, (value) ->
ary.push new SortedElement(value, callback.invoke(arguments))
ary = _arr.sort(ary, __cmpstrict)
_arr.map(ary, (se) -> se.value)
take: (coll, n) ->
_err.throw_argument() if n < 0
ary = []
_itr.catch_break (breaker) ->
_itr.each coll, ->
breaker.break() if ary.length is n
ary.push(BlockMulti.prototype.args(arguments))
ary
take_while: (coll, block) ->
ary = []
_itr.catch_break (breaker) ->
_itr.each coll, ->
breaker.break() if __falsey block.apply(coll, arguments)
ary.push(BlockMulti.prototype.args(arguments))
ary
to_a: (coll) ->
ary = []
_itr.each coll, ->
args = if arguments.length > 1 then nativeSlice.call(arguments, 0) else arguments[0]
ary.push(args)
null
ary
zip: (coll, others) ->
# TODO
# --- Aliases ---------------------------------------------------------------
detect: @prototype.find
select: @prototype.find_all
collectConcat: @prototype.collect_concat
dropWhile: @prototype.drop_while
eachCons: @prototype.each_cons
eachEntry: @prototype.each_entry
eachSlice: @prototype.each_slice
eachWithIndex: @prototype.each_with_index
eachWithObject: @prototype.each_with_object
findAll: @prototype.find_all
findIndex: @prototype.find_index
flatMap: @prototype.flat_map
groupBy: @prototype.group_by
maxBy: @prototype.max_by
minBy: @prototype.min_by
minmaxBy: @prototype.minmax_by
reverseEach: @prototype.reverse_each
sliceBefore: @prototype.slice_before
sortBy: @prototype.sort_by
takeWhile: @prototype.take_while
toA: @prototype.to_a
collect: @prototype.map
member: @prototype.include
reduce: @prototype.inject
entries: @prototype.to_a
# `value` is the original element and `sort_by` the one to be sorted by
#
# @private
class SortedElement
constructor: (@value, @sort_by) ->
cmp: (other) ->
@sort_by?.cmp(other.sort_by)
_itr = R._itr = new EnumerableMethods()
_enum = R._enum = _itr
class ArrayMethods extends EnumerableMethods
# Checks if arr is an Array or can be coerced to an array
# using valueOf()
#
# @example
# _a.isArray([]) // => true
# _a.isArray({}) // => false
# _a.isArray("") // => false
# _a.isArray(null) // => false
# // Arguments are not arrays
# function () { return _a.isArray(arguments) }(1,2) // => false
# // Checks if valueOf() returns an array
# _a.isArray({valueOf: function(){return [];}}) // => true
#
isArray: __isArr
isNativeArray: nativeArray.isArray or (obj) ->
nativeToString.call(obj) is '[object Array]'
# Checks if arrays have the same elements.
#
# @example
# _a.equals([1,2], [1,2]) // => true
# _a.equals([1,2,[3]], [1,2,[3]]) // => true
#
equals: (arr, other) ->
return true if arr is other
return false unless other?
if __isArr(other)
other = __arr(other)
else
return false
return false unless arr.length is other.length
i = 0
total = i + arr.length
while i < total
return false unless __equals(arr[i], other[i])
i += 1
true
append: (arr, obj) ->
arr.push(obj)
arr
# Set Intersection—Returns a new array containing elements common to the two
# arrays, with no duplicates.
#
# @example
# _a.intersection([1,1,3,5],[1,2,3]) // => [1, 3]
#
intersection: (arr, other) ->
other = __arr(other)
out = []
_arr.each arr, (el) ->
out.push(el) if _arr.include(other, el)
_arr.uniq(out)
# Comparison—Returns an integer (-1, 0, or +1) if this array is less than,
# equal to, or greater than other_ary. Each object in each array is compared
# (using <=>). If any value isn’t equal, then that inequality is the return
# value. If all the values found are equal, then the return is based on a
# comparison of the array lengths. Thus, two arrays are “equal” according to
# Array#<=> if and only if they have the same length and the value of each
# element is equal to the value of the corresponding element in the other
# array.
#
# @example
# _a.cmp(["a","a","c"], ["a","b","c"]) // => -1
# _a.cmp([1,2,3,4,5,6], [1,2]) // => +1
#
cmp: (arr, other) ->
return null unless other?
try
other = __arr(other)
catch e
return null
return 0 if _arr.equals(arr, other)
len = arr.length
other_total = other.length
# Thread.detect_recursion arr, other do
i = 0
total = if other_total < len then other_total else len
while total > i
diff = __cmp(arr[i], other[i])
return diff unless diff == 0
i += 1
# subtle: if we are recursing on that pair, then let's
# no go any further down into that pair;
# any difference will be found elsewhere if need be
__cmp(len, other_total)
# Returns the element at index. A negative index counts from the end of
# arr. Returns nil if the index is out of range. See also Array#[].
#
# @example
# a = [ "a", "b", "c", "d", "e" ]
# _a.at(a, 0) #=> "a"
# _a.at(a, -1) #=> "e"
#
# @return [Object]
#
at: (arr, index) ->
if index < 0
arr[arr.length + index]
else
arr[index]
# When invoked with a block, yields all combinations of length n of elements
# from ary and then returns ary itself. The implementation makes no
# guarantees about the order in which the combinations are yielded.
#
# If no block is given, an enumerator is returned instead.
#
# @example
# arr = [1, 2, 3, 4]
# _a.combination(arr, 1, function (arr) { _puts(arr) })
# _a.combination(arr, 1) #=> [[1],[2],[3],[4]]
# _a.combination(arr, 2) #=> [[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
# _a.combination(arr, 3) #=> [[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
# _a.combination(arr, 4) #=> [[1,2,3,4]]
# _a.combination(arr, 0) #=> [[]] # one combination of length 0
# _a.combination(arr, 5) #=> [] # no combinations of length 5
#
# @return Array
#
combination: (arr, num, block) ->
return __enumerate(_arr.combination, [arr, num]) unless block?.call?
num = __int(num)
len = arr.length
if num is 0
block([])
else if num == 1
_arr.each arr, (args...) ->
block.call(arr, args)
else if num == len
block(arr.slice(0))
else if num >= 0 && num < len
num = num
stack = (0 for i in [0..num+1])
chosen = []
lev = 0
done = false
stack[0] = -1
until done
chosen[lev] = arr[stack[lev+1]]
while lev < num - 1
lev += 1
stack[lev+1] = stack[lev] + 1
chosen[lev] = arr[stack[lev+1]]
block.call(arr, chosen.slice(0))
lev += 1
# this is begin ... while
done = lev == 0
stack[lev] += 1
lev = lev - 1
while (stack[lev+1] + num == len + lev + 1)
done = lev == 0
stack[lev] += 1
lev = lev - 1
arr
# Returns a copy of arr with all nil elements removed.
#
# @example
# _a.compact([ "a", null, "b", null, "c", null ])
# // => [ "a", "b", "c" ]
#
# @return [Array]
#
compact: (arr) ->
# one liner: _arr.select arr, (el) -> el?
ary = []
for el in arr
ary.push(el) if el?
ary
# Deletes items from arr that are equal to obj. If any items are found,
# returns obj. If the item is not found, returns nil. If the optional code
# block is given, returns the result of block if the item is not found. (To
# remove nil elements and get an informative return value, use compact!)
#
# @example
# a = [ "a", "b", "b", "b", "c" ]
# _a.delete(a, "b") // => "b"
# a // => ["a", "c"]
# _a.delete(a, "z") // => nil
# _a.delete(a, "z", function () { return 'not found'} )
# // => "not found"
#
# @destructive
#
# @return [Object]
#
delete: (arr, obj, block) ->
deleted = []
i = 0
len = arr.length
while i < len
if __equals(obj, arr[i])
deleted.push(i)
i += 1
if deleted.length > 0
arr.splice(i,1) for i in deleted.reverse()
return obj
if block then block() else null
# Deletes the element at the specified index, returning that element, or nil
# if the index is out of range. See also Array#slice!.
#
# @example
# arr = ['ant','bat','cat','dog']
# _a.delete_at(arr, 2) #=> "cat"
# arr #=> ["ant", "bat", "dog"]
# _a.delete_at(arr, 99) #=> null
#
# @return obj or null
#
# @destructive
delete_at: (arr, idx) ->
idx = idx + arr.length if idx < 0
return null if idx < 0 or idx >= arr.length
arr.splice(idx, 1)[0]
# Returns the first element, or the first n elements, of the enumerable. If
# the enumerable is empty, the first form returns nil, and the second form
# returns an empty array.
#
# @example
# arr = ['foo','bar','baz']
# _a.first(arr) // => "foo"
# _a.first(arr, 2) // => ["foo", "bar"]
# _a.first(arr, 10) // => ["foo", "bar", "baz"]
# _a.first([]) // => null
#
first: (arr, n) ->
if n?
_err.throw_argument() if n < 0
arr.slice(0,n)
else
arr[0]
# Returns a new array that is a one-dimensional flattening of this array
# (recursively). That is, for every element that is an array, extract its
# elements into the new array. If the optional level argument determines the
# level of recursion to flatten.
#
# @example
# s = [ 1, 2, 3 ]
# t = [ 4, 5, 6, [7, 8] ]
# arr = [ s, t, 9, 10 ] // => [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
# _a.flatten(arr) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# arr = [ 1, 2, [3, [4, 5] ] ]
# _a.flatten(arr, 1) // => [1, 2, 3, [4, 5]]
#
# @return [Array] flattened array
#
flatten: (arr, recursion = -1) ->
arr = __arr(arr)
ary = []
len = arr.length
idx = -1
while (++idx < len)
el = arr[idx]
if recursion != 0 && __isArr(el)
nativePush.apply(ary, _arr.flatten(el, recursion - 1))
else
ary.push(el)
ary
# Calls block once for each element in arr, passing that element as a
# parameter.
#
# If no block is given, an enumerator is returned instead.
#
# @example
# a = [ "a", "b", "c" ]
# str = ""
# _a.each(arr, function (x) { str += x} )
# // str: 'abc'
#
#
each: (arr, block) ->
return arr unless block?
block = Block.splat_arguments(block)
idx = -1
len = arr.length
while ++idx < arr.length
block(arr[idx])
arr
# each with a thisArg.
#
# @example
# arr = [ "a", "b", "c" ]
# acc = []
# _a.each_with_context(arr, acc, function (x) { this.push(x) } )
# // => ['a', 'b', 'c']
#
# @non-ruby
#
# @return thisArg
#
each_with_context: (arr, thisArg, block) ->
return __enumerate(_arr.each_with_context, [arr, thisArg]) unless block?
block = Block.splat_arguments(block)
idx = -1
len = arr.length
while ++idx < arr.length
block.call(thisArg, arr[idx])
arr
# Same as Array#each, but passes the index of the element instead of the
# element itself.
#
# If no block is given, an enumerator is returned instead.
#
# @example
# arr = [ "a", "b", "c" ]
# _a.each_index(arr, function (x) { R.puts "#{x} -- " })
# // 0 --
# // 1 --
# // 2 --
#
each_index: (arr, block) ->
return __enumerate(_arr.each_index, [arr]) unless block?
idx = -1
len = arr.length
while ++idx < len
block(idx)
this
get: (arr, b) ->
_arr.slice(arr,b)
# Returns true if arr contains no elements.
#
empty: (arr) ->
arr.length is 0
# Tries to return the element at position index. If the index lies outside
# the array, the first form throws an IndexError exception, the second form
# returns default, and the third form returns the value of invoking the
# block, passing in the index. Negative values of index count from the end
# of the array.
#
# @example
# arr = [ 11, 22, 33, 44 ]
# _a.fetch(arr, 1) // => 22
# _a.fetch(arr, -1) // => 44
# _a.fetch(arr, 4, 'cat') // => "cat"
# _a.fetch(arr, 4, function (i) { return i*i; }) // => 16
#
fetch: (arr, idx, default_or_block) ->
idx = __int(idx)
len = arr.length
orig = idx
idx = idx + len if idx < 0
if idx < 0 or idx >= len
return default_or_block(orig) if default_or_block?.call?
return default_or_block unless default_or_block is undefined
_err.throw_index()
arr[idx]
# Fills array with obj or block.
#
# _a.fill(arr, obj) → ary
# _a.fill(arr, obj, start [, length]) → ary
# _a.fill(arr, obj, range ) → ary
# _a.fill(arr, function (index) {}) → ary
# _a.fill(arr, start [, length], (index) -> block → ary
# # not yet implemented:
# _a.fill(arr, range, (index) -> block ) → ary
#
# The first three forms set the selected elements of arr (which may be the
# entire array) to obj. A start of nil is equivalent to zero. A length of
# nil is equivalent to arr.length. The last three forms fill the array with
# the value of the block. The block is passed the absolute index of each
# element to be filled. Negative values of start count from the end of the
# array.
#
# @example
# arr = [ "a", "b", "c", "d" ]
# _a.fill(arr, "x") // => ["x", "x", "x", "x"]
# _a.fill(arr, "z", 2, 2) // => ["x", "x", "z", "z"]
# _a.fill(arr, "y", 0..1) // => ["y", "y", "z", "z"]
# _a.fill(arr, (i) -> i*i // => [0, 1, 4, 9]
# _a.fill(arr, -2, function (i) { return i*i*i; }) // => [0, 1, 8, 27]
#
# @todo implement fill(range, ...)
#
# @destructive
#
fill: (arr, args...) ->
_err.throw_argument() if args.length == 0
block = __extract_block(args)
if block
_err.throw_argument() if args.length >= 3
one = args[0]; two = args[1]
else
_err.throw_argument() if args.length > 3
obj = args[0]; one = args[1]; two = args[2]
size = arr.length
if one?.is_range?
# TODO: implement fill with range
_err.throw_not_implemented()
else if one isnt undefined && one isnt null
left = __int(one)
left = left + size if left < 0
left = 0 if left < 0
if two isnt undefined && two isnt null
try
right = __int(two)
catch e
_err.throw_argument("second argument must be a Fixnum")
return arr if right is 0
right = right + left
else
right = size
else
left = 0
right = size
total = right
if right > size # pad with nul if length is greater than array
fill = _arr.__native_array_with__(right - size, null)
arr.push.apply(arr, fill)
total = right
i = left
if block
while total > i
v = block(i)
arr[i] = if v is undefined then null else v
i += 1
else
while total > i
arr[i] = obj
i += 1
arr
# Inserts the given values before the element with the given index (which
# may be negative).
#
# @example
# arr = ['a','b','c','d']
# _a.insert(arr, 2, 99) // => ["a", "b", 99, "c", "d"]
# _a.insert(arr, -2, 1, 2, 3) // => ["a", "b", 99, "c", 1, 2, 3, "d"]
#
# @destructive
#
insert: (arr, idx) ->
_err.throw_argument() if idx is undefined
return arr if arguments.length == 2
items = _coerce.split_args(arguments, 2)
len = arr.length
# Adjust the index for correct insertion
idx = idx + len + 1 if idx < 0 # Negatives add AFTER the element
# TODO: add message "#{idx} out of bounds"
_err.throw_index() if idx < 0
after = arr.slice(idx)
if idx > len
for i in [len...idx]
arr[i] = null
len = 0
for el, i in items
if el != undefined
arr[idx+i] = el
len += 1
for el, i in after
arr[idx+len+i] = el
arr
# Returns a string created by converting each element of the array to a
# string, separated by sep.
#
# @example
# arr = ['a', 'b', 'c']
# _a.join(arr) // => "abc"
# _a.join(arr,null) // => "abc"
# _a.join(arr,"-") // => "a-b-c"
# # joins nested arrays
# _a.join([1,[2,[3,4]]], '.') // => '1.2.3.4'
# # Default separator R['$,'] (in ruby: $,)
# R['$,'] // => null
# _a.join(arr) // => "abc"
# R['$,'] = '|' // => '|'
# _a.join(arr) // => "a|b|c"
#
join: (arr, separator) ->
return '' if arr.length == 0
separator = R['$,'] if separator is undefined
separator = '' if separator is null
nativeJoin.call(_arr.flatten(arr), separator)
# Deletes every element of arr for which block evaluates to false. See also
# Array#select!
#
# If no block is given, an enumerator is returned instead.
#
# @example
# arr = [1,2,3,4]
# _a.keep_if(arr, function (v) { return i < 3; } ) // => [1,2,3]
# _a.keep_if(arr, function (v) { return true; } ) // => [1,2,3,4]
#
# @todo make destructive
#
keep_if: (arr, block) ->
return __enumerate(_arr.keep_if, [arr]) unless block?
block = Block.splat_arguments(block)
ary = []
idx = -1
len = arr.length
while ++idx < len
el = arr[idx]
ary.push(el) unless __falsey(block(el))
ary
# Returns the last element(s) of arr. If the array is empty, the first form
# returns nil.
#
# @example
# arr = [ "w", "x", "y", "z" ]
# _a.last(arr) // => "z"
# _a.last(arr, 2) // => ["y", "z"]
#
last: (arr, n) ->
len = arr.length
if n is undefined
return arr[len-1]
if len is 0 or n is 0
return []
_err.throw_argument("count must be positive") if n < 0
n = len if n > len
arr[-n.. -1]
# Array Difference - Returns a new array that is a copy of the original
# array, removing any items that also appear in other_ary. (If you need set-
# like behavior, see the library class Set.)
#
# @note minus checks for identity using _a.include(el), which differs slightly
# from the reference which uses #hash and #eql?
#
# @example
# arr = [1, 1, 2, 2, 3, 3, 4, 5 ]
# _a.minus(arr, [1, 2, 4])
# // => [3, 3, 5]
#
# @todo recursive arrays not tested
#
minus: (arr, other) ->
other = __arr(other)
ary = []
idx = -1
len = arr.length
while ++idx < len
el = arr[idx]
ary.push(el) unless _arr.include(other, el)
ary
# Repetition - With a String argument, equivalent to _a.join(str).
# Otherwise, returns a new array built by concatenating the int copies of
# arr.
#
# @example
# arr = [ 1, 2, 3 ]
# _a.multiply(arr, 3 ) // => [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ]
# _a.multiply(arr, ",") // => "1,2,3"
#
multiply: (arr, multiplier) ->
_err.throw_type() if multiplier is null
if __isStr(multiplier)
return _arr.join(arr, __str(multiplier))
else
multiplier = __int(multiplier)
_err.throw_argument("count cannot be negative") if multiplier < 0
total = arr.length
if total is 0
return []
else if total is 1
return arr.slice(0)
ary = []
idx = -1
while ++idx < multiplier
ary = ary.concat(arr)
ary
# Removes the last element from arr and returns it, or nil if the array is empty.
#
# If a number n is given, returns an array of the last n elements (or less)
# just like array.slice!(-n, n) does.
#
# @example
# arr = [ "a", "b", "c", "d" ]
# _a.pop(arr,) // => "d"
# _a.pop(arr,2) // => ["b", "c"]
# arr // => ["a"]
#
pop: (arr, many) ->
if many is undefined
arr.pop()
else
many = __int(many)
_err.throw_argument("negative array size") if many < 0
ary = []
len = arr.length
many = len if many > len
while many--
ary[many] = arr.pop()
ary
# Returns an array of all combinations of elements from all arrays. The
# length of the returned array is the product of the length of arr and the
# argument arrays. If given a block, product will yield all combinations and
# return arr instead.
#
# @example
# _a.product( [1,2,3], [4,5]) // => [[1,4],[1,5],[2,4],[2,5],[3,4],[3,5]]
# _a.product( [1,2], [1,2]) // => [[1,1],[1,2],[2,1],[2,2]]
# _a.product( [1,2], [3,4],[5,6]) // => [[1,3,5],[1,3,6],[1,4,5],[1,4,6],
# // [2,3,5],[2,3,6],[2,4,5],[2,4,6]]
# _a.product( [1,2] ) // => [[1],[2]]
# _a.product( [1,2], []) // => []
#
# @todo does not check if the result size will fit in an Array.
#
product: (arr, args...) ->
result = []
block = __extract_block(args)
args = for a in args
__arr(a)
args = args.reverse()
args.push(arr)
# Implementation notes: We build a block that will generate all the
# combinations by building it up successively using "inject" and starting
# with one responsible to append the values.
outer = _arr.inject args, result.push, (trigger, values) ->
(partial) ->
for val in values
trigger.call(result, partial.concat(val))
outer( [] )
if block
block_result = arr
for v in result
block_result.push(block(v))
block_result
else
result
# Append—Pushes the given object(s) on to the end of this array. This
# expression returns the array i arr, so several appends may be chained
# together.
#
# @example
# arr = [ "a", "b", "c" ]
# _a.push(arr, "d", "e", "f")
# #=> ["a", "b", "c", "d", "e", "f"]
#
push: (arr, elements...) ->
arr.push.apply(arr, elements)
arr
# Searches through the array whose elements are also arrays. Compares obj
# with the second element of each contained array using ==. Returns the
# first contained array that matches. See also Array#assoc.
#
# @example
# arr = [[1, "one"], [2, "two"], [3, "three"], ["ii", "two"]]
# _a.rassoc(arr, "two") // => [2, "two"]
# _a.rassoc(arr, "four") // => nil
#
rassoc: (arr, obj) ->
len = arr.length
idx = -1
while ++idx < len
elem = arr[idx]
try
el = __arr(elem)
if __equals(el[1], obj)
return elem
catch e
null
null
# TODO: _a.replace
# Same as Array#each, but traverses arr in reverse order.
#
# @example
# arr = [ "a", "b", "c" ]
# acc = []
# _a.reverse_each arr, (x) -> acc.push("#{x} ")
# acc // => ['c ', 'b ', 'a ']
#
reverse_each: (arr, block) ->
return __enumerate(_arr.reverse_each, [arr]) unless block?
block = Block.splat_arguments(block)
idx = arr.length
while idx--
block(arr[idx])
arr
# Returns the index of the last object in arr == to obj. If a block is
# given instead of an argument, returns index of first object for which
# block is true, starting from the last object. Returns nil if no match is
# found. See also Array#index.
#
# If neither block nor argument is given, an enumerator is returned instead.
#
# @example
# arr = [ "a", "b", "b", "b", "c" ]
# _a.rindex(arr, "b") // => 3
# _a.rindex(arr, "z") // => nil
# _a.rindex(arr, function (x) { return x == "b" } // => 3
#
# @note does not check if array has changed.
#
rindex: (arr, other) ->
return __enumerate(_arr.rindex, [arr, other]) if other is undefined
len = arr.length
ridx = arr.length
if other.call?
block = Block.splat_arguments(other)
while ridx--
el = arr[ridx]
unless __falsey(block(el))
return ridx
else
# TODO: 2012-11-06 use a while loop with idx counting down
while ridx--
el = arr[ridx]
if __equals(el, other)
return ridx
null
# Returns new array by rotating arr so that the element at cnt in arr is
# the first element of the new array. If cnt is negative then it rotates in
# the opposite direction.
#
# @example
# arr = [ "a", "b", "c", "d" ]
# _a.rotate(arr) # => ["b", "c", "d", "a"]
# arr # => ["a", "b", "c", "d"]
# _a.rotate(arr, 2) # => ["c", "d", "a", "b"]
# _a.rotate(arr, -3) # => ["b", "c", "d", "a"]
#
rotate: (arr, cnt) ->
if cnt is undefined
cnt = 1
else
cnt = __int(cnt)
len = arr.length
return arr if len is 1
return [] if len is 0
idx = cnt % len
# TODO: optimize
sliced = arr.slice(0, idx)
arr.slice(idx).concat(sliced)
# Choose a random element or n random elements from the array. The elements
# are chosen by using random and unique indices into the array in order to
# ensure that an element doesn’t repeat itself unless the array already
# contained duplicate elements. If the array is empty the first form returns
# nil and the second form returns an empty array.
#
# If rng is given, it will be used as the random number generator.
#
# @example
# arr = [1,2,3]
# _a.sample(arr) // => 2
# _a.sample(arr, 2) // => [3,1]
# _a.sample(arr, 4) // => [2,1,3]
#
# @todo range is not implemented yet
#
sample: (arr, n, range = undefined) ->
len = arr.length
return arr[__rand(len)] if n is undefined
n = __int(n)
_err.throw_argument() if n < 0
n = len if n > len
ary = arr.slice(0)
idx = -1
while ++idx < n
ridx = idx + __rand(len - idx) # Random idx
tmp = ary[idx]
ary[idx] = ary[ridx]
ary[ridx] = tmp
ary.slice(0, n)
# Length of array.
#
# @example
# _a.size([]) // => 0
# _a.size([1,2]) // => 2
#
# @return [Number]
#
size: (arr) ->
arr.length
# Returns a new array with elements of this array shuffled.
#
# @example
# arr = [ 1, 2, 3 ]
# _a.shuffle(arr) //=> [2, 3, 1]
#
shuffle: (arr) ->
len = arr.length
ary = new Array(len)
idx = -1
_a.each arr, (val) ->
rnd = __random(++idx)
ary[idx] = ary[rnd]
ary[rnd] = val
ary
# forEach(collection, function(value) {
# var rand = random(++index);
# result[index] = result[rand];
# result[rand] = value;
# });
# Element Reference—Returns the element at index, or returns a subarray
# starting at start and continuing for length elements, or returns a
# subarray specified by range. Negative indices count backward from the end
# of the array (-1 is the last element). Returns null if the index (or
# starting index) are out of range.
#
# @example
# a = [ "a", "b", "c", "d", "e" ]
# _a.slice(arr, 2) + arr[0] + arr[1] // => "cab"
# _a.slice(arr, 6) // => null
# _a.slice(arr, 1, 2) // => [ "b", "c" ]
# _a.slice(arr, _r(1,3)) // => [ "b", "c", "d" ]
# _a.slice(arr, _r(4,7)) // => [ "e" ]
# _a.slice(arr, _r(6,10)) // => null
# _a.slice(arr, -3, 3) // => [ "c", "d", "e" ]
# # special cases
# _a.slice(arr, 5) // => null
# _a.slice(arr, 5, 1) // => []
# _a.slice(arr, _r(5,10)) // => []
#
# @todo Ranges not yet implemented correctly, use R.Range(...)
#
slice: (arr, idx, length) ->
_err.throw_type() if idx is null
size = arr.length
if idx?.is_range?
range = idx
range_start = __int(range.begin())
range_end = __int(range.end() )
range_start = range_start + size if range_start < 0
if range_end < 0
range_end = range_end + size
range_end = range_end + 1 unless range.exclude_end()
range_lenth = range_end - range_start
return null if range_start > size or range_start < 0
return arr.slice(range_start, range_end)
else
idx = __int(idx)
idx = size + idx if idx < 0
# return @$String('') if is_range and idx.lteq(size) and idx.gt(length)
if length is undefined
return null if idx < 0 or idx >= size
arr[idx]
else
length = __int(length)
return null if idx < 0 or idx > size or length < 0
arr.slice(idx, length + idx)
# Assumes that arr is an array of arrays and transposes the rows and columns.
#
# @example
# arr = [[1,2], [3,4], [5,6]]
# _a.transpose(arr) // => [[1, 3, 5], [2, 4, 6]]
#
transpose: (arr) ->
return [] if arr.length == 0
out = []
max = null
# TODO: dogfood
for ary in arr
ary = __arr(ary)
max ||= ary.length
# Catches too-large as well as too-small (for which #fetch would suffice)
# _err.throw_index("All arrays must be same length") if ary.size != max
_err.throw_index() unless ary.length == max
idx = -1
len = ary.length
while ++idx < len
out.push([]) unless out[idx]
entry = out[idx]
entry.push(ary[idx])
out
# Returns a new array by removing duplicate values in arr.
#
# @example
# arr = [ "a", "a", "b", "b", "c" ]
# _a.uniq(arr) // => ["a", "b", "c"]
# // Not yet implemented:
# c = [ "a:def", "a:xyz", "b:abc", "b:xyz", "c:jkl" ]
# _a.uniq(c, function(s) { return s[/^\w+/] })
# // => [ "a:def", "b:abc", "c:jkl" ]
#
# @note Not yet correctly implemented. should use #eql on objects, but uses @include().
#
uniq: (arr) ->
idx = -1
len = arr.length
ary = []
while (++idx < len)
el = arr[idx]
ary.push(el) if ary.indexOf(el) < 0
ary
unshift: (arr, args...) ->
args.concat(arr)
# Set Union—Returns a new array by joining this array with other_ary,
# removing duplicates.
#
# @example
# _a.union([ "a", "b", "c" ], [ "c", "d", "a" ])
# // => [ "a", "b", "c", "d" ]
#
union: (arr, other) ->
_arr.uniq(arr.concat(__arr(other)))
# Returns an array containing the elements in arr corresponding to the
# given selector(s). The selectors may be either integer indices or ranges.
# See also Array#select.
#
# @example
# arr = ['a', 'b', 'c', 'd', 'e', 'f']
# _a.values_at(arr, 1, 3, 5) // => ['b', 'd', 'f']
# _a.values_at(arr, 1, 3, 5, 7) // => ['b', 'd', 'f', null]
# _a.values_at(arr, -1, -3, -5, -7) // => ['f', 'd', 'b', null]
# // _a.values_at(arr, _r(1,3), _r(2,5, false))
#
# @todo not working with ranges
#
values_at: (arr) ->
len = arguments.length
ary = new Array(len - 1)
idx = 1
while idx < len
ary[idx - 1] = _arr.at(arr, __int(arguments[idx])) || null
idx += 1
ary
# ---- Enumerable implementations ------------------------
find_index: (arr, value) ->
len = arr.length
idx = -1
if typeof value is 'function' or (typeof value is 'object' && value.call?)
block = Block.splat_arguments(value)
else if value != null && typeof value is 'object'
block = (el) -> __equals(value, el)
else
while ++idx < len
return idx if arr[idx] == value
return null
while ++idx < len
return idx if block(arr[idx])
null
map: (arr, block) ->
callback = Block.splat_arguments(block)
idx = -1
len = arr.length
ary = new Array(len)
while ++idx < len
ary[idx] = callback(arr[idx])
ary
# @alias #first
take: @prototype.first
# @private
__native_array_with__: (size, obj) ->
ary = nativeArray(__int(size))
idx = -1
while ++idx < size
ary[idx] = obj
ary
_arr = R._arr = (arr) ->
new Chain(arr, _arr)
R.extend(_arr, new ArrayMethods())
class StringMethods
# @return [Boolean]
equals: (str, other) ->
str = str.valueOf() if typeof str is 'object'
other = other.valueOf() if typeof other is 'object'
str is other
# Converts str to camelCase.
#
# @example
# _s.camelCase('foo-bar') // => ('fooBar')
# _s.camelCase('foo-bar-baz') // => ('fooBarBaz')
# _s.camelCase('foo:bar_baz') // => ('fooBarBaz')
# _s.camelCase('') // => ('')
# _s.camelCase('foo') // => ('foo')
# _s.camelCase('fooBar') // => ('fooBar')
#
# @return [String]
#
camel_case: (str) ->
str.replace /([\:\-\_]+(.))/g, (_1, _2, letter, offset) ->
if offset then letter.toUpperCase() else letter
# Returns a copy of str with the first character converted to uppercase and
# the remainder to lowercase. Note: case conversion is effective only in
# ASCII region.
#
# @example
# _s.capitalize("hello") // => "Hello"
# _s.capitalize("HELLO") // => "Hello"
# _s.capitalize("äöü") // => "äöü"
# _s.capitalize("123ABC") // => "123abc"
#
# @note Doesn't handle special characters like ä, ö.
#
# @return [String]
#
capitalize: (str) ->
return "" if str.length == 0
b = _str.downcase(str)
a = _str.upcase(str[0])
a + nativeStrSlice.call(b, 1)
# TODO: casecmp
# If integer is greater than the length of str, returns a new String of
# length integer with str centered and padded with padstr; otherwise,
# returns str.
#
# @example
# _s.center("hello", 4) // => "hello"
# _s.center("hello", 20) // => " hello "
# _s.center("hello", 20, '123') // => "1231231hello12312312"
#
# @return [String]
#
center: (str, length, padString = ' ') ->
_err.throw_argument() if padString.length == 0
size = str.length
return str if size >= length
lft = Math.floor((length - size) / 2)
rgt = length - size - lft
max = if lft > rgt then lft else rgt
padString = _str.multiply(padString, max)
padString[0...lft] + str + padString[0...rgt]
# Passes each character in str to the given block, or returns an enumerator if
# no block is given.
#
# @example
# acc = []
# _s.chars("foo", function (c) { acc.push(c + ' ') })
# acc // => ["f ", "o ", "o "]
#
# @note Does *not* typecast chars to R.Strings.
#
chars: (str, block) ->
idx = -1
len = str.length
while ++idx < len
block(str[idx])
str
# @alias #chars
each_char: @prototype.chars
# Returns a new String with the given record separator removed from the end
# of str (if present). If $/ has not been changed from the default Ruby
# record separator, then chomp also removes carriage return characters (that
# is it will remove \n, \r, and \r\n).
#
# @example
# _s.chomp("hello") // => "hello"
# _s.chomp("hello\n") // => "hello"
# _s.chomp("hello\r\n") // => "hello"
# _s.chomp("hello\n\r") // => "hello\n"
# _s.chomp("hello\r") // => "hello"
# _s.chomp("hello \n there") // => "hello \n there"
# _s.chomp("hello", "llo") // => "he"
#
# @return [String]
#
chomp: (str, sep) ->
sep = "\n" unless sep?
sep = __str(sep)
if sep.length == 0
regexp = /((\r\n)|\n)+$/
else if sep is "\n" or sep is "\r" or sep is "\r\n"
ending = str.match(/((\r\n)|\n|\r)$/)?[0] || "\n"
regexp = new RegExp("(#{_rgx.escape(ending)})$")
else
regexp = new RegExp("(#{_rgx.escape(sep)})$")
str.replace(regexp, '')
# Returns a new String with the last character removed. If the string ends
# with \r\n, both characters are removed. Applying chop to an empty string
# returns an empty string. String#chomp is often a safer alternative, as it
# leaves the string unchanged if it doesn’t end in a record separator.
#
# @example
# _s.chop("string\r\n") // => "string"
# _s.chop("string\n\r") // => "string\n"
# _s.chop("string\n") // => "string"
# _s.chop("string") // => "strin"
# _s.chop(_s.chop("x")) // => ""
#
# @return [String]
#
chop: (str) ->
return str if str.length == 0
if str.lastIndexOf("\r\n") == str.length - 2
str.slice(0, -2)
else
str.slice(0, -1)
# Each other_str parameter defines a set of characters to count. The
# intersection of these sets defines the characters to count in str. Any
# other_str that starts with a caret (^) is negated. The sequence c1–c2 means
# all characters between c1 and c2.
#
# @example
# str = "hello world"
# _s.count(str, "lo") // => 5
# _s.count(str, "lo", "o") // => 2
# _s.count(str, "hello", "^l") // => 4
# _s.count(str, "ej-m") // => 4
#
# @param str [String]
# @param needles [String]
# @todo expect( s.count("A-a")).toEqual s.count("A-Z[\\]^_`a")
#
# @return [Number]
#
count: (str, needle) ->
_err.throw_argument("String.count needs arguments") if arguments.length == 1
args = _coerce.split_args(arguments, 1)
_str.__matched__(str, args).length
# Returns a copy of str with all characters in the intersection of its
# arguments deleted. Uses the same rules for building the set of characters as
# String#count.
#
# @example
# _s.delete("hello", "l","lo") // => "heo"
# _s.delete("hello", "lo") // => "he"
# _s.delete("hello", "aeiou", "^e") // => "hell"
# _s.delete("hello", "ej-m") // => "ho"
#
# @todo expect( R("ABCabc[]").delete("A-a") ).toEqual R("bc")
#
# @return [String]
#
'delete': (str) ->
_err.throw_argument() if arguments.length == 1
args = _coerce.split_args(arguments, 1)
trash = _str.__matched__(str, args)
str.replace(new RegExp("[#{trash}]", 'g'), '')
each_line: (str, separator, block) ->
unless block?
if separator?
if separator.call?
block = separator
separator = null
else
block(str)
return
# unless separator?
separator ||= R['$/']
if separator.length is 0
separator = "\n\n"
lft = 0
rgt = null
dup = str # allows the string to be changed with bang methods
while (rgt = _str.index(dup, separator, lft)) != null
rgt = rgt + 1
str = _str.slice(dup, lft, rgt - lft)
lft = rgt
block(str)
remainder = nativeStrSlice.call(dup, lft)
if remainder?
block(remainder) unless remainder.length == 0
this
# Returns a copy of str with all uppercase letters replaced with their
# lowercase counterparts. The operation is locale insensitive—only characters
# “A” to “Z” are affected. Note: case replacement is effective only in ASCII
# region.
#
# @example
# _s.downcase("hEllO") // => "hello"
#
# @note unlike toLowerCase, downcase doesnt change special characters ä,ö
#
# @return [String]
#
downcase: (str) ->
return str unless nativeStrMatch.call(str, /[A-Z]/)
str.replace /[A-Z]/g, (ch) ->
String.fromCharCode(ch.charCodeAt(0) | 32)
# @return [String]
#
dump: (str) ->
escaped = str.replace(/[\f]/g, '\\f')
.replace(/["]/g, "\\\"")
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r')
.replace(/[\t]/g, '\\t')
# .replace(/[\s]/g, '\\ ') # do not
"\"#{escaped}\""
# @return [Boolean]
#
empty: (str) ->
str.length == 0
# Returns true if str ends with one of the suffixes given.
#
# @return [Boolean]
#
end_with: (str) ->
needles = _coerce.split_args(arguments, 1)
for w in needles
try
w = __str(w)
if str.lastIndexOf(w) + w.length is str.length
return true
catch e
false
gsub: (str, pattern, replacement) ->
_err.throw_type() if pattern is null
pattern_lit = __try_str(pattern)
if pattern_lit isnt null
pattern = new RegExp(_rgx.escape(pattern_lit), 'g')
unless __isRgx(pattern)
_err.throw_type()
unless pattern.global
throw "String#gsub: #{pattern} has not set the global flag 'g'. #{pattern}g"
str.replace(pattern, replacement)
# Returns true if str contains the given string or character.
#
# @example
# _s.include("hello", "lo") // => true
# _s.include("hello", "ol") // => false
# _s.include("hello", "hh" ) // => true
#
# @return [Boolean]
#
include: (str, other) ->
str.indexOf(other) >= 0
# Returns the index of the first occurrence of the given substring or pattern
# (regexp) in str. Returns nil if not found. If the second parameter is
# present, it specifies the position in the string to begin the search.
#
# @example
# _s.index("hello", 'e') // => 1
# _s.index("hello", 'lo') // => 3
# _s.index("hello", 'a') // => null
# _s.index("hello", 'el') // => 1
# _s.index("hello", /[aeiou]/, -3) // => 4
#
# @todo #index(regexp)
#
# @return [Number]
#
index: (str, needle, offset) ->
needle = __str(needle)
if offset?
offset = __int(offset)
offset = str.length + offset if offset < 0
# unless needle.is_string? or needle.is_regexp? or needle.is_fixnum?
# _err.throw_type()
if offset? && (offset > str.length or offset < 0)
return null
idx = str.indexOf(needle, offset)
if idx < 0
null
else
idx
# Inserts other_str before the character at the given index, modifying str.
# Negative indices count from the end of the string, and insert after the
# given character. The intent is insert aString so that it starts at the
# given index.
#
# @example
# _s.insert("abcd", 0, 'X') // => "Xabcd"
# _s.insert("abcd", 3, 'X') // => "abcXd"
# _s.insert("abcd", 4, 'X') // => "abcdX"
#
# @example inserts after with negative counts
# _s.insert("abcd", -3, 'X') // => "abXcd"
# _s.insert("abcd", -1, 'X') // => "abcdX"
#
# @return [String]
#
insert: (str, idx, other) ->
if idx < 0
# On negative count
idx = str.length - Math.abs(idx) + 1
if idx < 0 or idx > str.length
_err.throw_index()
before = str.slice(0, idx)
after = str.slice(idx)
before + other + after
# If integer is greater than the length of str, returns a new String of
# length integer with str left justified and padded with padstr; otherwise,
# returns str.
#
# @example
# _s.ljust("hello", 4) // => "hello"
# _s.ljust("hello", 20) // => "hello "
# _s.ljust("hello", 20, '1234') // => "hello123412341234123"
#
# @return [String]
#
ljust: (str, width, padString = " ") ->
len = str.length
if len >= width
str
else
_err.throw_argument() if padString.length == 0
pad_length = width - len
idx = -1
out = ""
# TODO refactor
out += padString while ++idx <= pad_length
str + out.slice(0, pad_length)
# Returns a copy of str with leading whitespace removed. See also
# String#rstrip and String#strip.
#
# @example
# _s.lstrip(" hello ") // => "hello "
# _s.lstrip("hello") // => "hello"
#
# @return [String]
#
lstrip: (str) ->
str.replace(/^[\s\n\t]+/g, '')
match: (str, pattern, offset = null, block) ->
unless block?
if offset?.call?
block = offset
offset = null
# unless RString.isString(pattern) or __isRgx(pattern)
# _err.throw_type()
opts = {}
if offset?
opts = {string: str, offset: offset}
str = nativeStrSlice.call(str, offset)
matches = nativeStrMatch.call(str, pattern, offset)
else
# Firefox breaks if you'd pass str.match(..., undefined)
matches = nativeStrMatch.call(str, pattern)
result = if matches
new R.MatchData(matches, opts)
else
null
R['$~'] = result
if block
if result then block(result) else []
else
result
# Copy—Returns a new String containing integer copies of the receiver.
#
# @example
# _s.multiply("Ho! ", 3) // => "Ho! Ho! Ho! "
#
# @return [String]
#
multiply: (str, num) ->
_err.throw_argument() if num < 0
out = ""
n = 0
while ++n <= num
out += str
out
partition: (str, pattern) ->
# TODO: regexps
idx = _str.index(str, pattern)
unless idx is null
start = idx + pattern.length
a = _str.slice(str, 0, idx) || ''
b = pattern
c = nativeStrSlice.call(str, start)
[a,b,c]
else
[str, '', '']
# Returns a new string with the characters from str in reverse order.
#
# @example
# _s.reverse("stressed") // => "desserts"
#
# @return [String]
#
reverse: (str) ->
str.split("").reverse().join("")
# Returns the index of the last occurrence of the given substring or pattern
# (regexp) in str. Returns nil if not found. If the second parameter is
# present, it specifies the position in the string to end the
# search—characters beyond this point will not be considered.
#
# @example
# _s.rindex("hello", 'e') // => 1
# _s.rindex("hello", 'l') // => 3
# _s.rindex("hello", 'a') // => null
# _s.rindex("hello", /[aeiou]/, -2) // => 1
#
# @todo #rindex(/.../) does not add matches to R['$~'] as it should
#
# @return [Number, null]
#
rindex: (str, needle, offset) ->
if offset != undefined
offset = offset + str.length if offset < 0
return null if offset < 0
if typeof needle is 'string'
offset = offset + needle.length
ret = str[0...offset].lastIndexOf(needle)
else
ret = _str.__rindex_with_regexp__(str, needle, offset)
else
if typeof needle is 'string'
ret = str.lastIndexOf(needle)
else
ret = _str.__rindex_with_regexp__(str, needle)
if ret is -1 then null else ret
# @private
# @param needle R.Regexp
# @param offset [number]
__rindex_with_regexp__: (str, needle, offset) ->
unless needle.global
needle = new RegExp(needle.source, "g" + (if needle.ignoreCase then "i" else "") + (if needle.multiLine then "m" else ""));
offset = str.length unless offset?
idx = -1
stop = 0
while (result = needle.exec(str)) != null
break if result.index > offset
idx = result.index
needle.lastIndex = ++stop
idx
# If integer is greater than the length of str, returns a new String of
# length integer with str right justified and padded with padstr; otherwise,
# returns str.
#
# @example
# _s.rjust("hello", 4) // => "hello"
# _s.rjust("hello", 20) // => " hello"
# _s.rjust("hello", 20, '1234') // => "123412341234123hello"
#
# @return [String]
#
rjust: (str, width, pad_str = " ") ->
width = __int(width)
len = str.length
if len >= width
str
else
pad_str = __str(pad_str)
_err.throw_argument() if pad_str.length == 0
pad_len = width - len
_str.multiply(pad_str, pad_len)[0...pad_len] + str
# Searches sep or pattern (regexp) in the string from the end of the string,
# and returns the part before it, the match, and the part after it. If it is
# not found, returns two empty strings and str.
#
# @example
# _s.rpartition("hello", "l") // => ["hel", "l", "o"]
# _s.rpartition("hello", "x") // => ["", "", "hello"]
#
# @example edge cases
# _s.rpartition("hello", "x") // => ["", "", "hello"]
# _s.rpartition("hello", "hello") // => ["", "hello", ""]
#
# @example todo:
# _s.rpartition("hello", /.l/) // => ["he", "ll", "o"]
#
# @todo does not yet accept regexp as pattern
# @todo does not yet affect R['$~']
#
# @return [Array]
#
rpartition: (str, pattern) ->
pattern = __str(pattern)
idx = _str.rindex(str, pattern)
unless idx is null
start = idx + pattern.length
len = str.length - start
a = str.slice(0,idx)
b = pattern
c = str.slice(start)
[a,b,c]
else
['', '',str]
# Returns a copy of str with trailing whitespace removed. See also
# String#lstrip and String#strip.
#
# @example
# _s.rstrip(" hello ") // => " hello"
# _s.rstrip("hello") // => "hello"
#
# @return [String]
#
rstrip: (str) ->
str.replace(/[\s\n\t]+$/g, '')
# Both forms iterate through str, matching the pattern (which may be a
# Regexp or a String). For each match, a result is generated and either
# added to the result array or passed to the block. If the pattern contains
# no groups, each individual result consists of the matched string, $&. If
# the pattern contains groups, each individual result is itself an array
# containing one entry per group.
#
# @example
# str = "cruel world"
# _s.scan(str, /\w+/) #=> ["cruel", "world"]
# _s.scan(str, /.../) #=> ["cru", "el ", "wor"]
# _s.scan(str, /(...)/) #=> [["cru"], ["el "], ["wor"]]
# _s.scan(str, /(..)(..)/) #=> [["cr", "ue"], ["l ", "wo"]]
# // And the block form:
# _s.scan(str, /\w+/, function (w) { _puts("<<#{w}>> ") } )
# // > <<cruel>> <<world>>
# // TODO:
# // _s.scan(str, /(.)(.)/, function (x,y) { _puts(y, x)} )
# // > rceu lowlr
#
#
# @todo some untested specs
#
scan: (str, pattern, block = null) ->
unless __isRgx(pattern)
pattern = __str(pattern)
pattern = _rgx.quote(pattern)
index = 0
R['$~'] = null
match_arr = if block != null then str else []
# FIXME: different from rubinius implementation
while match = str[index..-1].match(pattern)
fin = index + match.index + match[0].length
fin += 1 if match[0].length == 0
R['$~'] = new R.MatchData(match, {offset: index, string: str})
if match.length > 1
val = match[1...match.length]
else
val = [match[0]]
if block != null
block(val)
else
val = val[0] if match.length == 1
match_arr.push val
index = fin
break if index > str.length
# return this if block was passed
if block != null then str else match_arr
# Length of string
#
# @example
# _s.size('') // => 0
# _s.size('foo') // => 3
#
# @return [Number]
#
size: (str) ->
str.length
# Builds a set of characters from the other_str parameter(s) using the
# procedure described for String#count. Returns a new string where runs of
# the same character that occur in this set are replaced by a single
# character. If no arguments are given, all runs of identical characters are
# replaced by a single character.
#
# @example
# _s.squeeze("yellow moon") // => "yelow mon"
# _s.squeeze(" now is the", " ") // => " now is the"
# _s.squeeze("putters shoot balls", "m-z") // => "puters shot balls"
#
# @todo Fix A-a bug
#
# @return [String]
#
squeeze: (str) ->
pattern = _coerce.split_args(arguments, 1)
trash = _str.__matched__(str, pattern)
chars = str.split("")
len = str.length
i = 1
j = 0
last = chars[0]
all = pattern.length == 0
while i < len
c = chars[i]
unless c == last and (all || trash.indexOf(c) >= 0)
chars[j+=1] = last = c
i += 1
if (j + 1) < len
chars = chars[0..j]
chars.join('')
# Returns a copy of str with leading and trailing whitespace removed.
#
# @example
# _s.strip(" hello ") // => "hello"
# _s.strip("\tgoodbye\r\n") // => "goodbye"
#
# @return [String]
#
strip: (str) ->
# TODO Optimize
_str.rstrip(_str.lstrip(str))
sub: (str, pattern, replacement) ->
_err.throw_type() if pattern is null
pattern_lit = __try_str(pattern)
if pattern_lit isnt null
pattern = new RegExp(_rgx.escape(pattern_lit))
unless __isRgx(pattern)
_err.throw_type()
if pattern.global
throw "String#sub: #{pattern} has set the global flag 'g'. #{pattern}g"
str.replace(pattern, replacement)
# Returns the successor to str. The successor is calculated by incrementing
# characters starting from the rightmost alphanumeric (or the rightmost
# character if there a