-
-
Notifications
You must be signed in to change notification settings - Fork 504
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
Feature tags step 1 #4884
base: main
Are you sure you want to change the base?
Feature tags step 1 #4884
Conversation
7fc06ef
to
50bea61
Compare
a0c0bb1
to
a5c8808
Compare
Requesting a review from you @cielf first as I want to make sure this implementation is functionally correct. |
I'm including in @dorner for parallel - and I might not get to it til the end of the week. |
@@ -44,7 +45,7 @@ def end_date_is_bigger_of_end_date | |||
return if start_date.nil? || end_date.nil? | |||
|
|||
if end_date < start_date | |||
errors.add(:end_date, 'End date must be after the start date') |
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.
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.
Unrelated change, so I can move it to another PR if needed.
# More concise test ("should") matchers | ||
gem "shoulda-matchers", "~> 6.2" |
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.
Moved this gem because otherwise rails g model
fails because shoulda-matcher
is needed for factory bot to create the spec factory files, so we need shoulda-matcher
in the dev environment as well.
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.
Looks pretty good at first go-through.
Sorry for the delay - I keep leaving this to last in my reviews because I have to think about it the most, and I always end up without enough time to address it :) Hopefully soon! |
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.
So happy this is getting started! I think we need to tweak the solution a bit though.
@@ -66,9 +69,9 @@ def show | |||
|
|||
def update | |||
@product_drive = current_organization.product_drives.find(params[:id]) | |||
if @product_drive.update(product_drive_params) | |||
@product_drive.attributes = product_drive_params.merge(tags: tags_from_params) | |||
if @product_drive.save |
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.
Is there a reason this pattern is different than in create?
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.
Not sure why i did that. Fixed in dbe77d7 to be more consistent with other update
actions.
app/models/concerns/taggable.rb
Outdated
private | ||
|
||
def set_organization_id(tagging) | ||
tagging.organization_id ||= organization_id |
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.
Why does the join table need an organization_id?
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.
Also this method is pretty broadly named for something that's going to be in a model... maybe rename it so it's specific to tagging?
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 there might be a modelling issue here. It's the Tag that should belong to the organization, not the Tagging. If we want to show only product drive tags, that should be an enum on the Tag record, not the Tagging. Tagging should be a simple join table between a "taggable" and a Tag.
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.
Either the tags
table or the taggings
join table need a foreign key reference to an organization. Functionally I think either would work. To me, it makes more sense that a tagging belongs to an organization rather than a tag. Because whoever is doing the tagging will belong to a certain organization so a tagging without an organization doesn't really make sense, whereas a tag doesn't necessarily need to belong to an organization it could be used by multiple organizations. If taggings references the organization, then tags can be referenced between organizations and this reduces the number of tag records. Adding type
to the tag model also means more duplicates if for example there is a donation tag and a product drive tag both use the same tag name.
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 know it doesn't really matter but out of curiosity I checked and it looks like the acts_as_taggable_on gem also uses a similar schema.
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 first case is a reasonable one.
@cielf do you want to weigh in - am I right in my assumptions here?
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.
Ok -- I'm going to muse a bit here. We definitely need to discuss this more before making any changes based on these musings.
What's in my head, here, certainly wasn't explicit in the issue. Nor was the idea of typed tags.
We want the tags to be a tool the banks can use for identifying groups of things that belong together that we haven't otherwise grouped for them.
The use case that prompted this is a "campaign" for product drives - the idea of linking product drives with a common theme, like Mothers' Day or Christmas. I could certainly see having distributions associated with either of those examples.
I can also imagine banks using this to tag things associated with particular grants -- purchases, eligible partners or partner groups, special items, distributions.
So, from a user's point of view, why do we have type associated with the tags, at least, yet?
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've added organization_id to the tags table as requested, so an organization can create/edit/delete tags independently of them being used to tag something.
Only remaining question is whether the tags table should have a type or not. We can either wait until that is a requirement in the future or I can add it to this PR. Up to y'all
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.
We discussed during the meeting. Tags should have a type. Taggings should not need to have one since it has the taggable_id / taggable_type for the polymorphic association.
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.
Fixed in 0cd6971.
app/models/organization.rb
Outdated
@@ -68,6 +68,9 @@ class Organization < ApplicationRecord | |||
has_many :purchases | |||
has_many :requests | |||
has_many :storage_locations | |||
has_many :taggings | |||
has_many :product_drive_tags, -> { merge(Tagging.by_type("ProductDrive")).distinct }, |
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.
So here this should be a simple has_many
instead of :through
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.
Is the refactor from 413a6e0 what you meant?
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.
Nope - see above. I meant moving organization_id
to tags
.
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.
Fixed in 8b7242d.
class Tag < ApplicationRecord | ||
has_many :taggings, dependent: :destroy | ||
|
||
scope :alphabetized, -> { order(:name) } |
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.
...and this should have the by_type
scope instead of Tagging.
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.
Moved by_type
scope to Tag in 413a6e0.
@@ -103,6 +107,7 @@ | |||
<td><%= product_drive.name %></td> | |||
<td><%= product_drive.start_date.strftime("%m-%d-%Y") %></td> | |||
<td><%= product_drive.end_date&.strftime("%m-%d-%Y") %></td> | |||
<td><%= product_drive.tags.map(&:name).sort.join(", ") %></td> |
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 like a method that should live on the Taggable concern.
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.
True. Fixed in 99f4f97.
spec/factories/tags.rb
Outdated
FactoryBot.define do | ||
factory :tag do | ||
name { "Holidays" } | ||
initialize_with { Tag.find_or_create_by(name:) } |
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'm not the biggest fan of quietly changing this from "create new tag" to "find existing tag"... certainly not in the base factory. I'd rather have helper methods to work with this if needed.
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.
True, I wanted to avoid issues from tags being created in tests with non unique names, but this wasn't the right way to achieve that.
Fixed in efd015f.
@@ -47,6 +47,23 @@ | |||
expect(response.body).not_to include(product_drive_path(product_drive_five.id)) | |||
end | |||
|
|||
it "allows filtering by tag" do |
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.
Should this be a shared example?
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.
Do you mean a shared example for filtering product drives? Or a shared example for filtering different resources by tags?
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.
Seems premature to me to create a shared example for filtering by tags, since only product drives have tags.
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 the whole point of the tag feature is to make it a generic way to attach tags to anything - we know we're not going to limit it to product drives.
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.
Ok then I will make it a shared example.
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.
Fixed in 374dea1.
@dorner Refactored organization_id to the tags table. |
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.
Minor quibble with a name.
@cielf this looks good to me now - do you want to take one more look on the QA side since the implementation has changed?
app/models/concerns/taggable.rb
Outdated
has_many :taggings, as: :taggable, dependent: :destroy | ||
has_many :tags, through: :taggings, source: :tag | ||
|
||
scope :by_tags, ->(tags) { left_joins(:tags).where(tags: {name: tags}) } |
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.
Can we name the parameter tag_names
? It was a bit confusing till I saw where it was used.
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.
Fixed in 4561a63.
@dorner Yup -- will try to take a look at it tomorrow. |
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.
Whoops, that was working at some point. I just pushed commits to fix the issue with corresponding request specs. @cielf It is ready for testing again. |
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 seem to be stopping as soon as a notice a problem. This time...
Any other field, if you click to a different field, the information stays.
Not so with tags.
So, for example, if you type in your tag and hit submit without hitting enter first, you lose it. Or if you type in your tag, notice a problem in the date and go to fix that, the tag disappears.
Is that something we can reasonably address?
@cielf Fixed in my last commit. That is configurable by select2, so I just had to set Here are the other configuration options for this dropdown in case you're curious: https://select2.org/configuration/options-api. |
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.
Hmm, Try this sequence. New product drive, don't enter a name, but enter the start date. In tags: "tagname1", hit enter, "tagname2", hit enter, "tagname3" then click save without hitting enter.
What I'm seeing in this case is that the tags go away.
I think what you might be experiencing is that new tags (ones that haven't yet been saved to a product drive) don't persist after a validation failure. That's because when the page re-renders the tag dropdown options are created from all currently existing product drive tags. Which since the validation failed, that product drive tag didn't get created and therefore doesn't appear any more. Is this considered an edge case? Or should i fix it? To fix it, I see two options:
|
Well, I don't think 1/ is any worse than if they had typed in those tags on a successful product drive creation, so I'm good with that. Honestly, the most likely error is someone mistyping a end date. |
@cielf Ok in that case, I just pushed a commit that implemented option 1. Ready for testing again. |
LGTM. @dorner do you want to take a quick look at the latter changes? |
Resolves #4820
Description
Type of change
How Has This Been Tested?
Added new tests
Screenshots