diff --git a/internal/app/index.go b/internal/app/index.go index 894563e..eea3c25 100644 --- a/internal/app/index.go +++ b/internal/app/index.go @@ -8,7 +8,7 @@ import ( ) type model struct { - Message deferrable.Defer[chan []string] + Message deferrable.Value[[]string] } func Index(addArtificialDelay bool) http.HandlerFunc { @@ -27,6 +27,6 @@ func Index(addArtificialDelay bool) http.HandlerFunc { close(data) }() - _ = websupport.Render(w, Resources, "index", model{Message: deferrable.New(w, data)}) + _ = websupport.Render(w, Resources, "index", model{Message: deferrable.NewValue(w, data)}) } } diff --git a/internal/app/resources/templates/index.gohtml b/internal/app/resources/templates/index.gohtml index 2bf1aa8..d484b06 100644 --- a/internal/app/resources/templates/index.gohtml +++ b/internal/app/resources/templates/index.gohtml @@ -19,17 +19,16 @@ - {{ range $items := .Message.Value }} -
-

- Success! -

+ {{ $items := .Message.Get }} +
+

+ Success! +

- -
- {{ end }} + +
{{- end}} diff --git a/pkg/deferrable/deferrable.go b/pkg/deferrable/deferrable.go deleted file mode 100644 index 73a9ce9..0000000 --- a/pkg/deferrable/deferrable.go +++ /dev/null @@ -1,20 +0,0 @@ -package deferrable - -import "net/http" - -type Defer[T any] struct { - writer http.ResponseWriter - value T -} - -func New[T any](writer http.ResponseWriter, value T) Defer[T] { - return Defer[T]{ - writer: writer, - value: value, - } -} - -func (d Defer[T]) Value() T { - _ = http.NewResponseController(d.writer).Flush() - return d.value -} diff --git a/pkg/deferrable/deferrable_test.go b/pkg/deferrable/deferrable_test.go deleted file mode 100644 index 484e91d..0000000 --- a/pkg/deferrable/deferrable_test.go +++ /dev/null @@ -1,37 +0,0 @@ -package deferrable_test - -import ( - "github.com/initialcapacity/go-streaming/pkg/deferrable" - "github.com/stretchr/testify/assert" - "net/http" - "testing" -) - -func TestDefer(t *testing.T) { - writer := ResponseWriterSpy{} - d := deferrable.New(&writer, "pickles") - - value := d.Value() - - assert.Equal(t, "pickles", value) - assert.True(t, writer.FlushCalled) -} - -type ResponseWriterSpy struct { - FlushCalled bool -} - -func (w *ResponseWriterSpy) Header() http.Header { - return map[string][]string{} -} - -func (w *ResponseWriterSpy) Write([]byte) (int, error) { - return 0, nil -} - -func (w *ResponseWriterSpy) WriteHeader(statusCode int) { -} - -func (w *ResponseWriterSpy) Flush() { - w.FlushCalled = true -} diff --git a/pkg/deferrable/flush.go b/pkg/deferrable/flush.go new file mode 100644 index 0000000..69884ed --- /dev/null +++ b/pkg/deferrable/flush.go @@ -0,0 +1,7 @@ +package deferrable + +import "net/http" + +func Flush(w http.ResponseWriter) error { + return http.NewResponseController(w).Flush() +} diff --git a/pkg/deferrable/range.go b/pkg/deferrable/range.go new file mode 100644 index 0000000..c789d37 --- /dev/null +++ b/pkg/deferrable/range.go @@ -0,0 +1,31 @@ +package deferrable + +import "net/http" + +type Range[T any] struct { + writer http.ResponseWriter + channel chan T +} + +func NewRange[T any](writer http.ResponseWriter, channel chan T) Range[T] { + return Range[T]{ + writer: writer, + channel: channel, + } +} + +func (d Range[T]) Get() chan T { + _ = Flush(d.writer) + flushChannel := make(chan T) + + go func() { + for val := range d.channel { + _ = Flush(d.writer) + flushChannel <- val + } + _ = Flush(d.writer) + close(flushChannel) + }() + + return flushChannel +} diff --git a/pkg/deferrable/range_test.go b/pkg/deferrable/range_test.go new file mode 100644 index 0000000..4db09b2 --- /dev/null +++ b/pkg/deferrable/range_test.go @@ -0,0 +1,26 @@ +package deferrable_test + +import ( + "github.com/initialcapacity/go-streaming/pkg/deferrable" + "github.com/initialcapacity/go-streaming/pkg/deferrable/test" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestRange(t *testing.T) { + writer := deferrable_test.ResponseWriterSpy{} + channel := make(chan string) + defer close(channel) + + go func() { + channel <- "pickles" + channel <- "chicken" + }() + + values := deferrable.NewRange(&writer, channel).Get() + + assert.Equal(t, uint64(1), writer.FlushCalls.Load()) + assert.Equal(t, "pickles", <-values) + assert.Equal(t, "chicken", <-values) + assert.Equal(t, uint64(3), writer.FlushCalls.Load()) +} diff --git a/pkg/deferrable/test/response_writer_spy.go b/pkg/deferrable/test/response_writer_spy.go new file mode 100644 index 0000000..e49d310 --- /dev/null +++ b/pkg/deferrable/test/response_writer_spy.go @@ -0,0 +1,25 @@ +package deferrable_test + +import ( + "net/http" + "sync/atomic" +) + +type ResponseWriterSpy struct { + FlushCalls atomic.Uint64 +} + +func (w *ResponseWriterSpy) Header() http.Header { + return map[string][]string{} +} + +func (w *ResponseWriterSpy) Write([]byte) (int, error) { + return 0, nil +} + +func (w *ResponseWriterSpy) WriteHeader(_ int) { +} + +func (w *ResponseWriterSpy) Flush() { + w.FlushCalls.Add(1) +} diff --git a/pkg/deferrable/value.go b/pkg/deferrable/value.go new file mode 100644 index 0000000..2cb20c6 --- /dev/null +++ b/pkg/deferrable/value.go @@ -0,0 +1,20 @@ +package deferrable + +import "net/http" + +type Value[T any] struct { + writer http.ResponseWriter + channel chan T +} + +func NewValue[T any](writer http.ResponseWriter, channel chan T) Value[T] { + return Value[T]{ + writer: writer, + channel: channel, + } +} + +func (d Value[T]) Get() T { + _ = Flush(d.writer) + return <-d.channel +} diff --git a/pkg/deferrable/value_test.go b/pkg/deferrable/value_test.go new file mode 100644 index 0000000..dc940de --- /dev/null +++ b/pkg/deferrable/value_test.go @@ -0,0 +1,23 @@ +package deferrable_test + +import ( + "github.com/initialcapacity/go-streaming/pkg/deferrable" + "github.com/initialcapacity/go-streaming/pkg/deferrable/test" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestChannel(t *testing.T) { + writer := deferrable_test.ResponseWriterSpy{} + channel := make(chan string) + defer close(channel) + + go func() { + channel <- "pickles" + }() + + value := deferrable.NewValue(&writer, channel).Get() + + assert.Equal(t, "pickles", value) + assert.Equal(t, uint64(1), writer.FlushCalls.Load()) +}