From 2c72fbaf29efb47459814b843392e70917ec69bb Mon Sep 17 00:00:00 2001 From: Max Roeleveld Date: Fri, 30 Oct 2015 20:06:04 +0100 Subject: [PATCH 1/5] Refactor Number into separate languages The old Number\Ordinal is no longer directly responsible for the ordinal string generation. Instead, it'll use a dynamically included closure for that. Closures for English and Dutch are included. --- spec/Coduo/PHPHumanizer/NumberSpec.php | 18 +++++++++++ src/Coduo/PHPHumanizer/Number.php | 18 ++++++++--- src/Coduo/PHPHumanizer/Number/Ordinal.php | 23 +++++++------- .../PHPHumanizer/Number/Ordinal/Builder.php | 30 +++++++++++++++++++ .../Resources/translations/ordinal.en.php | 22 ++++++++++++++ .../Resources/translations/ordinal.nl.php | 11 +++++++ 6 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php create mode 100644 src/Coduo/PHPHumanizer/Resources/translations/ordinal.en.php create mode 100644 src/Coduo/PHPHumanizer/Resources/translations/ordinal.nl.php diff --git a/spec/Coduo/PHPHumanizer/NumberSpec.php b/spec/Coduo/PHPHumanizer/NumberSpec.php index 6acaa24..1e03273 100644 --- a/spec/Coduo/PHPHumanizer/NumberSpec.php +++ b/spec/Coduo/PHPHumanizer/NumberSpec.php @@ -109,4 +109,22 @@ function it_throws_exception_when_converting_roman_number_is_invalid() $this->shouldThrow(new \InvalidArgumentException())->during('fromRoman', array("")); $this->shouldThrow(new \InvalidArgumentException())->during('fromRoman', array("foobar")); } + + function it_ordinalizes_numbers_for_dutch_locale() + { + $this->ordinalize(1, 'nl')->shouldReturn("1e"); + $this->ordinalize(2, 'nl')->shouldReturn("2e"); + $this->ordinalize(23, 'nl')->shouldReturn("23e"); + $this->ordinalize(1002, 'nl')->shouldReturn("1002e"); + $this->ordinalize(-111, 'nl')->shouldReturn("-111e"); + } + + function it_returns_ordinal_suffix_for_dutch_locale() + { + $this->ordinal(1, 'nl')->shouldReturn("e"); + $this->ordinal(2, 'nl')->shouldReturn("e"); + $this->ordinal(23, 'nl')->shouldReturn("e"); + $this->ordinal(1002, 'nl')->shouldReturn("e"); + $this->ordinal(-111, 'nl')->shouldReturn("e"); + } } diff --git a/src/Coduo/PHPHumanizer/Number.php b/src/Coduo/PHPHumanizer/Number.php index 560ed86..e34d440 100644 --- a/src/Coduo/PHPHumanizer/Number.php +++ b/src/Coduo/PHPHumanizer/Number.php @@ -9,14 +9,24 @@ class Number { - public static function ordinalize($number) + /** + * @param int|float $number + * @param string $locale + * @return string + */ + public static function ordinalize($number, $locale = 'en') { - return $number.self::ordinal($number); + return $number.self::ordinal($number, $locale); } - public static function ordinal($number) + /** + * @param int|float $number + * @param string $locale + * @return string + */ + public static function ordinal($number, $locale = 'en') { - return (string) new Ordinal($number); + return (string) new Ordinal($number, $locale); } public static function binarySuffix($number, $locale = 'en') diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal.php b/src/Coduo/PHPHumanizer/Number/Ordinal.php index f1ae6be..1be0042 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal.php @@ -9,27 +9,24 @@ class Ordinal */ private $number; + /** + * @var callable + */ + private $translator; + /** * @param int|float $number + * @param string $locale */ - public function __construct($number) + public function __construct($number, $locale = 'en') { $this->number = $number; + $this->translator = Ordinal\Builder::build($locale); } public function __toString() { - $absNumber = abs((integer) $this->number); - - if (in_array(($absNumber % 100), array(11, 12, 13))) { - return 'th'; - } - - switch ($absNumber % 10) { - case 1: return 'st'; - case 2: return 'nd'; - case 3: return 'rd'; - default: return 'th'; - } + $closure = $this->translator; + return (string) $closure($this->number); } } diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php b/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php new file mode 100644 index 0000000..ed15848 --- /dev/null +++ b/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php @@ -0,0 +1,30 @@ + Date: Sat, 7 Nov 2015 11:09:36 +0100 Subject: [PATCH 2/5] Reworked into Strategy pattern --- spec/Coduo/PHPHumanizer/NumberSpec.php | 130 ------------------ src/Coduo/PHPHumanizer/Number.php | 3 +- src/Coduo/PHPHumanizer/Number/Ordinal.php | 29 ++-- .../PHPHumanizer/Number/Ordinal/Builder.php | 31 +++-- .../Number/Ordinal/StrategyInterface.php | 12 ++ .../Resources/Ordinal/EnStrategy.php | 25 ++++ .../Resources/Ordinal/NlStrategy.php | 13 ++ .../Resources/translations/ordinal.en.php | 22 --- .../Resources/translations/ordinal.nl.php | 11 -- 9 files changed, 86 insertions(+), 190 deletions(-) delete mode 100644 spec/Coduo/PHPHumanizer/NumberSpec.php create mode 100644 src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php create mode 100644 src/Coduo/PHPHumanizer/Resources/Ordinal/EnStrategy.php create mode 100644 src/Coduo/PHPHumanizer/Resources/Ordinal/NlStrategy.php delete mode 100644 src/Coduo/PHPHumanizer/Resources/translations/ordinal.en.php delete mode 100644 src/Coduo/PHPHumanizer/Resources/translations/ordinal.nl.php diff --git a/spec/Coduo/PHPHumanizer/NumberSpec.php b/spec/Coduo/PHPHumanizer/NumberSpec.php deleted file mode 100644 index 1e03273..0000000 --- a/spec/Coduo/PHPHumanizer/NumberSpec.php +++ /dev/null @@ -1,130 +0,0 @@ -ordinalize(1)->shouldReturn("1st"); - $this->ordinalize(2)->shouldReturn("2nd"); - $this->ordinalize(23)->shouldReturn("23rd"); - $this->ordinalize(1002)->shouldReturn("1002nd"); - $this->ordinalize(-111)->shouldReturn("-111th"); - } - - function it_returns_ordinal_suffix() - { - $this->ordinal(1)->shouldReturn("st"); - $this->ordinal(2)->shouldReturn("nd"); - $this->ordinal(23)->shouldReturn("rd"); - $this->ordinal(1002)->shouldReturn("nd"); - $this->ordinal(-111)->shouldReturn("th"); - } - - function it_convert_number_to_string_with_binary_suffix() - { - $this->binarySuffix(-1)->shouldReturn(-1); - $this->binarySuffix(0)->shouldReturn("0 bytes"); - $this->binarySuffix(1)->shouldReturn("1 bytes"); - $this->binarySuffix(1024)->shouldReturn("1 kB"); - $this->binarySuffix(1025)->shouldReturn("1 kB"); - $this->binarySuffix(1536)->shouldReturn("1.5 kB"); - $this->binarySuffix(1048576 * 5)->shouldReturn("5 MB"); - $this->binarySuffix(1073741824 * 2)->shouldReturn("2 GB"); - $this->binarySuffix(1099511627776 * 3)->shouldReturn("3 TB"); - $this->binarySuffix(1325899906842624)->shouldReturn("1.18 PB"); - } - - function it_convert_number_to_string_with_binary_suffix_for_specific_locale() - { - $this->binarySuffix(1536, 'pl')->shouldReturn("1,5 kB"); - $this->binarySuffix(1325899906842624, 'pl')->shouldReturn("1,18 PB"); - } - - function it_throw_exception_when_converting_to_string_with_binary_suffix_non_numeric_values() - { - $this->shouldThrow(new \InvalidArgumentException("Binary suffix converter accept only numeric values.")) - ->during('binarySuffix', array('as12')); - } - - function it_convert_number_to_string_with_metric_suffix() - { - $this->metricSuffix(-1)->shouldReturn("-1"); - $this->metricSuffix(0)->shouldReturn("0"); - $this->metricSuffix(1)->shouldReturn("1"); - $this->metricSuffix(101)->shouldReturn("101"); - $this->metricSuffix(1000)->shouldReturn("1k"); - $this->metricSuffix(1240)->shouldReturn("1.2k"); - $this->metricSuffix(1240000)->shouldReturn("1.24M"); - $this->metricSuffix(3500000)->shouldReturn("3.5M"); - } - - function it_convert_number_to_string_with_metric_suffix_for_specific_locale() - { - $this->metricSuffix(1240, 'pl')->shouldReturn("1,2k"); - $this->metricSuffix(1240000, 'pl')->shouldReturn("1,24M"); - $this->metricSuffix(3500000, 'pl')->shouldReturn("3,5M"); - } - - function it_throw_exception_when_converting_to_string_with_metric_suffix_non_numeric_values() - { - $this->shouldThrow(new \InvalidArgumentException("Metric suffix converter accept only numeric values.")) - ->during('metricSuffix', array('as12')); - } - - function it_converts_numbers_to_roman() - { - $this->toRoman(1)->shouldReturn("I"); - $this->toRoman(5)->shouldReturn("V"); - $this->toRoman(9)->shouldReturn("IX"); - $this->toRoman(10)->shouldReturn("X"); - $this->toRoman(125)->shouldReturn("CXXV"); - $this->toRoman(1300)->shouldReturn("MCCC"); - $this->toRoman(3999)->shouldReturn("MMMCMXCIX"); - } - - function it_throws_exception_when_converting_number_is_out_of_range() - { - $this->shouldThrow(new \InvalidArgumentException())->during('toRoman', array(-1)); - $this->shouldThrow(new \InvalidArgumentException())->during('toRoman', array(4000)); - } - - function it_converts_roman_numbers_to_arabic() - { - $this->fromRoman("I")->shouldReturn(1); - $this->fromRoman("V")->shouldReturn(5); - $this->fromRoman("IX")->shouldReturn(9); - $this->fromRoman("CXXV")->shouldReturn(125); - $this->fromRoman("MCCC")->shouldReturn(1300); - $this->fromRoman("MMMCMXCIX")->shouldReturn(3999); - } - - function it_throws_exception_when_converting_roman_number_is_invalid() - { - $this->shouldThrow(new \InvalidArgumentException())->during('fromRoman', array(1234)); - $this->shouldThrow(new \InvalidArgumentException())->during('fromRoman', array("")); - $this->shouldThrow(new \InvalidArgumentException())->during('fromRoman', array("foobar")); - } - - function it_ordinalizes_numbers_for_dutch_locale() - { - $this->ordinalize(1, 'nl')->shouldReturn("1e"); - $this->ordinalize(2, 'nl')->shouldReturn("2e"); - $this->ordinalize(23, 'nl')->shouldReturn("23e"); - $this->ordinalize(1002, 'nl')->shouldReturn("1002e"); - $this->ordinalize(-111, 'nl')->shouldReturn("-111e"); - } - - function it_returns_ordinal_suffix_for_dutch_locale() - { - $this->ordinal(1, 'nl')->shouldReturn("e"); - $this->ordinal(2, 'nl')->shouldReturn("e"); - $this->ordinal(23, 'nl')->shouldReturn("e"); - $this->ordinal(1002, 'nl')->shouldReturn("e"); - $this->ordinal(-111, 'nl')->shouldReturn("e"); - } -} diff --git a/src/Coduo/PHPHumanizer/Number.php b/src/Coduo/PHPHumanizer/Number.php index 30b261f..aced7dd 100644 --- a/src/Coduo/PHPHumanizer/Number.php +++ b/src/Coduo/PHPHumanizer/Number.php @@ -26,7 +26,8 @@ public static function ordinalize($number, $locale = 'en') */ public static function ordinal($number, $locale = 'en') { - return (string) new Ordinal($number, $locale); + $ordinal = new Ordinal($locale); + return $ordinal->ordinal($number); } public static function binarySuffix($number, $locale = 'en') diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal.php b/src/Coduo/PHPHumanizer/Number/Ordinal.php index 1be0042..192399d 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal.php @@ -2,31 +2,32 @@ namespace Coduo\PHPHumanizer\Number; +use Coduo\PHPHumanizer\Number\Ordinal\Builder; +use Coduo\PHPHumanizer\Number\Ordinal\StrategyInterface; + class Ordinal { /** - * @var int|float - */ - private $number; - - /** - * @var callable + * @var StrategyInterface */ - private $translator; + private $strategy; /** - * @param int|float $number * @param string $locale */ - public function __construct($number, $locale = 'en') + public function __construct($locale) { - $this->number = $number; - $this->translator = Ordinal\Builder::build($locale); + $this->strategy = Builder::build($locale); } - public function __toString() + /** + * @param $number + * @return string + */ + public function ordinal($number) { - $closure = $this->translator; - return (string) $closure($this->number); + return $this + ->strategy + ->ordinalSuffix($number); } } diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php b/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php index ed15848..f1780b2 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal/Builder.php @@ -3,28 +3,35 @@ namespace Coduo\PHPHumanizer\Number\Ordinal; /** - * Tries to find a proper "translator" for ordinal numbers. + * Tries to find a proper strategy for ordinal numbers. */ class Builder { /** * @param string $locale - * @return Closure + * @return StrategyInterface + * @throws \RuntimeException */ public static function build($locale) { - $base = __DIR__ . '/../../Resources/translations/ordinal'; - $file = "$base.$locale.php"; + // $locale should be xx or xx_YY + if (!preg_match('/^([a-z]{2})(_([A-Z]{2}))?$/', $locale, $m)) { + throw new \RuntimeException("Invalid locale specified: '$locale'."); + } + + $strategy = ucfirst($m[1]); + if (!empty($m[3])) { + $strategy .= "_$m[3]"; + } + + $strategy = "\\Coduo\\PHPHumanizer\\Resources\\Ordinal\\{$strategy}Strategy"; - if (file_exists($file)) { - $translator = require $file; - } else { - // Fall-back - $translator = function ($number) { - return $number; - }; + if (class_exists($strategy)) { + return new $strategy; } - return $translator; + // Debatable: should we fallback to English? + // return self::build('en'); + throw new \RuntimeException("Strategy for locale $locale not found."); } } diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php b/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php new file mode 100644 index 0000000..5b417d9 --- /dev/null +++ b/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php @@ -0,0 +1,12 @@ + Date: Sun, 8 Nov 2015 10:30:14 +0100 Subject: [PATCH 3/5] Refactor back to `__toString()` usage Also added some tests. --- src/Coduo/PHPHumanizer/Number.php | 4 +- src/Coduo/PHPHumanizer/Number/Ordinal.php | 16 +++--- .../Resources/Ordinal/EnStrategy.php | 4 +- .../Resources/Ordinal/NlStrategy.php | 3 +- tests/Coduo/PHPHumanizer/Tests/NumberTest.php | 52 ++++++++++++++++++- 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/Coduo/PHPHumanizer/Number.php b/src/Coduo/PHPHumanizer/Number.php index aced7dd..3e7db4d 100644 --- a/src/Coduo/PHPHumanizer/Number.php +++ b/src/Coduo/PHPHumanizer/Number.php @@ -26,8 +26,8 @@ public static function ordinalize($number, $locale = 'en') */ public static function ordinal($number, $locale = 'en') { - $ordinal = new Ordinal($locale); - return $ordinal->ordinal($number); + $ordinal = new Ordinal($number, $locale); + return (string) $ordinal; } public static function binarySuffix($number, $locale = 'en') diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal.php b/src/Coduo/PHPHumanizer/Number/Ordinal.php index 192399d..b3de97c 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal.php @@ -7,27 +7,29 @@ class Ordinal { - /** - * @var StrategyInterface - */ + /** @type float|int */ + private $number; + + /** @var StrategyInterface */ private $strategy; /** + * @param float|int $number * @param string $locale */ - public function __construct($locale) + public function __construct($number, $locale) { + $this->number = $number; $this->strategy = Builder::build($locale); } /** - * @param $number * @return string */ - public function ordinal($number) + public function __toString() { return $this ->strategy - ->ordinalSuffix($number); + ->ordinalSuffix($this->number); } } diff --git a/src/Coduo/PHPHumanizer/Resources/Ordinal/EnStrategy.php b/src/Coduo/PHPHumanizer/Resources/Ordinal/EnStrategy.php index 4c777d3..b3979fd 100644 --- a/src/Coduo/PHPHumanizer/Resources/Ordinal/EnStrategy.php +++ b/src/Coduo/PHPHumanizer/Resources/Ordinal/EnStrategy.php @@ -6,7 +6,7 @@ class EnStrategy implements StrategyInterface { - + /** @inheritdoc */ public function ordinalSuffix($number) { $absNumber = abs((integer) $number); @@ -22,4 +22,4 @@ public function ordinalSuffix($number) default: return 'th'; } } -} \ No newline at end of file +} diff --git a/src/Coduo/PHPHumanizer/Resources/Ordinal/NlStrategy.php b/src/Coduo/PHPHumanizer/Resources/Ordinal/NlStrategy.php index ffd339e..9510fd3 100644 --- a/src/Coduo/PHPHumanizer/Resources/Ordinal/NlStrategy.php +++ b/src/Coduo/PHPHumanizer/Resources/Ordinal/NlStrategy.php @@ -6,8 +6,9 @@ class NlStrategy implements StrategyInterface { + /** @inheritdoc */ public function ordinalSuffix($number) { return "e"; } -} \ No newline at end of file +} diff --git a/tests/Coduo/PHPHumanizer/Tests/NumberTest.php b/tests/Coduo/PHPHumanizer/Tests/NumberTest.php index db31d65..bb7c142 100644 --- a/tests/Coduo/PHPHumanizer/Tests/NumberTest.php +++ b/tests/Coduo/PHPHumanizer/Tests/NumberTest.php @@ -17,6 +17,16 @@ public function test_return_ordinal_suffix($expected, $number) $this->assertEquals($expected, Number::ordinal($number)); } + /** + * @dataProvider ordinalSuffixDutchProvider + * @param $expected + * @param $number + */ + public function test_return_ordinal_suffix_dutch($expected, $number) + { + $this->assertEquals($expected, Number::ordinal($number, 'nl')); + } + /** * @dataProvider ordinalizeDataProvider * @depends test_return_ordinal_suffix @@ -29,6 +39,18 @@ public function test_ordinalize_numbers($expected, $number) $this->assertEquals($expected, Number::ordinalize($number)); } + /** + * @dataProvider ordinalizeDataDutchProvider + * @depends test_return_ordinal_suffix_dutch + * + * @param $expected + * @param $number + */ + public function test_ordinalize_numbers_dutch($expected, $number) + { + $this->assertEquals($expected, Number::ordinalize($number, 'nl')); + } + /** * @dataProvider binarySuffixDataProvider * @@ -156,6 +178,20 @@ public function ordinalizeDataProvider() ); } + /** + * @return array + */ + public function ordinalizeDataDutchProvider() + { + return array( + array('1e', 1), + array('2e', 2), + array('23e', 23), + array('1002e', 1002), + array('-111e', -111), + ); + } + /** * @return array */ @@ -170,6 +206,20 @@ public function ordinalSuffixProvider() ); } + /** + * @return array + */ + public function ordinalSuffixDutchProvider() + { + return array( + array('e', 1), + array('e', 2), + array('e', 23), + array('e', 1002), + array('e', -111), + ); + } + /** * @return array */ @@ -293,4 +343,4 @@ public function arabicExceptionProvider() array("foobar"), ); } -} \ No newline at end of file +} From 1c41211e6a27ceebe50ca8ced0152c0e8869d5fa Mon Sep 17 00:00:00 2001 From: Max Roeleveld Date: Sun, 8 Nov 2015 10:36:00 +0100 Subject: [PATCH 4/5] Untweaked useless comment tweaks. --- src/Coduo/PHPHumanizer/Number/Ordinal.php | 13 +++++++------ .../Number/Ordinal/StrategyInterface.php | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal.php b/src/Coduo/PHPHumanizer/Number/Ordinal.php index b3de97c..6058e2a 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal.php @@ -7,14 +7,18 @@ class Ordinal { - /** @type float|int */ + /** + * @type int|float + */ private $number; - /** @var StrategyInterface */ + /** + * @type StrategyInterface + */ private $strategy; /** - * @param float|int $number + * @param int|float $number * @param string $locale */ public function __construct($number, $locale) @@ -23,9 +27,6 @@ public function __construct($number, $locale) $this->strategy = Builder::build($locale); } - /** - * @return string - */ public function __toString() { return $this diff --git a/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php b/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php index 5b417d9..9f2a222 100644 --- a/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php +++ b/src/Coduo/PHPHumanizer/Number/Ordinal/StrategyInterface.php @@ -9,4 +9,4 @@ interface StrategyInterface * @return string */ public function ordinalSuffix($number); -} \ No newline at end of file +} From 586674beb69e1c21b3ad67a81ff4f088c0d7bdd9 Mon Sep 17 00:00:00 2001 From: Max Roeleveld Date: Thu, 12 Nov 2015 21:27:14 +0100 Subject: [PATCH 5/5] Added example for localized ordinalisation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d632d9d..656d560 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ echo Number::ordinalize(0); // "0th" echo Number::ordinalize(1); // "1st" echo Number::ordinalize(2); // "2nd" echo Number::ordinalize(23); // "23rd" -echo Number::ordinalize(1002); // "1002nd" +echo Number::ordinalize(1002, 'nl'); // "1002e" echo Number::ordinalize(-111); // "-111th" ``` @@ -88,7 +88,7 @@ echo Number::ordinal(1); // "st" echo Number::ordinal(2); // "nd" echo Number::ordinal(23); // "rd" echo Number::ordinal(1002); // "nd" -echo Number::ordinal(-111); // "th" +echo Number::ordinal(-111, 'nl'); // "e" ``` **Roman numbers**