Forum: Ruby on Rails Design question: Redirection to a "create" action.

0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-04 15:22
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?
8d85d54dfa0b9c3d3a8f2774d7610713?d=identicon&s=25 Jesse Knutsen (Guest)
on 2014-06-04 15:36
(Received via mailing list)
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/decoup...

You will need to adapt a bit to your needs, but the basics are sound and
should apply nicely.
A47e0a6beeb9d048ff054fc1c3a97418?d=identicon&s=25 Walter Davis (walterdavis)
on 2014-06-04 15:41
(Received via mailing list)
On Jun 4, 2014, at 9:22 AM, Ronald Fischer wrote:

>   match '/login', to: 'home#login', via: 'get'
>
> My HomeController.login method checks, whether the user is authorized,
> I was thinking of the workaround to use
> Any suggestions on how to proceed?
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
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-04 16:08
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.
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-04 16:17
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 :-(
8d85d54dfa0b9c3d3a8f2774d7610713?d=identicon&s=25 Jesse Knutsen (Guest)
on 2014-06-04 16:27
(Received via mailing list)
On 6/4/14, 10:08 AM, Ronald Fischer wrote:
>  From the model, user and login data are already separated from the Dicts
> 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.
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.
4c6bde00168d595053c09aac7e487f8e?d=identicon&s=25 Colin Law (Guest)
on 2014-06-04 16:33
(Received via mailing list)
On 4 June 2014 15:25, Jesse Knutsen <dracorna@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
8d85d54dfa0b9c3d3a8f2774d7610713?d=identicon&s=25 Jesse Knutsen (Guest)
on 2014-06-04 17:00
(Received via mailing list)
On 6/4/14, 10:30 AM, Colin Law wrote:
>
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?vi...
4c6bde00168d595053c09aac7e487f8e?d=identicon&s=25 Colin Law (Guest)
on 2014-06-04 17:51
(Received via mailing list)
On 4 June 2014 15:59, Jesse Knutsen <dracorna@gmail.com> wrote:
> 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
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-04 19:19
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?vi...

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?).
8d85d54dfa0b9c3d3a8f2774d7610713?d=identicon&s=25 Jesse Knutsen (Guest)
on 2014-06-04 19:34
(Received via mailing list)
On 6/4/14, 1:19 PM, Ronald Fischer wrote:
>>               create logic
>
> 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?vi...
> 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.
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-04 19:38
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.

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.
8d85d54dfa0b9c3d3a8f2774d7610713?d=identicon&s=25 Jesse Knutsen (Guest)
on 2014-06-04 19:47
(Received via mailing list)
On 6/4/14, 1:38 PM, Ronald Fischer wrote:
>> 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
F50d3b02eee623a2172b58c09fe31c2c?d=identicon&s=25 mike2r (Guest)
on 2014-06-04 22:48
(Received via mailing list)
On Wednesday, June 4, 2014 1:47:05 PM UTC-4, jess wrote:
> >> This method would conglomerate the two into 1: A login.
> > I thought that when my login logic gets more sophisticated (with
> > trouble.
> > 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
>

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.
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-05 11:36
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...
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-05 11:49
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.
A47e0a6beeb9d048ff054fc1c3a97418?d=identicon&s=25 Walter Davis (walterdavis)
on 2014-06-05 16:21
(Received via mailing list)
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
Bb007ea9916e45856033ffe97f8843ac?d=identicon&s=25 Salvatore Pelligra (iazel)
on 2014-06-09 05:06
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!
0fa73332c8e4a3b06ea439fd3f034322?d=identicon&s=25 Ronald Fischer (rovf)
on 2014-06-09 12:17
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!
Bb007ea9916e45856033ffe97f8843ac?d=identicon&s=25 Salvatore Pelligra (iazel)
on 2014-06-09 15:05
Reform is a really good gem and so are Cell
(https://github.com/apotonick/cells). Give them a try and happy coding!
:)
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.