-
Notifications
You must be signed in to change notification settings - Fork 130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor: templates #961
Refactor: templates #961
Conversation
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Codecov Report
@@ Coverage Diff @@
## main #961 +/- ##
==========================================
+ Coverage 43.85% 46.06% +2.21%
==========================================
Files 55 57 +2
Lines 5163 7454 +2291
==========================================
+ Hits 2264 3434 +1170
- Misses 2576 3695 +1119
- Partials 323 325 +2
Continue to review full report at Codecov.
|
3e52dbe
to
6b5fea1
Compare
6b5fea1
to
a63c10c
Compare
Make `fn.Template` interface not struct. Signed-off-by: Matej Vasek <mvasek@redhat.com>
a63c10c
to
2fa81cd
Compare
Signed-off-by: Matej Vasek <mvasek@redhat.com>
@zroubalik @lkingland @salaboy PTAL |
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really do like this update very much. I hope my long comments below do not obscure that fact. The improved clarity of copying and ability to use different implementations of a Template is really going to be nice to have (especially Dynamic Templates!)
My suggestions boil down to:
- Pass data structures by value (unless sharing a memory location is actually needed)
- (related) Use non-pointer receivers for methods (unless sharing a memory location is actually needed)
- (related) Use non-pointer arguments and return new data structures with mutations applied rather than ask a method to go mutate a memory location
- Rely on
Template.Write
to decide whether or not to mask manifest.yaml, rather than masking it from the filesystem. - Defaults should be applied in constructors (where used), with everywhere else failing fast
- Tests should use the API where possible and avoid hard-coding a specific cases or relying on a side-effect (where possible)
- Directly serialize Repository, Runtime and Template out at different levels rathern than using a shared nested config (high field overlap, but they are not the same!).
- Comments are a pain, but should really be expected, and follow the Go style of "all exported members have at least a sentence, beginning with the exported token".
/lgtm
/hold for others and for potentially update which address these above gentle suggestions
Overall looks good!
template.go
Outdated
return t.repository + "/" + t.name | ||
} | ||
|
||
func (t *template) Write(ctx context.Context, f *Function) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here also, to receive a Function as a reference is not really necessary since it is a data structure. Generally preferable to omit the pointer and pass by value unless its methods are clearly intended to mutate its internal state at a shared memory location.
As an example, here, simply calling .Write
and passing it a *Function
causes the passed function to have its state mutated. That is a side-effect and generally an antipattern unless the visitor pattern
is clearly expected and required.
In the case where this really is what's desired, it even then is often better for a method to accept an immutable struct and return a new struct with modifications applied.
This is, for example, the logic behind the API of the built-in append
. We do not:
append(&elements, "B")
Nor do we:
elements.append("B")
Rather, we:
elements = append(elements, "B")
I elaborate on the pros/cons in the other comment...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lkingland Little bit out of topic: To be honest I have mixed feelings from append
.
It doesn't modify slice you are appending to which is good.
However returned slice may or may not share underlying memory.
s1 := make([]int, 1, 1024)
s2 := append(s1, 1)
fmt.Println(s1)
s2[0] = 42 // it might look like you are not modifying s1 but you might be
fmt.Println(s1)
It's not working like conj
in Clojure
where collections are immutable.
|
||
type templateConfig struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the concept of a templateConfig
might be complicating what could really just be one object.
If the reason for separating these fields out is such that their fields can be embedded in Repository and Runtime structs, then I have a whole other long-winded reason why that may not be necessary (tl;dr they share fields, but are not the same because there are indeed small diferences at each level).
This might actualy be simpler and easer kept as a single template
struct, with a few duplicated fields as necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then I have a whole other long-winded reason why that may not be necessary (tl;dr they share fields, but are not the same because there are indeed small diferences at each level).
@lkingland what are those differences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me it seems that BuildConfig
, HealthEndpoints
, BuildEnvs
and Invocation
are just defaults for fn.Function
that can be defined at level of repository runtime, or template.
Is there any objective problem with that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the reason for separating these fields out is such that their fields can be embedded in Repository and Runtime structs,
That's exactly what I want to do in subsequent PR.
@@ -32,9 +30,76 @@ type Template struct { | |||
Invocation Invocation `yaml:"invocation,omitempty"` | |||
} | |||
|
|||
// template | |||
type template struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you do take my suggestion and keep template
only, by combining bacl in templateConfig
, you would just need to capitalize those fields you want serialized to disk (BuildConfig, Invocation)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: lkingland, matejvasek The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@lkingland To be clear I am huge fan of referential transparency and immutability whenever possible. But at least for If we want to play game of functional programming then we perhaps want to change it to something like: func (t template) GenerateProject(fn Function) (outFn Function, projectFiles fs.FS, err error) {} And let caller to do the write to filesystem. |
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
Signed-off-by: Matej Vasek <mvasek@redhat.com>
@lkingland I addressed some of your comments: |
@lkingland please re-LGMT |
/lgtm |
@zroubalik @salaboy if you don't have objections I'll merge this. |
/unhold |
Make
fn.Template
an interface instead of a concrete struct.