Design question: Redirection to a "create" action.

44 views
Skip to first unread message

Ronald Fischer

unread,
Jun 4, 2014, 1:23:39 PM6/4/14
to rubyonra...@googlegroups.com
My application should behave like this:

- My application manages (among others) a resource called "Dicts", i.e.
there is a dicts_controller, and my routes.rb contains a "resources
:dicts".

- I have a home page (starting page), which will eventually contain some
user authentification (not implemente yet), and then allow the user to
manage the Dicts objects. I have a home_controller and my routes.rb
contains
match '/login', to: 'home#login', via: 'get'
root 'home#init'
and init.html.erb contains the login form.

So far it's quite conventional. Now to the perhaps unusual part:

- For convenience to the user, the login form contains not only fields
for entering the user data (userid, password), but also an entry field
for a Dicts object, and *two* submit buttons, one with the meaning of
"login and create a new dictionary", and the other one with the meaning
"login and open a existing dictionary":

<%= form_tag("/login",method:"get", enforce_utf8:true) do %>
....
<%= submit_tag(value="Create New Dictionary", class:
'kanren_button', name: 'create') %>
<%= submit_tag(value="Open/Pick Existing Dictionary", class:
'kanren_button', name: 'open') %>
<% end %>

Now the problem:

My HomeController.login method checks, whether the user is authorized,
and if he is, needs to go to the Dict controller and either :create a
new Dict object or :show an existing one.

My problem is that to :create a Dict, would require a POST action (if I
want to stick with the REST model), but a

redirect_to url_for(:controller => dicts,...)

will always create a GET request.

I was thinking of the workaround to use

redirect_to url_for(:controller => :dicts, :action => :new)

and inside Dicts#new use the parameters passed, to create a new Dicts
object, and (if successful) redirect to Dicts#show or whatever, but this
doesn't seem to be an elegant solution.

Another possibility is to invoke Dicts.create from my Home.login method,
but this doesn't seem to be good style either.

Any suggestions on how to proceed?

--
Posted via http://www.ruby-forum.com/.

Jesse Knutsen

unread,
Jun 4, 2014, 1:35:28 PM6/4/14
to rubyonra...@googlegroups.com
I am not a huge fan of an approach that would need to redirect in this way.

Instead, why not create a new class called login or something like that.

To see a possible usage checkout the following article

http://matthewrobertson.org/blog/2012/09/20/decoupling-rails-forms-from-the-database/

You will need to adapt a bit to your needs, but the basics are sound and
should apply nicely.

Walter Lee Davis

unread,
Jun 4, 2014, 1:41:16 PM6/4/14
to rubyonra...@googlegroups.com
When you start implementing your authentication and authorization solution, you may find that you need/want to refactor this initial approach that you've sketched. Most authentication systems (Devise, auth_logic) use a SessionsController and the notion of a session as the repository of who the user is at the moment. Authorization engines, like CanCan, hook on to that session to determine what the current_user can do at the moment to whatever object is in play.

With those two things in mind, you may want to structure this application differently. Let's say you have a link to your new_dict_path somewhere. Your user (logged in or not) clicks that link and should be directed to the form where a new dict can be made. If your authorization framework has determined that creating a dict is something that only a logged-in user can do, then you would be redirected to the login form, and then upon a successful login, redirected back to that form, already populated with the user_id of the current_user. If the user was already logged in, then they would skip that intermediate step.

I would definitely not mix the login logic with the "show the home page" logic as you seem to have done here -- think about your controllers as mediating a particular resource between the system and the user -- your home page is not the session, nor vice-versa.

Walter

>
> --
> Posted via http://www.ruby-forum.com/.
>
> --
> You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.
> To post to this group, send email to rubyonra...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/3a716898bf5663774754e11db41c90b6%40ruby-forum.com.
> For more options, visit https://groups.google.com/d/optout.

Ronald Fischer

unread,
Jun 4, 2014, 2:08:38 PM6/4/14
to rubyonra...@googlegroups.com
Jesse Knutsen wrote in post #1148805:
> I am not a huge fan of an approach that would need to redirect in this
> way.
>
> Instead, why not create a new class called login or something like that.
>
> You will need to adapt a bit to your needs, but the basics are sound and
> should apply nicely.

I don't think this would really solve the problem:

From the model, user and login data are already separated from the Dicts
data (the user id is a foreign key in the Dicts mode, pointing to the
user who owns the Dict).

In the controller side, I have them also separated: The HomeController
is responsible for the Login (I could also have named it
LoginController), and the DictController is responsible for managing the
Dict objects. I don't see wll how what can be improved in this area.

The problem is the user interface. Clicking one button performs a login
AND at the same time either CREATES or OPENS a dict object.

If I would split this into two views, one for login and one for the
usual interface of maintaining dictionaries, I wouldn't run into this
problem, but this would be less convenient to the user.

Ronald Fischer

unread,
Jun 4, 2014, 2:17:50 PM6/4/14
to rubyonra...@googlegroups.com
Walter Davis wrote in post #1148806:
> I would definitely not mix the login logic with the "show the home page"
> logic as you seem to have done here

I fully agree that from a programmer's viewpoint it would make the
problem much cleaner to solve. However, when developing software, I try
to see the user's perspective, and I'm reluctant to change the user
interface just because it makes my task as a programmer easier. After
all, we are writing programs *for* the user, not for ourselves.

I think most users would accept the approach you are suggesting (where
in my case, BTW, no operation will be permitted unless being logged in),
since they are kind of used to it, but this doesn't mean that we
shouldn't make him the life easier.

In my application, the user will *always* provide an username, and
*always* provide a dictionary name, before s/he can start any sensible
work, so it certainly makes sense to request both in one go, when the
session is being established established.

But at least I learned from the answers, that no really clean solution
seems to exist for *this* kind of user interface :-(

Jesse Knutsen

unread,
Jun 4, 2014, 2:26:38 PM6/4/14
to rubyonra...@googlegroups.com
This solution is not splitting the actions up, Its replacing them with
super action that will validate and invoke the business logic of both.
At the same time you would have a single controller and view.

Essentially you are calling two different actions right now (in your
design) where the first leads to the second through a redirect. This
redirect will actually redirect the user on the browser level, its not
just a render. While you can do that, its not efficient.

This method would conglomerate the two into 1: A login.

If valid (valid being defined as a valid set of user credentials and
valid entries for the new Dicts) it would trigger a session creation and
the appropriate Dict creation, based on the button that is clicked.

Colin Law

unread,
Jun 4, 2014, 2:32:18 PM6/4/14
to rubyonra...@googlegroups.com
On 4 June 2014 15:25, Jesse Knutsen <drac...@gmail.com> wrote:
>
> Essentially you are calling two different actions right now (in your design)
> where the first leads to the second through a redirect. This redirect will
> actually redirect the user on the browser level, its not just a render.
> While you can do that, its not efficient.

Actually I don't think you can, unless the http spec has changed, I
believe that redirect to a POST action is not permitted by the spec,
or at least was not in version 1.1.

Colin

Jesse Knutsen

unread,
Jun 4, 2014, 2:59:56 PM6/4/14
to rubyonra...@googlegroups.com
You are 100% correct, but he was going to redirect to Dicts#new. I took that to mean that he would pass the data along and then, when present, invoke new as if it were create.

I'm envisioning something that he was envisioning something such as

SessionsController
    ...
    create
        ...
        redirect_to new_dict_path(new_dict: params[:new_dict]


DictController
    ...
    new
        if params[:new_dict]
            create logic
        else
            new logic

This would get very convoluted and inelegant.


Hence why I feel a Class of login that will do both the login logic and the create Dict logic in one place

Its similar to http://railscasts.com/episodes/416-form-objects?view=asciicast

Colin Law

unread,
Jun 4, 2014, 3:50:25 PM6/4/14
to rubyonra...@googlegroups.com
On 4 June 2014 15:59, Jesse Knutsen <drac...@gmail.com> wrote:
>
> On 6/4/14, 10:30 AM, Colin Law wrote:
>
> On 4 June 2014 15:25, Jesse Knutsen <drac...@gmail.com> wrote:
>
> Essentially you are calling two different actions right now (in your design)
> where the first leads to the second through a redirect. This redirect will
> actually redirect the user on the browser level, its not just a render.
> While you can do that, its not efficient.
>
> Actually I don't think you can, unless the http spec has changed, I
> believe that redirect to a POST action is not permitted by the spec,
> or at least was not in version 1.1.
>
> Colin
>
> You are 100% correct, but he was going to redirect to Dicts#new. I took that
> to mean that he would pass the data along and then, when present, invoke new
> as if it were create.

Yes, you are right, I was looking at his original intention which was
to post to the create action.

Colin

Ronald Fischer

unread,
Jun 4, 2014, 5:19:50 PM6/4/14
to rubyonra...@googlegroups.com
Jesse Knutsen wrote in post #1148818:
> On 6/4/14, 10:30 AM, Colin Law wrote:
>>
> You are 100% correct, but he was going to redirect to Dicts#new.

Only as a work-around, but as I said, I was not so happy with this
either.

> DictController
> ...
> new
> if params[:new_dict]
> create logic
> else
> new logic
>
> This would get very convoluted and inelegant.

Yes, absolutely!

> Hence why I feel a Class of login that will do both the login logic and
> the create Dict logic in one place

This would be what I called HomeController, isn't it? Or do you mean a
separate helper class, which is *not* a controller?

I thought about doing the Dict creation in HomeController.login, but
then I have the logic of dealing with Dict objects twice: The larger
part will be in the DictController, and the creation logic will be in
HomeController. In addition, I expect in a later version HomeController
*also* to be able to create Dict objects.

> Its similar to
> _http://railscasts.com/episodes/416-form-objects?view=asciicast_

I will have a look at this.

Meanwhile I was thinking whether the main problem comes from a different
place: A form can issue a GET or a POST, right? But the method must be
specified inside the form; in my case, I have it set to GET.

However, the form has two buttons, and one is (from the viewpoint of
logic) doing a GET and the other one should do a POST. Of course I can't
have both. Maybe it is the form which needs to be modified?

If I could attach the method (GET/POST) to the submit button instead of
the form itself, this would solve my problem. Maybe you could have a
look at my form and let me know whether I could do this better?

One other issue is that I am using the 'form_tag' method, but Rails also
offer form_for. Would I get some benefit of using form_for (maybe based
on a Dict object?).

Jesse Knutsen

unread,
Jun 4, 2014, 5:34:08 PM6/4/14
to rubyonra...@googlegroups.com

On 6/4/14, 1:19 PM, Ronald Fischer wrote:
> Jesse Knutsen wrote in post #1148818:
>> On 6/4/14, 10:30 AM, Colin Law wrote:
>> You are 100% correct, but he was going to redirect to Dicts#new.
> Only as a work-around, but as I said, I was not so happy with this
> either.
>
>> DictController
>> ...
>> new
>> if params[:new_dict]
>> create logic
>> else
>> new logic
>>
>> This would get very convoluted and inelegant.
> Yes, absolutely!
>
>> Hence why I feel a Class of login that will do both the login logic and
>> the create Dict logic in one place
> This would be what I called HomeController, isn't it? Or do you mean a
> separate helper class, which is *not* a controller?
>
> I thought about doing the Dict creation in HomeController.login, but
> then I have the logic of dealing with Dict objects twice: The larger
> part will be in the DictController, and the creation logic will be in
> HomeController. In addition, I expect in a later version HomeController
> *also* to be able to create Dict objects.
This would not be the HomeController. That would just be a dashboard. My
idea is a whole MVC component that is an object called login that has
not database backing in and of itself. Rather than being a true object
you could think of it as a functional object that can be validated. When
valid it /converts/ its virtual attributes into the attributes of the
objects that it is concerned with managing, in this case a user session
(the credentials) and a dict (or even multiple dicts) and instantiates them.
>
>> Its similar to
>> _http://railscasts.com/episodes/416-form-objects?view=asciicast_
> I will have a look at this.
Yes please do. I think that this will give you a better idea of what I
mean by a functional object and will show you the basic technique needed
to make this happen.
>
> Meanwhile I was thinking whether the main problem comes from a different
> place: A form can issue a GET or a POST, right? But the method must be
> specified inside the form; in my case, I have it set to GET.
>
> However, the form has two buttons, and one is (from the viewpoint of
> logic) doing a GET and the other one should do a POST. Of course I can't
> have both. Maybe it is the form which needs to be modified?
A form can do a get or a post, but not both at the same time. You would
need to change the form action though JS. also you would have to have
your routes set up accordingly.

Ronald Fischer

unread,
Jun 4, 2014, 5:38:40 PM6/4/14
to rubyonra...@googlegroups.com
Jesse Knutsen wrote in post #1148812:
So, basically, my current "login" method would go into the Dict object?
This indeed would make it easier.

The reason why I had a separate controller for the login stuff was, that
I thought that when my login logic gets more sophisticated (with
password, authentification and all that thing), it would make more sense
to have a separate controller for this.

I now start to realize that I want to have the cake AND eat it. Either I
should completely separate the login from the rest of the program, as
for instance Walter Davies suggested above, or I want to have it
together in one form, but then it means that two controllers cause
trouble.

Actually, I feel that I am putting too much logic into a controller. It
would be cleaner to factor these things out to "helper classes" which
can be called from everywhere. Just a thought experiment: Imagine that
my design would have in the footer of every page an entry field and a
button saying "create a Dict", I don't think I would have to redirect to
the Dict controller and then going back to the original place just for
this. Instead I would have somewhere a "Dict factory", which just
creates, initializes (and returns) the new Dict object.

Jesse Knutsen

unread,
Jun 4, 2014, 5:47:05 PM6/4/14
to rubyonra...@googlegroups.com

On 6/4/14, 1:38 PM, Ronald Fischer wrote:
> Jesse Knutsen wrote in post #1148812:
>> On 6/4/14, 10:08 AM, Ronald Fischer wrote:
>> Essentially you are calling two different actions right now (in your
>> design) where the first leads to the second through a redirect. This
>> redirect will actually redirect the user on the browser level, its not
>> just a render. While you can do that, its not efficient.
>>
>> This method would conglomerate the two into 1: A login.
>>
>> If valid (valid being defined as a valid set of user credentials and
>> valid entries for the new Dicts) it would trigger a session creation and
>> the appropriate Dict creation, based on the button that is clicked.
> So, basically, my current "login" method would go into the Dict object?
> This indeed would make it easier.
yes it would handle both login and dict creation/update
>
> The reason why I had a separate controller for the login stuff was, that
> I thought that when my login logic gets more sophisticated (with
> password, authentification and all that thing), it would make more sense
> to have a separate controller for this.
The actual authentication and login should be business logic of the
UserSession model or the User model (you can justify either way), which
should be able to accessed from elsewhere (skinny controller)
>
> I now start to realize that I want to have the cake AND eat it. Either I
> should completely separate the login from the rest of the program, as
> for instance Walter Davies suggested above, or I want to have it
> together in one form, but then it means that two controllers cause
> trouble.
I would agree on this. Its kind of strange to try to both at the same
time. What I would suggest for ease is just do a /normal/ flow with each
model having its separate views and take it from there. See what the
flow you want is and figure out what makes the best sense for your users.
> Actually, I feel that I am putting too much logic into a controller. It
> would be cleaner to factor these things out to "helper classes" which
> can be called from everywhere. Just a thought experiment: Imagine that
> my design would have in the footer of every page an entry field and a
> button saying "create a Dict", I don't think I would have to redirect to
> the Dict controller and then going back to the original place just for
> this. Instead I would have somewhere a "Dict factory", which just
> creates, initializes (and returns) the new Dict object.
>
In that case I would have the field and submit button and the form
points to the dict controller but it functions as ajax and the form
passes a v attribute that tells it that this create does not need a
render return and display a success growl instead

mike2r

unread,
Jun 4, 2014, 8:46:49 PM6/4/14
to rubyonra...@googlegroups.com
I have to disagree.  First of all, I think it's trouble to combine the login and dictionary create/select existing logic in the same controller or controller actions.  It is true that a redirect involves a round trip to the browser, but I think the performance hit (since it should only happen once in a session) is minor compared to the cost in terms of programming which I'll illustrate below.

Login is usually not part of the User model nor any another model, it is associated with the session resource which typically has its own session (or UserSession) controller and does not have an associated model (although it can).  The session is its own resource with its own new, create, and destroy actions.  To the extent anything is stored in a session, it's stored to the session store which, by default, is in a cookie.  Personally, I don't think it belongs in the User model ever.  It does access the User model, of course, as part of the authentication process, but any actions associated with a user (such as creating, changing password, etc.) are not part of login, they're part of the user resource.

Dict, in this case, is its own resource and, IMO, should have its own, separate controller with the full set of controller actions (new, create, etc) and that's where the logic belongs.

To demonstrate this, let's walk through once scenario and assume that you have both login and create/select existing dictionary in one controller.  The user enters the login information, and in this scenario, the name of an existing dictionary and clicks on the button to submit to use an existing dictionary.  The login authentication passes but, unfortunately, the user entered the name of a dictionary that, in fact, does not exist.  What do you do now?  Do you assume that the user meant to create a new dictionary?  Or do you assume the user made a typo?  The normal response would be to display the entry form with the data entered and with an error message asking the user to correct it.  In this case, are you going to make the user login again?  If not, are you now going to need two new actions to present a different form for the user to correct their mistake and another action to process that once it's submitted (since there would be no login with this)?  If you do all of this you will now have the logic to create and select a dictionary in two different actions and that is far worse, IMO, than the performance hit of a redirect.

I would also consider that a user may want to use an existing dictionary and then later in their training session, may want to create a new dictionary.  Are they going to now have to login again?  Or will there now be another set of forms & actions?

Please note that the structure of your controllers and actions do not dictate your presentation to the user.  You can still have a login page and have the user designate a dictionary or create a dictionary at the same time.  However, in this case, I would reconsider as the above posts recommend. I would think it's a lot more user friendly to present a drop down list of existing dictionaries to select, or an option to create a new dictionary after login.  Necessarily, that has to occur after login because in order to make the drop down list, you need to know who the user is.  

I do think that ajax is a good recommendation.  Selecting, creating, or changing a dictionary could be accessed from any page making it much more user friendly and a success growl is fine, but again, you should be prepared to handle the case where a user tries to create a dictionary that already exists (which could be a simple error message instead of a success growl).

When you talk about helper classes being called from anywhere, it sounds like you are referring to view helpers and you should never have controller logic in a view helper.  You could make a controller helper, but those are intended to be called by a controller action, not from within a view.   I'm not sure exactly where you were headed with that, but, again, if you use ajax, you would then submit directly to the dict resource create action.




Ronald Fischer

unread,
Jun 5, 2014, 9:37:21 AM6/5/14
to rubyonra...@googlegroups.com
Jesse Knutsen wrote in post #1148836:
> On 6/4/14, 1:19 PM, Ronald Fischer wrote:
>> However, the form has two buttons, and one is (from the viewpoint of
>> logic) doing a GET and the other one should do a POST. Of course I can't
>> have both. Maybe it is the form which needs to be modified?
> A form can do a get or a post, but not both at the same time. You would
> need to change the form action though JS. also you would have to have
> your routes set up accordingly.

I didn't think of the possibility to use JS in this case. This would be
certainly a possibility, but I wonder: Would you still call it good
design? Or should I design the user interface in a different way, for
instance having two forms (one for the GET and one for the POST case)?
The latter might be cleaner from the programmer's viewpoint, but perhaps
confusing to the user.

This is a problem which I still will have to solve, when abandoning the
current Login design and use the "login" functional object you
suggested, because even then, I would have a single form which,
depending on the button being pressed, might to a creation of a Dict
object, or a retrieval...

Ronald Fischer

unread,
Jun 5, 2014, 9:49:53 AM6/5/14
to rubyonra...@googlegroups.com
mike2r wrote in post #1148859:
> I do think that ajax is a good recommendation. Selecting, creating, or
> changing a dictionary could be accessed from any page making it much
> more
> user friendly and a success growl is fine, but again, you should be
> prepared to handle the case where a user tries to create a dictionary
> that
> already exists (which could be a simple error message instead of a
> success
> growl).

Thank you for your elaborate answer. After having read all the
recommendations, I think I will proceed in the following way:

- I will clearly separate "login" from "Dict maintenance", since the
possible benefits I hoped from my original design, are small compared to
the trouble it will cause in other places.

- Since I have no experience with Ajax, and are even a newbie when it
comes to Rails, I will first make the application as simple and
straightforward as possible, not using the benefits of Ajax, even if it
means that the page might be less user friendly, and after I mastered
Rails fully, I will learn Ajax and do a redesign.

Walter Lee Davis

unread,
Jun 5, 2014, 2:20:46 PM6/5/14
to rubyonra...@googlegroups.com

On Jun 5, 2014, at 5:49 AM, Ronald Fischer wrote:

> - Since I have no experience with Ajax, and are even a newbie when it
> comes to Rails, I will first make the application as simple and
> straightforward as possible, not using the benefits of Ajax, even if it
> means that the page might be less user friendly, and after I mastered
> Rails fully, I will learn Ajax and do a redesign.


This is an excellent idea, and if you read the Agile Web Design with RoR book, is exactly the approach they recommend. It has two immediate benefits:

1. The site still works in the presence of a broken browser or a screen reader.

2. The logic you build is well thought out, and doesn't go "off the rails". The Rails UJS helpers make it easy to "Ajaxify" a form or flow that already works without Ajax.

Walter

Salvatore Pelligra

unread,
Jun 9, 2014, 3:07:34 AM6/9/14
to rubyonra...@googlegroups.com
IMHO, strictly binding data with controller is *not* a good mindset,
even if in this case separate login and dict creation makes more sense,
especially for the "open a dict" feature.

However, just for fun and experimentation, one possible solution to keep
the old UI design could be this:
- Using reform (https://github.com/apotonick/reform), create 2 form: one
for Login and one for Dict, so we can separate the business logic and
validations.
- Create a workflow object to isolate Dict open and creation
- In login controller, check data validation and perform the workflow
accordingly.
- In dict controller, just use the workflow object

I've done a code example here: http://codeshare.io/tkL5n (but again,
take this only as an exercise)
I've not personally checked if it has errors, but it logically works.

Try to not think too much in term of Model<->Controller and remember
that ruby is an OO language, where even a simple number is an instance!

Ronald Fischer

unread,
Jun 9, 2014, 10:18:31 AM6/9/14
to rubyonra...@googlegroups.com
Thank you so much to go through all the trouble and even lay out an
example for the code.

While I have meanwhile already redesigned the login logic, reading the
short tutorial of Reform on github, and also your example, let's me
think that I could use Reform well for another piece of my application,
which is kind of a "control center" and is connected to several objects
in my model. I think I'll give it a try at that point!

Salvatore Pelligra

unread,
Jun 9, 2014, 1:06:08 PM6/9/14
to rubyonra...@googlegroups.com
Reform is a really good gem and so are Cell
(https://github.com/apotonick/cells). Give them a try and happy coding!
:)
Reply all
Reply to author
Forward
0 new messages