diff --git a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy index 10c248d..3b5f0e6 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/SiteSpec.groovy @@ -1,5 +1,6 @@ package com.jessebrault.ssg +import com.jessebrault.ssg.util.Monoid import groovy.transform.EqualsAndHashCode import groovy.transform.NullCheck import groovy.transform.TupleConstructor @@ -9,6 +10,11 @@ import groovy.transform.TupleConstructor @EqualsAndHashCode final class SiteSpec { + static final Monoid defaultSemiGroup = new Monoid<>( + SiteSpec::concat, + SiteSpec::getBlank + ) + static SiteSpec getBlank() { new SiteSpec('', '') } diff --git a/api/src/main/groovy/com/jessebrault/ssg/buildscript/domain/MutableSiteSpec.groovy b/api/src/main/groovy/com/jessebrault/ssg/buildscript/domain/MutableSiteSpec.groovy new file mode 100644 index 0000000..8c738ad --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/buildscript/domain/MutableSiteSpec.groovy @@ -0,0 +1,29 @@ +package com.jessebrault.ssg.buildscript.domain + +import com.jessebrault.ssg.property.Properties +import com.jessebrault.ssg.property.Property +import com.jessebrault.ssg.util.Monoid + +final class MutableSiteSpec { + + private static final Monoid nameAndBaseUrlMonoid = new Monoid<>( + { s0, s1 -> s1 }, + { '' } + ) + + static final Monoid MONOID = new Monoid<>( + MutableSiteSpec::concat, + MutableSiteSpec::new + ) + + private static MutableSiteSpec concat(MutableSiteSpec ms0, MutableSiteSpec ms1) { + new MutableSiteSpec().tap { + name.set(ms1.name.get()) + baseUrl.set(ms1.baseUrl.get()) + } + } + + final Property name = Properties.get(nameAndBaseUrlMonoid) + final Property baseUrl = Properties.get(nameAndBaseUrlMonoid) + +} diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/CollectionProperty.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/CollectionProperty.groovy deleted file mode 100644 index aefbe29..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/property/CollectionProperty.groovy +++ /dev/null @@ -1,5 +0,0 @@ -package com.jessebrault.ssg.property - -interface CollectionProperty extends Property { - // TODO -} \ No newline at end of file diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/MapProperty.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/MapProperty.groovy deleted file mode 100644 index 4a0763e..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/property/MapProperty.groovy +++ /dev/null @@ -1,7 +0,0 @@ -package com.jessebrault.ssg.property - -interface MapProperty extends Property> { - V get(K key) - void put(K key, V value) - void putAll(Map map) -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/Properties.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/Properties.groovy index f4f8811..fb95e30 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/property/Properties.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/property/Properties.groovy @@ -1,32 +1,19 @@ package com.jessebrault.ssg.property +import com.jessebrault.ssg.util.Monoid + final class Properties { - static Property get() { - new SimpleProperty<>() + static Property get(Monoid semiGroup) { + new SimpleProperty<>(semiGroup) } - static Property get(T convention) { - new SimpleProperty<>(convention) + static Property get(Monoid semiGroup, T convention) { + new SimpleProperty<>(semiGroup, convention) } - static Property get(T convention, T t) { - new SimpleProperty<>(convention, t) - } - - static MapProperty getMap() { - new SimpleMapProperty() - } - - static MapProperty getMap(Map convention) { - new SimpleMapProperty(convention) - } - - static MapProperty getMap( - Map convention, - Map value - ) { - new SimpleMapProperty(convention, value) + static Property get(Monoid semiGroup, T convention, T t) { + new SimpleProperty<>(semiGroup, convention, t) } private Properties() {} diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/Property.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/Property.groovy index 48574d1..654ae78 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/property/Property.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/property/Property.groovy @@ -1,38 +1,19 @@ package com.jessebrault.ssg.property -import com.jessebrault.ssg.provider.Provider - -import java.util.function.Function import java.util.function.UnaryOperator -interface Property extends Provider { - +interface Property { + T get() void set(T t) - void set(Provider provider) void unset() - default void leftShift(T t) { - this.set(t) - } - - default void leftShift(Provider provider) { - this.set(provider) - } - T getConvention() void setConvention(T t) - void setConvention(Provider provider) void unsetConvention() - void map(UnaryOperator operator) - void flatMap(Function> function) - - default void rightShift(UnaryOperator operator) { - this.map(operator) - } - - default void rightShift(Function> function) { - this.flatMap(function) - } - + void map(UnaryOperator mapper) + void merge( + @DelegatesTo(type = 'T', strategy = Closure.DELEGATE_FIRST) + Closure configurator + ) } \ No newline at end of file diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/SimpleMapProperty.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/SimpleMapProperty.groovy deleted file mode 100644 index 7f348b3..0000000 --- a/api/src/main/groovy/com/jessebrault/ssg/property/SimpleMapProperty.groovy +++ /dev/null @@ -1,122 +0,0 @@ -package com.jessebrault.ssg.property - -import com.jessebrault.ssg.provider.CollectionProvider -import com.jessebrault.ssg.provider.Provider -import groovy.transform.EqualsAndHashCode -import groovy.transform.NullCheck -import groovy.transform.PackageScope - -import java.util.function.Function -import java.util.function.UnaryOperator - -@PackageScope -@NullCheck -@EqualsAndHashCode(includeFields = true) -final class SimpleMapProperty implements MapProperty { - - private final Map convention = [:] - private final Map m = [:] - - SimpleMapProperty( - Map convention, - Map m - ) { - this.convention.putAll(convention) - this.m.putAll(m) - } - - SimpleMapProperty(Map convention) { - this(convention, [:] as Map) - } - - SimpleMapProperty() {} - - @Override - V get(K key) { - if (m[key] != null) { - m[key] - } else if (convention[key] != null) { - convention[key] - } else { - throw new NullPointerException("no such key: ${ key }") - } - } - - @Override - void put(K key, V value) { - this.m.put(key, value) - } - - @Override - void putAll(Map map) { - this.m.putAll(map) - } - - @Override - void set(Map kvMap) { - this.m.clear() - this.m.putAll(kvMap) - } - - @Override - void set(Provider> provider) { - // TODO - } - - @Override - void unset() { - this.m.clear() - } - - @Override - Map getConvention() { - this.convention - } - - @Override - void setConvention(Map kvMap) { - this.convention.clear() - this.convention.putAll(kvMap) - } - - @Override - void setConvention(Provider> provider) { - // TODO - } - - @Override - void unsetConvention() { - this.convention.clear() - } - - @Override - void map(UnaryOperator> operator) { - this.m.putAll(operator.apply(this.convention + this.m)) - } - - @Override - void flatMap(Function, Provider>> function) { - // TODO - } - - @Override - Map provide() { - this.convention + this.m - } - - @Override - CollectionProvider> plus(Provider> other) { - // TODO - } - - @Override - CollectionProvider> asType(Class collectionProviderClass) { - // TODO - } - - @Override - boolean isEmpty() { - return false - } - -} diff --git a/api/src/main/groovy/com/jessebrault/ssg/property/SimpleProperty.groovy b/api/src/main/groovy/com/jessebrault/ssg/property/SimpleProperty.groovy index 34b4ae5..792daeb 100644 --- a/api/src/main/groovy/com/jessebrault/ssg/property/SimpleProperty.groovy +++ b/api/src/main/groovy/com/jessebrault/ssg/property/SimpleProperty.groovy @@ -1,93 +1,85 @@ package com.jessebrault.ssg.property -import com.jessebrault.ssg.provider.AbstractProvider -import com.jessebrault.ssg.provider.Provider -import com.jessebrault.ssg.provider.Providers +import com.jessebrault.ssg.util.Monoid import groovy.transform.EqualsAndHashCode import groovy.transform.NullCheck import groovy.transform.PackageScope -import java.util.function.Function import java.util.function.UnaryOperator +import static java.util.Objects.requireNonNull + @PackageScope -@NullCheck +@NullCheck(includeGenerated = true) @EqualsAndHashCode(includeFields = true) -final class SimpleProperty extends AbstractProvider implements Property { +final class SimpleProperty implements Property { - private Provider convention = Providers.getEmpty() - private Provider tProvider = Providers.getEmpty() + private final Monoid monoid - SimpleProperty(T convention) { - this.convention = Providers.of(convention) + private T t + private T convention + + SimpleProperty(Monoid monoid) { + this.monoid = monoid + this.convention = this.monoid.empty.get() } - SimpleProperty(T convention, T t) { - this.convention = Providers.of(convention) - this.tProvider = Providers.of(t) + SimpleProperty(Monoid monoid, T convention) { + this.monoid = monoid + this.convention = convention } - SimpleProperty() {} + SimpleProperty(Monoid monoid, T convention, T t) { + this.monoid = monoid + this.t = t + this.convention = convention + } @Override - T provide() { - this.tProvider.present ? this.tProvider.provide() : this.convention.provide() + T get() { + this.t != null ? this.t : requireNonNull(this.convention) } @Override void set(T t) { - this.tProvider = Providers.of(t) - } - - @Override - void set(Provider provider) { - this.tProvider = provider + this.t = t } @Override void unset() { - this.tProvider = Providers.getEmpty() + this.t = null } @Override T getConvention() { - this.convention.provide() + requireNonNull(this.convention) } @Override void setConvention(T t) { - this.convention = Providers.of(t) - } - - @Override - void setConvention(Provider provider) { - this.convention = provider + this.convention = t } @Override void unsetConvention() { - this.convention = Providers.getEmpty() + this.t = null } @Override - void map(UnaryOperator operator) { - def oldTProvider = this.tProvider - this.tProvider = Providers.fromSupplier { - operator.apply(oldTProvider.provide()) - } + void map(UnaryOperator mapper) { + this.t = requireNonNull(mapper.apply(this.t)) } @Override - void flatMap(Function> function) { - def oldTProvider = this.tProvider - this.tProvider = Providers.fromSupplier { - function.apply(oldTProvider.provide()).provide() - } - } - - @Override - String toString() { - "SimpleProperty(convention: ${ this.convention }, tProvider: ${ this.tProvider })" + void merge( + @DelegatesTo(type = 'T', strategy = Closure.DELEGATE_FIRST) + Closure configurator + ) { + def d = requireNonNull(this.monoid.empty.get()) + configurator.delegate = d + configurator.resolveStrategy = Closure.DELEGATE_FIRST + configurator() + this.t = requireNonNull(this.monoid.concat.apply(this.t != null ? this.t : this.monoid.empty.get(), d)) } } diff --git a/api/src/main/groovy/com/jessebrault/ssg/util/Monoid.groovy b/api/src/main/groovy/com/jessebrault/ssg/util/Monoid.groovy new file mode 100644 index 0000000..71becf2 --- /dev/null +++ b/api/src/main/groovy/com/jessebrault/ssg/util/Monoid.groovy @@ -0,0 +1,16 @@ +package com.jessebrault.ssg.util + +import groovy.transform.EqualsAndHashCode +import groovy.transform.NullCheck +import groovy.transform.TupleConstructor + +import java.util.function.BinaryOperator +import java.util.function.Supplier + +@TupleConstructor(defaults = false) +@NullCheck(includeGenerated = true) +@EqualsAndHashCode +final class Monoid { + final BinaryOperator concat + final Supplier empty +} diff --git a/api/src/test/groovy/com/jessebrault/ssg/property/SimplePropertyTests.groovy b/api/src/test/groovy/com/jessebrault/ssg/property/SimplePropertyTests.groovy new file mode 100644 index 0000000..64729f9 --- /dev/null +++ b/api/src/test/groovy/com/jessebrault/ssg/property/SimplePropertyTests.groovy @@ -0,0 +1,24 @@ +package com.jessebrault.ssg.property + +import com.jessebrault.ssg.buildscript.domain.MutableSiteSpec +import org.junit.jupiter.api.Test + +import static org.junit.jupiter.api.Assertions.assertEquals + +final class SimplePropertyTests { + + @Test + void merge() { + def p = Properties.get(MutableSiteSpec.MONOID) + p.merge { + name.set('Hello') + } + p.map { + it.name.map { it + ', World!' } + it + } + def ms = p.get() + assertEquals('Hello, World!', ms.name.get()) + } + +} diff --git a/sketchingBaseBuild.groovy b/sketchingBaseBuild.groovy index ec0ea15..f52faa5 100644 --- a/sketchingBaseBuild.groovy +++ b/sketchingBaseBuild.groovy @@ -1,16 +1,17 @@ abstractBuild(name: 'redDogAll', extends: 'default') { - siteSpec.merge { + // siteSpec(Closure) is short for siteSpec.merge(Closure) + siteSpec { name = 'Red Dog Ensemble' baseUrl = 'https://reddogensemble.com' } - globals.merge { + globals { greeting = 'Say hello to good music!' } sources { types -> models.map { acc -> - old + someMethodThatGetsEventModels() + acc + someMethodThatGetsEventModels() } } @@ -26,11 +27,11 @@ abstractBuild(name: 'redDogAll', extends: 'default') { } build(name: 'preview', extends: 'redDogAll') { - siteSpec.merge { - baseUrl += '/preview' // if possible + siteSpec { base -> + baseUrl = base.baseUrl + '/preview' // if possible } - globals.merge { + globals { greeting = 'Hello from preview!' } } \ No newline at end of file