0
\$\begingroup\$

I have a form that I feel I am overcomplicating because of the relationship between models that are involved. Can you let me know if there's a more elegant solution to what I have?

(for reference) The form ultimately looks like this:

Form that uses dots to represent select options

The relationship between the involved models:

user_relation has_many :user_relation_skills
user_relation_skills has_many :progresses
progress has_many :ratings, :as => :ratable

The form looks like this (I removed most of the classes/extraneous information but left the divs in to give a better idea of whats going on):

<%= simple_form_for(@user_relation) do |f| %>
    <%= f.fields_for :user_relation_skills do |ff| %>
        <%= ff.fields_for :progresses do |fff| %>
            <%= fff.fields_for :ratings do |ffff| %>
                <div>   
                    <div> 
                        <%= link_to(user_relation_skill_path(ffff.object.scope)) do %>
                          <%= ffff.object.scope.skill.category.name %>&nbsp<i class='icon-arrow-right'></i>&nbsp<%= ffff.object.scope.skill.name %>
                        <% end %>
                    </div>
                </div>

                <% if @current_type_for_views == 'coach' %>
                    <div>   
                        <div>
                            <%= ffff.select("value", 1..8, {}, {class: "ratings-skill"}) %>
                        </div>
                        <div>   
                            <div class='small-offset-8 small-4 columns'>
                                <%= button_tag(type: 'submit', class: "small button") do %>
                                    <i class="icon-check"></i>Update 
                                <% end %>
                            </div>
                        </div>
                    </div>

                <% else %>

                    <div>   
                        <div>
                            <%= ffff.select("value", 1..8, {}, {class: "static-rating"}) %>
                        </div>
                    </div>
                <% end %>

            <% end %>
        <% end %>
    <% end %>
<% end %>

Are this many 'nests' in a form typical or should I be doing something else?

I'm including part of a simple UML diagram with a focus on the "Rating" model that is attached to many things (there are lots of things in the app that can be rated):

Most of the lines going up out of the screen connect either to unimportant models or Active Record directly. The only pertinent thing that is not pictured is the fact that Rating rates Users (a user model that someone would typically expect).

Not pictured is the fact that Rating rates Users

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

This view knows far too much about your data model structure.

One level of nesting is reasonably acceptable, but this many is dangerous : in fact, this view needs knowledge about no less than 4 different business objects, not to mention it has to know the relations between them.

Solution 1 : the facade pattern

Abstract away all those details in another object, which would be the sole source of truth for your view : call it a form object, or a presenter, or a context in DCI idiom, whatever.

The idea is that this object has a simple interface that maps to the complex underlying system. We'd need more details on your business logic to be able to help you on this one.

Solution 2 : cook your own "accepts_nested_attributes" method

on your UserRelation model, create a ratings method that provides direct access to your ratings. Then create another method that allows assignment, like ratings_attributes=. In this method, process the passed parameters to create / update associated ratings ; you can find inspiration for this in the source for the accept_nested_attributes method.

\$\endgroup\$
1
  • \$\begingroup\$ One of these solutions seems likely. Coming from the .NET world - it is standard to use ViewModels to solve many of these sorts of issues (too much model information in the view). I will give this a little while to stew and reply at a later time. (For now I have updated the question with a bit more information due to a response from another user - although that information is likely not pertinent to your response) \$\endgroup\$
    – Ecnalyr
    Commented Oct 30, 2013 at 12:36
0
\$\begingroup\$

This is hard to say because I don't have a broader context but you may have over normalized your database. Something to consider when building these is that it can sometimes be better to add items to a model instead of doing a has relationship with them. This is the first 4 level deep nested form that I have came across.

This will ultimately be a judgement call on your part, but I follow YAGNI (you ain't gonna need it) pretty strongly and program what i'm doing for just the problem at hand and any glaring long term consequences of the decision.

Outside of that, you could do something like a Factory Pattern that takes care of all of this, I would start with reconsidering your model setup though.

What is the type of problem you are trying to solve with this form and these models?

\$\endgroup\$
1
  • \$\begingroup\$ I added a simple uml diagram to show how Rating is tied to many other models not involved in this form (probably why it seems 'over-normalized' when looking at just this form -- we needed a lot of normalization because we use rating in a lot of places). As far as the problem we are trying to solve: We have an administrator-like user signed in who needs to assign a new rating to one or many of the displayed skills. . . So when the user, who is making progress in these skills, signs in they can see the progress they've made in the skill. \$\endgroup\$
    – Ecnalyr
    Commented Oct 30, 2013 at 12:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.