rubymonkey
v1.4.0
Published
Monkey patching JavaScript to make it more Ruby!
Downloads
105
Maintainers
Readme
Ruby Monkey
Unashamedly monkey patching JavaScript to be more like Ruby.
Ruby (and Rails) has loads of really nice methods, now you can use them in JS as well!
Ruby Monkey helps to make your JavaScript code more code elegant, fun and productive!
Now you can write JS code like this:
[1,2,3].last // 3
[1,2,3].count // 3
(21).ordinalize // "21st"
"RubyMonkey".downcase.reverse // "yeknomybur"
[1,2,3].sum.squared // 36
["A","A","C","A","B","A","B"].tally // {"A": 4, "C": 1, "B": 2}
(1).day.ago // yesterdayUsage
npm install rubymonkeyThen just add either require "rubymonkey" or import "rubymonkey" to the top of any JS file and suddenly coding in JS becomes a lot more fun and productive!
In general, if you know the Ruby methods you should be able to use them in almost the same way, with a few slight changes:
- Blocks change to arrow functions
- JavaScript does not support appending symbols to the end of function names, so Boolean methods can't end in a
?, so these have 2 versions, one without the?at the end and another withisprepended to the beginning.
So for example, this Ruby:
[1,2,3].count{ |n| n.odd? }Would be written in Ruby Monkey as either of the following:
[1,2,3].count( n => n.isOdd )[1,2,3].count( n => n.odd )Template to Func
Ruby has this really nice syntax to make calling methods on objects easier, so instead of [1,2,3].map { |n| n.next } you can just write [1,2,3].map(&:next)
JavaScript doesn't let you use & and doesn't have symbol literals, but you can use $ and it does have template literals and tag functions, so in Ruby Doo, you can do the same thing like this:
[1,2,3].map($`next`)Number Methods
number.even & number.isEven
Checks if the number is even.
(4).even; // true
(5).even; // falsenumber.odd & number.isOdd
Checks if the number is odd.
(3).odd; // true
(10).odd; // falsenumber.to_s
Converts the number to a string.
(42).to_s; // "42"number.next
Returns the next integer.
(10).next; // 11number.round
Rounds the number to the nearest integer.
(4.7).round; // 5
(4.2).round; // 4number.ceil
Returns the smallest integer greater than or equal to the number.
(4.2).ceil; // 5number.floor
Returns the largest integer less than or equal to the number.
(4.9).floor; // 4number.digits
Returns an array of the digits of the number.
(123).digits; // [1, 2, 3]number.factors
Returns an array of all factors of the number.
(12).factors; // [1, 2, 3, 4, 6, 12]number.prime
Checks if the number is prime.
(7).prime; // true
(9).prime; // falsenumber.integer and number.isInteger
Checks if the number is an integer.
(10.5).integer; // false
(10).integer; // truenumber.positive & number.isPositive
Checks if the number is positive.
(5).positive; // true
(-3).positive; // falsenumber.negative & number.isNegative
Checks if the number is negative.
(-10).negative; // true
(5).negative; // falsenumber.zero & number.isZero
Checks if the number is zero.
(0).zero; // true
(1).zero; // falsenumber.squared
Returns the square of the number.
(4).squared; // 16number.cubed
Returns the cube of the number.
(3).cubed; // 27number.ordinal
Returns the ordinal suffix of the number.
(1).ordinal; // "st"
(2).ordinal; // "nd"
(3).ordinal; // "rd"
(4).ordinal; // "th"
(11).ordinal; // "th"number.ordinalize
Returns the number as an ordinal string.
(1).ordinalize; // "1st"
(2).ordinalize; // "2nd"
(3).ordinalize; // "3rd"
(4).ordinalize; // "4th"
(11).ordinalize; // "11th"number.upto(n, func?)
Iterates from the current number up to n, calling func if provided.
(3).upto(6, console.log);
// Logs: 3, 4, 5, 6
(3).upto(6);
// Returns: [3, 4, 5, 6]number.times(func)
Executes func the given number of times, passing the index as an optional argument.
(3).times(_ => console.log("Ruby!"));
// Logs: Ruby!Ruby!Ruby!
(3).times(i => console.log(`Iteration: ${i}`));
// Logs: Iteration: 0, Iteration: 1, Iteration: 2number.mod(n)
Returns the remainder of the number divided by n.
(10).mod(3); // 1number.divmod(n)
Returns an array containing the quotient and remainder of division by n.
(10).divmod(3); // [3, 1]number.gcd(n)
Computes the greatest common divisor (GCD) of the number and n.
(48).gcd(18); // 6number.lcm(n)
Computes the least common multiple (LCM) of the number and n.
(4).lcm(6); // 12number.between(a, b) & number.isBetween(a,b)
Checks if the number is between a and b (inclusive).
(5).between(1, 10); // true
(15).between(1, 10); // falsenumber.eql(n)
Checks if the number is strictly equal to n.
(5).eql(5); // true
(5).eql(3); // falsenumber.multiple_of(n) & number.divisble_by(n)
Checks if the number is a multiple of n.
(10).multiple_of(5); // true
(10).multiple_of(3); // false(10).divisible_by(5); // true
(10).divisible_by(3); // falsenumber.divisor_of(n) & number.factor_of(n)
Checks if the number is a divisor (factor) of n.
(5).divisor_of(10); // true
(3).divisor_of(10); // false(5).factor_of(10); // true
(3).factor_of(10); // falsenumber.pred
Returns the predecessor (this - 1).
(10).pred; // 9
(1).pred; // 0number.downto(n, func?)
Iterates from the current number down to n, calling func if provided.
(6).downto(3, console.log);
// Logs: 6, 5, 4, 3
(6).downto(3);
// Returns: [6, 5, 4, 3]number.div(n)
Returns the integer division result (floor of division).
(10).div(3); // 3
(7).div(2); // 3number.modulo(n)
Returns the modulo of the number divided by n. Alias for mod.
(10).modulo(3); // 1
(-10).modulo(3); // 2number.hcf(n)
Highest common factor. Alias for gcd.
(48).hcf(18); // 6number.nonzero & number.isNonzero
isNonzero returns true if the number is not zero. nonzero returns the number itself if non-zero, otherwise undefined.
(5).isNonzero; // true
(0).isNonzero; // false
(5).nonzero; // 5
(0).nonzero; // undefinednumber.abs
Returns the absolute value of the number.
(-5).abs; // 5
(5).abs; // 5number.add(n)
Returns the sum of the number and n.
(5).add(3); // 8number.subtract(n) & number.minus(n)
Returns the difference of the number and n.
(10).subtract(3); // 7
(10).minus(3); // 7number.multiply(n)
Returns the product of the number and n.
(5).multiply(3); // 15number.divide(n)
Returns the quotient of the number divided by n.
(10).divide(2); // 5
(7).divide(2); // 3.5String Methods
string.reverse
Returns the string reversed.
"hello".reverse; // "olleh"string.size
Returns the length of the string.
"hello".size; // 5string.to_i
Converts the string to an integer, returning 0 if conversion fails.
"123".to_i; // 123
"abc".to_i; // 0string.to_f
Converts the string to a float, returning 0 if conversion fails.
"12.34".to_f; // 12.34
"abc".to_f; // 0string.downcase
Returns the string in lowercase.
"Hello".downcase; // "hello"string.upcase
Returns the string in uppercase.
"hello".upcase; // "HELLO"string.upcase_first
Capitalizes only the first character of the string.
"hello world".upcase_first; // "Hello world"string.downcase_first
Lowercases only the first character of the string.
"Hello World".downcase_first; // "hello World"
string.squish
Removes leading, trailing, and multiple consecutive spaces.
" Hello world ".squish; // "Hello world"string.blank
Checks if the string is empty or contains only whitespace.
" ".blank; // true
"hello".blank; // falsestring.empty
Checks if the string is completely empty (not even whitespace).
"".empty; // true
" ".empty; // falsestring.humanize
Removes _id from the end (if present) and replaces underscores with spaces, capitalizing the first letter.
"user_name".humanize; // "User name"
"post_id".humanize; // "Post"string.titleize (Alias: titlecase)
Capitalizes each word in the string.
"hello world".titleize; // "Hello World"string.parameterize
Converts the string into a URL-friendly format (lowercase, hyphenated).
"Hello, World!".parameterize; // "hello-world"string.chars
Returns an array of individual characters.
"hello".chars; // ["h", "e", "l", "l", "o"]string.count(substring)
Returns the number of times substring appears in the string.
"hello world".count("l"); // 3string.starts_with(substring)
Checks if the string starts with the given substring.
"hello world".starts_with("hello"); // truestring.ends_with(substring)
Checks if the string ends with the given substring.
"hello world".ends_with("world"); // truestring.first
Returns the first character of the string.
"hello".first; // "h"string.first_(n)
Returns the first n characters of the string.
"hello".first(2); // "he"
"hello".first(5); // "hello"string.last
Returns the last character of the string.
"hello".last; // "o"string.last_(n)
Returns the last n characters of the string. If n is omitted, returns the last character.
"hello".last_(2); // "lo"
"hello".last_(5); // "hello"string.eql(str)
Checks if the string is strictly equal to str.
("hello").eql("hello"); // true
("Hello!").eql("hello"); // falsestring.to_s
Returns the string itself.
"hello".to_s; // "hello"string.isBlank & string.blank
isBlank and blank are aliases. Checks if the string is empty or contains only whitespace.
" ".isBlank; // true
"hello".blank; // falsestring.isEmpty & string.empty
isEmpty and empty are aliases. Checks if the string is completely empty (not even whitespace).
"".isEmpty; // true
" ".empty; // falseArray Methods
Property Methods
array.first
Returns the first element of the array.
[1, 2, 3].first; // 1
[].first; // undefinedarray.second, array.third, array.fourth, array.fifth
Returns the second, third, fourth, or fifth element of the array.
[10, 20, 30].second; // 20
[10].third; // undefinedarray.forty_two
Returns the 42nd element (index 41) of the array.
Array(50).fill(0).map((_, i) => i + 1).forty_two; // 42### array.third_to_last, array.second_to_last, array.last
Returns the third-to-last, second-to-last, or last element of the array.
[1, 2, 3, 4].second_to_last; // 3
[].last; // undefinedarray.empty
Returns true if the array is empty, false otherwise.
[].empty; // true
[1].empty; // falsearray.clear
Clears all elements from the array.
let arr = [1, 2, 3];
arr.clear;
console.log(arr); // []array.size
Returns the length of the array.
[1, 2, 3].size; // 3array.min, array.max
Returns the smallest or largest number in the array.
[5, 3, 9].min; // 3
[5, 3, 9].max; // 9
[].min; // undefinedarray.uniq
Returns a new array with duplicate elements removed.
[1, 2, 2, 3].uniq; // [1, 2, 3]array.to_sentence
Converts the array into a human-readable sentence.
["a", "b", "c"].to_sentence; // "a, b and c"array.compact
Returns a new array with null and undefined values removed.
[1, null, 2, undefined, 3].compact; // [1, 2, 3]array.to_param
Converts the array into a string joined by /.
["users", 42, "edit"].to_param; // "users/42/edit"Functional Methods
array.any(func?)
Returns true if at least one element satisfies func, or if the array is not empty.
[1, 2, 3].any(x => x > 2); // true
[].any(); // falsearray.one(func?)
Returns true if exactly one element satisfies func.
[1, 2, 3].one(x => x > 2); // true
[1, 2, 3, 4].one(x => x > 2); // falsearray.sum
Returns the sum of all elements, or applies func before summing.
[1, 2, 3].sum; // 6array.reject(func)
Returns a new array without elements matching func.
[1, 2, 3, 4].reject(x => x % 2 === 0); // [1, 3]
array.partition(func)
Splits the array into two: one matching func, one not.
[1, 2, 3, 4].partition(x => x % 2 === 0); // [[2, 4], [1, 3]]array.count(func?)
Returns the number of elements satisfying func, or the total length.
[1, 2, 3, 4].count(x => x % 2 === 0); // 2
[1, 2, 3].count(); // 3array.pluck(prop)
Extracts values of the given property from an array of objects.
[{id: 1}, {id: 2}].pluck("id"); // [1, 2]array.from(n)
Returns a new array starting from index n.
[10, 20, 30, 40].from(2); // [30, 40]
### `array.product(arr)`
Returns all possible combinations of elements from both arrays.
```javascript
[1, 2, 3].product([4,5]); // [[1,4], [1,5], [2,4],[2,5], [3,4], [3,5]]array.combination(n)
Returns all possible combinations of n elements.
[1, 2, 3].combination(2); // [[1,2], [1,3], [2,3]]array.tally()
Counts occurrences of each unique element.
["a", "b", "a"].tally(); // { a: 2, b: 1 }array.each_cons(n)
Returns overlapping subarrays of size n.
[1, 2, 3, 4].each_cons(2); // [[1,2], [2,3], [3,4]]array.rotate(n = 1)
Returns a rotated array by n places.
[1, 2, 3].rotate(); // [2, 3, 1]array.sample(n = 1)
Returns n random elements.
[1, 2, 3, 4].sample(2); // Random subsetarray.zip(arr)
Zips two arrays together.
[1, 2, 3].zip(["a", "b", "c"]); // [[1, "a"], [2, "b"], [3, "c"]]array.union(...arrs)
Returns a merged array without duplicates.
[1, 2].union([2, 3], [3, 4]); // [1, 2, 3, 4]array.intersection(...arrs)
Returns elements common to all arrays.
[1, 2, 3].intersection([2, 3, 4]); // [2, 3]array.minmax
Returns a tuple of [min, max] values in the array.
[5, 1, 9, 3].minmax; // [1, 9]array.multiply
Returns the product of all elements in the array.
[1, 2, 3, 4].multiply; // 24array.shuffle
Returns a shuffled copy of the array.
[1, 2, 3].shuffle; // e.g., [3, 1, 2]array.transpose
Transposes a 2D array (swaps rows and columns).
[[1, 2], [3, 4]].transpose; // [[1, 3], [2, 4]]array.sort
Returns a sorted copy of the array (numeric sort).
[3, 1, 2].sort; // [1, 2, 3]array.flatten
Flattens nested arrays by one level.
[[1, 2], [3, [4, 5]]].flatten; // [1, 2, 3, [4, 5]]array.drop(n)
Alias for slice(n). Returns elements after dropping the first n.
[1, 2, 3, 4].drop(2); // [3, 4]array.sort_by(func, order?)
Sorts the array by the value returned from func. Pass 'desc' as the second argument to sort in descending order.
// Sort strings by length
["apple", "fig", "banana"].sort_by(s => s.length);
// ["fig", "apple", "banana"]
// Sort strings by length descending
["apple", "fig", "banana"].sort_by(s => s.length, 'desc');
// ["banana", "apple", "fig"]
// Sort strings by last character
["apple", "banana", "cherry"].sort_by(s => s.last);
// ["banana", "apple", "cherry"]
// Sort objects by property
const users = [
{ name: "Charlie", age: 35 },
{ name: "Alice", age: 30 },
{ name: "Bob", age: 25 }
];
users.sort_by(u => u.age);
// [{ name: "Bob", age: 25 }, { name: "Alice", age: 30 }, { name: "Charlie", age: 35 }]
// Sort by computed value descending
const products = [
{ name: "Widget", price: 25, quantity: 10 },
{ name: "Gadget", price: 50, quantity: 3 },
{ name: "Gizmo", price: 15, quantity: 20 }
];
products.sort_by(p => p.price * p.quantity, 'desc');
// [Gizmo (300), Widget (250), Gadget (150)]array.dig(...indices)
Safely retrieves a nested value using a sequence of keys or indexes.
Returns undefined if any step in the chain is missing.
[ { a: { b: 10 } } ].dig(0, "a", "b"); // 10
[ { a: {} } ].dig(0, "a", "c"); // undefinedarray.eql(arr)
Deep-compares two arrays for equality.
Uses an element’s custom .eql method if present, otherwise strict equality (===).
[1, 2, 3].eql([1, 2, 3]); // true
[1, 2, 3].eql([1, 2, "3"]); // falsearray.filter_map(func)
Maps the array with func and removes null/undefined values (.compact).
[1, 2, 3].filter_map(n => (n % 2 === 0 ? n * 2 : null));
// [4]array.each_with_object(obj, func)
Iterates over the array and yields each element with the provided object.
const acc = [];
[1, 2, 3].each_with_object(acc, (n, arr) => arr.push(n * 2));
acc; // [2, 4, 6]array.first_(n)
Returns the first n elements of the array.
If n < 1, returns an empty array.
[1, 2, 3].first_(2); // [1,2]
[1, 2, 3].first_(3); // [1, 2, 3]array.last_(n)
Returns the last n elements of the array.
If n < 1, returns an empty array.
[1, 2, 3].last_(2); // [2, 3]
[1, 2, 3].last_(3); // [1, 2, 3]Aliases
collect → map
all → every
select → filter
each → forEach
detect → find
inject → reduce
delete_if → reject
flat_map → flatMap
drop → slice
empty → isEmpty
Object Methods
object.empty
Checks if an object has no keys.
({}).empty; // true
({ a: 1 }).empty; // falseobject.size
Returns the number of keys in the object.
({ a: 1, b: 2 }).size; // 2`
object.values
Returns an array of the object's values.
({ a: 1, b: 2 }).values; // [1, 2]object.keys
Returns an array of the object's keys.
({ a: 1, b: 2 }).keys; // ["a", "b"]object.entries
Returns an array of [key, value] pairs.
({ a: 1, b: 2 }).entries; // [["a", 1], ["b", 2]]object.clear
Removes all properties from an object (mutates it).
const obj = { a: 1, b: 2 }; obj.clear console.log(obj); // {}
object.compact
Returns a new object with null and undefined values removed.
({ a: 1, b: null, c: undefined }).compact; // { a: 1 }Methods
object.select(func)
Returns a new object with key-value pairs where func(key, value) is true.
({ a: 1, b: 2 }).select(([k, v]) => v > 1); // { b: 2 }object.keep_if(func)
Alias for select.
object.reject(func)
Returns a new object with key-value pairs where func(key, value) is false.
({ a: 1, b: 2 }).reject(([k, v]) => v > 1); // { a: 1 }object.delete_if(func)
Alias for reject.
object.has_key(key)
Checks if an object has a given key.
({ a: 1 }).has_key("a"); // true
({ a: 1 }).has_key("b"); // falseobject.has_value(value)
Checks if an object contains a given value.
({ a: 1, b: 2 }).has_value(2); // true
({ a: 1 }).has_value(3); // falseobject.key(value)
Returns the first key where the value matches, or undefined if not found.
({ a: 1, b: 2 }).key(2); // "b"
({ a: 1 }).key(3); // undefinedobject.any([func])
If func is provided, checks if any key-value pair matches func(key, value). If func is omitted, returns true if the object is not empty.
({ a: 1, b: 2 }).any(); // true
({}).any(); // false
({ a: 1, b: 2 }).any(([k, v]) => v > 1); // trueobject.except(...keys)
Returns a new object excluding specified keys.
({ a: 1, b: 2, c: 3 }).except("b", "c"); // { a: 1 }object.transform_keys(func)
Returns a new object with keys transformed by func.
({ a: 1, b: 2 }).transform_keys(k => k.toUpperCase()); // { A: 1, B: 2 }object.transform_values(func)
Returns a new object with values transformed by func.
({ a: 1, b: 2 }).transform_values(v => v * 10); // { a: 10, b: 20 }object.dig(...keys)
Safely retrieves a nested value using a sequence of keys.
({ a: { b: { c: 42 } } }).dig("a", "b", "c"); // 42
({ a: {} }).dig("a", "x"); // undefinedobject.each(func)
Iterates over each [key, value] entry, calling func.
({ a: 1, b: 2 }).each(([k, v]) => console.log(k, v));
// Logs: "a" 1, "b" 2object.each_key(func)
Iterates over each key, calling func.
({ a: 1, b: 2 }).each_key(k => console.log(k));
// Logs: "a", "b"object.each_value(func)
Iterates over each value, calling func.
({ a: 1, b: 2 }).each_value(v => console.log(v));
// Logs: 1, 2object.fetch(key)
Returns the value for the given key.
({ a: 1, b: 2 }).fetch("a"); // 1object.fetch_values(...keys)
Returns an array of values for the given keys.
({ a: 1, b: 2, c: 3 }).fetch_values("a", "c"); // [1, 3]object.eql(obj)
Deep equality check between objects.
({ a: 1, b: 2 }).eql({ a: 1, b: 2 }); // true
({ a: 1 }).eql({ a: "1" }); // falseobject.to_s
Returns a string representation of the object.
({ a: 1, b: 2 }).to_s; // "{ a: 1, b: 2 }"Aliases
each_pair → each
filter → select
includes → has_key
Date Utilities Library
A lightweight set of extensions for working with dates, durations, and date ranges based on the Rails helper methods. Includes:
- DateRange — iterate or inspect ranges of dates
- Duration — express time spans like (3).days or (2).months
- Prototype helpers on Date for navigation, ranges, and comparisons
- Convenience accessors like Date.today, Date.current, date.isToday
- Iterable ranges and unit-based iteration (each_day, each_month, etc.)
DateRange
range.includes(date)
Returns true if the date falls inside the range (inclusive).
range.includes(Date.today); // truerange.each(callback, step = 1)
Iterates day-by-day through the range, calling the callback for each date.
step controls the day increment.
range.each(d => console.log(d));
range.each(d => console.log(d), 2); // Every 2 daysrange.each_day(callback, step = 1)
Alias for .each.
Iterates through days in the range.
range.each_day(d => console.log(d));range.each_week(callback, step = 1)
Iterates through the range in weekly steps.
range.each_week(d => console.log(d)); // Every 7 days
range.each_week(d => console.log(d), 2); // Every 14 daysrange.each_month(callback, step = 1)
Iterates month-to-month, preserving the original day when possible (and adjusting for month length differences automatically).
range.each_month(d => console.log(d));range.each_quarter(callback, step = 1)
Iterates in increments of 3 months.
range.each_quarter(d => console.log(d));range.each_year(callback, step = 1)
Iterates year-to-year through the range.
range.each_year(d => console.log(d));Duration
new Duration({ years, months, weeks, days, hours, minutes, seconds })
Creates a duration object.
const d = new Duration({ days: 3, hours: 5 });duration.ago
Shifts backward from Date.current.
(3).days.ago; // 3 days before nowduration.since(date)
Moves forward from the given date.
(2).weeks.since(Date.today);duration.before(date)
Moves backward from the given date.
(1).month.before(Date.today);duration.after(date)
Alias for duration.since.
(6).hours.after(Date.current);duration.until(date)
Returns the date minus the duration.
(10).days.until(Date.today);duration.from_now
Shifts forward from Date.current.
(30).minutes.from_now;duration.advance_from(date)
Applies all duration components to the given date.
new Duration({ days: 1, months: 1 }).advance_from(Date.today);Number Duration Extensions
All numbers gain convenience getters for generating a Duration:
second, seconds
minute, minutes
hour, hours
day, days
week, weeks
month, months
year, yearsExample usage:
(3).days; // Duration { days: 3 }
(1).year; // Duration { years: 1 }
(2).weeks.from_now;Date Range Helpers
date.all_day, date.all_week, date.all_month, date.all_quarter, date.all_year
Returns a DateRange covering the full period.
Date.today.all_week.each(d => console.log(d));date.at_beginning_of_day, date.at_beginning_of_month, ... date.at_end_of_day, date.at_end_of_month, ...
Convenient accessors for the start or end of a period.
Date.today.at_beginning_of_month;
Date.today.at_end_of_year;Named Day Predicates
date.isYesterday, date.isToday, date.isTomorrowChecks if the date falls within the corresponding day.
Date.today.isToday; // trueStatic Date Helpers
Date.currentReturns the current UTC date with full time precision.
Date.current;
Date.todayReturns today's UTC date at midnight.
Date.today;
Date.yesterday
Date.tomorrowUTC midnight versions of today, yesterday and tomorrow.
Date Prototype Methods
date.yesterday
Returns the previous day at the beginning of day.
Date.current.yesterday;date.tomorrow
Returns the next day at the beginning of day.
Date.current.tomorrow;date.days_in_month
Returns the number of days in the current month.
new Date(Date.UTC(2024, 1, 1)).days_in_month; // 29date.advance(duration)
Returns a new date advanced by the given duration.
Date.today.advance({ weeks: 1 });date.change({ year, month, day, hour, minute, second, ms })
Returns a new date with selected UTC fields replaced.
Date.current.change({ hour: 0, minute: 0 });