Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1 @@
require 'ae'
@@ -0,0 +1,34 @@
require 'fileutils'
require 'pathname'
def temporary_directory
@temporary_directory ||= 'tmp'
end
Before :document do
if File.exist?(temporary_directory)
FileUtils.rm_r(temporary_directory)
end
FileUtils.mkdir(temporary_directory)
end
When /Given a directory '(.*?)' containing/ do |dir, text|
#abort unless /^#{temporary_directory}/ =~ dir
text.lines.each do |file|
next if file =~ /^\s*$/
file = File.join(dir, file.strip)
path = File.dirname(file)
FileUtils.mkdir_p(path)
File.open(file, 'w'){ |f| f << "SPINICH" }
end
end
# Return project root directory by looking for +lib+ directory.
def root_directory
@root_directory ||= (
Pathname.new(Dir.pwd).ascend do |root|
break root if root.join('lib').directory?
end
)
end
@@ -0,0 +1,13 @@
## Array#after
require 'facets/array/after'
`Array#after` returns the first value that comes after a given value. The
value after the last is the first. Returns `nil` if the given value is not
in the array.
sequence = ['a', 'b', 'c']
sequence.after('a').assert == 'b'
sequence.after('b').assert == 'c'
sequence.after('c').assert == 'a'
sequence.after('d').assert == nil
@@ -0,0 +1,17 @@
## Array#arrange
require 'facets/array/arrange'
The `arrange` method produces appropriate ranges from the objects in the array.
It assumes inclusive ranges (i.e. `1..4`) and `range.first <= range.last`.
Works with integers, dates and strings. However, all the objects in the array must
be of the same class.
[1,2,3,6,7,8].arrange.assert == [1..3, 6..8]
[10..15, 16..20, 21, 22].arrange.assert == [10..22]
a = %w{a b c g h i j k m o}
a.arrange.assert == ['a'..'c','g'..'k', 'm' , 'o']
@@ -0,0 +1,14 @@
## Array#before
require 'facets/array/before'
Returns the value prior to the given value. The value previous
to the first is the last. Returns nil if the given value is not
in the array.
sequence = ['a', 'b', 'c']
sequence.before('a').assert == 'c'
sequence.before('b').assert == 'a'
sequence.before('c').assert == 'b'
sequence.before('d').assert == nil
@@ -0,0 +1,15 @@
## Array#collapse
require 'facets/array/collapse'
Simplify an array by flattening it then compacting it.
[1,[2,nil,[3]],nil,4].collapse.assert == [1,2,3,4]
## Array#collapse!
Simplify an array by flattening it in place then compacting it in place.
a = [1, nil, [1, 2], nil, [[3]]]
a.collapse!
a.assert == [1, 1, 2, 3]
@@ -0,0 +1,30 @@
## Array#collisions
_alias for Array#commonality_.
require 'facets/array/collisions'
Get a list of all items that have something in common in terms of the
supplied block. If no block is given objects are considered to be in
common if they return the same value for Object#hash and if obj1 == obj2.
This can be useful, for instance, in determining all persons that share
their last name with another person.
Person = Struct.new(:name)
persons = %w{Wayne Pennyworth Grayson Wayne Pennyworth}.map{|n| Person.new(n)}
persons.collisions { |person| person.name }.keys.assert == ["Wayne","Pennyworth"]
The method is similar to #group_by which is a standard Ruby method as of 1.9.
To get effectively the same results with #group_by use `select{ |k,v| v.size > 1 }`.
expected = { 2 => [2, 2], 4 => [4, 4] }
[1, 2, 2, 3, 4, 4].group_by{ |e| e }.
select{ |k,v| v.size > 1 }.assert == expected
[1, 2, 2, 3, 4, 4].collisions.assert == expected
%w{foo bar baz}.collisions{|str| str[0]}.assert == {'b' =>["bar", "baz"] }
Returns a Hash mapping common attribute to those elements.
@@ -0,0 +1,31 @@
## Array#commonality
require 'facets/array/commonality'
Get a list of all items that have something in common in terms of the
supplied block. If no block is given objects are considered to be in
common if they return the same value for Object#hash and if obj1 == obj2.
This can be useful, for instance, in determining all persons that share
their last name with another person.
Person = Struct.new(:name)
persons = %w{Wayne Pennyworth Grayson Wayne Pennyworth}.map{|n| Person.new(n)}
persons.commonality { |person| person.name }.keys # => ["Wayne","Pennyworth"]
The method is similar to #group_by which is a standard Ruby method as of 1.9.
To get effectively the same results with #group_by use `select{ |k,v| v.size > 1 }`.
expected = { 2 => [2, 2], 4 => [4, 4] }
[1, 2, 2, 3, 4, 4].group_by{ |e| e }.
select{ |k,v| v.size > 1 }.assert == expected
[1, 2, 2, 3, 4, 4].commonality.assert == expected
%w{foo bar baz}.commonality{|str| str[0]}.assert == {'b' =>["bar", "baz"] }
Returns a Hash mapping common attribute to those elements.
@@ -0,0 +1,47 @@
## Array#conjoin
require 'facets/array/conjoin'
This is more advanced form of #join. It allows for fine control
of separators.
NOTE: The old version used to default its separator to ", " and
default the terminating separator to " and ". This is no longer
the case. You must specifically provide these parameters.
If no parameters are given, it acts like #join but will a space
separator.
[1,2,3].conjoin.assert == "1 2 3"
Use comma+space and 'and' on tail.
[1,2,3].conjoin(', ', ' and ').assert == "1, 2 and 3"
Use comma+space and 'or' on tail using :last option.
[1,2,3].conjoin(', ', :last => ' or ').assert == "1, 2 or 3"
Use semicolon+space and ampersand on tail using index.
[1,2,3].conjoin('; ', -1 => ' & ').assert == "1; 2 & 3"
Can take a block to determine separator.
[1,2,3,4].conjoin{ |i, a, b| i % 2 == 0 ? '.' : '-' }.assert == "1.2-3.4"
This makes very esoteric transformation possible.
[1,1,2,2].conjoin{ |i, a, b| a == b ? '=' : ' != ' }.assert == "1=1 != 2=2"
[1,2,3,4].conjoin{ |i, x, y| "<#{i} #{x} #{y}>" }.assert == "1<0 1 2>2<1 2 3>3<2 3 4>4"
There are also spacing options. Providing the :space option
pads the separators.
[1,2,3].conjoin(',', '&', :space=>2).assert == "1 , 2 & 3"
And the :spacer option can set an alternate spacing string.
[1,2,3].conjoin('|', '>', :space=>2, :spacer=>'-').assert == "1--|--2-->--3"
@@ -0,0 +1,9 @@
## Array#contains?
_alias of Array#include? _
require 'facets/array/contains'
[1,2,3,4].assert.contains?(2)
[1,2,3,4].refute.contains?(9)
@@ -0,0 +1,13 @@
## Array#delete_unless
require 'facets/array/delete_unless'
The method `#delete_unless` is the inverse of `#delete_if`.
a = [1,2,3]
a.delete_unless{ |e| e == 2 }
a.assert == [2]
[1,2,3].delete_unless{ |x| x < 2 }.assert == [1]
[1,2,3].delete_unless(&:even?).assert == [2]
@@ -0,0 +1,10 @@
## Array#delete_values
require 'facets/array/delete_values'
Delete multiple values from array.
a = [1,2,3,4]
a.delete_values(1,2) #=> [1,2]
a #=> [3,4]
@@ -0,0 +1,15 @@
## Array#delete_values_at
require 'facets/array/delete_values_at'
Delete multiple values from array given
indexes or index range.
a = [1,2,3,4]
a.delete_values_at(1,2).assert == [2,3]
a.assert == [1,4]
a = [1,2,3,4]
a.delete_values_at(0..2).assert == [1,2,3]
a.assert == [4]
@@ -0,0 +1,14 @@
## Array#divide
require 'facets/array/divide'
Divide into sub arrays at elemetns matching given pattern
r = ['a1','b1','a2','b2'].divide(/^a/)
r.assert == [['a1','b1'],['a2','b2']]
r = ['a1','b1','a2','b2'].divide(/^b/)
r.assert == [['a1'],['b1','a2'],['b2']]
r = ['a1','b1','a2','b2'].divide(/c/)
r.assert = [['a1','b1','a2','b2']]
@@ -0,0 +1,14 @@
## Array#duplicates
require 'facets/array/duplicates'
Return list of duplicate elements.
[1,1,2,3].duplicates #=> [1]
Return list of those duplicated min times.
[1,1,2,3,2,4,5,4,2].duplicates(3) #=> [2]
@@ -0,0 +1,12 @@
## Array#each_overlap
require 'facets/array/each_overlap'
Iterate over each slice where the last n values of a preceding slice
overlap with the first n values of the following slice. The value of n
is specified by the second `overlap` argument.
a, r = [1,2,3,4,5], []
a.each_overlap(2,1) { |x,y| r << [x,y] }
r.assert == [[1,2],[2,3],[3,4],[4,5]]
@@ -0,0 +1,13 @@
## Array#each_pair
require 'facets/array/each_pair'
Iterate over an array yielding with index and value.
result = {}
a = [:a,:b,:c,:d]
a.each_pair{|i,e| result[e] = i + 1}
result.assert == {a: 1, b: 2, c: 3, d: 4}
*NOTE* this is the inverse of `Array#each_with_index`, and is intended to provide
polymorphism with `Hash#each_pair`.
@@ -0,0 +1,12 @@
## Array#each_value
require 'facets/array/each_value'
This is an alias of Alias#each, and is provided to provide polymorphism with
Hash#each_value.
a = [ "a", "b", "c" ]
out = ''
a.each_value {|x| out += "#{x} -- " }
out.assert == 'a -- b -- c -- '
@@ -0,0 +1,13 @@
## Array#entropy
require 'facets/array/entropy'
Shannon's entropy for an array - returns the average
bits per symbol required to encode the array.
Lower values mean less "entropy" - i.e. less unique
information in the array.
e = %w{ a b c d e e e }.entropy
("%.3f" % e) #=> "2.128"
@@ -0,0 +1,13 @@
## Array#extract_options!
require 'facets/array/extract_options'
Extracts options from a set of arguments. Removes and returns the last
element in the array if it's a hash, otherwise returns a blank hash.
def options(*args)
args.extract_options!
end
options(1, 2).assert == {}
options(1, 2, :a => :b).assert == {:a=>:b}
@@ -0,0 +1,11 @@
## Array#first!
require 'facets/array/indexable'
Alias for shift, which removes and returns the first element in an array
a = ['a','y','z']
a.first!.assert == 'a'
a.assert ['y','z']
@@ -0,0 +1,11 @@
## Array#thru
require 'facets/array/from'
Returns elements from index i until the end.
%w{W o r l d}.from(3).assert == %w{l d}
Returns entire array for i > length
%w{W o r l d}.from(7).assert == nil
@@ -0,0 +1,11 @@
## Array#ideal_entropy
require 'facets/array/entropy'
Returns the maximum possible Shannon entropy of the array
with given size assuming that it is an "order-0" source
(each element is selected independently of the next).
%w{ a b }.ideal_entropy.assert == 1.0
%w{ a a b b }.ideal_entropy.assert == 2.0
@@ -0,0 +1,11 @@
## Array#intersection
require 'facets/array/intersection'
Returns the values in common for an array set (nil, single value/object, or range).
[1,2].intersection.assert == nil
[1..10,11..20].intersection.assert == nil
[10,1..10].intersection.assert == 10
[1..10,5,5..8,4..8].intersection.assert == 5
[1..10, 5..8, 5..10 ].intersection.assert == (5..8)
@@ -0,0 +1,11 @@
## Array#last!
require 'facets/array/indexable'
Alias for pop, which removes and returns the last element in an
array.
a = [1,2,3]
a.last!.assert == 3
a.assert == [1,2]
@@ -0,0 +1,23 @@
# Array#median
require 'facets/array/median'
Determines the _sorted_ middle element.
a = %w{a a b b c c c}
a.median #=> "b"
When there are an even number of elements, the greater
of the two middle elements is given.
a = %w{a a b b c c c d}
a.median #=> "c"
An offset can be supplied to get an element relative
to the middle.
a = %w{a a b b c c c d}
a.median(-1) #=> "b"
The the array is empty, +nil+ is returned.
@@ -0,0 +1,15 @@
## Array#merge
require 'facets/array/merge'
a = [1,2,3]
b = [3,4,5]
a.merge(b).assert == [1,2,3,4,5]
## Array#merge!
a = [1,2,3]
b = [3,4,5]
a.merge!(b)
a.assert == [1,2,3,4,5]
@@ -0,0 +1,10 @@
## Array#missing
require 'facets/array/missing'
Determine the 'holes' in the values of an array. Returns the missing elements
in an array set.
[1,3..3].missing.assert == [2]
[1..5,10..12].missing.assert == [6..9]
[100, 9..11, 14, 1..5, 16, 10..12, 17..17].missing.assert == [6..8, 13, 15, 18..99]
@@ -0,0 +1,9 @@
## Array#mode
require 'facets/array/mode'
In Statistics, mode is the value that occurs most frequently in a given set of
data. This method returns an array in case there is a tie.
[1, 1, 2, 3].mode.assert == [1]
[1, 1, 2, 2, 3].mode.assert == [1,2]
@@ -0,0 +1,16 @@
## Array#nonuniq
require 'facets/array/nonuniq'
Returns a list of non uniq elements.
a = [1, 1, 2, 2, 3, 4, 5]
a.nonuniq.sort.assert == [1, 2]
## Array#nonuniq!
Same as #nonuniq but acts in place.
a = [1, 1, 2, 2, 3, 4, 5]
a.nonuniq!
a.sort.assert == [1,2]
@@ -0,0 +1,8 @@
## Array#not_empty?
require 'facets/array/not_empty'
Array is not empty?
[1,2].assert.not_empty?
[].refute.not_empty?
@@ -0,0 +1,18 @@
## Array#occur
require 'facets/array/occur'
Returns a list of elements that occur +n+ times.
If +n+ is a Range then returns elements that occur a number
of time within the range.
a = [:a,:b,:a]
a.occur(1).assert == [:b]
a.occur(2).assert == [:a]
a = [:a,:b,:a]
a.occur(1..2).assert == [:a,:b]
a = [:a,:b,:a]
a.occur{ |n| n % 2 == 0 }.assert == [:a]
@@ -0,0 +1,13 @@
## Array#occurrence
require 'facets/array/occurrence'
Create a hash of each uniq element of the array
and how many time each appears.
r = [:a,:a,:b,:c,:c,:c].occurrence
r.assert == { :a => 2, :b => 1, :c => 3 }
r = [2,2,3,4,4,4].occurrence{|i| i % 2}
r.assert == { 0 => 5, 1 => 1 }
@@ -0,0 +1,9 @@
## Array#only
require 'facets/array/only'
[5].only.assert == 5
[nil].only.assert == nil
expect(IndexError){ [].only }
expect(IndexError){ [1,2,3].only }
@@ -0,0 +1,10 @@
## Array#**
require 'facets/array/op_pow'
Alias for Array#product
NOTE: This method is not a common core extension and is not loaded automatically
when using ```require 'facets'```
([1,2] ** [3,4]).assert == [[1, 3], [1, 4], [2, 3], [2, 4]]
@@ -0,0 +1,28 @@
## Array#pad
require 'facets/array/pad'
r = [0,1,2,3].pad(7,"x")
x = [0,1,2,3,"x","x","x"]
r.assert == x
negative index
r = [0,1,2,3].pad(-7,"n")
x = ["n","n","n",0,1,2,3]
r.assert == x
## Array#pad!
a = [0,1,2,3]
r = a.pad!(6,"y")
x = [0,1,2,3,"y","y"]
r.assert == x
negative index
a = [0,1,2,3]
r = a.pad!(-6,"q")
x = ["q","q",0,1,2,3]
r.assert == x
@@ -0,0 +1,10 @@
## Array#peek
require 'facets/array/peek'
Provide an index to inspect the array from back to front.
[1,2,3].peek .assert == 3
[1,2,3].peek(1) .assert == 2
[1,2,3].peek(-1).assert == 1
@@ -0,0 +1,15 @@
## Array#poke
require 'facets/array/poke'
Put an object on the bottom of the stack (front of the array).
a = [2,3]
a.poke(1)
a.assert == [1,2,3]
Or supply an index and #poke works like insert.
a = [1,3]
a.poke(2,1)
a.assert == [1,2,3]
@@ -0,0 +1,8 @@
## Array#probability
require 'facets/array/probability'
Generates a hash mapping each unique element in the array to the relative
frequency, i.e. the probability, of it's appearance.
[:a, :b, :c, :c].probability.assert == {a: 0.25, b: 0.25, c: 0.50}
@@ -0,0 +1,10 @@
## Array#pull
require 'facets/array/pull'
Alias for Array#shift which removes an object off the first slot of an array.
This is the opposite of pop.
a = [1,2,3]
a.pull.assert == 1
a.assert == [2,3]
@@ -0,0 +1,10 @@
## Array#recurse
require 'facets/array/recurse'
Apply a block to array, and recursively apply that block to each sub-array
or +type+.
a = ["a", ["b", "c", nil], nil]
r = a.recurse{|a| a.compact!}
r.assert == ["a", ["b", "c"]]
@@ -0,0 +1,20 @@
## Array#recursively
require 'facets/array/recursively'
Apply a method to array, and recursively apply that method to each sub-array
or given +types+.
By default the sub-types are passed through unaffected. Passing a block
to #recursively can be used to change this.
each
r = []
[1,2,['a','b'], 3].recursively.each{|v| r << v}
r.assert = [1,2,'a','b', 3]
map
arr = ['foo','bar',['a','b']]
arr.recursively.map{|v| v.to_sym}.assert == [:foo,:bar,[:a,:b]]
@@ -0,0 +1,10 @@
## Array#reject_values
require 'facets/array/reject_values'
Non-destructive form of Array#delete_values. Unlike delete_values this method
returns a new array.
a = [1,2,3,4,5]
a.reject_values(2,4).assert == [1,3,5]
a.assert [1,2,3,4,5]
@@ -0,0 +1,14 @@
## Array#splice
require 'facets/array/splice'
Splice acts as a combination of #slice! and #store. If one argument is given it
calls #slice!, if two are given it calls #store.
a = [1,2,3]
a.splice(1).assert == 2
a.assert == [1,3]
b = [1,2,3]
b.splice(1,4).assert == 4
b.assert == [1,4,3]
@@ -0,0 +1,13 @@
## Array#split
require 'facets/array/split'
Split on matching pattern. Unlike #divide, this does not include matching
elements.
['a','b','c'].split('b').assert == [['a'],['c']]
a = ['a1','a2','b1','a3','b2','a4']
a.split(/^b/).assert == [['a1','a2'],['a3'],['a4']]
@@ -0,0 +1,14 @@
## Array#squeeze!
require 'facets/array/squeeze'
Destructive version of Enumerable#squeeze.
a = [1,2,2,3,3,2,1]
a.squeeze!
a.assert == [1,2,3,2,1]
a = [1,2,2,3,3,2,1]
a.squeeze!(3)
a.assert == [1,2,2,3,2,1]
@@ -0,0 +1,16 @@
## Array#step
require 'facets/array/step'
Iterate over every nth element of an array. Without a block, it returns an
Enumerator.
r = []
[:a, :b, :c, :d].step(2) { |x| r << x }
r.assert == [:b, :d]
[:a, :b, :c, :d].step(1).to_a.assert == [:a, :b, :c, :d]
[:a, :b, :c, :d].step(2).to_a.assert == [:b, :d]
[:a, :b, :c, :d].step(3).to_a.assert == [:c]
[:a, :b, :c, :d].step(4).to_a.assert == [:d]
[:a, :b, :c, :d].step(5).to_a.assert == []
@@ -0,0 +1,9 @@
## Array#store
require 'facets/array/store'
Store a value at a given index. Store is an alias for #[]=
a = []
a.store(1,"A")
a[1].assert == "A"
@@ -0,0 +1,12 @@
## Array#thru
require 'facets/array/thru'
Fetch Values from a start index thru an end index.
[1,2,3,4,5].thru(0,2) #=> [1,2,3]
[1,2,3,4,5].thru(2,4) #=> [3,4,5]
[1,2,3,4,5].thru(2) #=> [1,2,3]
[1,2,3,4,5].thru(4) #=> [1,2,3,4,5]
@@ -0,0 +1,11 @@
## Array#to_h
require 'facets/array/to_h'
Convert an associative array to a Hash. Each element of the associative array
should be a 1 or 2 element array
Note this is built into ruby as of 2.1.0
arr = [[:a, 1], [:b, 2]]
arr.to_h.assert == {:a => 1, :b => 2}
@@ -0,0 +1,20 @@
## Array#traverse
require 'facets/array/traverse'
Construct a new array created by traversing the array and its sub-arrays,
executing the given block on the elements.
h = ['A', 'B', ['X', 'Y']]
g = h.traverse{ |e| e.downcase }
g.assert = ['a','b',['x', 'y']]
## Array#traverse!
Like #traverse, but will change the array in place.
h = ['A', 'B', ['X', 'Y']]
h.traverse!{ |e| e.downcase }
h.assert = ['a','b',['x', 'y']]
@@ -0,0 +1,16 @@
## Array#uniq_by!
require 'facets/array/uniq_by'
Like #uniq, but determines uniqueness based on a given block.
As can be seen from the examples, order is significant.
r = (-5..5).to_a
r.uniq_by!{ |i| i*i }
r.assert == [-5, -4, -3, -2, -1, 0]
r = (-5..5).to_a.reverse
r.uniq_by!{ |i| i*i }
r.assert == [5, 4, 3, 2, 1, 0]
@@ -0,0 +1,14 @@
## Array#unique_permutation
require 'facets/array/unique_permutation'
Enumerates permutation of Array. Unlike Array#permutation, there are no
duplicates in generated permutations. Instead elements must be comparable
p = [1,1,2,2,3].unique_permutation(2).to_a
e = [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2]]
p.assert == e
[1,1,2,3].unique_permutation.to_a.assert != [1,1,2,3].permutation.to_a
[1,1,2,3].unique_permutation.to_a.assert == [1,1,2,3].permutation.to_a.uniq
@@ -0,0 +1,8 @@
## Array::zip
require 'facets/array/zip'
Class level rendition of Array#zip
Array.zip([1,2], [3,4]).assert == [[1,3],[2,4]]
@@ -0,0 +1,57 @@
The caller.rb script includes a few related methods.
We will use the following code to demonstrate their use.
require 'facets/binding/caller'
a = 1
b = 2
x = "hello"
@bind = binding
@line = __LINE__ # the line number must be updated if it moves
@file = __FILE__ # why does it equal basename only?
## Binding#caller
@bind.caller
## Binding#callstack
@bind.callstack.assert.is_a?(Array)
## Binding#__LINE__
@bind.__LINE__.assert == @line - 1
## Binding#__FILE__
@bind.__FILE__.assert == @file
## Binding#__DIR__
@bind.__DIR__.assert == File.dirname(@file)
## Binding#__callee__
This only works for certain versions.
def alternate_callee
binding
end
unless RUBY_VERSION < "1.9"
alternate_callee.__callee__.assert == :alternate_callee
end
## Binding#__method__
This only works for certain versions.
def alternate_method
binding
end
unless RUBY_VERSION < "1.8.7"
alternate_method.__method__.assert == :alternate_method
end
@@ -0,0 +1,10 @@
## Binding#defined?
require 'facets/binding/defined'
a = 1
b = 2
x = "hello"
binding.assert.defined?("x")
@@ -0,0 +1,16 @@
## Binding#[]
require 'facets/binding/op'
a = 1
b = 2
x = "hello"
binding["x"].assert == "hello"
## Binding#[]=
binding["x"] = "goodbye"
binding["x"].assert == "goodbye"
@@ -0,0 +1,6 @@
## Binding#self
require 'facets/binding/self'
binding.self == self
@@ -0,0 +1,13 @@
## Class#descendants
require 'facets/class/descendants'
The `Class#descendants` method returns a list of classes that are subclasses
of a class.
a = Class.new
b = Class.new(a)
c = Class.new(b)
a.descendants.assert == [b,c]
@@ -0,0 +1,11 @@
## Class#to_proc
require 'facets/class/to_proc'
The `Class#to_proc` method provides a convenient way to create new
instances from a set of initialization data.
person = Struct.new(:name)
people = ["joe"].map(&person)
people[0].name.assert == "joe"
@@ -0,0 +1,11 @@
## Comparable#at_least
require 'facets/comparable/at_least'
Limits a number to at least a given amount.
3.at_least(4).assert == 4
4.at_least(4).assert == 4
5.at_least(4).assert == 5
This is similar to `#clip`.
@@ -0,0 +1,11 @@
## Comparable#at_most
require 'facets/comparable/at_most'
Limits a number to at most a given amount.
3.at_most(4).assert == 3
4.at_most(4).assert == 4
5.at_most(4).assert == 4
This is similar to `#cap`.
@@ -0,0 +1,13 @@
## Comparable#bound
require 'facets/comparable/bound'
The `#bound` method is an alias for `#clip`. It is the original name
of the method before the `clip`/`cap` duo was decided upon.
3.bound(4).assert == 4
4.bound(3,5).assert == 4
'd'.bound('c','e').assert == 'd'
@@ -0,0 +1,11 @@
## Comparable#cap
require 'facets/comparable/cap'
Put a cap on what a number can be.
3.cap(4).assert == 3
4.cap(4).assert == 4
5.cap(4).assert == 4
This is very similar to `#at_most`.
@@ -0,0 +1,29 @@
## Comparable#clip
require 'facets/comparable/clip'
Given a single argument `#clip` behaves like `#at_least`.
3.clip(4).assert == 4
4.clip(4).assert == 4
5.clip(4).assert == 5
With two arguments `#clip` puts both a lower and an upper limit
on the return value.
4.clip(3,5).assert == 4
3.clip(3,5).assert == 3
5.clip(3,5).assert == 5
2.clip(3,5).assert == 3
6.clip(3,5).assert == 5
This almost works on characters (using ASCII order).
'd'.clip('c','e').assert == 'd'
'c'.clip('c','e').assert == 'c'
'e'.clip('c','e').assert == 'e'
'b'.clip('c','e').assert == 'c'
'f'.clip('c','e').assert == 'e'
This method is aliases as `bound`.
@@ -0,0 +1,18 @@
## Comparable#cmp
require 'facets/comparable/cmp'
On integers
3.cmp(4).assert == -1
3.cmp(3).assert == 0
3.cmp(2).assert == 1
On strings
"abc".cmp("abc").assert == 0
"abc".cmp("abcd").assert == -1
"abcd".cmp("abc").assert == 1
"abc".cmp("bcd").assert == -1
"bcd".cmp("abc").assert == 1
@@ -0,0 +1,32 @@
## Comparable.[]
require 'facets/comparable/op_get'
Allows `Comparable` to be included as a *parmetric mixin*. This makes it easy to define
a class as comparable on a limited set of attributes.
c = Class.new do
include Comparable[:a,:b]
attr_accessor :a, :b
def initialize(a,b)
@a=a; @b=b
end
end
a = [c.new(10,20),c.new(10,30)]
a.sort.assert == a
a = [c.new(10,30),c.new(10,20)]
a.sort.assert == a.reverse
a = [c.new(10,10),c.new(20,10)]
a.sort.assert == a
a = [c.new(20,10),c.new(10,10)]
a.sort.assert == a.reverse
a = [c.new(10,30),c.new(20,10)]
a.sort.assert == a
@@ -0,0 +1,24 @@
## Dir#ascend
require 'facets/dir/ascend'
path = 'A/B/C'
tmp = []
Dir.ascend(path) do |d|
tmp << d
end
tmp.assert == %w{A/B/C A/B A}
Exclude current path.
tmp = []
Dir.ascend(path, false) do |d|
tmp << d
end
tmp.assert == %w{A/B A}
@@ -0,0 +1,16 @@
## Dir#descend
require 'facets/dir/descend'
Descend thru a file path.
path = 'A/B/C'
tmp = []
Dir.descend(path) do |d|
tmp << d
end
tmp.assert == %w{A A/B A/B/C}
@@ -0,0 +1,41 @@
## Dir#multiglob
Dir#multglob is like Dir#glob but it can handle more than one match
parameter at a time.
Given a directory 'multiglob' containing:
A.txt
A/B.txt
A/B/C.txt
We can use #multglob to find multiple matches.
require 'facets/dir/multiglob'
x = %w{
multiglob/A
multiglob/A.txt
}
r = Dir.multiglob('multiglob/A', 'multiglob/A.*').sort
r.assert == x
## Dir#multiglob_r
Dir#multiglob_r is like #multiglob but automatically searches
directories recrusively.
x = %w{
multiglob/A
multiglob/A/B
multiglob/A.txt
multiglob/A/B.txt
multiglob/A/B/C.txt
}.sort
r = Dir.multiglob_r('multiglob/*').sort
r.assert == x
@@ -0,0 +1,6 @@
## Dir#parent
require 'facets/dir/parent'
Dir.assert.parent?("a/b/c", "a/b/c/d")
@@ -0,0 +1,41 @@
## Dir#recurse
Given a directory 'recurse' containing:
A.txt
A/B.txt
A/B/C.txt
We can use #recurse to gather a complete recursive
list of all the entries.
require 'facets/dir/recurse'
x = %w{
recurse/A
recurse/A.txt
recurse/A/B
recurse/A/B.txt
recurse/A/B/C.txt
}.sort
r = Dir.recurse('recurse').sort
r.assert == x
## Dir#ls_r
Dir#ls_r is an alias for #recurse which can also be used to gather
a complete recursive list of all the entries.
x = %w{
recurse/A
recurse/A.txt
recurse/A/B
recurse/A/B.txt
recurse/A/B/C.txt
}.sort
r = Dir.ls_r('recurse').sort
r.assert == x
@@ -0,0 +1,19 @@
## Exception#detail
require 'facets/exception/detail'
Provides a more detailed error message.
begin
raise ArgumentError
rescue ArgumentError => err
r = err.detail
r.assert.include?('ArgumentError')
end
The output of `#detail` looks something like this:
ArgumentError: ArgumentError
example.rb:4:in `<main>'
LOGGED FROM: example.rb:6:in `rescue in <main>'
@@ -0,0 +1,6 @@
# Requiring Facets
We should be able to require all the core libraries by simply calling,
require 'facets'
@@ -0,0 +1,13 @@
## FileTest#root?
require 'facets/filetest/root'
Returns true if the given directory is the file system root.
Obviously on a Linux system them means `/`.
FileTest.assert.root?('/')
Anything else will return false.
FileTest.refute.root?('/home')
@@ -0,0 +1,21 @@
## Hash#recursively
require 'facets/hash/recursively'
h = {:a=>1, :b=>{:c=>3}}
Without a block passed to the `#recursively` method, the block passed to the
enumeratig method will be used for both recursive and non-recursive yields.
r = h.recursively.map{ |k,v| [k.to_s, v] }
r.assert == [['a', 1], ['b', [['c', 3]]]]
But if we pass a block to `#recursively` we can treat the recusive yield
separately, which in turn allows us to manipulate the non-recursive
yields with a different procedure.
r = h.recursively{ |k,v| [k.to_s, v] }.map{ |k,v| [k.to_s,v.to_s] }
r.assert == [['a','1'], ['b', [['c', '3']]]]
@@ -0,0 +1,95 @@
# Indexable
Indexable it a module, like Enumerable, but provides methods specific
to an indexable collection. The Indexable module can be used independently,
but Facets can also automatically use it to extend Array.
require 'facets/array/indexable'
## Indexable#head
a = [1,2,3,4,5]
a.head.assert = [1]
## Indexable#tail
a = [1,2,3,4,5]
a.tail.assert = [2,3,4,5]
## Indexable#foot
a = [1,2,3,4,5]
a.foot.assert = [5]
## Indexable#body
a = [1,2,3,4,5]
a.body.assert = [1,2,3,4]
## Indexable#mid
a = [1,2,3,4,5]
b = [1,2,3,4,5,6]
a.mid.assert = 3
b.mid.assert = 4
a.mid(1).assert = 4
b.mid(1).assert = 5
b.mid(2).assert = 6
b.mid(-1).assert = 3
## Indexable#middle
a = [1,2,3,4,5]
b = [1,2,3,4,5,6]
a.middle.assert = [3]
b.middle.assert = [3,4]
## Indexable#thru
[0,1,2,3,4,5].thru(2,4).assert = [2,3,4]
[0,1,2,3,4,5].thru(0,1).assert = [0,1]
## Indexable#first=
a = [1,2]
a.first = 0
a.assert = [0,2]
## Indexable#last=
a = [1,2]
a.last = 3
a.assert = [1,3]
## Indexable#ends
[1,2,3,4,5].ends.assert = 4
## Indexable#pos
a = [1,2,3,4,5]
a.pos(1).assert = 0
a.pos(-1).assert = 4
## Indexable#range
a = [1,2,3,4,5]
b = [1,2,3,4,5,6]
a.range.assert = (0..4)
b.range.assert = (0..5)
a.range(2,4).assert = (1..3)
b.range(2,3).assert = (1..2)
b.range(4,2).assert = (3..1)
## Indexable#first!
a = [1,2,3]
a.first!.assert = 1
a.assert = [2,3]
## Indexable#last!
a = [1,2,3]
a.last!.assert = 3
a.assert = [1,2]
@@ -0,0 +1,46 @@
## Integer#bit
require 'facets/integer/bitmask'
0.bit(0).assert == 1
0.bit(1).assert == 2
0.bit(2).assert == 4
0.bit(3).assert == 8
Negate
1.bit(~0).assert == 0
2.bit(~1).assert == 0
4.bit(~2).assert == 0
8.bit(~3).assert == 0
## Integer#bit?
a = 8
assert(! a.bit?(0))
assert(! a.bit?(1))
assert(! a.bit?(2))
assert( a.bit?(3))
assert(! a.bit?(4))
assert(! a.bit?(5))
## Integer#bit_clear
1.bit_clear(0).assert == 0
2.bit_clear(1).assert == 0
4.bit_clear(2).assert == 0
8.bit_clear(3).assert == 0
## Integer#bitmask
a = 1
m = Bit(4)
a = a.bitmask(m)
a.assert == 17
assert( a.bitmask?(m) )
## Kernel#Bit
n = Bit(4)
n.assert == 16
@@ -0,0 +1,10 @@
## Integer#factorial
require 'facets/integer/factorial'
0.factorial.assert == 1
1.factorial.assert == 1
2.factorial.assert == 2
3.factorial.assert == 6
4.factorial.assert == 24
@@ -0,0 +1,7 @@
## Numeric#length
require 'facets/numeric/length'
10.length.assert == 10
10.0.length.assert == 10
@@ -0,0 +1,12 @@
## Integer#multiple?
require 'facets/integer/multiple'
Is a number an even multiple of another?
1.multiple?(2) #=> false
5.multiple?(3) #=> false
2.multiple?(2) #=> true
6.multiple?(3) #=> true
@@ -0,0 +1,11 @@
## Integer#of
require 'facets/integer/of'
Similar to `Enumerable#map` and `Enumerable#collect` but for a number
of times.
a = 4
b = a.of{ |i| i*2 }
b.assert == [0,2,4,6]
@@ -0,0 +1,11 @@
## Integer#ordinal
require 'facets/integer/ordinal'
Produce the ordinal name of a number.
1.ordinal.assert == '1st'
2.ordinal.assert == '2nd'
3.ordinal.assert == '3rd'
4.ordinal.assert == '4th'
@@ -0,0 +1,9 @@
## Integer#times_collect
require 'facets/integer/of'
a = 4
b = a.times_collect{ |i| i*2 }
b.assert == [0,2,4,6]
See `Integer#of` which is an alias.
@@ -0,0 +1,7 @@
## MatchData#match
require 'facets/matchdata/match'
md = /X(a)(b)(c)X/.match("YXabcXY")
md.match.assert == "XabcX"
@@ -0,0 +1,7 @@
## MatchData#matchset
require 'facets/matchdata/matchset'
md = /(bb)(cc(dd))(ee)/.match "XXaabbccddeeffXX"
md.matchset.assert == ["XXaa", [["bb"], ["cc", ["dd"]], ["ee"]], "ffXX"]
@@ -0,0 +1,10 @@
## MatchData#matchtree
require 'facets/matchdata/matchtree'
md = /(bb)(cc(dd))(ee)/.match "XXaabbccddeeffXX"
md.matchtree.assert == [["bb"], ["cc", ["dd"]], ["ee"]]
md = /(bb)c(c(dd))(ee)/.match "XXaabbccddeeffXX"
md.matchtree.assert == [["bb"], "c", ["c", ["dd"]], ["ee"]]
@@ -0,0 +1,43 @@
## Class#cattr
require 'facets/module/cattr'
class CAttrMockObject
def initialize
@@a = 10
end
def b ; @@b ; end
end
Exception.refute.raised? do
CAttrMockObject.class_eval{ cattr :a }
end
t = CAttrMockObject.new
t.a.assert == 10
## Class#cattr_reader
Exception.refute.raised? do
CAttrMockObject.class_eval { cattr_reader :a }
end
t = CAttrMockObject.new
t.a.assert == 10
## Class#cattr_writer
Exception.refute.raised? do
CAttrMockObject.class_eval { cattr_writer :b }
end
t = CAttrMockObject.new
t.b = 5
t.b.assert == 5
## Class#cattr_accessor
Exception.refute.raised? do
CAttrMockObject.class_eval { cattr_accessor :c }
end
t = CAttrMockObject.new
t.c = 50
t.c.assert == 50
@@ -0,0 +1,64 @@
## Module#class_extend
require 'facets/module/class_extend'
Module using class_extend
mix = Module.new do
def i ; end
class_extend do
def n ; 42 ; end
def s ; self ; end
end
end
Extends module
mix.n.assert == 42
mix.s.assert == mix
Has expected methods
mix.instance_methods.map{ |m| m.to_s }.assert == ['i']
transfers class methods to including class
mod = Module.new do
include mix
end
mod.n.assert == 42
mod.s.assert == mod
Doesn't overwrite instance methods
c = Class.new do
include mix
def n ; 11 ; end
end
c.new.n.assert == 11
Is overridable via including module
mod = Module.new do
include mix
class_extend do
def n ; super + 1 ; end
end
end
mod.n.assert == 43 # notice the difference!
mod.s.assert == mod
Transfers class methods thru multiple levels of include
alt = Module.new do
include mix
class_extend do
def n ; super + 1 ; end
end
end
mod = Module.new do
include alt
end
mod.n.assert == 43
mod.s.assert == mod
@@ -0,0 +1,126 @@
## Module#class_inheritor
require 'facets/module/class_inheritor'
Subclass with inheritor
c = Class.new do
class_inheritor :koko, [], :+
koko! << 1
end
d = Class.new(c) do
class_inheritor :koko, [], :+
koko! << 2
end
We can see that
c.koko!.assert == [1]
c.koko.assert == [1]
d.koko!.assert == [2]
d.koko.assert == [1,2]
Subclass without class_inheritor
c = Class.new do
class_inheritor :koko, [], :+
koko! << 1
end
d = Class.new(c)
Likewise
c.koko!.assert == [1]
c.koko.assert == [1]
d.koko!.assert == []
d.koko.assert == [1]
Include module with class_inheritor
c = Class.new do
class_inheritor :x, {}, :merge
x![:a] = 1
end
m = Module.new do
class_inheritor :x, {}, :merge
x![:b] = 2
end
d = Class.new(c) do
include m
class_inheritor :x, {}, :merge
x![:c] = 3
end
Then
c.x.assert == {:a=>1}
m.x[:b].assert == 2
d.x.assert == {:a=>1,:b=>2,:c=>3}
c.x[:a].assert == 1
c.x[:b].assert == nil
c.x[:c].assert == nil
d.x[:a].assert == 1
d.x[:b].assert == 2
d.x[:c].assert == 3
And
d.x![:d] = 4
d.x[:d].assert == 4
Using #concat as the class_inheritor operator
c = Class.new do
class_inheritor :relations, [], :concat
end
d = Class.new(c) do
#class_inheritor :relations, [], :concat
end
c.relations! << 1
c.relations! << 2
d.relations! << 3
Notice
c.relations.assert == [1,2]
d.relations.assert == [1,2,3]
On Module
m = Module.new do
class_inheritor :koko, [], :+
koko! << 1
end
c1 = Class.new do
include m
#class_inheritor :koko, [], :+
koko! << 2
koko! << 3
end
c2 = Class.new do
include m
#class_inheritor :koko, [], :+
koko! << 4
end
m.koko.assert == [1]
c1.koko.assert == [1,2,3]
c2.koko.assert == [1,4]
@@ -0,0 +1,30 @@
## Module#copy_inheritor
require 'facets/module/copy_inheritor'
We see `#copy_inheritor` in action.
m = Module.new do
copy_inheritor :koko, []
koko << 1
end
c1 = Class.new do
include m
#inheritor :koko, [], :+
koko << 2
koko << 3
end
c2 = Class.new do
include m
#inheritor :koko, [], :+
koko << 4
end
And the result.
m.koko.assert == [1]
c1.koko.assert == [1,2,3]
c2.koko.assert == [1,4]
@@ -0,0 +1,31 @@
## Module#include_as
require 'facets/module/include_as'
Module#include_as utilizes #method_space to allow modules to be included
within a separated namespace.
module T
def t ; "Tt" ; end
def r ; "Tr" ; end
def q ; super ; end
end
class N
def q ; "Nq" ; end
end
class X < N
include_as :test => T
def n ; test.inspect ; end
def m ; test.t ; end
def o ; test.r ; end
end
x = X.new
x.m.assert == "Tt"
x.o.assert == "Tr"
x.q.assert == "Nq"
@@ -0,0 +1,24 @@
## Module#method_space
require 'facets/module/method_space'
class A
attr_writer :x
method_space :inside do
def x; @x; end
end
end
a = A.new
a.x = 10
We can see that the `inside` method space has access to the instance space.
a.inside.x #=> 10
But there is no reader in the instance space.
expect NoMethodError do
a.x
end
@@ -0,0 +1,9 @@
## String#acronym
require 'facets/string/acronym'
Transform a string into an acronym.
'abc'.acronym.assert == 'a'
'abc def'.acronym.assert == 'ad'
'abc def ghi'.acronym.assert == 'adg'
@@ -0,0 +1,34 @@
## String#align
require 'facets/string/align'
Align a string to a given position. This method simply delegates
to the other three more specific align methods.
"xxx".align(:right, 9).assert == " xxx"
"xxx".align(:left, 9).assert == "xxx "
## String#align_right
Align a string to the right. The default alignment separation is
a newline ("\n"). This can be changed as can be the padding string
which defaults to a single space (' ').
"xxx".align_right(9).assert == " xxx"
## String#align_left
Align a string to the left. The default alignment separation is a
newline ("\n"). This can be changed as can be the padding string
which defaults to a single space (' ').
"xxx".align_left(9).assert == "xxx "
## String#align_center
Centers each line of a string. The default alignment separation is
a new line ("\n"). This can be changed as can be the padding string
which defaults to a single space (' ').
"xxx".align_center(9).assert == " xxx "
@@ -0,0 +1,37 @@
## String#ascii_only
require 'facets/string/ascii_only'
Return a new string with non-ASCII characters removed.
'abc'.ascii_only.assert == 'abc'
'中文123'.ascii_only.assert == '123'
Return a new string with non-ASCII characters replaced.
'中文123'.ascii_only('!').assert == '!!123'
## String#ascii_only!
Modifiy string with non-ASCII chracters removed.
s = 'abc'
s.ascii_only!
s.assert == 'abc'
s = '中文123'
s.ascii_only!
s.assert == '123'
Modifiy string with non-ASCII chracters replaced.
s = 'abc'
s.ascii_only!('!')
s.assert == 'abc'
s = '中文123'
s.ascii_only!('!')
s.assert == '!!123'
@@ -0,0 +1,35 @@
## String#bracket
require 'facets/string/bracket'
'X'.bracket('#').assert == '#X#'
'X'.bracket('x','!').assert == 'xX!'
'X'.bracket('{','}').assert == '{X}'
'X'.bracket('<').assert == '<X>'
'X'.bracket('(').assert == '(X)'
'X'.bracket('[').assert == '[X]'
'X'.bracket('{').assert == '{X}'
## String#bracket!
a = 'X' ; a.bracket!('#')
a.assert == '#X#'
a = 'X' ; a.bracket!('x','!')
a.assert == 'xX!'
a = 'X' ; a.bracket!('{','}')
a.assert == '{X}'
a = 'X' ; a.bracket!('<')
a.assert == '<X>'
a = 'X' ; a.bracket!('(')
a.assert == '(X)'
a = 'X' ; a.bracket!('[')
a.assert == '[X]'
a = 'X' ; a.bracket!('{')
a.assert == '{X}'
@@ -0,0 +1,47 @@
## String#Camelcase
require 'facets/string/camelcase'
The `camelcase` method converts snakecase strings into camelcase strings.
"abc_xyz".camelcase.assert == "abcXyz"
Continious underscore characters are treated as if a single underscore
character.
"abc____xyz".camelcase.assert == "abcXyz"
Without an argument the first character is left alone.
"Camel_case".camelcase.assert == "CamelCase"
Passing +:upper+ or +true+ to #camelcase captializes the first letter.
This is known as upper-camelcase.
"Abc_xyz".camelcase(true).assert == "AbcXyz"
"Abc____xyz".camelcase(:upper).assert == "AbcXyz"
Where as passing +false+ or +:lower+ downcases the first character,
known as lower-camelcase.
"abc_xyz".camelcase(false).assert == "abcXyz"
"abc____xyz".camelcase(false).assert == "abcXyz"
"Abc_xyz".camelcase(:lower).assert == "abcXyz"
"Abc____xyz".camelcase(:lower).assert == "abcXyz"
By passing a match string to the method we can make +camelcase+
apply to each word in a string.
"abc xyz".camelcase(' ').assert == "abcXyz"
"abc xyz".camelcase(/\s/).assert == "abcXyz"
"abc\txyz".camelcase(/\s/).assert == "abcXyz"
"abc\nxyz".camelcase(/\s/).assert == "abcXyz"
## String#lower_camelcase
"abc_xyz".lower_camelcase.assert == "abcXyz"
## String#upper_camelcase
"abc_xyz".upper_camelcase.assert == "AbcXyz"
@@ -0,0 +1,27 @@
## String#capitalized?
String#capitalized? return true if a string begins with a capitalized
letter, false otherwise.
require 'facets/string/capitalized'
'Abc'.assert.capitalized?
## String#downcase?
In addition String#downcase? is provided which checks to see if the
whole string is composed of lowercase letters.
'abc'.assert.downcase?
#'abc'.assert.lowercase?
## String#upcase?
And String#upcase? which checks to see if the whole string is composed
of uppercase letters.
'ABC'.assert.upcase?
#'ABC'.assert.uppercase?
@@ -0,0 +1,11 @@
## String#characters
The String#chars methods simply splits a string into
an Array of character strings.
require 'facets/string/characters'
"abc".characters.assert == ["a","b","c"]
"ab\nc".characters.assert == ["a","b","\n","c"]

Some files were not shown because too many files have changed in this diff Show More