Rails multiple create/update forms - ruby-on-rails

Due to business logic, I want certain resources in my Rails app to be able to be created and updated from various forms. So for example, I have a resource Business, and depending on whether the user has just signed up or has been on the site for a while, a Business can be created from /account_setup (first time user) or /businesses/new (existing users).
I want to have both forms post to the same :create method and I want the method to be smart enough to figure out which page did the posting so it can render that page if it fails a validation. Just for clarity:
GET /account_setup => POST /businesses => businesses#create => {fail} => render 'account_setup'
GET /businesses_new => POST /businesses => businesses#create => {fail} => render 'new'
Since both are looking to create a Business, it's pointless to have two separate controller methods, and I'm assuming there's a Rails-y way to solve this. What's a good way to detect which of my two forms did the post?

Use request.original_fullpath in businesses#create or embed values specific to the pages(controllers) that show the forms using hidden_field.

You can put hidden fields in the form which will store the type of the form. Keep in mind, if it's a new user then you will have to create the user before creating business assuming the models are associated.

Related

code reusability issue in active admin rails

How to avoid common code used in active admin action item and controller action.
I have seen people used to write the same code in the controller and active admin action item.
Is there any way to avoid it?
Example is like i want to cancel a user and it requires 3-4 steps to do it.
So i have writtent this code in users controller.
Now i have resource user in active admin and i want to delete the user from active admin. I have created an action item and again written the same code of deleting the user in the member action.
Is there any way to avoid above.
If you want to share code between different controllers, you should look to the rails concerns https://api.rubyonrails.org/v5.2.2/classes/ActiveSupport/Concern.html
Assuming this is in the User register block (probably at app/admin/users.rb) this might help you. The action_item only contains a link (actually a form that posts) to the actual member_action. This is just example code:
action_item :cancel_user, :only => :edit do
link_to 'Cancel user', do_cancel_user_admin_user_path(resource), method => :post
end
member_action :do_cancel_user, :method => :post do
flash.notice = "User will be canceled"
resource.cancel # I guess this would the 4 lines of code that you are repeating
redirect_to edit_admin_user_path(resource) and return
end
Let me know if this did/did not confuse you. Good luck!
P.S. A few weeks ago someone asked something similar, this might also be of help: How to reset userĀ“s password by Devise authentication token directly from edit_page in Active Admin?

how to add a `preview` action to resources?

I am looking for the right approach to include a preview action between new and create actions.
Let's assume I have the following:
resources :users
By default, when the form is submitted:
if new, call create action;
if edit, call update action.
In this way, I can use the same form (partial) for new and create, which is great!
How can I configure the resources to include a preview between actions. I mean, forcing new to call preview and then preview to call create.
I could add a new route/action and point the form for that action, however the same form cannot be used for new and edit.
There is a way to configure the resources to do that?
Have a look at this railscast: multibutton form, it shows a form with both a 'preview' and 'submit' button, maybe that's something you might want to do.
By the way, couldn't you use the same form by passing locals to the partial? For example:
<%= form_for #profile, url: dynamic_path do |f| %>
...
<% end %>
<%= render 'form', dynamic_path: profile_preview_path %>
you have several ways to do this:
you change the url of the form to your preview action (for which you have to add a route).
you use your create action for preview and create:
i.e. you add a parameter (like ':go_to_preview') to the form submit request. if you find it in the controller you render preview.
when the user wants to confirm the preview, you submit the data again (without that parameter) and this time create the record.
there are also 2 more dynamic possibilities:
you create the preview in real-time - if that is possible (like here on SO) - and use just the create action,
a variation of the first option: when the user submits the form, you send an ajax post request to a preview action, render a partial and include it on the page, then while your user still has the form he just filled, the user decides if she wants to modify or submit definitely.
I would suggest adding a DateTime column "finished_at", "published_on", etc... whatever is appropriate for your domain.
Using blog posts as an example:
scope :published, where("published_on IS NOT NULL")
scope :draft, where(:published_on => nil)
Use the scopes and new field where appropriate to limit the follow up actions.
This approach gives you more than you asked for :
a way to limit processing based on "state"
Data on creation times versus publishing times

Rails accepting terms before creation

Let's say before a user is allowed to view the creation page of a Model, they are required to accept some terms. The workflow would be like:
User clicks on "create MODEL" -> Brought to a terms page, must accept before moving on -> MODEL creation page
If a user copies the url to the creation page, they should be redirected to the terms page.
What's the best way of going about this? I was thinking of using the session variable somehow...but I can't think of a clever enough idea. Any suggestions?
You can have the following routes:
get 'terms' => 'MODEL#terms'
get 'new' => 'MODEL#terms'
post 'new' => 'MODEL#new'
The "create MODEL" should send a GET request for /terms. Accepting the terms should POST to /new. If the user pasted /new in the URL, he'll be directed to terms instead.
Alternatively (or additionally), you can have the /terms POST a variable, :terms_accepted => true to the MODEL creation page, and on that page, check if :terms_accepted == true. If not, redirect to the terms page.
You can add the acceptance of the terms to the model itself, then you don't need a construction with an extra page and redirect if someone enters in the wrong place. Instead the user can only submit the form for creation when he/she accepts the terms.
You can add the following to the model:
class Model < ActiveRecord::Base
validates :terms_of_service, :acceptance => true
end
And then make sure you have the checkbox for this in the new %{model} form.
Also see: http://guides.rubyonrails.org/active_record_validations_callbacks.html#acceptance and Accept terms of use rails

One model and Many edit views

I have a model I named User, and I want use two different Views to edit it: the usual edit view and another view I called edit_profile.
I had no problem in creating routing, controller and views: I added edit_profile and update_profile views, and I added on routes.rb the line:
map.resources :users ,:member => {:edit_profile => :get, :update_profile => :put}
The problem is: when I submit the form in edit_profile and some error occur in some input fields, rails reload the edit_path page instead of edit_profile_path page !
This is the form on edit_profile.html.erb
form_for(:user, #user, :url => {:action => :update_profile}, :html => { :method => :put} ) do |f|
f.text_field :description
f.text_area :description
f.error_message_on :description
....
....
f.submit 'Update profile'
After clicking Update profile, if input errors occur I want to show edit_profile view instead of edit view
Where is the problem ?
Do You have some ideas ?
many thanks
Adding extra actions to a RESTful controller is often a code smell, an indication that there's a better way to model what you're trying to do. In this case, profile is really a sub-resource of user:
map.resources :users, :has_one => :profile
making your profile routes like
GET /users/1/profile # show
GET /users/1/profile/edit #edit
PUT /users/1/profile # update
DELETE /users/1/profile #destroy
You will have a separate ProfilesController for these actions... much cleaner.
How you model the data is up to you, (you don't have to have a one-to-one correlation between your models and your controllers!), but in this case I'd probably use ActiveRecord's aggregations to model the relationship between User and Profile. Think of it as an embedded has_one: http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html
Note that adding additional actions to RESTful controllers isn't always "wrong" ... its up to you to determine when its appropriate to split off the new actions into a separate resource. In this case, however, I think it's very clear-cut!
Your controller's action (the edit action, I assume) will need to know whether it has been reached via the normal edit page or the edit_profile page. You can use a hidden field named, perhaps, profile to post a breadcrumb that will tell it that. By doing this, you can redirect conditionally based on the existence of a profile param.
A cleaner way is to create a new action called edit_profile and extract the editing code to a common method that is called from both edit and edit_profile let the public methods handle any redirects.
Take a look in your user_controller file's update method. That's where submitting the edit form takes you. You'll see there that if the record can't be updated, it redirects back to the edit method.
One way to do what you want is to make your edit_profile form point to a new method, perhaps called update_profile, which is the same as the edit method but redirects to edit_profile when the record can't be saved.
Perhaps a better and DRY-er way to do it would be to pass a parameter from the edit_profile form that you can detect in your existing update method to differentiate between update attempts coming from edit / edit_profile.
Good luck!

Showing error messages in a redirected request

I am using Authlogic to do some simple signup and login stuff. In my WelcomeController I want to have the signup and login forms on the same page 'index' with form actions set to their individual controllers, UsersController and UserSessionsController, respectively. These controllers redirect the user to the protected profile page within the site on successful signup/login. On error I need to redirect back to the WelcomeController#index and display their errors. Upon doing a redirect this information is lost and I cannot use a render because it is a different controller. What is the correct way to handle this behavior?
I could perhaps store the error messages in the Flash Hash. This seems like the easiest solution.
Lately I have stumbled across this problem in another application I was writing where I needed to render summary pages from researcher submitted RFP forms from a PeerReviewerController. Seemed like in that case the use of now deprecated components would have been the right way to handle this. ie: render_component :controller => 'RFPForms', :action => 'summary', :id => 213
Components seem like the DRY way to do something like this. Now that we don't have them what is the correct solution?
One simple and easy way to do this is to pass a parameter on the redirect:
redirect_to welcome_url(:login_error=>true)
In your view or controller you can then test for that param:
<% if params[:error] -%>
<div class="error">My Error Message</div>
<% end -%>
You could do a render:
render :template => "welcome/index"
But you'd have to make sure that any variables that page expects are loaded, or it won't render.
Drew's answer does not cater for more complex error data (e.g. an array of validation errors), and Marten's answer would risk violating the DRY rule by needing to duplicate code from WelcomeController#index.
A better-looking answer (which is an elaboration on the original poster's idea of storing error data in the flash) is available in Rails validation over redirect although unfortunately I am still personally struggling to get it working ...

Resources