:toc: macro :toclevels: 5 :figure-caption!:
Refinements
¶ ↑
These refinements augment and enhance Ruby primitives so you can avoid https://alchemists.io/articles/ruby_antipatterns/#_monkey_patches[monkey patches]. They also allow you to develop clean and concise implementations while using less code. By refining your code, you can acquire the functionality you wish the core primitives had!
toc::[]
Features¶ ↑
Enhances the following objects:
-
Array
-
Binding
-
Data
-
DateTime
-
Hash
-
IO
-
LogDevice
-
Logger
-
Module
-
Object
-
Pathname
-
String
-
StringIO
-
Struct
-
Symbol
Requirements¶ ↑
. www.ruby-lang.org[Ruby]. . A solid understanding of https://alchemists.io/articles/ruby_refinements[refinements].
Setup¶ ↑
To install with security, run:
- source,bash
# 💡 Skip this line if you already have the public certificate installed. gem cert –add <(curl –compressed –location alchemists.io/gems.pem) gem install refinements –trust-policy HighSecurity
To install without security, run:
- source,bash
gem install refinements
You can also add the gem directly to your project:
- source,bash
bundle add refinements
Once the gem is installed, you only need to require it:
- source,ruby
require “refinements”
Usage¶ ↑
Requires¶ ↑
If no refinements are desired, then add the following to your ‘Gemfile` instead:
- source,ruby
gem “refinements”, require: false
…then require the specific refinement, as needed. Example:
- source,ruby
require “refinements/array” require “refinements/binding” require “refinements/data” require “refinements/date_time” require “refinements/hash” require “refinements/io” require “refinements/log_device” require “refinements/logger” require “refinements/module” require “refinements/object” require “refinements/pathname” require “refinements/string” require “refinements/string_io” require “refinements/struct” require “refinements/symbol”
Using¶ ↑
Much like including/extending a module, you’ll need to modify your object(s) to use the refinement(s):
- source,ruby
class Example
using Refinements::Array using Refinements::Binding using Refinements::Data using Refinements::DateTime using Refinements::Hash using Refinements::IO using Refinements::LogDevice using Refinements::Logger using Refinements::Module using Refinements::Object using Refinements::Pathname using Refinements::String using Refinements::StringIO using Refinements::Struct using Refinements::Symbol
end
Examples¶ ↑
The following sections demonstrate how each refinement enriches your objects with new capabilities.
Array¶ ↑
combinatorial?¶ ↑
Answers if an array is equal to another array when the elements are equal but in any order and/or subset.
- source,ruby
example = %w[a b c]
example.combinatorial? %w[a b c] # true example.combinatorial? %w[c a b] # true example.combinatorial? %w # true example.combinatorial? %w[c b] # true example.combinatorial? %w # false example.combinatorial? %w[z b c] # false example.combinatorial? %w[a b c d] # false example.combinatorial? [] # false
compress¶ ↑
Removes ‘nil` and empty objects without mutating itself. Answers itself if there is nothing to remove.
- source,ruby
object = Object.new example = [1, “blueberry”, nil, “”, [], {}, object]
- ].compress # [
- 1, 2].compress # [1, 2
-
example.compress # [1, “blueberry”, object] example # [1, “blueberry”, nil, “”, [], {}, object]
compress!¶ ↑
Removes ‘nil` and empty values while mutating itself. Answers `nil` if there is nothing to remove.
- source,ruby
object = Object.new example = [1, “blueberry”, nil, “”, [], {}, object]
[].compress! # nil [1, 2].compress! # nil example.compress! # [1, “blueberry”, object] example # [1, “blueberry”, object]
excluding¶ ↑
Removes given array or elements without mutating itself.
- source,ruby
- 1, 2, 3, 4, 5].excluding [4, 5
-
# [1, 2, 3]
- 1, 2, 3, 4, 5].excluding 4, 5 # [1, 2, 3
filter_find¶ ↑
⚠️ _This is deprecated and will be removed in Version 13.0.0._
Answers the first element which evaluates to true from a filtered collection.
- source,ruby
handlers = [
-> object { object if object == :b }, proc { false }, -> object { object if object == :a }
]
handlers.filter_find # Enumerator::Lazy handlers.filter_find { |handler| handler.call :a } # :a handlers.filter_find { |handler| handler.call :x } # nil
including¶ ↑
Adds given array or elements without mutating itself.
- source,ruby
- 1, 2, 3].including [4, 5
-
# [1, 2, 3, 4, 5]
- 1, 2, 3].including 4, 5 # [1, 2, 3, 4, 5
intersperse¶ ↑
Inserts additional elements, or an array, between all members of given array.
- source,ruby
- 1, 2, 3].intersperse :a # [1, :a, 2, :a, 3
- 1, 2, 3].intersperse :a, :b # [1, :a, :b, 2, :a, :b, 3
- 1, 2, 3].intersperse %i[a b c
- 1, 2, 3].intersperse :a, :b # [1, :a, :b, 2, :a, :b, 3
-
# [1, :a, :b, :c, 2, :a, :b, :c, 3]
many?¶ ↑
Answers true if an array has more than one element. Can take a block which evaluates as truthy or falsey.
- source,ruby
[1, 2].many? # true [1, 2, 3].many?(&:odd?) # true [1].many? # false [].many? # false
maximum¶ ↑
Answers the maximum extracted value from a collection of objects.
- source,ruby
Point = Data.define :x, :y points = [Point[x: 1, y: 2], Point[x: 0, y: 1], Point[x: 2, y: 3]]
points.maximum :x # 2 points.maximum :y # 3 points.maximum :z # undefined method ‘z’ for
mean¶ ↑
Answers mean/average all elements within an array.
- source,ruby
[].mean # 0 [5].mean # 5 [1, 2, 3].mean # 2 [1.25, 1.5, 1.75].mean # 1.5
minimum¶ ↑
Answers the minimum extracted value from a collection of objects.
- source,ruby
Point = Data.define :x, :y points = [Point[x: 1, y: 2], Point[x: 0, y: 1], Point[x: 2, y: 3]]
points.minimum :x # 0 points.minimum :y # 1 points.minimum :z # undefined method ‘z’ for
pad¶ ↑
Answers new array padded with given value up to a maximum size. Useful in situations where an array needs to be a specific size with padded values.
- source,ruby
- “a”].pad 0 # [“a”
- “a”].pad “-”, 3 # [“a”, “-”, “-”
-
%w[a b].pad “-”, 3 # [“a”, “b”, “-”]
pick¶ ↑
Answers value of first element that matches given key.
- source,ruby
array = [{name: “a”, label: “A”}, {name: “b”, label: “B”}, {name: “c”, label: “C”}]
array.pick :name # “a” array.pick :name, :label # [“a”, “A”] array.pick # nil [].pick :other # nil
pluck¶ ↑
Answers values of all elements that match given keys.
- source,ruby
array = [{name: “a”, label: “A”}, {name: “b”, label: “B”}, {name: “c”, label: “C”}]
array.pluck :name # [“a”, “b”, “c”] array.pluck :name, :label # [[“a”, “A”], [“b”, “B”], [“c”, “C”]] array.pluck # []
- ].pluck :other # [
replace_at¶ ↑
Answers mutated array where an element – at a specific index – is replaced by single or multiple elements.
- source,ruby
%i[a b c].replace_at 0, :x # [:x, :b, :c] %i[a b c].replace_at 1, :x # [:a, :x, :c] %i[a b c].replace_at 1, :x, :y # [:a, :x, :y, :c] %i[a b c].replace_at -1, :x # [:a, :b, :x]
ring¶ ↑
Answers a circular array which can enumerate before, current, after elements.
- source,ruby
example = [1, 2, 3] example.ring # “#<Enumerator: …>” example.ring { |(before, current, after)| puts “#{before} #{current} #{after}” }
# [3 1 2] # [1 2 3] # [2 3 1]
supplant¶ ↑
Answers mutated array where first target element found is replaced by single or multiple elements.
- source,ruby
%i[a b a].supplant :a, :z # [:z, :b, :a] %i[a b a].supplant :a, :z, :y # [:z, :y, :b, :a] %i[a b a].supplant :a, %i[z y] # [[:z, :y], :b, :a]
supplant_if¶ ↑
Answers mutated array where all target elements are replaced by single or multiple elements.
⚠️ Be aware that this can be an expensive operation on large arrays.
- source,ruby
%i[a b a].supplant_if :a, :z # [:z, :b, :z] %i[a b a].supplant_if :a, :z, :y # [:z, :y, :b, :z, :y] %i[a b a].supplant_if :a, %i[z y] # [[:z, :y], :b, [:z, :y]]
to_sentence¶ ↑
Answers a sentence using ‘“and”` as the default conjunction and `“, ”` as the default delimiter. Useful when building documentation, answering human readable error messages, etc.
- source,ruby
[].to_sentence # “” [“demo”].to_sentence # “demo” [“a”, :b].to_sentence # “a and b” [1, “a”, :b, 2.0, /w+/].to_sentence # “1, a, b, 2.0, and (?-mix:\w+)” %w[one two three].to_sentence # “one, two, and three” %w[eins zwei drei].to_sentence “und”, delimiter: “ ” # “eins zwei und drei”
💡 You can use a string or a symbol for the conjunction (i.e. ‘“and”` or `:and`).
to_usage¶ ↑
Further enhances ‘#to_sentence` by answering a sentence where all elements are inspected (i.e. `#inspect`) before turned into a sentence using `“and”` as the default conjunction and `“, ”` as the default delimiter. This is useful when providing detailed error messages and you need the types of all elements preserved.
- source,ruby
[].to_usage # “” [“demo”].to_usage # “"demo"” [“a”, :b].to_usage # “"a" and :b” [1, “a”, :b, 2.0, /w+/].to_usage # “1, "a", :b, 2.0, and /\w+/” %w[one two three].to_usage # “"one", "two", and "three"” %w[eins zwei drei].to_usage “und”, delimiter: “ ” # “"eins" "zwei" und "drei"”
💡 You can use a string or a symbol for the conjunction (i.e. ‘“and”` or `:and`).
Binding¶ ↑
[]¶ ↑
Allows you to obtain a local variable. This is an alias to ‘#local_variable_get`.
- source,ruby
a = 1 binding # 1 binding # ‘bogus’ is not defined (NameError)
[]=¶ ↑
Allows you to set a local variable. This is an alias to ‘#local_variable_set`.
- source,ruby
a = 1 binding = 5 binding = “bad”
binding # 5 binding # # ‘bogus’ is not defined (NameError)
local?¶ ↑
Allows you to check if local variable is defined. This is an alias to ‘#local_variable_defined?`.
- source,ruby
a = 1
binding.local? :a # true binding.local? :b # false
locals¶ ↑
Allows you to acquire all locally defined variables. This is an alias to ‘#local_variables`.
- source,ruby
binding.locals # []
a = 1 b = 2
binding.locals # [:a, :b]
Data¶ ↑
diff¶ ↑
Allows you to obtain the differences between two objects.
- source,ruby
implementation = Data.define :a, :b, :c
one = implementation.new a: 1, b: 2, c: 3 two = implementation.new a: 3, b: 2, c: 1 three = Data.define(:x, :y).new x: 1, y: 2
one.diff one # {} one.diff two # {:a=>[1, 3], :c=>[3, 1]} one.diff three # {:a=>[1, nil], :b=>[2, nil], :c=>[3, nil]} one.diff Object.new # {:a=>[1, nil], :b=>[2, nil], :c=>[3, nil]}
Any object that _is not_ the same type will have a ‘nil` value as shown in the last two examples.
DateTime¶ ↑
.utc¶ ↑
Answers new DateTime object for current UTC date/time.
- source,ruby
DateTime.utc # “#<DateTime: 2019-12-31T18:17:00+00:00 ((2458849j,65820s,181867000n),+0s,2299161j)>”
Hash¶ ↑
.infinite¶ ↑
Answers new hash where missing keys, even deeply nested, answer an empty hash.
- source,ruby
example = Hash.infinite example # {} example[:b] # {}
.with_default¶ ↑
Answers new hash where every top-level missing key has the same default value.
- source,ruby
example = Hash.with_default “” example # “”
example = Hash.with_default [] example # []
compress¶ ↑
Removes ‘nil` and empty objects without mutating itself. Answers itself if nothing to remove.
- source,ruby
object = Object.new example = {a: 1, b: “blueberry”, c: nil, d: “”, e: [], f: {}, g: object}
{}.compress # {} {a: 1, b: 2}.compress # {a: 1, b: 2} example.compress # {a: 1, b: “blueberry”, g: object} example # {a: 1, b: “blueberry”, c: nil, d: “”, e: [], f: {}, g: object}
compress!¶ ↑
Removes ‘nil` and empty objects while mutating itself. Answers `nil` if nothing to remove.
- source,ruby
object = Object.new example = {a: 1, b: “blueberry”, c: nil, d: “”, e: [], f: {}, g: object}
{}.compress! # nil {a: 1, b: 2}.compress! # nil example.compress! # {a: 1, b: “blueberry”, g: object} example # {a: 1, b: “blueberry”, g: object}
deep_merge¶ ↑
Merges deeply nested hashes together without mutating itself.
- source,ruby
example = {a: “A”, b: {one: “One”, two: “Two”}}
example.deep_merge b: {one: 1} # {a: “A”, b: {one: 1, two: “Two”}} example # {a: “A”, b: {one: “One”, two: “Two”}}
deep_merge!¶ ↑
Merges deeply nested hashes together while mutating itself.
- source,ruby
example = {a: “A”, b: {one: “One”, two: “Two”}}
example.deep_merge! b: {one: 1} # {a: “A”, b: {one: 1, two: “Two”}} example # {a: “A”, b: {one: 1, two: “Two”}}
deep_stringify_keys¶ ↑
Answers string keys of a nested hash without mutating itself. Does not handle nested arrays, though.
- source,ruby
example = {a: {b: 2}} example.deep_stringify_keys # {“a” => {“b” => 1}} example # {a: {b: 2}}
deep_stringify_keys!¶ ↑
Answers string keys of nested hash while mutating itself. Does not handle nested arrays, though.
- source,ruby
example = {a: {b: 2}} example.deep_stringify_keys! # {“a” => {“b” => 1}} example # {“a” => {“b” => 1}}
deep_symbolize_keys¶ ↑
Symbolizes keys of nested hash without mutating itself. Does not handle nested arrays, though.
- source,ruby
example = {“a” => {“b” => 2}} example.deep_symbolize_keys # {a: {b: 1}} example # {“a” => {“b” => 2}}
deep_symbolize_keys!¶ ↑
Symbolizes keys of nested hash while mutating itself. Does not handle nested arrays, though.
- source,ruby
example = {“a” => {“b” => 2}} example.deep_symbolize_keys! # {a: {b: 1}} example # {a: {b: 1}}
diff¶ ↑
Allows you to obtain the differences between two objects.
- source,ruby
one = {a: 1, b: 2, c: 3} two = {a: 3, b: 2, c: 1} three = {c: 3, b: 2, a: 1} four = Data.define(:x, :y).new x: 1, y: 2
one.diff one # {} one.diff two # {:a=>[1, 3], :c=>[3, 1]} one.diff three # {} one.diff four # {:a=>[1, nil], :b=>[2, nil], :c=>[3, nil]} one.diff Object.new # {:a=>[1, nil], :b=>[2, nil], :c=>[3, nil]}
Any object that _is not_ the same type will have a ‘nil` value as shown in the last two examples. Two hashes with the same keys but defined in different order behave as if they had the same key order.
fetch_value¶ ↑
Fetches value for exiting or missing key. Behavior is identical to ‘#fetch` except when the value of the key is `nil` you’ll get the default value instead. This eliminates the need for using an or expression: ‘example.fetch(:desired_key) || “default_value”`.
- source,ruby
{a: “demo”}.fetch_value :a, “default” # “demo” {a: “demo”}.fetch_value :a # “demo” {a: nil}.fetch_value :a, “default” # “default” {a: nil}.fetch_value(:a) { “default” } # “default” {}.fetch_value :a # KeyError {}.fetch_value(:a) { “default” } # “default” {a: “demo”}.fetch_value # ArgumentError
flatten_keys¶ ↑
Flattens nested keys as top-level keys without mutating itself. Keys are converted to symbols. Does not handle nested arrays.
- source,ruby
{a: {b: 1}}.flatten_keys prefix: :demo # {demo_a_b: 1} {a: {b: 1}}.flatten_keys delimiter: :| # {:“a|b” => 1}
example = {a: {b: 1}} example.flatten_keys # {a_b: 1} example # {a: {b: 1}}
flatten_keys!¶ ↑
Flattens nested keys as top-level keys while mutating itself. Keys are converted to symbols. Does not handle nested arrays.
- source,ruby
{a: {b: 1}}.flatten_keys! prefix: :demo # {demo_a_b: 1} {a: {b: 1}}.flatten_keys! delimiter: :| # {:“a|b” => 1}
example = {a: {b: 1}} example.flatten_keys! # {a_b: 1} example # {a_b: 1}
many?¶ ↑
Answers true if a hash has more than one element. Can take a block which evaluates as truthy or falsey.
- source,ruby
{a: 1, b: 2}.many? # true {a: 1, b: 2, c: 2}.many? { |_key, value| value == 2 } # true {a: 1}.many? # false {}.many? # false
recurse¶ ↑
Recursively iterates over the hash and any hash value by applying the given block to it. Does not handle nested arrays, though.
- source,ruby
example = {“a” => {“b” => 1}} example.recurse(&:symbolize_keys) # {a: {b: 1}} example.recurse(&:invert) # {{“b” => 1} => “a”}
stringify_keys¶ ↑
Converts keys to strings without mutating itself.
- source,ruby
example = {a: 1, b: 2} example.stringify_keys # {“a” => 1, “b” => 2} example # {a: 1, b: 2}
stringify_keys!¶ ↑
Converts keys to strings while mutating itself.
- source,ruby
example = {a: 1, b: 2} example.stringify_keys! # {“a” => 1, “b” => 2} example # {“a” => 1, “b” => 2}
symbolize_keys¶ ↑
Converts keys to symbols without mutating itself.
- source,ruby
example = {“a” => 1, “b” => 2} example.symbolize_keys # {a: 1, b: 2} example # {“a” => 1, “b” => 2}
symbolize_keys!¶ ↑
Converts keys to symbols while mutating itself.
- source,ruby
example = {“a” => 1, “b” => 2} example.symbolize_keys! # {a: 1, b: 2} example # {a: 1, b: 2}
transform_value¶ ↑
Transforms a value for the specified key only if the key exists and a block is given. Otherwise, the original hash is answered. Does not mutate itself.
- source,ruby
example = {a: 1, b: 2}
example.transform_value :b # {a: 1, b: 2} example.transform_value(:b) { 20 } # {a: 1, b: 20} example.transform_value(:b) { |value| value * 10 } # {a: 1, b: 20} example.transform_value :c # {a: 1, b: 2} example.transform_value(:c) { :bogus } # {a: 1, b: 2}
The original object _is not_ mutated:
- source,ruby
example.transform_value(:b) { 20 } # {a: 1, b: 20} example # {a: 1, b: 2}
transform_value!¶ ↑
Transforms a value for the specified key only if the key exists and a block is given. Otherwise, the original hash is answered. Mutates itself.
- source,ruby
example = {a: 1, b: 2}
example.transform_value! :b # {a: 1, b: 2} example.transform_value!(:b) { 20 } # {a: 1, b: 20} example.transform_value!(:b) { |value| value * 10 } # {a: 1, b: 20} example.transform_value! :c # {a: 1, b: 2} example.transform_value!(:c) { :bogus } # {a: 1, b: 2}
The original object is mutated:
- source,ruby
example.transform_value!(:b) { 20 } # {a: 1, b: 20} example # {a: 1, b: 20}
transform_with¶ ↑
Transforms values of keys using specific operations (i.e. any object that responds to ‘#call`). Does not mutate itself and you can transform multiple values at once:
- source,ruby
example = {name: “Jayne Doe”, email: “<jd@example.com>”}
example.transform_with name: -> value { value.delete_suffix “ Doe” },
email: -> value { value.tr "<>", "" }
# {name: “Jayne”, email: “jd@example.com”}
Invalid keys are ignored:
- source,ruby
example.transform_with bogus: -> value { value.tr “<>”, “” } # {email: “<jd@example.com>”}
The original object _is not_ mutated:
- source,ruby
example # {name: “Jayne Doe”, email: “<jd@example.com>”}
transform_with!¶ ↑
Transforms values of keys using specific operations (i.e. any object that responds to ‘#call`). Mutates itself and you can transform multiple values at once:
- source,ruby
example = {name: “Jayne Doe”, email: “<jd@example.com>”}
example.transform_with! name: -> value { value.delete_suffix “ Doe” },
email: -> value { value.tr "<>", "" }
# {name: “Jayne”, email: “jd@example.com”}
Invalid keys are ignored:
- source,ruby
example.transform_with! bogus: -> value { value.tr “<>”, “” } # {email: “<jd@example.com>”}
The original object is mutated:
- source,ruby
example # {name: “Jayne”, email: “jd@example.com”}
use¶ ↑
Uses the hash’s keys as block arguments where the value of the block argument is equal to the value of the key found within the hash. Works best with hashes that use symbols for keys but falls back to string keys when symbol keys can’t be found.
- source,ruby
example = {unit: “221B”, street: “Baker Street”, city: “London”, country: “UK”}
example.use { |unit, street| “#{unit} #{street}” } # “221B Baker Street”
IO¶ ↑
.void¶ ↑
Answers an IO stream which points to ‘/dev/null` in order to ignore any reads or writes to the stream. When given a block, the stream will automatically close upon block exit. When not given a block, you’ll need to close the stream manually.
- source,ruby
io = IO.void # “#<IO:fd 20>” io = IO.void { |void| void.write “nevermore” } # “#<IO:(closed)>”
redirect¶ ↑
Redirects current stream to other stream when given a block. Without a block, the original stream is answered instead.
- source,ruby
io = IO.new IO.sysopen(Pathname(“demo.txt”).to_s, “w+”) other = IO.new IO.sysopen(Pathname(“other.txt”).to_s, “w+”)
io.redirect other # “#<IO:fd 20>” io.redirect(other) { |stream| stream.write “demo” } # “#<IO:fd 21>”
reread¶ ↑
Answers full stream by rewinding to beginning of stream and reading all content.
- source,ruby
io = IO.new IO.sysopen(Pathname(“demo.txt”).to_s, “w+”) io.write “This is a demo.”
io.reread # “This is a demo.” io.reread 4 # “This”
buffer = “”.dup io.reread(buffer:) # “This is a demo.” buffer # “This is a demo.”
squelch¶ ↑
Temporarily ignores any reads/writes for code executed within a block. Answers itself without any arguments or when given a block.
- source,ruby
io = IO.new IO.sysopen(Pathname(“test.txt”).to_s, “w+”)
io.squelch # “#<IO:fd 20>” io.squelch { io.write “Test” } # “#<IO:fd 20>” io.reread # “”
LogDevice¶ ↑
reread¶ ↑
Answers previously written content by rewinding to beginning of device.
- source,ruby
# With File. device = Logger::LogDevice.new “test.log” device.write “Test.” device.reread # “Test.”
# With StringIO. device = Logger::LogDevice.new StringIO.new device.write “Test.” device.reread # “Test.”
# With STDOUT. device = Logger::LogDevice.new $stdout device.write “Test.” device.reread # “”
Logger¶ ↑
reread¶ ↑
Answers previously written content by rewinding to beginning of log.
- source,ruby
# With File. logger = Logger.new “test.log” logger.write “Test.” logger.reread # “Test.”
# With StringIO. logger = Logger.new StringIO.new logger.write “Test.” logger.reread # “Test.”
# With STDOUT. logger = Logger.new $stdout logger.write “Test.” logger.reread # “”
any¶ ↑
Allows you to log any message which is identical in behavior and functionality to the ‘Logger#unknown` method only this requires less typing and better matches the terminology used by the `#unknown` method.
- source,ruby
logger = Logger.new STDOUT
logger.any “Test.” # A, [2000-01-10T09:00:00.847428 #44925] ANY – : Test.
logger.any { “Test.” } A, [2000-01-10T09:00:00.330719 #44925] ANY – : Test.
Module¶ ↑
pseudonym¶ ↑
Allows you to set a temporary name for your anonymous ‘Module` (or `Class` since `Class` inherits from `Module`) with a better default than what `
#set_temporary_name
` provides.- source,ruby
Module.new.pseudonym “demo” # demo-44600 Module.new.pseudonym “demo”, delimiter: “_” # demo_60900 Module.new.pseudonym “demo”, nil # demo- Module.new.pseudonym “demo”, nil, delimiter: nil # demo
The same applies for anonymous classes since classes inherit from modules:
- source,ruby
Class.new.pseudonym “demo” # demo-44600 < Object Class.new.pseudonym “demo”, delimiter: “_” # demo_60900 < Object Class.new.pseudonym “demo”, nil # demo- < Object Class.new.pseudonym “demo”, nil, delimiter: nil # demo < Object
💡 While convenient, if you find yourself nullifying the suffix and/or delimiter, you’re better off using ‘
#set_temporary_name
`.Object¶ ↑
in?¶ ↑
Allows you to know if ‘self` is included in, or an element of, the target object.
- source,ruby
1.in? [1, 2, 3] # true 9.in? [1, 2, 3] # false
“a”.in? %w[a b c] # true “z”.in? %w[a b c] # false
:a.in? %i[a b c] # true :z.in? %i[a b c] # false
:a.in? %i[a b c].to_enum # true :z.in? %i[a b c].to_enum # false
:a.in?({a: 1, b: 2, c: 3}) # true :z.in?({a: 1, b: 2, c: 3}) # false
1.in? 1..5 # true 9.in? 1..5 # false
1.in? Set[1, 2, 3] # true 9.in? Set[1, 2, 3] # false
“a”.in? “abcde” # true “z”.in? “abcde” # false
“z”.in? Object.new # ‘String#include?` must be implemented. (NoMethodError)
to_proc¶ ↑
Allows you to cast any object to a proc.
- source,ruby
one = Class.new { def call = :test }
.new
two = Object.new
one.to_proc #
Pathname¶ ↑
Pathname¶ ↑
Enhances the ‘Kernel` conversion function which casts `nil` into a pathname in order to avoid: `TypeError (no implicit conversion of nil into String)`. The pathname remains invalid but at least you have an instance of `Pathname`, which behaves like a _Null Object_, that can be used to construct a valid path.
- source,ruby
Pathname nil # Pathname(“”)
.home¶ ↑
Answers user home directory.
- source,ruby
Pathname.home # Pathname “/Users/demo”
.make_temp_dir¶ ↑
Wraps ‘Dir.mktmpdir` with the following behavior (see https://rubyapi.org/o/Dir.mktmpdir#method-c-mktmpdir[Dir.mktmpdir] for details):
-
*Without Block* - Answers a newly created Pathname instance which is not automatically cleaned up.
-
*With Block* Yields a Pathname instance, answers result of given block, and automatically cleans up temporary directory after block exits.
The following examples use truncated temporary directories for illustration purposes only. In reality, these paths will be longer depending on which operating system you are using.
- source,ruby
Pathname.make_temp_dir # Pathname:/var/folders/T/temp-20200101-16940-r8 Pathname.make_temp_dir prefix: “prefix-” # Pathname:/var/folders/T/prefix-20200101-16940-r8 Pathname.make_temp_dir suffix: “-suffix” # Pathname:/var/folders/T/temp-20200101-16940-r8-suffix Pathname.make_temp_dir prefix: “prefix-”, suffix: “-suffix” # Pathname:/var/folders/T/prefix-20200101-16940-r8-suffix Pathname.make_temp_dir root: “/example” # Pathname:/example/temp-20200101-16940-r8 Pathname.make_temp_dir { “I am a block result” } # “I am a block result” Pathname.make_temp_dir { |path| path.join “sub_dir” } # Pathname:/var/folders/T/temp-20200101-16940-r8/sub_dir
.require_tree¶ ↑
Requires all Ruby files in given root path and corresponding nested tree structure. All files are sorted before being required to ensure consistent behavior. Example:
- source,ruby
# Before Dir[File.join(__dir__, “support/shared_contexts/*/.rb”)].sort.each { |path| require path }
# After Pathname.require_tree “#{__dir__}/support/shared_contexts”
The following are further examples of potential usage:
- source,ruby
# Requires all files in root directory and below. Pathname.require_tree __dir__
# Requires all files in ‘/test/*/.rb` and below. Pathname.require_tree “/test”
# Requires all files in RSpec shared examples directory structure. Pathname.require_tree SPEC_ROOT.join(“support/shared_examples”)
.root¶ ↑
Answers operating system root path.
- source,ruby
Pathname.root # Pathname “/”
change_dir¶ ↑
Wraps ‘Dir.chdir` behavior by changing to directory of current path. See https://rubyapi.org/o/Dir.chdir#method-c-chdir[Dir.chdir] for details.
- source,ruby
current = Pathname.pwd # “$HOME/demo” (Present Working Directory) custom = current.join(“test”).make_dir # Pathname “$HOME/demo/test” custom.change_dir # “$HOME/demo/test” (Present Working Directory) current.change_dir # “$HOME/demo” (Present Working Directory) custom.change_dir { “example” } # “example” custom.change_dir { |path| path } # Pathname “$HOME/demo/test” Pathname.pwd # “$HOME/demo” (Present Working Directory)
copy¶ ↑
Copies file from current location to new location while answering itself so it can be chained.
- source,ruby
Pathname(“input.txt”).copy Pathname(“output.txt”) # Pathname(“input.txt”)
deep_touch¶ ↑
Has all of the same functionality as the ‘#touch` method while being able to create ancestor directories no matter how deeply nested the file might be.
- source,ruby
Pathname(“a/b/c/d.txt”).touch # Pathname(“a/b/c/d.txt”) Pathname(“a/b/c/d.txt”).touch Time.now - 1 # Pathname(“a/b/c/d.txt”)
delete¶ ↑
Deletes file or directory and answers itself so it can be chained.
- source,ruby
# When path exists. Pathname(“/example.txt”).touch.delete # Pathname(“/example”)
# When path doesn’t exist. Pathname(“/example.txt”).delete # Errno::ENOENT
delete_prefix¶ ↑
Deletes a path prefix and answers new pathname.
- source,ruby
Pathname(“a/path/example-test.rb”).delete_prefix “example-” # Pathname(“a/path/test.rb”) Pathname(“example-test.rb”).delete_prefix “example-” # Pathname(“test.rb”) Pathname(“example-test.rb”).delete_prefix “miss” # Pathname(“example-test.rb”)
delete_suffix¶ ↑
Deletes a path suffix and answers new pathname.
- source,ruby
Pathname(“a/path/test-example.rb”).delete_suffix “-example” # Pathname(“a/path/test.rb”) Pathname(“test-example.rb”).delete_suffix “-example” # Pathname(“test.rb”) Pathname(“test-example.rb”).delete_suffix “miss” # Pathname(“test-example.rb”)
directories¶ ↑
Answers all directories or filtered directories for current path.
- source,ruby
Pathname(“/example”).directories # [Pathname(“a”), Pathname(“b”)] Pathname(“/example”).directories “a*” # [Pathname(“a”)] Pathname(“/example”).directories flag: File::FNM_DOTMATCH # [Pathname(“..”), Pathname(“.”)]
empty¶ ↑
Empties a directory of children (i.e. folders, nested folders, or files) or clears an existing file of contents. If a directory or file doesn’t exist, it will be created.
- source,ruby
directory = Pathname(“test”).make_path file = directory.join(“test.txt”).write(“example”)
file.empty.read # “” directory.empty.children # []
extensions¶ ↑
Answers file extensions as an array.
- source,ruby
Pathname(“example.txt.erb”).extensions # [“.txt”, “.erb”]
files¶ ↑
Answers all files or filtered files for current path.
- source,ruby
Pathname(“/example”).files # [Pathname(“a.txt”), Pathname(“a.png”)] Pathname(“/example”).files “*.png” # [Pathname(“a.png”)] Pathname(“/example”).files flag: File::FNM_DOTMATCH # [Pathname(“.ruby-version”)]
gsub¶ ↑
Same behavior as ‘String#gsub` but answers a path with patterns replaced with desired substitutes.
- source,ruby
Pathname(“/a/path/some/path”).gsub “path”, “test” # Pathname(“/a/test/some/test”)
Pathname(“/%placeholder%/some/%placeholder%”).gsub “%placeholder%”, “test” # Pathname(“/test/some/test”)
make_ancestors¶ ↑
Ensures all ancestor directories are created for a path.
- source,ruby
Pathname(“/one/two”).make_ancestors # Pathname(“/one/two”) Pathname(“/one”).exist? # true Pathname(“/one/two”).exist? # false
make_dir¶ ↑
Provides alternative ‘#mkdir` behavior by always answering itself (even when directory exists) and not throwing errors when directory does exist in order to ensure the pathname can be chained.
- source,ruby
Pathname(“/one”).make_dir # Pathname(“/one”) Pathname(“/one”).make_dir.make_dir # Pathname(“/one”)
make_path¶ ↑
Provides alternative ‘#mkpath` behavior by always answering itself (even when full path exists) and not throwing errors when directory does exist in order to ensure the pathname can be chained.
- source,ruby
Pathname(“/one/two/three”).make_path # Pathname(“/one/two/three”) Pathname(“/one/two/three”).make_path.make_path # Pathname(“/one/two/three”)
name¶ ↑
Answers file name without extension.
- source,ruby
Pathname(“example.txt”).name # Pathname(“example”)
puts¶ ↑
Wraps ‘#write` by writing content to file with new line and answering itself. Allows you to more easily swap out a `Pathname` object with similar IO objects who support `#puts`: `IO`, `StringIO`, `File`, `Kernel`, and so forth.
- source,ruby
path = Pathname(“test.txt”).touch path.puts “Test.” path.read # “Test.n”
Pathname(“text.txt”).touch.puts(“Test.”).read # “Test.n”
relative_parent¶ ↑
Answers relative path from parent directory. This complements: ‘#relative_path_from`.
- source,ruby
Pathname(“/one/two/three”).relative_parent “/one” # Pathname “two”
remove_dir¶ ↑
Provides alternative ‘#rmdir` behavior by always answering itself (even when full path exists) and not throwing errors when directory does exist in order to ensure the pathname can be chained.
- source,ruby
Pathname(“/test”).make_dir.remove_dir.exist? # false Pathname(“/test”).remove_dir # Pathname(“/test”) Pathname(“/test”).remove_dir.remove_dir # Pathname(“/test”)
remove_tree¶ ↑
Provides alternative ‘#rmtree` behavior by always answering itself (even when full path exists) and not throwing errors when directory does exist in order to ensure the pathname can be chained.
- source,ruby
parent_path = Pathname “/one” child_path = parent_path.join “two”
child_path.make_path parent_path.remove_tree # Pathname “/one” child_path.exist? # false parent_path.exist? # false
child_path.make_path child_path.remove_tree # Pathname “/one/two” child_path.exist? # false parent_path.exist? # true
rewrite¶ ↑
When given a block, it provides the contents of the recently read file for manipulation and immediate writing back to the same file.
- source,ruby
Pathname(“/test.txt”).rewrite # Pathname(“/test.txt”) Pathname(“/test.txt”).rewrite { |body| body.sub “[token]”, “example” } # Pathname(“/test.txt”)
touch¶ ↑
Updates access and modification times for an existing path by defaulting to current time. When path doesn’t exist, it will be created as a file.
- source,ruby
Pathname(“example”).touch # Pathname(“example”) Pathname(“example”).touch Time.now - 1 # Pathname(“example”) Pathname(“example.txt”).touch # Pathname(“example.txt”) Pathname(“example.txt”).touch Time.now - 1 # Pathname(“example.txt”)
write¶ ↑
Writes to file and answers itself so it can be chained. See ‘IO.write` for details on additional options.
- source,ruby
Pathname(“example.txt”).write “test” # Pathname(“example.txt”) Pathname(“example.txt”).write “test”, offset: 1 # Pathname(“example.txt”) Pathname(“example.txt”).write “test”, mode: “a” # Pathname(“example.txt”)
String¶ ↑
blank?¶ ↑
Answers ‘true`/`false` based on whether string is blank, `<space>`, `n`, `t`, and/or `r`.
- source,ruby
“ ntr”.blank? # true
camelcase¶ ↑
Answers a camel cased string.
- source,ruby
“this_is_an_example”.camelcase # “ThisIsAnExample”
down¶ ↑
Answers string with only first letter down cased.
- source,ruby
“EXAMPLE”.down # “eXAMPLE”
first¶ ↑
Answers first character of a string or first set of characters if given a number.
- source,ruby
“example”.first # “e” “example”.first 4 # “exam”
indent¶ ↑
Answers indentation (string) which is the result of the multiplier times padding. By default, the multiplier is ‘1` and the padding is `“ ”` which equates to two spaces.
- source,ruby
“example”.indent # “ example” “example”.indent 0 # “example” “example”.indent -1 # “example” “example”.indent 2 # “ example” “example”.indent 3, pad: “ ” # “ example”
last¶ ↑
Answers last character of a string or last set of characters if given a number.
- source,ruby
“instant”.last # “t” “instant”.last 3 # “ant”
pluralize¶ ↑
Answers plural form of self when given a suffix to add. The plural form of the word can be dynamically calculated when given a count and a replacement pattern (i.e. string or regular expression) can be supplied for further specificity. Usage is based on https://en.wikipedia.org/wiki/English_plurals[plurals in English] which may or may not work well in other languages.
- source,ruby
“apple”.pluralize “s” # apples “apple”.pluralize “s”, 0 # apples “apple”.pluralize “s”, 1 # apple “apple”.pluralize “s”, -1 # apple “apple”.pluralize “s”, 2 # apples “apple”.pluralize “s”, -2 # apples “cactus”.pluralize “i”, replace: “us” # cacti “cul-de-sac”.pluralize “ls”, replace: “l” # culs-de-sac
singularize¶ ↑
Answers singular form of self when given a suffix to remove (can be a string or a regular expression). The singular form of the word can be dynamically calculated when given a count and a replacement string can be supplied for further specificity. Usage is based on https://en.wikipedia.org/wiki/English_plurals[plurals in English] which may or may not work well in other languages.
- source,ruby
“apples”.singularize “s” # apple “sacks”.singularize /s$/ # sack “apples”.singularize “s”, 0 # apples “apples”.singularize “s”, 1 # apple “apples”.singularize “s”, -1 # apple “apples”.singularize “s”, 2 # apples “apples”.singularize “s”, -2 # apples “cacti”.singularize “i”, replace: “us” # cactus “culs-de-sac”.singularize “ls”, replace: “l” # cul-de-sac
snakecase¶ ↑
Answers a snake cased string.
- source,ruby
“ThisIsAnExample”.snakecase # “this_is_an_example”
squish¶ ↑
Removes leading, in body, and trailing whitespace, including any tabs or newlines, without mutating itself. Processes ASCII and unicode whitespace as well.
- source,ruby
“one two three”.squish # “one two three” “ one two n t three ”.squish # “one two three”
titleize¶ ↑
Answers a title string with proper capitalization of each word.
- source,ruby
“ThisIsAnExample”.titleize # “This Is An Example”
truncate¶ ↑
Answers a truncated, non-mutated, string for given length with optional delimiter and/or overflow.
The delimiter is the second positional parameter (optional) and is ‘nil` by default. A custom string or regular expression can be used to customize truncation behavior.
The trailer is an optional keyword parameter that is an ellipsis (i.e. ‘“…”`) by default. The trailer can be a custom or empty string. The string length of the trailer is added to the length of the string being truncated, so keep this in mind when setting truncation length.
- source,ruby
demo = “It was the best of times” length = demo.length
demo.truncate 9 # “It was…” demo.truncate 12 # “It was th…” demo.truncate length # “It was the best of times” demo.truncate Float::INFINITY # “It was the best of times” demo.truncate 12, “ ” # “It was…” demo.truncate 12, /s/ # “It was…” demo.truncate 6, trailer: “” # “It was” demo.truncate 16, trailer: “… (more)” # “It was… (more)” “demo”.truncate 3 # “…”
to_bool¶ ↑
Answers string as a boolean.
- source,ruby
“true”.to_bool # true “yes”.to_bool # true “1”.to_bool # true “”.to_bool # false “example”.to_bool # false
up¶ ↑
Answers string with only first letter capitalized.
- source,ruby
“example”.up # “Example”
String IO¶ ↑
reread¶ ↑
Answers full string by rewinding to beginning of string and reading all content.
- source,ruby
io = StringIO.new io.write “This is a test.”
io.reread # “This is a test.” io.reread 4 # “This”
buffer = “”.dup io.reread(buffer:) # “This is a test.” buffer # “This is a test.”
to_s
-
- “a”].pad 0 # [“a”
- 1, 2, 3].intersperse :a # [1, :a, 2, :a, 3
- ].compress # [