{"id":13878647,"url":"https://github.com/bkuhlmann/refinements","last_synced_at":"2025-04-12T17:43:30.751Z","repository":{"id":43475289,"uuid":"39353291","full_name":"bkuhlmann/refinements","owner":"bkuhlmann","description":"A collection of core object refinements.","archived":false,"fork":false,"pushed_at":"2025-03-25T02:12:24.000Z","size":1025,"stargazers_count":79,"open_issues_count":0,"forks_count":5,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-03T20:11:09.184Z","etag":null,"topics":["refinement","ruby-language"],"latest_commit_sha":null,"homepage":"https://alchemists.io/projects/refinements","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bkuhlmann.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.adoc","code_of_conduct":null,"threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["bkuhlmann"]}},"created_at":"2015-07-19T23:49:31.000Z","updated_at":"2025-03-25T02:12:28.000Z","dependencies_parsed_at":"2023-10-16T08:56:31.259Z","dependency_job_id":"7626f570-a431-4bb4-8442-4c35eb539160","html_url":"https://github.com/bkuhlmann/refinements","commit_stats":{"total_commits":782,"total_committers":3,"mean_commits":260.6666666666667,"dds":0.002557544757033292,"last_synced_commit":"76d05de8d57a52ee5285f5d69fe2fa47cd23d83d"},"previous_names":[],"tags_count":118,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Frefinements","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Frefinements/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Frefinements/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bkuhlmann%2Frefinements/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bkuhlmann","download_url":"https://codeload.github.com/bkuhlmann/refinements/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248608792,"owners_count":21132785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["refinement","ruby-language"],"created_at":"2024-08-06T08:01:55.615Z","updated_at":"2025-04-12T17:43:30.712Z","avatar_url":"https://github.com/bkuhlmann.png","language":"Ruby","funding_links":["https://github.com/sponsors/bkuhlmann"],"categories":["Ruby"],"sub_categories":[],"readme":":toc: macro\n:toclevels: 5\n:figure-caption!:\n\n= Refinements\n\nThese refinements augment and enhance Ruby primitives so you can avoid link: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!\n\ntoc::[]\n\n== Features\n\nEnhances the following objects:\n\n* Array\n* Binding\n* Data\n* DateTime\n* Hash\n* IO\n* Module\n* Object\n* Pathname\n* String\n* StringIO\n* Struct\n* Symbol\n\n== Requirements\n\n. https://www.ruby-lang.org[Ruby].\n. A solid understanding of link:https://alchemists.io/articles/ruby_refinements[refinements].\n\n== Setup\n\nTo install _with_ security, run:\n\n[source,bash]\n----\n# 💡 Skip this line if you already have the public certificate installed.\ngem cert --add \u003c(curl --compressed --location https://alchemists.io/gems.pem)\ngem install refinements --trust-policy HighSecurity\n----\n\nTo install _without_ security, run:\n\n[source,bash]\n----\ngem install refinements\n----\n\nYou can also add the gem directly to your project:\n\n[source,bash]\n----\nbundle add refinements\n----\n\nOnce the gem is installed, you only need to require it:\n\n[source,ruby]\n----\nrequire \"refinements\"\n----\n\n== Usage\n\n=== Requires\n\nIf no refinements are desired, then add the following to your `Gemfile` instead:\n\n[source,ruby]\n----\ngem \"refinements\", require: false\n----\n\n...then require the specific refinement, as needed. Example:\n\n[source,ruby]\n----\nrequire \"refinements/array\"\nrequire \"refinements/binding\"\nrequire \"refinements/data\"\nrequire \"refinements/date_time\"\nrequire \"refinements/hash\"\nrequire \"refinements/io\"\nrequire \"refinements/module\"\nrequire \"refinements/object\"\nrequire \"refinements/pathname\"\nrequire \"refinements/string\"\nrequire \"refinements/string_io\"\nrequire \"refinements/struct\"\nrequire \"refinements/symbol\"\n----\n\n=== Using\n\nMuch like including/extending a module, you’ll need to modify your object(s) to use the\nrefinement(s):\n\n[source,ruby]\n----\nclass Example\n  using Refinements::Array\n  using Refinements::Binding\n  using Refinements::Data\n  using Refinements::DateTime\n  using Refinements::Hash\n  using Refinements::IO\n  using Refinements::Module\n  using Refinements::Object\n  using Refinements::Pathname\n  using Refinements::String\n  using Refinements::StringIO\n  using Refinements::Struct\n  using Refinements::Symbol\nend\n----\n\n=== Examples\n\nThe following sections demonstrate how each refinement enriches your objects with new capabilities.\n\n==== Array\n\n===== #combinatorial?\n\nAnswers if an array is equal to another array when the elements are equal but in any order and/or subset.\n\n[source,ruby]\n----\nexample = %w[a b c]\n\nexample.combinatorial? %w[a b c]    # true\nexample.combinatorial? %w[c a b]    # true\nexample.combinatorial? %w[c]        # true\nexample.combinatorial? %w[c b]      # true\nexample.combinatorial? %w[x]        # false\nexample.combinatorial? %w[z b c]    # false\nexample.combinatorial? %w[a b c d]  # false\nexample.combinatorial? []           # false\n----\n\n===== #compress\n\nRemoves `nil` and empty objects without mutating itself. Answers itself if there is nothing to remove.\n\n[source,ruby]\n----\nobject = Object.new\nexample = [1, \"blueberry\", nil, \"\", [], {}, object]\n\n[].compress       # []\n[1, 2].compress   # [1, 2]\nexample.compress  # [1, \"blueberry\", object]\nexample           # [1, \"blueberry\", nil, \"\", [], {}, object]\n----\n\n===== #compress!\n\nRemoves `nil` and empty values while mutating itself. Answers `nil` if there is nothing to remove.\n\n[source,ruby]\n----\nobject = Object.new\nexample = [1, \"blueberry\", nil, \"\", [], {}, object]\n\n[].compress!       # nil\n[1, 2].compress!   # nil\nexample.compress!  # [1, \"blueberry\", object]\nexample            # [1, \"blueberry\", object]\n----\n\n===== #excluding\n\nRemoves given array or elements without mutating itself.\n\n[source,ruby]\n----\n[1, 2, 3, 4, 5].excluding [4, 5]  # [1, 2, 3]\n[1, 2, 3, 4, 5].excluding 4, 5    # [1, 2, 3]\n----\n\n===== #including\n\nAdds given array or elements without mutating itself.\n\n[source,ruby]\n----\n[1, 2, 3].including [4, 5]  # [1, 2, 3, 4, 5]\n[1, 2, 3].including 4, 5    # [1, 2, 3, 4, 5]\n----\n\n===== #intersperse\n\nInserts additional elements, or an array, between all members of given array.\n\n[source,ruby]\n----\n[1, 2, 3].intersperse :a         # [1, :a, 2, :a, 3]\n[1, 2, 3].intersperse :a, :b     # [1, :a, :b, 2, :a, :b, 3]\n[1, 2, 3].intersperse %i[a b c]  # [1, :a, :b, :c, 2, :a, :b, :c, 3]\n----\n\n===== #many?\n\nAnswers true if an array has more than one element. Can take a block which evaluates as truthy or\nfalsey.\n\n[source,ruby]\n----\n[1, 2].many?             # true\n[1, 2, 3].many?(\u0026:odd?)  # true\n[1].many?                # false\n[].many?                 # false\n----\n\n===== #maximum\n\nAnswers the maximum extracted value from a collection of objects.\n\n[source,ruby]\n----\nPoint = Data.define :x, :y\npoints = [Point[x: 1, y: 2], Point[x: 0, y: 1], Point[x: 2, y: 3]]\n\npoints.maximum :x  # 2\npoints.maximum :y  # 3\npoints.maximum :z  # undefined method `z' for #\u003cdata Point x=1, y=2\u003e (NoMethodError)\n[].maximum :x      # nil\n----\n\n===== #mean\n\nAnswers mean/average all elements within an array.\n\n[source,ruby]\n----\n[].mean                 # 0\n[5].mean                # 5\n[1, 2, 3].mean          # 2\n[1.25, 1.5, 1.75].mean  # 1.5\n----\n\n===== #minimum\n\nAnswers the minimum extracted value from a collection of objects.\n\n[source,ruby]\n----\nPoint = Data.define :x, :y\npoints = [Point[x: 1, y: 2], Point[x: 0, y: 1], Point[x: 2, y: 3]]\n\npoints.minimum :x  # 0\npoints.minimum :y  # 1\npoints.minimum :z  # undefined method `z' for #\u003cdata Point x=1, y=2\u003e (NoMethodError)\n[].minimum :x      # nil\n----\n\n===== #pad\n\nAnswers new array padded with given value up to a maximum size. Useful in situations where an array\nneeds to be a specific size with padded values.\n\n[source,ruby]\n----\n[\"a\"].pad 0         # [\"a\"]\n[\"a\"].pad \"-\", 3    # [\"a\", \"-\", \"-\"]\n%w[a b].pad \"-\", 3  # [\"a\", \"b\", \"-\"]\n----\n\n===== #pick\n\nAnswers value of first element that matches given key.\n\n[source,ruby]\n----\narray = [{name: \"a\", label: \"A\"}, {name: \"b\", label: \"B\"}, {name: \"c\", label: \"C\"}]\n\narray.pick :name          # \"a\"\narray.pick :name, :label  # [\"a\", \"A\"]\narray.pick                # nil\n[].pick :other            # nil\n----\n\n===== #pluck\n\nAnswers values of all elements that match given keys.\n\n[source,ruby]\n----\narray = [{name: \"a\", label: \"A\"}, {name: \"b\", label: \"B\"}, {name: \"c\", label: \"C\"}]\n\narray.pluck :name          # [\"a\", \"b\", \"c\"]\narray.pluck :name, :label  # [[\"a\", \"A\"], [\"b\", \"B\"], [\"c\", \"C\"]]\narray.pluck                # []\n[].pluck :other            # []\n----\n\n===== #replace_at\n\nAnswers mutated array where an element -- at a specific index -- is replaced by single or multiple elements.\n\n[source,ruby]\n----\n%i[a b c].replace_at 0, :x      # [:x, :b, :c]\n%i[a b c].replace_at 1, :x      # [:a, :x, :c]\n%i[a b c].replace_at 1, :x, :y  # [:a, :x, :y, :c]\n%i[a b c].replace_at -1, :x     # [:a, :b, :x]\n----\n\n===== #ring\n\nAnswers a circular array which can enumerate before, current, after elements.\n\n[source,ruby]\n----\nexample = [1, 2, 3]\nexample.ring  # \"#\u003cEnumerator: ...\u003e\"\nexample.ring { |(before, current, after)| puts \"#{before} #{current} #{after}\" }\n\n# [3 1 2]\n# [1 2 3]\n# [2 3 1]\n----\n\n===== #supplant\n\nAnswers mutated array where first target element found is replaced by single or multiple elements.\n\n[source,ruby]\n----\n%i[a b a].supplant :a, :z       # [:z, :b, :a]\n%i[a b a].supplant :a, :z, :y   # [:z, :y, :b, :a]\n%i[a b a].supplant :a, %i[z y]  # [[:z, :y], :b, :a]\n----\n\n===== #supplant_if\n\nAnswers mutated array where all target elements are replaced by single or multiple elements.\n\n⚠️ Be aware that this can be an expensive operation on large arrays.\n\n[source,ruby]\n----\n%i[a b a].supplant_if :a, :z       # [:z, :b, :z]\n%i[a b a].supplant_if :a, :z, :y   # [:z, :y, :b, :z, :y]\n%i[a b a].supplant_if :a, %i[z y]  # [[:z, :y], :b, [:z, :y]]\n----\n\n===== #to_sentence\n\nAnswers a sentence using `\"and\"` as the default conjunction and `\", \"` as the default delimiter.\nUseful when building documentation, answering human readable error messages, etc.\n\n[source,ruby]\n----\n[].to_sentence                                        # \"\"\n[\"demo\"].to_sentence                                  # \"demo\"\n[\"a\", :b].to_sentence                                 # \"a and b\"\n[1, \"a\", :b, 2.0, /\\w+/].to_sentence                  # \"1, a, b, 2.0, and (?-mix:\\\\w+)\"\n%w[one two three].to_sentence                         # \"one, two, and three\"\n%w[eins zwei drei].to_sentence \"und\", delimiter: \" \"  # \"eins zwei und drei\"\n----\n\n💡 You can use a string or a symbol for the conjunction (i.e. `\"and\"` or `:and`).\n\n===== #to_usage\n\nFurther 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.\n\n[source,ruby]\n----\n[].to_usage                                        # \"\"\n[\"demo\"].to_usage                                  # \"\\\"demo\\\"\"\n[\"a\", :b].to_usage                                 # \"\\\"a\\\" and :b\"\n[1, \"a\", :b, 2.0, /\\w+/].to_usage                  # \"1, \\\"a\\\", :b, 2.0, and /\\\\w+/\"\n%w[one two three].to_usage                         # \"\\\"one\\\", \\\"two\\\", and \\\"three\\\"\"\n%w[eins zwei drei].to_usage \"und\", delimiter: \" \"  # \"\\\"eins\\\" \\\"zwei\\\" und \\\"drei\\\"\"\n----\n\n💡 You can use a string or a symbol for the conjunction (i.e. `\"and\"` or `:and`).\n\n==== Binding\n\n===== #[]\n\nAllows you to obtain a local variable. This is an alias to `#local_variable_get`.\n\n[source,ruby]\n----\na = 1\nbinding[:a]      # 1\nbinding[:bogus]  # `bogus' is not defined (NameError)\n----\n\n===== #[]=\n\nAllows you to set a local variable. This is an alias to `#local_variable_set`.\n\n[source,ruby]\n----\na = 1\nbinding[:a] = 5\nbinding[:bogus] = \"bad\"\n\nbinding[:a]      # 5\nbinding[:bogus]  # # `bogus' is not defined (NameError)\n----\n\n===== #local?\n\nAllows you to check if local variable is defined. This is an alias to `#local_variable_defined?`.\n\n[source,ruby]\n----\na = 1\n\nbinding.local? :a  # true\nbinding.local? :b  # false\n----\n\n===== #locals\n\nAllows you to acquire all locally defined variables. This is an alias to `#local_variables`.\n\n[source,ruby]\n----\nbinding.locals  # []\n\na = 1\nb = 2\n\nbinding.locals  # [:a, :b]\n----\n\n==== Data\n\n===== #diff\n\nAllows you to obtain the differences between two objects.\n\n[source,ruby]\n----\nimplementation = Data.define :a, :b, :c\n\none = implementation.new a: 1, b: 2, c: 3\ntwo = implementation.new a: 3, b: 2, c: 1\nthree = Data.define(:x, :y).new x: 1, y: 2\n\none.diff one         # {}\none.diff two         # {:a=\u003e[1, 3], :c=\u003e[3, 1]}\none.diff three       # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\none.diff Object.new  # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\n----\n\nAny object that _is not_ the same type will have a `nil` value as shown in the last two examples.\n\n==== DateTime\n\n===== .utc\n\nAnswers new DateTime object for current UTC date/time.\n\n[source,ruby]\n----\nDateTime.utc # \"#\u003cDateTime: 2019-12-31T18:17:00+00:00 ((2458849j,65820s,181867000n),+0s,2299161j)\u003e\"\n----\n\n==== Hash\n\n===== .infinite\n\nAnswers new hash where missing keys, even deeply nested, answer an empty hash.\n\n[source,ruby]\n----\nexample = Hash.infinite\nexample[:a]          # {}\nexample[:a][:b][:c]  # {}\n----\n\n===== .with_default\n\nAnswers new hash where every top-level missing key has the same default value.\n\n[source,ruby]\n----\nexample = Hash.with_default \"\"\nexample[:a]  # \"\"\n\nexample = Hash.with_default []\nexample[:b]  # []\n----\n\n===== #compress\n\nRemoves `nil` and empty objects without mutating itself. Answers itself if nothing to remove.\n\n[source,ruby]\n----\nobject = Object.new\nexample = {a: 1, b: \"blueberry\", c: nil, d: \"\", e: [], f: {}, g: object}\n\n{}.compress            # {}\n{a: 1, b: 2}.compress  # {a: 1, b: 2}\nexample.compress       # {a: 1, b: \"blueberry\", g: object}\nexample                # {a: 1, b: \"blueberry\", c: nil, d: \"\", e: [], f: {}, g: object}\n----\n\n===== #compress!\n\nRemoves `nil` and empty objects while mutating itself. Answers `nil` if nothing to remove.\n\n[source,ruby]\n----\nobject = Object.new\nexample = {a: 1, b: \"blueberry\", c: nil, d: \"\", e: [], f: {}, g: object}\n\n{}.compress!            # nil\n{a: 1, b: 2}.compress!  # nil\nexample.compress!       # {a: 1, b: \"blueberry\", g: object}\nexample                 # {a: 1, b: \"blueberry\", g: object}\n----\n\n===== #deep_merge\n\nMerges deeply nested hashes together without mutating itself.\n\n[source,ruby]\n----\nexample = {a: \"A\", b: {one: \"One\", two: \"Two\"}}\n\nexample.deep_merge b: {one: 1}  # {a: \"A\", b: {one: 1, two: \"Two\"}}\nexample                         # {a: \"A\", b: {one: \"One\", two: \"Two\"}}\n----\n\n===== #deep_merge!\n\nMerges deeply nested hashes together while mutating itself.\n\n[source,ruby]\n----\nexample = {a: \"A\", b: {one: \"One\", two: \"Two\"}}\n\nexample.deep_merge! b: {one: 1}  # {a: \"A\", b: {one: 1, two: \"Two\"}}\nexample                          # {a: \"A\", b: {one: 1, two: \"Two\"}}\n----\n\n===== #deep_stringify_keys\n\nAnswers string keys of a nested hash without mutating itself. Does not handle nested arrays, though.\n\n[source,ruby]\n----\nexample = {a: {b: 2}}\nexample.deep_stringify_keys  # {\"a\" =\u003e {\"b\" =\u003e 1}}\nexample                      # {a: {b: 2}}\n----\n\n===== #deep_stringify_keys!\n\nAnswers string keys of nested hash while mutating itself. Does not handle nested arrays, though.\n\n[source,ruby]\n----\nexample = {a: {b: 2}}\nexample.deep_stringify_keys!  # {\"a\" =\u003e {\"b\" =\u003e 1}}\nexample                       # {\"a\" =\u003e {\"b\" =\u003e 1}}\n----\n\n===== #deep_symbolize_keys\n\nSymbolizes keys of nested hash without mutating itself. Does not handle nested arrays, though.\n\n[source,ruby]\n----\nexample = {\"a\" =\u003e {\"b\" =\u003e 2}}\nexample.deep_symbolize_keys  # {a: {b: 1}}\nexample                      # {\"a\" =\u003e {\"b\" =\u003e 2}}\n----\n\n===== #deep_symbolize_keys!\n\nSymbolizes keys of nested hash while mutating itself. Does not handle nested arrays, though.\n\n[source,ruby]\n----\nexample = {\"a\" =\u003e {\"b\" =\u003e 2}}\nexample.deep_symbolize_keys!  # {a: {b: 1}}\nexample                       # {a: {b: 1}}\n----\n\n===== #diff\n\nAllows you to obtain the differences between two objects.\n\n[source,ruby]\n----\none = {a: 1, b: 2, c: 3}\ntwo = {a: 3, b: 2, c: 1}\nthree = {c: 3, b: 2, a: 1}\nfour = Data.define(:x, :y).new x: 1, y: 2\n\none.diff one         # {}\none.diff two         # {:a=\u003e[1, 3], :c=\u003e[3, 1]}\none.diff three       # {}\none.diff four        # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\none.diff Object.new  # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\n----\n\nAny 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.\n\n===== #fetch_value\n\nFetches value for exiting or missing key. Behavior is identical to `#fetch` except when the value of\nthe key is `nil` you'll get the default value instead. This eliminates the need for using an _or_\nexpression: `example.fetch(:desired_key) || \"default_value\"`.\n\n[source,ruby]\n----\n{a: \"demo\"}.fetch_value :a, \"default\"   # \"demo\"\n{a: \"demo\"}.fetch_value :a              # \"demo\"\n{a: nil}.fetch_value :a, \"default\"      # \"default\"\n{a: nil}.fetch_value(:a) { \"default\" }  # \"default\"\n{}.fetch_value :a                       # KeyError\n{}.fetch_value(:a) { \"default\" }        # \"default\"\n{a: \"demo\"}.fetch_value                 # ArgumentError\n----\n\n===== #flatten_keys\n\nFlattens nested keys as top-level keys without mutating itself. Keys are converted to symbols. Does not handle nested arrays.\n\n[source,ruby]\n----\n{a: {b: 1}}.flatten_keys prefix: :demo          # {demo_a_b: 1}\n{a: {b: 1}}.flatten_keys delimiter: :|          # {:\"a|b\" =\u003e 1}\n\nexample = {a: {b: 1}}\nexample.flatten_keys                            # {a_b: 1}\nexample                                         # {a: {b: 1}}\n----\n\n===== #flatten_keys!\n\nFlattens nested keys as top-level keys while mutating itself. Keys are converted to symbols. Does not handle nested arrays.\n\n[source,ruby]\n----\n{a: {b: 1}}.flatten_keys! prefix: :demo          # {demo_a_b: 1}\n{a: {b: 1}}.flatten_keys! delimiter: :|          # {:\"a|b\" =\u003e 1}\n\nexample = {a: {b: 1}}\nexample.flatten_keys!  # {a_b: 1}\nexample                # {a_b: 1}\n----\n\n===== #many?\n\nAnswers true if a hash has more than one element. Can take a block which evaluates as truthy or\nfalsey.\n\n[source,ruby]\n----\n{a: 1, b: 2}.many?                                     # true\n{a: 1, b: 2, c: 2}.many? { |_key, value| value == 2 }  # true\n{a: 1}.many?                                           # false\n{}.many?                                               # false\n----\n\n===== #recurse\n\nRecursively iterates over the hash and any hash value by applying the given block to it. Does not\nhandle nested arrays, though.\n\n[source,ruby]\n----\nexample = {\"a\" =\u003e {\"b\" =\u003e 1}}\nexample.recurse(\u0026:symbolize_keys)  # {a: {b: 1}}\nexample.recurse(\u0026:invert)          # {{\"b\" =\u003e 1} =\u003e \"a\"}\n----\n\n===== #stringify_keys\n\nConverts keys to strings without mutating itself.\n\n[source,ruby]\n----\nexample = {a: 1, b: 2}\nexample.stringify_keys  # {\"a\" =\u003e 1, \"b\" =\u003e 2}\nexample                 # {a: 1, b: 2}\n----\n\n===== #stringify_keys!\n\nConverts keys to strings while mutating itself.\n\n[source,ruby]\n----\nexample = {a: 1, b: 2}\nexample.stringify_keys!  # {\"a\" =\u003e 1, \"b\" =\u003e 2}\nexample                  # {\"a\" =\u003e 1, \"b\" =\u003e 2}\n----\n\n===== #symbolize_keys\n\nConverts keys to symbols without mutating itself.\n\n[source,ruby]\n----\nexample = {\"a\" =\u003e 1, \"b\" =\u003e 2}\nexample.symbolize_keys  # {a: 1, b: 2}\nexample                 # {\"a\" =\u003e 1, \"b\" =\u003e 2}\n----\n\n===== #symbolize_keys!\n\nConverts keys to symbols while mutating itself.\n\n[source,ruby]\n----\nexample = {\"a\" =\u003e 1, \"b\" =\u003e 2}\nexample.symbolize_keys!  # {a: 1, b: 2}\nexample                  # {a: 1, b: 2}\n----\n\n===== #transform_value\n\nTransforms 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.\n\n[source,ruby]\n----\nexample = {a: 1, b: 2}\n\nexample.transform_value :b                          # {a: 1, b: 2}\nexample.transform_value(:b) { 20 }                  # {a: 1, b: 20}\nexample.transform_value(:b) { |value| value * 10 }  # {a: 1, b: 20}\nexample.transform_value :c                          # {a: 1, b: 2}\nexample.transform_value(:c) { :bogus }              # {a: 1, b: 2}\n----\n\nThe original object _is not_ mutated:\n\n[source,ruby]\n----\nexample.transform_value(:b) { 20 }  # {a: 1, b: 20}\nexample                             # {a: 1, b: 2}\n----\n\n===== #transform_value!\n\nTransforms a value for the specified key _only_ if the key exists and a block is given. Otherwise, the original hash is answered. Mutates itself.\n\n[source,ruby]\n----\nexample = {a: 1, b: 2}\n\nexample.transform_value! :b                          # {a: 1, b: 2}\nexample.transform_value!(:b) { 20 }                  # {a: 1, b: 20}\nexample.transform_value!(:b) { |value| value * 10 }  # {a: 1, b: 20}\nexample.transform_value! :c                          # {a: 1, b: 2}\nexample.transform_value!(:c) { :bogus }              # {a: 1, b: 2}\n----\n\nThe original object _is_ mutated:\n\n[source,ruby]\n----\nexample.transform_value!(:b) { 20 }  # {a: 1, b: 20}\nexample                              # {a: 1, b: 20}\n----\n\n===== #transform_with\n\nTransforms 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:\n\n[source,ruby]\n----\nexample = {name: \"Jayne Doe\", email: \"\u003cjd@example.com\u003e\"}\n\nexample.transform_with name: -\u003e value { value.delete_suffix \" Doe\" },\n                       email: -\u003e value { value.tr \"\u003c\u003e\", \"\" }\n# {name: \"Jayne Doe\", email: \"\u003cjd@example.com\u003e\"}\n----\n\nInvalid keys are ignored:\n\n[source,ruby]\n----\nexample.transform_with bogus: -\u003e value { value.tr \"\u003c\u003e\", \"\" }\n# {name: \"Jayne Doe\", email: \"jd@example.com\"}\n----\n\nThe original object _is not_ mutated:\n\n[source,ruby]\n----\nexample  # {name: \"Jayne Doe\", email: \"\u003cjd@example.com\u003e\"}\n----\n\n===== #transform_with!\n\nTransforms values of keys using specific operations (i.e. any object that responds to `#call`). Mutates itself and you can transform multiple values at once:\n\n[source,ruby]\n----\nexample = {name: \"Jayne Doe\", email: \"\u003cjd@example.com\u003e\"}\n\nexample.transform_with! name: -\u003e value { value.delete_suffix \" Doe\" },\n                        email: -\u003e value { value.tr \"\u003c\u003e\", \"\" }\n# {name: \"Jayne\", email: \"jd@example.com\"}\n----\n\nInvalid keys are ignored:\n\n[source,ruby]\n----\nexample.transform_with! bogus: -\u003e value { value.tr \"\u003c\u003e\", \"\" }\n# {name: \"Jayne\", email: \"jd@example.com\"}\n----\n\nThe original object _is_ mutated:\n\n[source,ruby]\n----\nexample  # {name: \"Jayne\", email: \"jd@example.com\"}\n----\n\n===== #use\n\nUses 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.\n\n[source,ruby]\n----\nexample = {unit: \"221B\", street: \"Baker Street\", city: \"London\", country: \"UK\"}\n\nexample.use { |unit, street| \"#{unit} #{street}\" }  # \"221B Baker Street\"\n----\n\n==== IO\n\n===== .void\n\nAnswers an IO stream which points to `/dev/null` in order to ignore any reads or writes to the\nstream. When given a block, the stream will automatically close upon block exit. When not given a\nblock, you'll need to close the stream manually.\n\n[source,ruby]\n----\nio = IO.void                                    # \"#\u003cIO:fd 20\u003e\"\nio = IO.void { |void| void.write \"nevermore\" }  # \"#\u003cIO:(closed)\u003e\"\n----\n\n===== #redirect\n\nRedirects current stream to other stream when given a block. Without a block, the original stream is\nanswered instead.\n\n[source,ruby]\n----\nio = IO.new IO.sysopen(Pathname(\"demo.txt\").to_s, \"w+\")\nother = IO.new IO.sysopen(Pathname(\"other.txt\").to_s, \"w+\")\n\nio.redirect other                                    # \"#\u003cIO:fd 20\u003e\"\nio.redirect(other) { |stream| stream.write \"demo\" }  # \"#\u003cIO:fd 21\u003e\"\n----\n\n===== #reread\n\nAnswers full stream by rewinding to beginning of stream and reading all content.\n\n[source,ruby]\n----\nio = IO.new IO.sysopen(Pathname(\"demo.txt\").to_s, \"w+\")\nio.write \"This is a demo.\"\n\nio.reread           # \"This is a demo.\"\nio.reread 4         # \"This\"\n\nbuffer = \"\".dup\nio.reread(buffer:)  # \"This is a demo.\"\nbuffer              # \"This is a demo.\"\n----\n\n===== #squelch\n\nTemporarily ignores any reads/writes for code executed within a block. Answers itself without any\narguments or when given a block.\n\n[source,ruby]\n----\nio = IO.new IO.sysopen(Pathname(\"test.txt\").to_s, \"w+\")\n\nio.squelch                      # \"#\u003cIO:fd 20\u003e\"\nio.squelch { io.write \"Test\" }  # \"#\u003cIO:fd 20\u003e\"\nio.reread                       # \"\"\n----\n\n==== Module\n\n===== #pseudonym\n\nAllows 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.\n\n[source,ruby]\n----\nModule.new.pseudonym \"demo\"                       # demo-44600\nModule.new.pseudonym \"demo\", delimiter: \"_\"       # demo_60900\nModule.new.pseudonym \"demo\", nil                  # demo-\nModule.new.pseudonym \"demo\", nil, delimiter: nil  # demo\n----\n\nThe same applies for anonymous classes since classes inherit from modules:\n\n[source,ruby]\n----\nClass.new.pseudonym \"demo\"                       # demo-44600 \u003c Object\nClass.new.pseudonym \"demo\", delimiter: \"_\"       # demo_60900 \u003c Object\nClass.new.pseudonym \"demo\", nil                  # demo- \u003c Object\nClass.new.pseudonym \"demo\", nil, delimiter: nil  # demo \u003c Object\n----\n\n💡 While convenient, if you find yourself nullifying the suffix and/or delimiter, you're better off using `+#set_temporary_name+`.\n\n==== Object\n\n===== #in?\n\nAllows you to know if `self` is included in, or an element of, the target object.\n\n[source,ruby]\n----\n1.in? [1, 2, 3]             # true\n9.in? [1, 2, 3]             # false\n\n\"a\".in? %w[a b c]           # true\n\"z\".in? %w[a b c]           # false\n\n:a.in? %i[a b c]            # true\n:z.in? %i[a b c]            # false\n\n:a.in? %i[a b c].to_enum    # true\n:z.in? %i[a b c].to_enum    # false\n\n:a.in?({a: 1, b: 2, c: 3})  # true\n:z.in?({a: 1, b: 2, c: 3})  # false\n\n1.in? 1..5                  # true\n9.in? 1..5                  # false\n\n1.in? Set[1, 2, 3]          # true\n9.in? Set[1, 2, 3]          # false\n\n\"a\".in? \"abcde\"             # true\n\"z\".in? \"abcde\"             # false\n\n\"z\".in? Object.new          # `String#include?` must be implemented. (NoMethodError)\n----\n\n===== #to_proc\n\nAllows you to cast any object to a proc.\n\n[source,ruby]\n----\none = Class.new { def call = :test }\n           .new\ntwo = Object.new\n\none.to_proc  # #\u003cProc:0x0000000124019580 (lambda)\u003e\ntwo.to_proc  # `Object#call` must be implemented. (NoMethodError)\n----\n\n==== Pathname\n\n===== Pathname\n\nEnhances the `Kernel` conversion function which casts `nil` into a pathname in order to avoid:\n`TypeError (no implicit conversion of nil into String)`. The pathname remains invalid but at least\nyou have an instance of `Pathname`, which behaves like a _Null Object_, that can be used to\nconstruct a valid path.\n\n[source,ruby]\n----\nPathname nil  # Pathname(\"\")\n----\n\n===== .home\n\nAnswers user home directory.\n\n[source,ruby]\n----\nPathname.home  # Pathname \"/Users/demo\"\n----\n\n===== .require_tree\n\nRequires all Ruby files in given root path and corresponding nested tree structure. All files are sorted before being required to ensure consistent behavior. Example:\n\n[source,ruby]\n----\n# Before\nDir[File.join(__dir__, \"support/shared_contexts/**/*.rb\")].sort.each { |path| require path }\n\n# After\nPathname.require_tree \"#{__dir__}/support/shared_contexts\"\n----\n\nThe following are further examples of potential usage:\n\n[source,ruby]\n----\n# Requires all files in root directory and below.\nPathname.require_tree __dir__\n\n# Requires all files in `/test/**/*.rb` and below.\nPathname.require_tree \"/test\"\n\n# Requires all files in RSpec shared examples directory structure.\nPathname.require_tree SPEC_ROOT.join(\"support/shared_examples\")\n----\n\n===== .root\n\nAnswers operating system root path.\n\n[source,ruby]\n----\nPathname.root  # Pathname \"/\"\n----\n\n===== #change_dir\n\nWraps `Dir.chdir` behavior by changing to directory of current path. See\nlink:https://rubyapi.org/o/Dir.chdir#method-c-chdir[Dir.chdir] for details.\n\n[source,ruby]\n----\ncurrent = Pathname.pwd                  # \"$HOME/demo\" (Present Working Directory)\ncustom = current.join(\"test\").make_dir  # Pathname \"$HOME/demo/test\"\ncustom.change_dir                       # \"$HOME/demo/test\" (Present Working Directory)\ncurrent.change_dir                      # \"$HOME/demo\" (Present Working Directory)\ncustom.change_dir { \"example\" }         # \"example\"\ncustom.change_dir { |path| path }       # Pathname \"$HOME/demo/test\"\nPathname.pwd                            # \"$HOME/demo\" (Present Working Directory)\n----\n\n⚠️ This method is _not thread safe_ and suffers the same issues as found with native `Dir.chdir` functionality because the underlying native call is global. Use with care.\n\n===== #copy\n\nCopies file from current location to new location while answering itself so it can be chained.\n\n[source,ruby]\n----\nPathname(\"input.txt\").copy Pathname(\"output.txt\")  # Pathname(\"input.txt\")\n----\n\n===== #deep_touch\n\nHas all of the same functionality as the `#touch` method while being able to create ancestor\ndirectories no matter how deeply nested the file might be.\n\n[source,ruby]\n----\nPathname(\"a/b/c/d.txt\").touch               # Pathname(\"a/b/c/d.txt\")\nPathname(\"a/b/c/d.txt\").touch Time.now - 1  # Pathname(\"a/b/c/d.txt\")\n----\n\n===== #delete\n\nDeletes file or directory and answers itself so it can be chained.\n\n[source,ruby]\n----\n# When path exists.\nPathname(\"/example.txt\").touch.delete  # Pathname(\"/example\")\n\n# When path doesn't exist.\nPathname(\"/example.txt\").delete        # Errno::ENOENT\n----\n\n===== #delete_prefix\n\nDeletes a path prefix and answers new pathname.\n\n[source,ruby]\n----\nPathname(\"a/path/example-test.rb\").delete_prefix \"example-\"  # Pathname(\"a/path/test.rb\")\nPathname(\"example-test.rb\").delete_prefix \"example-\"         # Pathname(\"test.rb\")\nPathname(\"example-test.rb\").delete_prefix \"miss\"             # Pathname(\"example-test.rb\")\n----\n\n===== #delete_suffix\n\nDeletes a path suffix and answers new pathname.\n\n[source,ruby]\n----\nPathname(\"a/path/test-example.rb\").delete_suffix \"-example\"  # Pathname(\"a/path/test.rb\")\nPathname(\"test-example.rb\").delete_suffix \"-example\"         # Pathname(\"test.rb\")\nPathname(\"test-example.rb\").delete_suffix \"miss\"             # Pathname(\"test-example.rb\")\n----\n\n===== #directories\n\nAnswers all directories or filtered directories for current path.\n\n[source,ruby]\n----\nPathname(\"/example\").directories                           # [Pathname(\"a\"), Pathname(\"b\")]\nPathname(\"/example\").directories \"a*\"                      # [Pathname(\"a\")]\nPathname(\"/example\").directories flag: File::FNM_DOTMATCH  # [Pathname(\"..\"), Pathname(\".\")]\n----\n\n===== #empty\n\nEmpties a directory of children (i.e. folders, nested folders, or files) or clears an existing file\nof contents. If a directory or file doesn't exist, it will be created.\n\n[source,ruby]\n----\ndirectory = Pathname(\"test\").mkpath\nfile = directory.join(\"test.txt\").write(\"example\")\n\nfile.empty.read           # \"\"\ndirectory.empty.children  # []\n----\n\n===== #extensions\n\nAnswers file extensions as an array.\n\n[source,ruby]\n----\nPathname(\"example.txt.erb\").extensions  # [\".txt\", \".erb\"]\n----\n\n===== #files\n\nAnswers all files or filtered files for current path.\n\n[source,ruby]\n----\nPathname(\"/example\").files                           # [Pathname(\"a.txt\"), Pathname(\"a.png\")]\nPathname(\"/example\").files \"*.png\"                   # [Pathname(\"a.png\")]\nPathname(\"/example\").files flag: File::FNM_DOTMATCH  # [Pathname(\".ruby-version\")]\n----\n\n===== #gsub\n\nSame behavior as `String#gsub` but answers a path with patterns replaced with desired substitutes.\n\n[source,ruby]\n----\nPathname(\"/a/path/some/path\").gsub \"path\", \"test\"\n# Pathname(\"/a/test/some/test\")\n\nPathname(\"/%placeholder%/some/%placeholder%\").gsub \"%placeholder%\", \"test\"\n# Pathname(\"/test/some/test\")\n----\n\n===== #make_ancestors\n\nEnsures all ancestor directories are created for a path.\n\n[source,ruby]\n----\nPathname(\"/one/two\").make_ancestors  # Pathname(\"/one/two\")\nPathname(\"/one\").exist?              # true\nPathname(\"/one/two\").exist?          # false\n----\n\n===== #make_dir\n\nProvides alternative `#mkdir` behavior by always answering itself (even when directory exists) and\nnot throwing errors when directory does exist in order to ensure the pathname can be chained.\n\n[source,ruby]\n----\nPathname(\"/one\").make_dir           # Pathname(\"/one\")\nPathname(\"/one\").make_dir.make_dir  # Pathname(\"/one\")\n----\n\n===== #name\n\nAnswers file name without extension.\n\n[source,ruby]\n----\nPathname(\"example.txt\").name # Pathname(\"example\")\n----\n\n===== #puts\n\nWraps `#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.\n\n[source,ruby]\n----\npath = Pathname(\"test.txt\").touch\npath.puts \"Test.\"\npath.read                                      # \"Test.\\n\"\n\nPathname(\"text.txt\").touch.puts(\"Test.\").read  # \"Test.\\n\"\n----\n\n===== #relative_parent\n\nAnswers relative path from parent directory. This complements: `#relative_path_from`.\n\n[source,ruby]\n----\nPathname(\"/one/two/three\").relative_parent \"/one\"  # Pathname \"two\"\n----\n\n===== #remove_dir\n\nProvides alternative `#rmdir` behavior by always answering itself (even when full path exists) and\nnot throwing errors when directory does exist in order to ensure the pathname can be chained.\n\n[source,ruby]\n----\nPathname(\"/test\").make_dir.remove_dir.exist?  # false\nPathname(\"/test\").remove_dir                  # Pathname(\"/test\")\nPathname(\"/test\").remove_dir.remove_dir       # Pathname(\"/test\")\n----\n\n===== #rewrite\n\nWhen given a block, it provides the contents of the recently read file for manipulation and\nimmediate writing back to the same file.\n\n[source,ruby]\n----\nPathname(\"/test.txt\").rewrite                                           # Pathname(\"/test.txt\")\nPathname(\"/test.txt\").rewrite { |body| body.sub \"[token]\", \"example\" }  # Pathname(\"/test.txt\")\n----\n\n===== #touch\n\nUpdates access and modification times for an existing path by defaulting to current time. When path\ndoesn't exist, it will be created as a file.\n\n[source,ruby]\n----\nPathname(\"example\").touch                   # Pathname(\"example\")\nPathname(\"example\").touch Time.now - 1      # Pathname(\"example\")\nPathname(\"example.txt\").touch               # Pathname(\"example.txt\")\nPathname(\"example.txt\").touch Time.now - 1  # Pathname(\"example.txt\")\n----\n\n===== #write\n\nWrites to file and answers itself so it can be chained. See `IO.write` for details on additional\noptions.\n\n[source,ruby]\n----\nPathname(\"example.txt\").write \"test\"             # Pathname(\"example.txt\")\nPathname(\"example.txt\").write \"test\", offset: 1  # Pathname(\"example.txt\")\nPathname(\"example.txt\").write \"test\", mode: \"a\"  # Pathname(\"example.txt\")\n----\n\n==== String\n\n===== #blank?\n\nAnswers `true`/`false` based on whether string is blank, `\u003cspace\u003e`, `\\n`, `\\t`, and/or `\\r`.\n\n[source,ruby]\n----\n\" \\n\\t\\r\".blank?  # true\n----\n\n===== #camelcase\n\nAnswers a camel cased string.\n\n[source,ruby]\n----\n\"this_is_an_example\".camelcase  # \"ThisIsAnExample\"\n----\n\n===== #down\n\nAnswers string with only first letter down cased.\n\n[source,ruby]\n----\n\"EXAMPLE\".down  # \"eXAMPLE\"\n----\n\n===== #first\n\nAnswers first character of a string or first set of characters if given a number.\n\n[source,ruby]\n----\n\"example\".first    # \"e\"\n\"example\".first 4  # \"exam\"\n----\n\n===== #indent\n\nAnswers 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.\n\n[source,ruby]\n----\n\"example\".indent              # \"  example\"\n\"example\".indent 0            # \"example\"\n\"example\".indent -1           # \"example\"\n\"example\".indent 2            # \"    example\"\n\"example\".indent 3, pad: \" \"  # \"   example\"\n----\n\n===== #last\n\nAnswers last character of a string or last set of characters if given a number.\n\n[source,ruby]\n----\n\"instant\".last    # \"t\"\n\"instant\".last 3  # \"ant\"\n----\n\n===== #pluralize\n\nAnswers plural form of self when given a suffix to add. The plural form of the word can be\ndynamically calculated when given a count and a replacement pattern (i.e. string or regular\nexpression) can be supplied for further specificity. Usage is based on\nlink:https://en.wikipedia.org/wiki/English_plurals[plurals in English] which may or may not work\nwell in other languages.\n\n[source,ruby]\n----\n\"apple\".pluralize \"s\"                      # apples\n\"apple\".pluralize \"s\", 0                   # apples\n\"apple\".pluralize \"s\", 1                   # apple\n\"apple\".pluralize \"s\", -1                  # apple\n\"apple\".pluralize \"s\", 2                   # apples\n\"apple\".pluralize \"s\", -2                  # apples\n\"cactus\".pluralize \"i\", replace: \"us\"      # cacti\n\"cul-de-sac\".pluralize \"ls\", replace: \"l\"  # culs-de-sac\n----\n\n===== #singularize\n\nAnswers singular form of self when given a suffix to remove (can be a string or a regular\nexpression). The singular form of the word can be dynamically calculated when given a count and a\nreplacement string can be supplied for further specificity. Usage is based on\nlink:https://en.wikipedia.org/wiki/English_plurals[plurals in English] which may or may not work\nwell in other languages.\n\n[source,ruby]\n----\n\"apples\".singularize \"s\"                      # apple\n\"sacks\".singularize /s$/                      # sack\n\"apples\".singularize \"s\", 0                   # apples\n\"apples\".singularize \"s\", 1                   # apple\n\"apples\".singularize \"s\", -1                  # apple\n\"apples\".singularize \"s\", 2                   # apples\n\"apples\".singularize \"s\", -2                  # apples\n\"cacti\".singularize \"i\", replace: \"us\"        # cactus\n\"culs-de-sac\".singularize \"ls\", replace: \"l\"  # cul-de-sac\n----\n\n===== #snakecase\n\nAnswers a snake cased string.\n\n[source,ruby]\n----\n\"ThisIsAnExample\".snakecase  # \"this_is_an_example\"\n----\n\n===== #squish\n\nRemoves leading, in body, and trailing whitespace, including any tabs or newlines, without mutating itself. Processes ASCII and unicode whitespace as well.\n\n[source,ruby]\n----\n\"one two three\".squish                  # \"one two three\"\n\" one  two   \\n    \\t   three \".squish  # \"one two three\"\n----\n\n===== #titleize\n\nAnswers a title string with proper capitalization of each word.\n\n[source,ruby]\n----\n\"ThisIsAnExample\".titleize  # \"This Is An Example\"\n----\n\n===== #truncate\n\nAnswers a truncated, non-mutated, string for given length with optional delimiter and/or overflow.\n\nThe 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.\n\nThe 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.\n\n[source,ruby]\n----\ndemo = \"It was the best of times\"\nlength = demo.length\n\ndemo.truncate 9                          # \"It was...\"\ndemo.truncate 12                         # \"It was th...\"\ndemo.truncate length                     # \"It was the best of times\"\ndemo.truncate Float::INFINITY            # \"It was the best of times\"\ndemo.truncate 12, \" \"                    # \"It was...\"\ndemo.truncate 12, /\\s/                   # \"It was...\"\ndemo.truncate 6, trailer: \"\"             # \"It was\"\ndemo.truncate 16, trailer: \"... (more)\"  # \"It was... (more)\"\n\"demo\".truncate 3                        # \"...\"\n----\n\n===== #to_bool\n\nAnswers string as a boolean.\n\n[source,ruby]\n----\n\"true\".to_bool     # true\n\"yes\".to_bool      # true\n\"1\".to_bool        # true\n\"\".to_bool         # false\n\"example\".to_bool  # false\n----\n\n===== #up\n\nAnswers string with only first letter capitalized.\n\n[source,ruby]\n----\n\"example\".up  # \"Example\"\n----\n\n==== String IO\n\n===== #reread\n\nAnswers full string by rewinding to beginning of string and reading all content.\n\n[source,ruby]\n----\nio = StringIO.new\nio.write \"This is a test.\"\n\nio.reread    # \"This is a test.\"\nio.reread 4  # \"This\"\n\nbuffer = \"\".dup\nio.reread(buffer:)  # \"This is a test.\"\nbuffer              # \"This is a test.\"\n----\n\n===== #to_s\n\nAnswers underlying string representation for _explicit_ conversion.\n\n[source,ruby]\n----\nio = StringIO.new\nio.write \"One\"\nio.write \", \"\nio.write \"Two.\"\nio.to_s # \"One, Two.\"\n----\n\n===== #to_str\n\nAnswers underlying string representation for _implicit_ conversion.\n\n[source,ruby]\n----\nio = StringIO.new\nio.write \"One\"\nio.write \", \"\nio.write \"Two.\"\nio.to_str # \"One, Two.\"\n----\n\n==== Struct\n\n===== #diff\n\nAllows you to obtain the differences between two objects.\n\n[source,ruby]\n----\nimplementation = Struct.new :a, :b, :c\n\none = implementation.new a: 1, b: 2, c: 3\ntwo = implementation.new a: 3, b: 2, c: 1\nthree = Struct.new(:x, :y).new x: 1, y: 2\n\none.diff one         # {}\none.diff two         # {:a=\u003e[1, 3], :c=\u003e[3, 1]}\none.diff three       # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\none.diff Object.new  # {:a=\u003e[1, nil], :b=\u003e[2, nil], :c=\u003e[3, nil]}\n----\n\nAny object that _is not_ the same type will have a `nil` value as shown in the last two examples.\n\n===== #merge\n\nMerges multiple attributes without mutating itself and supports any object that responds to `#to_h`.\nWorks regardless of whether the struct is constructed with positional or keyword arguments.\n\n[source,ruby]\n----\nexample = Struct.new(\"Example\", :a, :b, :c).new 1, 2, 3\nother = Struct.new(\"Other\", :a, :b, :c).new 7, 8, 9\n\nexample.merge a: 10                # #\u003cstruct Struct::Example a=10, b=2, c=3\u003e\nexample.merge a: 10, c: 30         # #\u003cstruct Struct::Example a=10, b=2, c=30\u003e\nexample.merge a: 10, b: 20, c: 30  # #\u003cstruct Struct::Example a=10, b=20, c=30\u003e\nexample.merge other                # #\u003cstruct Struct::Example a=7, b=8, c=9\u003e\nexample                            # #\u003cstruct Struct::Example a=1, b=2, c=3\u003e\n----\n\n===== #merge!\n\nMerges multiple attributes while mutating itself and supports any object that responds to `#to_h`.\nWorks regardless of whether the struct is constructed with positional or keyword arguments.\n\n[source,ruby]\n----\nexample = Struct.new(\"Example\", :a, :b, :c).new 1, 2, 3\nother = Struct.new(\"Other\", :a, :b, :c).new 7, 8, 9\n\nexample.merge! a: 10                # #\u003cstruct Struct::Example a=10, b=2, c=3\u003e\nexample.merge! a: 10, c: 30         # #\u003cstruct Struct::Example a=10, b=2, c=30\u003e\nexample.merge! other                # #\u003cstruct Struct::Example a=7, b=8, c=9\u003e\nexample.merge! a: 10, b: 20, c: 30  # #\u003cstruct Struct::Example a=10, b=20, c=30\u003e\nexample                             # #\u003cstruct Struct::Example a=10, b=20, c=30\u003e\n----\n\n===== #transmute\n\nTransmutes given enumerable by using the foreign key map and merging those key values into the\ncurrent struct while not mutating itself. Works regardless of whether the struct is constructed with\npositional or keyword arguments.\n\n[source,ruby]\n----\na = Struct.new(\"A\", :a, :b, :c).new 1, 2, 3\nb = Struct.new(\"B\", :x, :y, :z).new 7, 8, 9\nc = {r: 10, s: 20, t: 30}\n\na.transmute b, a: :x, b: :y, c: :z  # #\u003cstruct Struct::A a=7, b=8, c=9\u003e\na.transmute b, b: :y                # #\u003cstruct Struct::A a=1, b=8, c=3\u003e\na.transmute c, c: :t                # #\u003cstruct Struct::A a=1, b=2, c=30\u003e\na                                   # #\u003cstruct Struct::A a=1, b=2, c=3\u003e\n----\n\n===== #transmute!\n\nTransmutes given enumerable by using the foreign key map and merging those key values into the\ncurrent struct while mutating itself. Works regardless of whether the struct is constructed with\npositional or keyword arguments.\n\n[source,ruby]\n----\na = Struct.new(\"A\", :a, :b, :c).new 1, 2, 3\nb = Struct.new(\"B\", :x, :y, :z).new 7, 8, 9\nc = {r: 10, s: 20, t: 30}\n\na.transmute! b, a: :x, b: :y, c: :z  # #\u003cstruct Struct::A a=7, b=8, c=9\u003e\na.transmute! b, b: :y                # #\u003cstruct Struct::A a=1, b=8, c=3\u003e\na.transmute! c, c: :t                # #\u003cstruct Struct::A a=1, b=2, c=30\u003e\na                                    # #\u003cstruct Struct::A a=7, b=8, c=30\u003e\n----\n\n===== #with\n\nAn alias of `#merge` and identical in behavior (see `#merge` documentation for details). Allows you to use `Struct` and `Data` objects more interchangeably since they share the same method.\n\n==== Symbol\n\n===== #call\n\nEnhances symbol-to-proc functionality by allowing you to send positional, keyword, and/or a block arguments. This only works with public methods in order to not break encapsulation.\n\n[source,ruby]\n----\n\n%w[clue crow cow].map(\u0026:tr.call(\"c\", \"b\"))                              # [\"blue\", \"brow\", \"bow\"]\n[1.3, 1.5, 1.9].map(\u0026:round.call(half: :up))                            # [1, 2, 2]\n[%w[a b c], %w[c a b]].map(\u0026:index.call { |element| element == \"b\" })   # [1, 2]\n%w[1.out 2.in].map(\u0026:sub.call(/\\./) { |bullet| bullet + \" \" })          # [\"1. out\", \"2. in\"]\n[1, 2, 3].map(\u0026:to_s.call)                                              # [\"1\", \"2\", \"3\"]\n----\n\n⚠️ Use of `#call` without any arguments should be avoided in order to not incur extra processing costs since the original symbol-to-proc call can be used instead.\n\n== Development\n\nTo contribute, run:\n\n[source,bash]\n----\ngit clone https://github.com/bkuhlmann/refinements\ncd refinements\nbin/setup\n----\n\nYou can also use the IRB console for direct access to all objects:\n\n[source,bash]\n----\nbin/console\n----\n\n== Tests\n\nTo test, run:\n\n[source,bash]\n----\nbin/rake\n----\n\n== link:https://alchemists.io/policies/license[License]\n\n== link:https://alchemists.io/policies/security[Security]\n\n== link:https://alchemists.io/policies/code_of_conduct[Code of Conduct]\n\n== link:https://alchemists.io/policies/contributions[Contributions]\n\n== link:https://alchemists.io/policies/developer_certificate_of_origin[Developer Certificate of Origin]\n\n== link:https://alchemists.io/projects/refinements/versions[Versions]\n\n== link:https://alchemists.io/community[Community]\n\n== Credits\n\n* Built with link:https://alchemists.io/projects/gemsmith[Gemsmith].\n* Engineered by link:https://alchemists.io/team/brooke_kuhlmann[Brooke Kuhlmann].\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkuhlmann%2Frefinements","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbkuhlmann%2Frefinements","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbkuhlmann%2Frefinements/lists"}