diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7178301e3..bcb50c1a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org).
-## [v9.4.1](https://github.com/puppetlabs/puppetlabs-stdlib/tree/v9.4.1) - 2023-11-08
+## [v9.5.0](https://github.com/puppetlabs/puppetlabs-stdlib/tree/v9.5.0) - 2024-03-11
+
+[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v9.4.1...v9.5.0)
+
+### Added
+
+- Add function stdlib::sort_by [#1384](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1384) ([XMol](https://github.com/XMol))
+
+### Fixed
+
+- (#1389) - pw_hash with bcrypt not working on puppet master [#1410](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1410) ([TuningYourCode](https://github.com/TuningYourCode))
+
+### Other
+
+- Deprecate `time` function [#1417](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1417) ([alexjfisher](https://github.com/alexjfisher))
+
+## [v9.4.1](https://github.com/puppetlabs/puppetlabs-stdlib/tree/v9.4.1) - 2023-11-09
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v9.4.0...v9.4.1)
@@ -21,10 +37,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
- Modernise `fqdn_rotate` function [#1341](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1341) ([alexjfisher](https://github.com/alexjfisher))
-### Other
-
-- Remove unused parser deprecation function [#1392](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1392) ([ekohl](https://github.com/ekohl))
-
## [v9.3.0](https://github.com/puppetlabs/puppetlabs-stdlib/tree/v9.3.0) - 2023-08-01
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v9.2.0...v9.3.0)
@@ -68,11 +80,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v8.6.0...v9.0.0)
-### Added
-
-- Namespace Puppet 4.x functions [#1356](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1356) ([smortex](https://github.com/smortex))
-- Add a function to update / regenerate deprecated shims [#1349](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1349) ([smortex](https://github.com/smortex))
-
### Changed
- Deprecate the `validate_legacy()` function [#1353](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1353) ([smortex](https://github.com/smortex))
- Remove deprecated functions [#1352](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1352) ([smortex](https://github.com/smortex))
@@ -108,6 +115,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
- Remove deprecated type and type3x functions [#1309](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1309) ([MartyEwings](https://github.com/MartyEwings))
- (CONT-801) Puppet 8 support / Drop Puppet 6 support [#1307](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1307) ([LukasAud](https://github.com/LukasAud))
+### Added
+
+- Namespace Puppet 4.x functions [#1356](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1356) ([smortex](https://github.com/smortex))
+- Add a function to update / regenerate deprecated shims [#1349](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1349) ([smortex](https://github.com/smortex))
+
### Fixed
- Remove deprecated File.exists? [#1357](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1357) ([ekohl](https://github.com/ekohl))
@@ -226,18 +238,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v7.1.0...v8.0.0)
+### Changed
+- Flip installed and present in Function ensure_packages [#1196](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1196) ([cocker-cc](https://github.com/cocker-cc))
+
### Added
- New function to_python() / to_ruby() [#1200](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1200) ([smortex](https://github.com/smortex))
- pdksync - (IAC-1709) - Add Support for Debian 11 [#1199](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1199) ([david22swan](https://github.com/david22swan))
- Stdlib::Http::Method: Add new type for http methods [#1192](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1192) ([b4ldr](https://github.com/b4ldr))
-### Changed
-- Flip installed and present in Function ensure_packages [#1196](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1196) ([cocker-cc](https://github.com/cocker-cc))
-
### Fixed
-- (MODULES-11099) Make merge parameter data types actually backwards compatible [#1191](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1191) ([SimonPe](https://github.com/SimonPe))
+- (MODULES-11099) Make merge parameter data types actually backwards compatible [#1191](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1191) ([LadyNamedLaura](https://github.com/LadyNamedLaura))
## [v7.1.0](https://github.com/puppetlabs/puppetlabs-stdlib/tree/v7.1.0) - 2021-05-17
@@ -259,13 +271,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/v6.6.0...v7.0.0)
+### Changed
+- pdksync - Remove Puppet 5 from testing and bump minimal version to 6.0.0 [#1164](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1164) ([carabasdaniel](https://github.com/carabasdaniel))
+
### Added
- Stdlib::Email type [#1160](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1160) ([b4ldr](https://github.com/b4ldr))
-### Changed
-- pdksync - Remove Puppet 5 from testing and bump minimal version to 6.0.0 [#1164](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1164) ([carabasdaniel](https://github.com/carabasdaniel))
-
### Fixed
- (bugfix) Setting stricter email validation [#1163](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1163) ([pmcmaw](https://github.com/pmcmaw))
@@ -367,14 +379,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/5.2.0...v6.0.0)
+### Changed
+- pdksync - (MODULES-8444) - Raise lower Puppet bound [#1011](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1011) ([david22swan](https://github.com/david22swan))
+
### Added
- (MODULES-8760) Add iterative feature to merge() function [#1008](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1008) ([hlindberg](https://github.com/hlindberg))
- Add a stdlib::ip_in_range() function [#1003](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1003) ([iglov](https://github.com/iglov))
-### Changed
-- pdksync - (MODULES-8444) - Raise lower Puppet bound [#1011](https://github.com/puppetlabs/puppetlabs-stdlib/pull/1011) ([david22swan](https://github.com/david22swan))
-
## [5.2.0](https://github.com/puppetlabs/puppetlabs-stdlib/tree/5.2.0) - 2019-01-18
[Full Changelog](https://github.com/puppetlabs/puppetlabs-stdlib/compare/5.1.0...5.2.0)
diff --git a/REFERENCE.md b/REFERENCE.md
index 5e97d1ac8..8e6621420 100644
--- a/REFERENCE.md
+++ b/REFERENCE.md
@@ -139,6 +139,7 @@ Puppet structure
* [`stdlib::seeded_rand_string`](#stdlib--seeded_rand_string): Generates a consistent random string of specific length based on provided seed.
* [`stdlib::sha256`](#stdlib--sha256): Run a SHA256 calculation against a given value.
* [`stdlib::shell_escape`](#stdlib--shell_escape): Escapes a string so that it can be safely used in a Bourne shell command line.
+* [`stdlib::sort_by`](#stdlib--sort_by): Sort an Array, Hash or String by mapping values through a given block.
* [`stdlib::start_with`](#stdlib--start_with): Returns true if str starts with one of the prefixes given. Each of the prefixes should be a String.
* [`stdlib::str2resource`](#stdlib--str2resource): This converts a string to a puppet resource.
* [`stdlib::time`](#stdlib--time): This function is deprecated. It implements the functionality of the original non-namespaced stdlib `time` function.
@@ -4003,6 +4004,75 @@ Data type: `Any`
The string to escape
+### `stdlib::sort_by`
+
+Type: Ruby 4.x API
+
+Sort an Array, Hash or String by mapping values through a given block.
+
+#### Examples
+
+##### Sort local devices according to their used space.
+
+```puppet
+$facts['mountpoints'].stdlib::sort_by |$m| { $m.dig(1, 'used_bytes') }
+```
+
+#### `stdlib::sort_by(Array $ary, Callable[1,1] &$block)`
+
+The stdlib::sort_by function.
+
+Returns: `Array` Returns an ordered copy of ary.
+
+##### `ary`
+
+Data type: `Array`
+
+The Array to sort.
+
+##### `&block`
+
+Data type: `Callable[1,1]`
+
+The block for transforming elements of ary.
+
+#### `stdlib::sort_by(String $str, Callable[1,1] &$block)`
+
+The stdlib::sort_by function.
+
+Returns: `String` Returns an ordered copy of str.
+
+##### `str`
+
+Data type: `String`
+
+The String to sort.
+
+##### `&block`
+
+Data type: `Callable[1,1]`
+
+The block for transforming elements of str.
+
+#### `stdlib::sort_by(Hash $hsh, Variant[Callable[1,1], Callable[2,2]] &$block)`
+
+The stdlib::sort_by function.
+
+Returns: `Hash` Returns an ordered copy of hsh.
+
+##### `hsh`
+
+Data type: `Hash`
+
+The Hash to sort.
+
+##### `&block`
+
+Data type: `Variant[Callable[1,1], Callable[2,2]]`
+
+The block for transforming elements of hsh.
+The block may have arity of one or two.
+
### `stdlib::start_with`
Type: Ruby 4.x API
diff --git a/lib/puppet/functions/stdlib/sort_by.rb b/lib/puppet/functions/stdlib/sort_by.rb
new file mode 100644
index 000000000..30b69b5f6
--- /dev/null
+++ b/lib/puppet/functions/stdlib/sort_by.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+# @summary Sort an Array, Hash or String by mapping values through a given block.
+#
+# @example Sort local devices according to their used space.
+# $facts['mountpoints'].stdlib::sort_by |$m| { $m.dig(1, 'used_bytes') }
+#
+Puppet::Functions.create_function(:'stdlib::sort_by') do
+ # @param ary The Array to sort.
+ # @param block The block for transforming elements of ary.
+ # @return [Array] Returns an ordered copy of ary.
+ dispatch :sort_by_array do
+ param 'Array', :ary
+ block_param 'Callable[1,1]', :block
+ end
+
+ # @param str The String to sort.
+ # @param block The block for transforming elements of str.
+ # @return [String] Returns an ordered copy of str.
+ dispatch :sort_by_string do
+ param 'String', :str
+ block_param 'Callable[1,1]', :block
+ end
+
+ # @param hsh The Hash to sort.
+ # @param block The block for transforming elements of hsh.
+ # The block may have arity of one or two.
+ # @return [Hash] Returns an ordered copy of hsh.
+ dispatch :sort_by_hash do
+ param 'Hash', :hsh
+ block_param 'Variant[Callable[1,1], Callable[2,2]]', :block
+ end
+
+ def sort_by_iterable(iterable, &block)
+ Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).sort_by(&block)
+ end
+
+ def sort_by_array(ary, &block)
+ sort_by_iterable(ary, &block)
+ end
+
+ def sort_by_string(str, &block)
+ sort_by_iterable(str, &block).join
+ end
+
+ def sort_by_hash(hsh, &block)
+ sort_by_iterable(hsh, &block).to_h
+ end
+end
diff --git a/lib/puppet/parser/functions/pw_hash.rb b/lib/puppet/parser/functions/pw_hash.rb
index 6196e14ac..7bd8fd62a 100644
--- a/lib/puppet/parser/functions/pw_hash.rb
+++ b/lib/puppet/parser/functions/pw_hash.rb
@@ -76,7 +76,7 @@
# handle weak implementations of String#crypt
# dup the string to get rid of frozen status for testing
- if RUBY_PLATFORM == 'java'
+ if RUBY_PLATFORM == 'java' && !args[1].downcase.start_with?('bcrypt')
# puppetserver bundles Apache Commons Codec
org.apache.commons.codec.digest.Crypt.crypt(password.to_java_bytes, salt)
elsif (+'test').crypt('$1$1') == '$1$1$Bp8CU9Oujr9SSEw53WV6G.'
diff --git a/metadata.json b/metadata.json
index ad43452cf..1befd5b66 100644
--- a/metadata.json
+++ b/metadata.json
@@ -1,6 +1,6 @@
{
"name": "puppetlabs-stdlib",
- "version": "9.4.1",
+ "version": "9.5.0",
"author": "puppetlabs",
"summary": "Standard library of resources for Puppet modules.",
"license": "Apache-2.0",
diff --git a/spec/functions/sort_by_spec.rb b/spec/functions/sort_by_spec.rb
new file mode 100644
index 000000000..6a14f2977
--- /dev/null
+++ b/spec/functions/sort_by_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'stdlib::sort_by' do
+ it { is_expected.not_to be_nil }
+
+ describe 'raise exception with inappropriate parameters' do
+ it { is_expected.to run.with_params.and_raise_error(ArgumentError, Regexp.new('expects 1 argument, got none')) }
+ it { is_expected.to run.with_params([]).and_raise_error(ArgumentError, Regexp.new('expects a block')) }
+ it { is_expected.to run.with_params(:undef).and_raise_error(ArgumentError, Regexp.new("rejected: parameter 'ary' expects an Array value, got Undef")) }
+ it { is_expected.to run.with_params(true).and_raise_error(ArgumentError, Regexp.new("rejected: parameter 'ary' expects an Array value, got Boolean")) }
+ it { is_expected.to run.with_params(1).and_raise_error(ArgumentError, Regexp.new("rejected: parameter 'ary' expects an Array value, got Integer")) }
+ it { is_expected.to run.with_params({}).with_lambda { 1 }.and_raise_error(ArgumentError, Regexp.new('block expects between 1 and 2 arguments, got none')) }
+ end
+
+ # Puppet's each iterator considers Integers, Strings, Arrays and Hashes to be Iterable.
+ unordered_array = ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']
+ ordered_array = ['The', 'brown', 'dog', 'fox', 'jumps', 'lazy', 'over', 'quick', 'the']
+ unordered_hash = { 'The' => 'quick', 'brown' => 'fox', 'jumps' => 'over', 'the' => 'lazy', 'dog' => '.' }
+ ordered_hash = { 'dog' => '.', 'brown' => 'fox', 'the' => 'lazy', 'jumps' => 'over', 'The' => 'quick' }
+ unordered_string = 'The quick brown fox jumps over the lazy dog.'
+ ordered_string = ' .Tabcdeeefghhijklmnoooopqrrstuuvwxyz'
+
+ describe 'with sane input' do
+ it 'does sort Array' do
+ expect(subject).to run \
+ .with_params(unordered_array) \
+ .with_lambda { |e| e } \
+ .and_return(ordered_array)
+ end
+
+ it 'does sort Hash by entry' do
+ expect(subject).to run \
+ .with_params(unordered_hash) \
+ .with_lambda { |e| e[1] } \
+ .and_return(ordered_hash)
+ end
+
+ it 'does sort Hash by key-value pairs' do
+ expect(subject).to run \
+ .with_params(unordered_hash) \
+ .with_lambda { |_, v| v } \
+ .and_return(ordered_hash)
+ end
+
+ it 'does sort String' do
+ expect(subject).to run \
+ .with_params(unordered_string) \
+ .with_lambda { |e| e } \
+ .and_return(ordered_string)
+ end
+ end
+end