-
-
Notifications
You must be signed in to change notification settings - Fork 20
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
🧹🔨🥗Marketplace
: Show Order#delivery_window
in Order::EmailReceiptComponent
#1319
🧹🔨🥗Marketplace
: Show Order#delivery_window
in Order::EmailReceiptComponent
#1319
Conversation
b60fd1c
to
38ff75f
Compare
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.
This is a weird PR. Apologies in advance.
I noticed that the Order#delivery_window
was not showing up in the EmailReceiptComponent
in production, but it was in the mailer_preview
. That was because I was using a FactoryBot.build(:marketplace_order)
to populate the values, so the delivery window had never needed to be retrieved from the database! And I had done the same in the Marketplace::OrderPlacedMailer
specs... So at no time was Order#delivery_window
being called on an Order
that had actually been pulled out of the database.
Womp. Womp.
And when I was testing it via the UI, I was using the Cart
object... Which did have a Cart#delivery_window
.
I started by just copy and pasting the delivery_window
method into Order
, but that offended my oh-so-delicate sensibilities and I revolted. And since I ... reallllllly want.... a Delivery
class..... I thought "Well, what if I sprout that Delivery
model?! It sounds like the time!"
And I wanted to relearn the ActiveRecord::Type
bits since I haven't had much time to do hands-on-keys coding in a bit; and I want to have a better feel for how the pieces work together...
So anyway, that's all the justification I have for why I didn't create a marketplace_deliveries
table and store the data that I've been copy-pasting between tables.
I could see wanting to go down that path sooner rather than later tho. I was really hoping the Type
stuff would feel like a solid win...
I'm going to add some more precise tests at the Type
and Model
level.
@@ -35,7 +35,7 @@ | |||
expect { perform_request } | |||
.to change(delivery, :contact_email).to(delivery_attributes[:contact_email]) | |||
.and change(delivery, :contact_phone_number).to(delivery_attributes[:contact_phone_number]) | |||
.and change(delivery, :delivery_window).to(DateTime.parse(delivery_attributes[:delivery_window])) | |||
.and change { delivery.reload.delivery_window.value }.to(delivery_attributes[:delivery_window]) |
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.
This feels clunky as heck
app/furniture/marketplace/order/email_receipt_component.html.erb
Outdated
Show resolved
Hide resolved
@@ -12,6 +12,7 @@ class Order < Record | |||
has_many :products, through: :ordered_products, inverse_of: :orders | |||
|
|||
has_encrypted :delivery_address | |||
attribute :delivery_window, ::Marketplace::Delivery::WindowType.new |
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 feel like this should work with delegate :window, to: :delivery, prefix: true
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.
Note! It does not becausebecomes
does not re-initialize the data from the database, so the attribute
needs to be selected and cast(ed?)
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.
This will work when we make a marketplace_deliveries
table instead of using the becomes
hack.
DateTime.parse(super) || 48.hours.from_now | ||
rescue Date::Error => _e | ||
48.hours.from_now | ||
end |
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.
But hey at least I didn't copy and paste this code everywhere 🤣
<div> | ||
<%= form.label attribute %> | ||
<%= form.datetime_field attribute %> | ||
<%= form.datetime_field attribute, disabled: disabled %> |
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 feel like this could be merged independently, and maybe wombo-comboed with a FieldComponent
so more of our :x_field
partials have standardized inputs.
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.
Wombo-comboed! That's a new one for me.
Marketplace
: Sprout Delivery::Window
objectMarketplace
: Sprout Delivery::Window
object
- #1185 So, the `delivery_window` field which can either be a plain string or a particular date or even a date range is a much more sophisticated object. I took some time last night to explore what it could look like to use `ActiveModel::Type`s to create a value object which can take responsibility for representing the data more appropriately. That said, I think `store_model` will be better ™️, but I didn't want to try to dig into that just yet.
We can't use `alias_method` with `attribute`, which is weird but ok.
…nd `Delivery::WindowType`
OK, that makes sense... We keep the type castin gin the type... and that makes everything else lighter.
d2414a0
to
cd0c585
Compare
Keeps the refactor just a tiny bit more cleaner and full
Marketplace
: Sprout Delivery::Window
objectMarketplace
: Show Order#delivery_window
in Order::EmailReceiptComponent
alias_method :==, :eql? | ||
|
||
def time_like? | ||
value.respond_to?(:strftime) |
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.
Nit: maybe inlining this method would make the strftime
method clearer -- you are calling strftime if it responds to it -- saving the extra indirection of going "is time_like?
a built-in thing? oh, no, it is defined down here, let's see... oh, i see".
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.
Yea, there's an indirection bump but time_like?
is used in a few other places (in particular figuring out which field to render for the form and how to present the window on the receipt)
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.
Ah, fair enough.
|
||
def strftime(*args, **kwargs) | ||
return value.strftime(*args, **kwargs) if time_like? | ||
value |
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.
Callers of strftime
will probably expect a string coming back from this call. Is value
a string? Or can it be something else that should be cast to a string? (ETA: Something else that isn't time-like)
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.
Oo that's a good point... I could just call to_s
? For now, value
is either a string or a DateTime
; but if we start wanting it to hold a date range or other less primitive data structures it will be weird to return them.
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.
It's not that weird. But perhaps I'm not the best judge of that.
Thanks for all the extra testing goodness!
Marketplace
:DeliveryConstraint
s #1185So, the
delivery_window
field which can either be a plain string or a particular date or even a date range is a much more sophisticated object.I took some time to explore what it could look like to use
ActiveModel::Type
s to create a value object which can take responsibility for representing the data more appropriately.I think the next step would be to back the
Delivery
class with amarketplace_deliveries
table and moveOrder#contact_email
, etc. into it.In the meantime, I also added some tests to where the
delivery_window
is being used, so that when / if we make that change we have some guard rails in place.No UI changes, but this does add
capybara
and configure ourview_component
library for testing.