:toc: macro :toclevels: 5 :figure-caption!: = Refinements [link=http://badge.fury.io/rb/refinements] image::https://badge.fury.io/rb/refinements.svg[Gem Version] [link=https://circleci.com/gh/bkuhlmann/refinements] image::https://circleci.com/gh/bkuhlmann/refinements.svg?style=svg[Circle CI Status] A collection of refinements (enhancements) to core Ruby objects. toc::[] == Features * *Arrays*: ** `#compress` - Removes `nil` and empty values without modifying itself. ** `#compress!` - Removes `nil` and empty values while modifying itself. ** `#ring` - Answers a circular array which can enumerate before, current, after elements. * *BigDecimals*: ** `#inspect` - Allows one to inspect a big decimal with numeric representation. * *DateTimes*: ** `.utc` - Answers new DateTime object for current UTC date/time. * *Files*: ** `#rewrite` - When given a file path and a block, it provides the contents of the recently read file for manipulation and immediate writing back to the same file. * *Hashes*: ** `#except` - Answers new hash with given keys removed without modifying itself. ** `#except!` - Answers new hash with given keys removed while modifying itself. ** `#symbolize_keys` - Converts keys to symbols without modifying itself. ** `#symbolize_keys!` - Converts keys to symbols while modifying itself. ** `#deep_merge` - Merges deeply nested hashes together without modifying itself. ** `#deep_merge!` - Merges deeply nested hashes together while modifying itself. ** `#deep_symbolize_keys` - Symbolizes keys of nested hash without modifying itself. Does not handle nested arrays, though. ** `#deep_symbolize_keys!` - Symbolizes keys of nested hash while modifying itself. Does not handle nested arrays, though. ** `#recurse` - Applies block to nested hash. Does not handle nested arrays, though. ** `#rekey` - Transforms keys per mapping (size of mapping can vary) without modifying itself. ** `#rekey!` - Transforms keys per mapping (size of mapping can vary) while modifying itself. ** `#reverse_merge` - Merges calling hash into passed in hash without modifying itself. ** `#reverse_merge!` - Merges calling hash into passed in hash while modifying itself. ** `#use` - Passes each hash value as a block argument for further processing. * *Pathnames*: ** `Pathname` - Conversion function (refined from `Kernel`) which can cast `nil` into a pathname. ** `#name` - Answers file name without extension. ** `#copy` - Copies file from current location to new location. ** `#directories` - Answers all or filtered directories for current path. ** `#extensions` - Answers file extensions as an array. ** `#files` - Answers all or filtered files for current path. ** `#relative_parent_from` - Answers relative path from parent directory. This is a complement to `#relative_path_from`. ** `#make_ancestors` - Ensures all ancestor directories are created for a path. ** `#rewrite` - When given a block, it provides the contents of the recently read file for manipulation and immediate writing back to the same file. ** `#touch` - Updates access and modification times for path. Defaults to current time. * *Strings*: ** `#first` - Answers first character of a string or first set of characters if given a number. ** `#last` - Answers last character of a string or last set of characters if given a number. ** `#blank?` - Answers `true`/`false` based on whether string is blank or not (i.e. ``, `\n`, `\t`, `\r`). ** `#up` - Answers string with only first letter upcased. ** `#down` - Answers string with only first letter downcased. ** `#camelcase` - Answers a camelcased string. ** `#snakecase` - Answers a snakecased string. ** `#titleize` - Answers titleized string. ** `#to_bool` - Answers string as a boolean. == Requirements . https://www.ruby-lang.org[Ruby 2.7.x]. . A solid understanding of link:https://www.alchemists.io/articles/ruby_refinements[Ruby refinements and lexical scope]. == Setup === Production To install, run: [source,bash] ---- gem install refinements ---- Add the following to your Gemfile file: [source,ruby] ---- gem "refinements" ---- === Development To contribute, run: [source,bash] ---- git clone https://github.com/bkuhlmann/refinements.git cd refinements bin/setup ---- You can also use the IRB console for direct access to all objects: [source,bash] ---- bin/console ---- == Usage === Requires If all refinements are not desired, 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/arrays" require "refinements/big_decimals" require "refinements/date_times" require "refinements/files" require "refinements/hashes" require "refinements/pathnames" require "refinements/strings" ---- === Using Much like including/extending a module, you’ll need modify your object(s) to use the refinement(s): [source,ruby] ---- class Example using Refinements::Arrays using Refinements::BigDecimals using Refinements::DateTimes using Refinements::Files using Refinements::Hashes using Refinements::Pathnames using Refinements::Strings end ---- === Examples The following sections demonstrate how each refinement enriches your objects with new capabilities. ==== Array [source,ruby] ---- example = ["An", nil, "", "Example"] example.compress # => ["An", "Example"] example # => ["An", nil, "", "Example"] example = ["An", nil, "", "Example"] example.compress! # => ["An", "Example"] example # => ["An", "Example"] example = [1, 2, 3] example.ring # => # example.ring { |(before, current, after)| puts "#{before} #{current} #{after}" } # [3 1 2] # [1 2 3] # [2 3 1] ---- ==== Big Decimal [source,ruby] ---- BigDecimal.new("5.0E-10").inspect # => "#" ---- ==== DateTime [source,ruby] ---- DateTime.utc # => # ---- ==== File [source,ruby] ---- File.rewrite("/test.txt") { |content| content.gsub "[placeholder]", "example" } ---- ==== Hash [source,ruby] ---- example = {a: 1, b: 2, c: 3} example.except :a, :b # => {c: 3} example # => {a: 1, b: 2, c: 3} example = {a: 1, b: 2, c: 3} example.except! :a, :b # => {c: 3} example # => {c: 3} example = {"a" => 1, "b" => 2} example.symbolize_keys # => {a: 1, b: 2} example # => {"a" => 1, "b" => 2} example = {"a" => 1, "b" => 2} example.symbolize_keys! # => {a: 1, b: 2} example # => {a: 1, b: 2} example = {a: 1, b: 2, c: 3} example.slice :a, :c # => {a: 1, c: 3} example # => {a: 1, b: 2, c: 3} example = {a: 1, b: 2, c: 3} example.slice! :a, :c # => {a: 1, c: 3} example # => {a: 1, c: 3} 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"}} 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"}} example = {"a" => {"b" => 2}} example.deep_symbolize_keys # => {a: {b: 1}} example # => {"a" => {"b" => 2}} example = {"a" => {"b" => 2}} example.deep_symbolize_keys! # => {a: {b: 1}} example # => {a: {b: 1}} example = {"a" => {"b" => 1}} example.recurse(&:symbolize_keys) # => {a: {b: 1}} example.recurse(&:invert) # => {{"b" => 1} => "a"} example = {a: 1, b: 2, c: 3} example.rekey a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3} example # => {a: 1, b: 2, c: 3} example = {a: 1, b: 2, c: 3} example.rekey! a: :amber, b: :blue # => {amber: 1, blue: 2, c: 3} example # => {amber: 1, blue: 2, c: 3} example = {a: 1, b: 2} example.reverse_merge a: 0, c: 3 # => {a: 1, b: 2, c: 3} example # => {a: 1, b: 2} example = {a: 1, b: 2} example.reverse_merge! a: 0, c: 3 # => {a: 1, b: 2, c: 3} example # => {a: 1, b: 2, c: 3} example = {unit: "221B", street: "Baker Street", city: "London", country: "UK"} example.use { |unit, street| "#{unit} #{street}" } # => "221B Baker Street" ---- ==== Pathname [source,ruby] ---- Pathname(nil) # => Pathname("") Pathname("example.txt").name # => Pathname("example") Pathname("input.txt").copy Pathname("output.txt") Pathname("/example").directories # => [Pathname("a"), Pathname("b")] Pathname("/example").directories "a*" # => [Pathname("a")] Pathname("example.txt.erb").extensions # => [".txt", ".erb"] Pathname("/example").files # => [Pathname("a.txt"), Pathname("a.png")] Pathname("/example").files "*.png" # => [Pathname("a.png")] Pathname("/one/two/three").relative_parent_from("/one") # => Pathname "two" Pathname("/one/two").make_ancestors Pathname("/one").exist? # => true Pathname("/one/two").exist? # => false Pathname("/test.txt").rewrite { |content| content.sub "[placeholder]", "example" } Pathname("example.txt").touch Pathname("example.txt").touch at: Time.now - 1 ---- ==== String [source,ruby] ---- "example".first # => "e" "example".first 4 # => "exam" "instant".last # => "t" "instant".last 3 # => "ant" " \n\t\r".blank? # => true "example".up # => "Example" "EXAMPLE".down # => "eXAMPLE" "this_is_an_example".camelcase # => "ThisIsAnExample" "ThisIsAnExample".snakecase # => "this_is_an_example" "ThisIsAnExample".titleize # => "This Is An Example" "true".to_bool # => true "yes".to_bool # => true "1".to_bool # => true "".to_bool # => false "example".to_bool # => false ---- == Tests To test, run: [source,bash] ---- bundle exec rake ---- == Versioning Read link:https://semver.org[Semantic Versioning] for details. Briefly, it means: * Major (X.y.z) - Incremented for any backwards incompatible public API changes. * Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes. * Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes. == Code of Conduct Please note that this project is released with a link:CODE_OF_CONDUCT.adoc[CODE OF CONDUCT]. By participating in this project you agree to abide by its terms. == Contributions Read link:CONTRIBUTING.adoc[CONTRIBUTING] for details. == License Read link:LICENSE.adoc[LICENSE] for details. == History Read link:CHANGES.adoc[CHANGES] for details. == Credits Engineered by link:https://www.alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].