Skip to content

Commit 64134f5

Browse files
committed
added conditionable trait functionality
1 parent 53d7a42 commit 64134f5

File tree

7 files changed

+111
-143
lines changed

7 files changed

+111
-143
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@balboacodes/laravel-helpers",
3-
"version": "0.11.0",
3+
"version": "0.12.0",
44
"description": "A TypeScript port of Laravel's helpers",
55
"keywords": [
66
"laravel",

src/Collection.ts

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
// prettier-ignore
22
import {
3-
array_all, array_any, array_chunk, array_combine, array_diff, array_diff_assoc, array_diff_key, array_diff_uassoc, array_filter, array_find_key, array_flip, array_intersect, array_intersect_assoc, array_intersect_key, array_intersect_uassoc, array_keys, array_map, array_merge, array_merge_recursive, array_pad, array_pop, array_push, array_replace, array_replace_recursive, array_reverse, array_search, array_shift, array_slice, array_splice, array_uintersect, array_unique, arsort, asort, count, empty, in_array, intval, isset, krsort, ksort, range, SORT_FLAG_CASE, SORT_LOCALE_STRING, SORT_NATURAL, SORT_NUMERIC, SORT_REGULAR, SORT_STRING, strcasecmp, strcmp, strcoll, strnatcasecmp, strnatcmp, uasort, uksort, unset,
3+
array_all, array_any, array_chunk, array_combine, array_diff, array_diff_assoc, array_diff_key, array_diff_uassoc, array_filter, array_find_key, array_flip, array_intersect, array_intersect_assoc, array_intersect_key, array_intersect_uassoc, array_keys, array_map, array_merge, array_merge_recursive, array_pad, array_pop, array_push, array_replace, array_replace_recursive, array_reverse, array_search, array_shift, array_slice, array_splice, array_uintersect, array_unique, arsort, asort, count, empty, in_array, intval, isset, krsort, ksort, range, SORT_FLAG_CASE, SORT_LOCALE_STRING, SORT_NATURAL, SORT_NUMERIC, SORT_REGULAR, SORT_STRING, strcasecmp, strcmp, strcoll, strnatcasecmp, strnatcmp, uasort, uksort, unset,
44
} from '@balboacodes/php-utils';
55
import { Arr } from './Arr';
6+
import { Conditionable } from './Concerns/Conditionable';
7+
import { use } from './Concerns/decorator';
68
import { data_get, data_has, value } from './helpers';
79

810
type Enumerable<TKey extends number | string, TValue> = TValue[] | Record<TKey, TValue> | Collection<TKey, TValue>;
911

12+
export interface Collection<TKey extends number | string, TValue> extends Conditionable {}
13+
14+
@use(Conditionable)
1015
export class Collection<TKey extends number | string, TValue> {
1116
/**
1217
* The items contained in the collection.
@@ -1638,25 +1643,6 @@ export class Collection<TKey extends number | string, TValue> {
16381643
return this.unique(key, true);
16391644
}
16401645

1641-
/**
1642-
* Apply the callback if the given "value" is (or resolves to) falsy.
1643-
*/
1644-
public unless<TUnlessParameter, TUnlessReturnType>(
1645-
value: ((instance: this) => TUnlessParameter) | TUnlessParameter,
1646-
callback: (instance: this, value: TUnlessParameter) => TUnlessReturnType,
1647-
defaultValue?: (instance: this, value: TUnlessParameter) => TUnlessReturnType,
1648-
): this | TUnlessReturnType {
1649-
value = typeof value === 'function' ? (value as Function)(this) : value;
1650-
1651-
if (!value) {
1652-
return callback(this, value as TUnlessParameter) ?? this;
1653-
} else if (defaultValue) {
1654-
return defaultValue(this, value as TUnlessParameter) ?? this;
1655-
}
1656-
1657-
return this;
1658-
}
1659-
16601646
/**
16611647
* Apply the callback unless the collection is empty.
16621648
*/
@@ -1709,29 +1695,6 @@ export class Collection<TKey extends number | string, TValue> {
17091695
return new Collection({ ...Object.values(this.items) }) as any;
17101696
}
17111697

1712-
/**
1713-
* Apply the callback if the given "value" is (or resolves to) truthy.
1714-
*/
1715-
public when<TWhenParameter, TWhenReturnType>(
1716-
when?: ((instance: this) => TWhenParameter) | TWhenParameter,
1717-
callback?: (instance: this, when: TWhenParameter) => TWhenReturnType,
1718-
defaultValue?: (instance: this, when: TWhenParameter) => TWhenReturnType,
1719-
): this | TWhenReturnType {
1720-
when = typeof when === 'function' ? (when as Function)(this) : when;
1721-
1722-
if (arguments.length === 0) {
1723-
return this;
1724-
}
1725-
1726-
if (when) {
1727-
return callback?.(this, when as any) ?? this;
1728-
} else if (defaultValue) {
1729-
return defaultValue(this, when as any) ?? this;
1730-
}
1731-
1732-
return this;
1733-
}
1734-
17351698
/**
17361699
* Apply the callback if the collection is empty.
17371700
*/

src/Concerns/Conditionable.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
export class Conditionable {
2+
/**
3+
* Apply the callback if the given "value" is (or resolves to) falsy.
4+
*/
5+
public unless<TUnlessParameter, TUnlessReturnType>(
6+
value?: ((instance: this) => TUnlessParameter) | TUnlessParameter,
7+
callback?: (instance: this, unless: TUnlessParameter) => TUnlessReturnType,
8+
defaultValue?: (instance: this, unless: TUnlessParameter) => TUnlessReturnType,
9+
): this | TUnlessReturnType {
10+
value = typeof value === 'function' ? (value as (instance: this) => TUnlessParameter)(this) : value;
11+
12+
if (arguments.length === 0) {
13+
return this;
14+
}
15+
16+
if (!value) {
17+
return callback?.(this, value as TUnlessParameter) ?? this;
18+
} else if (defaultValue) {
19+
return defaultValue(this, value as TUnlessParameter) ?? this;
20+
}
21+
22+
return this;
23+
}
24+
25+
/**
26+
* Apply the callback if the given "value" is (or resolves to) truthy.
27+
*/
28+
public when<TWhenParameter, TWhenReturnType>(
29+
value?: ((instance: this) => TWhenParameter) | TWhenParameter,
30+
callback?: (instance: this, when: TWhenParameter) => TWhenReturnType,
31+
defaultValue?: (instance: this, when: TWhenParameter) => TWhenReturnType,
32+
): this | TWhenReturnType {
33+
value = typeof value === 'function' ? (value as (instance: this) => TWhenParameter)(this) : value;
34+
35+
if (arguments.length === 0) {
36+
return this;
37+
}
38+
39+
if (value) {
40+
return callback?.(this, value as TWhenParameter) ?? this;
41+
} else if (defaultValue) {
42+
return defaultValue(this, value as TWhenParameter) ?? this;
43+
}
44+
45+
return this;
46+
}
47+
}

src/Concerns/decorator.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
type Class<T = any> = new (...args: any[]) => T;
2+
3+
function defineTrait(target: any, trait: any, key: string) {
4+
const descriptor = Object.getOwnPropertyDescriptor(trait, key);
5+
6+
if (descriptor) {
7+
Object.defineProperty(target, key, descriptor);
8+
}
9+
}
10+
11+
export function use(...traits: Class[]): ClassDecorator {
12+
return (target: Function) => {
13+
for (const trait of traits) {
14+
const targets = [
15+
[target, trait], // Static members.
16+
[target.prototype, trait.prototype], // Instance members.
17+
];
18+
19+
targets.forEach(([target, trait]) => {
20+
for (const key of Object.getOwnPropertyNames(trait)) {
21+
if (key === 'constructor' || key === 'prototype' || key === 'length' || key === 'name') {
22+
continue;
23+
}
24+
25+
defineTrait(target, trait, key);
26+
}
27+
});
28+
}
29+
};
30+
}

src/Pipeline.ts

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { array_push, array_reduce, array_reverse } from '@balboacodes/php-utils';
2+
import { Conditionable } from './Concerns/Conditionable';
3+
import { use } from './Concerns/decorator';
24

5+
export interface Pipeline extends Conditionable {}
6+
7+
@use(Conditionable)
38
export class Pipeline {
49
/**
510
* The final callback to be executed after the pipeline ends regardless of the outcome.
@@ -88,29 +93,6 @@ export class Pipeline {
8893
return this;
8994
}
9095

91-
/**
92-
* Apply the callback if the given "value" is (or resolves to) falsy.
93-
*/
94-
public unless<TUnlessParameter, TUnlessReturnType>(
95-
value?: ((instance: this) => TUnlessParameter) | TUnlessParameter,
96-
callback?: (instance: this, unless: TUnlessParameter) => TUnlessReturnType,
97-
defaultValue?: (instance: this, unless: TUnlessParameter) => TUnlessReturnType,
98-
): this | TUnlessReturnType {
99-
value = typeof value === 'function' ? (value as (instance: this) => TUnlessParameter)(this) : value;
100-
101-
if (arguments.length === 0) {
102-
return this;
103-
}
104-
105-
if (!value) {
106-
return callback?.(this, value as TUnlessParameter) ?? this;
107-
} else if (defaultValue) {
108-
return defaultValue(this, value as TUnlessParameter) ?? this;
109-
}
110-
111-
return this;
112-
}
113-
11496
/**
11597
* Set the method to call on the pipes.
11698
*/
@@ -120,29 +102,6 @@ export class Pipeline {
120102
return this;
121103
}
122104

123-
/**
124-
* Apply the callback if the given "value" is (or resolves to) truthy.
125-
*/
126-
public when<TWhenParameter, TWhenReturnType>(
127-
value?: ((instance: this) => TWhenParameter) | TWhenParameter,
128-
callback?: (instance: this, when: TWhenParameter) => TWhenReturnType,
129-
defaultValue?: (instance: this, when: TWhenParameter) => TWhenReturnType,
130-
): this | TWhenReturnType {
131-
value = typeof value === 'function' ? (value as (instance: this) => TWhenParameter)(this) : value;
132-
133-
if (arguments.length === 0) {
134-
return this;
135-
}
136-
137-
if (value) {
138-
return callback?.(this, value as TWhenParameter) ?? this;
139-
} else if (defaultValue) {
140-
return defaultValue(this, value as TWhenParameter) ?? this;
141-
}
142-
143-
return this;
144-
}
145-
146105
/**
147106
* Get a Closure that represents a slice of the application onion.
148107
*/

src/Stringable.ts

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22
import {
33
basename, dirname, explode, FILTER_VALIDATE_INT, filter_var, hash, mb_str_split, preg_split, sscanf, strip_tags,
44
} from '@balboacodes/php-utils';
5+
import { Conditionable } from './Concerns/Conditionable';
6+
import { use } from './Concerns/decorator';
57
import { Str } from './Str';
68

9+
export interface Stringable extends Conditionable {}
10+
11+
@use(Conditionable)
712
export class Stringable {
813
/**
914
* The underlying string value.
@@ -594,6 +599,13 @@ export class Stringable {
594599
return Str.ucsplit(this.value);
595600
}
596601

602+
/**
603+
* Unwrap the string with the given strings.
604+
*/
605+
public unwrap(before: string, after?: string): Stringable {
606+
return new Stringable(Str.unwrap(this.value, before, after));
607+
}
608+
597609
/**
598610
* Convert the given string to upper-case.
599611
*/
@@ -629,25 +641,6 @@ export class Stringable {
629641
return new Stringable(Str.wrap(this.value, before, after));
630642
}
631643

632-
/**
633-
* Apply the callback if the given "value" is (or resolves to) truthy.
634-
*/
635-
public when(
636-
value?: ((self: Stringable) => any) | any,
637-
callback?: (self: Stringable, val: any) => Stringable,
638-
defaultCallback?: (self: Stringable, val: any) => Stringable,
639-
): Stringable {
640-
const resolved = typeof value === 'function' ? value(this) : value;
641-
642-
if (resolved) {
643-
return callback?.(this, resolved) ?? this;
644-
} else if (defaultCallback) {
645-
return defaultCallback(this, resolved) ?? this;
646-
}
647-
648-
return this;
649-
}
650-
651644
/**
652645
* Execute the given callback if the string contains a given substring.
653646
*/
@@ -777,30 +770,4 @@ export class Stringable {
777770
): Stringable {
778771
return this.when(this.test(pattern), callback, defaultCallback);
779772
}
780-
781-
/**
782-
* Apply the callback if the given "value" is (or resolves to) falsy.
783-
*/
784-
public unless(
785-
value?: ((self: Stringable) => any) | any,
786-
callback?: (self: Stringable, val: any) => Stringable,
787-
defaultCallback?: (self: Stringable, val: any) => Stringable,
788-
): Stringable {
789-
const resolved = typeof value === 'function' ? value(this) : value;
790-
791-
if (!resolved) {
792-
return callback?.(this, resolved) ?? this;
793-
} else if (defaultCallback) {
794-
return defaultCallback(this, resolved) ?? this;
795-
}
796-
797-
return this;
798-
}
799-
800-
/**
801-
* Unwrap the string with the given strings.
802-
*/
803-
public unwrap(before: string, after?: string): Stringable {
804-
return new Stringable(Str.unwrap(this.value, before, after));
805-
}
806773
}

tests/Stringable.test.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,14 @@ test('test', () => {
109109
});
110110

111111
test('when', () => {
112-
expect(new Stringable('when').when(false, (self, val) => self.append(val).append('false')).toString()).toBe('when');
112+
expect(new Stringable('when').when(false, (self, val) => self.append(String(val)).append('false')).toString()).toBe(
113+
'when',
114+
);
113115
expect(
114116
new Stringable('when false ')
115117
.when(
116118
false,
117-
(self, val) => self.append(val),
119+
(self, val) => self.append(String(val)),
118120
(self) => self.append('fallbacks to default'),
119121
)
120122
.toString(),
@@ -450,33 +452,33 @@ test('unless', () => {
450452
new Stringable('unless')
451453
.unless(
452454
true,
453-
(self, val) => self.append(val),
455+
(self, val) => self.append(String(val)),
454456
(self) => self.append(' true fallbacks to default'),
455457
)
456458
.toString(),
457459
).toBe('unless true fallbacks to default');
458460
});
459461

460462
test('unless', () => {
461-
expect(new Stringable('unless').unless(1, (self, val) => self.append(val).append('true')).toString()).toBe(
463+
expect(new Stringable('unless').unless(1, (self, val) => self.append(String(val)).append('true')).toString()).toBe(
462464
'unless',
463465
);
464466
expect(
465467
new Stringable('unless true ')
466468
.unless(
467469
1,
468-
(self, val) => self.append(val),
469-
(self, val) => self.append('fallbacks to default with value ').append(val),
470+
(self, val) => self.append(String(val)),
471+
(self, val) => self.append('fallbacks to default with value ').append(String(val)),
470472
)
471473
.toString(),
472474
).toBe('unless true fallbacks to default with value 1');
473475

474-
expect(new Stringable('unless ').unless(0, (self, val) => self.append(val)).toString()).toBe('unless 0');
476+
expect(new Stringable('unless ').unless(0, (self, val) => self.append(String(val))).toString()).toBe('unless 0');
475477
expect(
476478
new Stringable('gets the value ')
477479
.unless(
478480
0,
479-
(self, val) => self.append(val),
481+
(self, val) => self.append(String(val)),
480482
(self) => self.append('fallbacks to default'),
481483
)
482484
.toString(),

0 commit comments

Comments
 (0)