-
Notifications
You must be signed in to change notification settings - Fork 69
Use membership decorator in view #351
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
Conversation
Lots of spec-related noise, I will squash all commits after CR is done, before merging. |
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.
Very nice refactor @rewritten 👏 Decorators (and view models) are really useful for clean up logic inside views.
I also like a lot the dry-*
family, but not sure if we should introduce new deps. for things we can easily implement with Ruby stdlib, even if they are nice additions. In this case, it's a really lightweight gem, so not really a big deal, but in general I'd prefer "plain" Ruby vs "syntax sugar" plugins.
I left another small comment above. But really nice PR 👌. Happy to see you here again! 😃
app/decorators/member_decorator.rb
Outdated
end | ||
|
||
def inactive_icon | ||
v.tag(:span, class: %w[glyphicon glyphicon-time]) unless active? |
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.
@rewritten I realized this produces a different html compared to the original code: an anchor inside the span (when called along with link_to_self
). I think this is because this line returns a self-closed span. It looks like this:
We can fix that by using the glyph
helper:
v.glyph('time') unless active?
I just tried it locally and it looks fine:
Welcome back home @rewritten !!
I couldn't agree more. I particular decision we took about the development of the project (and we should state in the documentation) is that we want to keep it as simple and lightweight as possible, in terms of both infrastructure and code, to keep its maintenance cost at the minimum. We don't have enough resources to spend dev time on it and so we aim to be 80% contributor-driven. This directly affects how serious we take the addition of new dependencies such as gems, which need to be maintained in the long run. So I suggest we remove that dry-initializer gem (I know it's hard. I do also like dry-* gems). Besides this, I ❤️ the direction this PR is taking. |
app/controllers/users_controller.rb
Outdated
|
||
@members = current_organization. | ||
members. | ||
where(user_id: user_ids). |
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 rather do an AR merge? This avoids one SQL query which saves a network round trip, etc. PostgreSQL knows how to match against those users better than us.
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.
PostgreSQL knows, ActiveRecord sometimes messes up the joins. I will try to see if this is working in this case.
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.
Indeed it is not working in this case. As @users
already comes from a JOIN, AR is not really able to distinguish how to prepare the association chain.
Here it is a direct ransack on members, so we don't have to mix stuff up.
app/decorators/member_decorator.rb
Outdated
|
||
private | ||
|
||
def h |
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.
what about a more meaningful name like routes
? It would help other devs understand all this.
wrong_email_member.user_id => wrong_email_member, | ||
empty_email_member.user_id => empty_email_member | ||
}) | ||
assigns(:members).each { |m| expect(m).to be_a(MemberDecorator) } |
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.
IMO it's a not a good idea to couple to particular class types. Makes future changes harder and it's sort of against Ruby's duck-type principle.
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 but still somewhat I want to specify that what's sent to the view is a list of decorated objects. What about a general method decorated_object
on the decorator? Instead of member
. I have used member
just for laziness, for instance when using draper I tend to use object
instead of the named accessor.
app/controllers/users_controller.rb
Outdated
# merge(@search.result(distinct: false)). | ||
# page(params[:page]). | ||
# per(25). | ||
# map { |m| MemberDecorator.new(m, self.class.helpers) } |
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.
👍
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.
Actually I left this here to remind about making the member table ransack against members itself. I will try to complete the feature, and make the whole page based on members so we don't need the additional @users
variable here.
Ready for your second review :) |
app/controllers/users_controller.rb
Outdated
default_sort | ||
|
||
@search = | ||
current_organization.members.joins(:account, :user).ransack(params[:q]) |
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 doesn't seem to be any difference between doing joins
before ransack.result
or after it, and I don't have a clear reason for it to be in any of them instead of the other. The only "intuition" I have is that by joining after the search, we may risk joining twice. Any input?
Maybe I should just ditch the joins and use only eager_load
.
app/controllers/users_controller.rb
Outdated
@members = | ||
@search.result.page(params[:page]).per(25) | ||
|
||
@member_view_models = |
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 is no easy way to keep the pagination info exposed and at the same time to decorate the objects. It's this or another "paginated collection decorator", which most of the times I find ugly.
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.
👏 congrats @rewritten ! This improves a lot the view and sets the foundation for further improvements 🎉
@markets, @sauloperez what's the process now? Who merges? Who deploys? |
we need @sseerrggii and/or his testing crew to give it a try. As soon as it gets their approve we can go ahead and merge. |
I have tested and everything works fine for me 👍 |
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.
Nice, thanks @rewritten . Welcome back! 💪
Solves #349
What
I add here a decorator for membership. It has access to the view context, and exposes exactly what's needed to render the view.
Why
User list view (table rows mostly) uses several models in parallel and it's difficult to follow, with all the conditionals. A decorator encapsulates all the logic in a testable class.
Bonus
I have added dry-initializer gem, which is a very lightweight library to help with different kinds of instance initialization strategies. It is not really needed here, but it's a nice addition in general, as I would also like to introduce to more dry-* gems in the future.