0

So I have OrdersController#Create, that I would like the user to be redirected to right after they register (so it can do some post-registration stuff).

Before I implemented the registration part of this workflow, this is what the link_to for that resource looked like:

<%= link_to 'Submit to Scheduling', orders_path(cart_id: @cart), method: :post, data: { confirm: "Are you sure?" }, class: "primary button btn" %>

So basically, I would like to achieve the above functionality (including passing the @cart object as a param), but automatically from within the Devise::RegistrationsController#Create.

I am using Devise, and so I have created a /users/registrations_controller.rb and in that controller I am doing this:

  def after_sign_up_path_for(resource)
    orders_path(cart_id: @cart)
    super(resource)
  end

When I did the above, it successfully created the user and redirected me to Orders#Index which is not what I want, see the logs below:

 User Create (1.3ms)  INSERT INTO "users" ("email", "encrypted_password", "first_name", "last_name", "created_at", "updated_at", "company_name", "company_title", "phone") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"  [["email", "abc3@test.com"], ["encrypted_password", "$2a$11$ZC2X2vCXd5JwVO"], ["first_name", "Test"], ["last_name", "User 3"], ["created_at", "2020-01-22 06:16:11.358863"], ["updated_at", "2020-01-22 06:16:11.358863"], ["company_name", "Acme Inc"], ["company_title", "CFO"], ["phone", "9876543210"]]
  ↳ app/controllers/users/registrations_controller.rb:14:in `create'
   (0.6ms)  COMMIT
  ↳ app/controllers/users/registrations_controller.rb:14:in `create'
Redirected to http://localhost:3000/orders
Completed 302 Found in 162ms (ActiveRecord: 5.3ms | Allocations: 6383)


Started GET "/orders" for ::1 at 2020-01-22 01:16:11 -0500
Processing by OrdersController#index as HTML

I even tried orders_path(cart_id: @cart, method: :post) and that didn't work.

How do I achieve what I am trying to do?

Edit 1

So I discovered url_for and it ALMOST gets me there, but doesn't quite work.

This is what I have:

url_for(controller: '/orders', action: 'create', method: :post, cart_id: @cart.id, only_path: true)

This is what happens:

↳ app/controllers/users/registrations_controller.rb:14:in `create'
  User Create (5.5ms)  INSERT INTO "users" ("email", "encrypted_password", "first_name", "last_name", "created_at", "updated_at", "company_name", "company_title", "phone") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING "id"  [["email", "abc2@test.com"], ["encrypted_password", "$2a$ezj6"], ["first_name", "Test"], ["last_name", "User 2"], ["created_at", "2020-01-22 07:26:09.589560"], ["updated_at", "2020-01-22 07:26:09.589560"], ["company_name", "Acme Inc"], ["company_title", "CEO"], ["phone", "9876543210"]]
  ↳ app/controllers/users/registrations_controller.rb:14:in `create'
   (1.1ms)  COMMIT
  ↳ app/controllers/users/registrations_controller.rb:14:in `create'
/.rvm/gems/ruby-2.7.0@myapp/gems/devise-4.7.1/app/controllers/devise_controller.rb:187: warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
.rvm/gems/ruby-2.7.0@myapp/gems/i18n-1.8.1/lib/i18n.rb:195: warning: The called method `t' is defined here
Redirected to http://localhost:3000/orders?cart_id=10&method=post
Completed 302 Found in 307ms (ActiveRecord: 13.2ms | Allocations: 8600)


Started GET "/orders?cart_id=10&method=post" for ::1 at 2020-01-22 02:26:09 -0500
Processing by OrdersController#index as HTML

So it still doesn't work. It is still sending me to OrdersController#Index.

marcamillion
  • 32,933
  • 55
  • 189
  • 380
  • What was the behavior before your change? Did it redirect you to another path or it created a new resource? – Thang Jan 22 '20 at 08:47

4 Answers4

1

You don't need super(resource).

In my understanding, you want to redirect to orders_path(cart_id: @cart) with post method. It's so weird. after_sign_up_path_for should return a some_path, which accessed by get method.

You can try another way, perform another action from another controller. I think this will helpful to you.

Thuy Nguyen
  • 342
  • 2
  • 8
  • So the `url_for` method is very close to what I want. The issue is that I need to also pass the `@cart` instance variable. How do I do that? – marcamillion Jan 22 '20 at 07:14
  • You can't pass an instance variable like `@cart`, but you can pass id of `@cart` and refind with the id. – Thuy Nguyen Jan 22 '20 at 07:17
  • How do I pass the method? I want the `create` action of the `orders_path` route to be hit, not the `index`. Even though I have specified `create` in the action, it is still going to `orders#index`. In other words, I want to send the same resource with a `method: :post` to `orders_path` and have it be sent to `Order#Create` and not `Order#Index`. Thoughts? – marcamillion Jan 22 '20 at 07:30
0
  request.params.merge!(cart_id: @cart.id)
  controller_you_want = OrdersController.new
  controller_you_want.request = request
  controller_you_want.response = response
  controller_you_want.create

Did you try this ?

Thuy Nguyen
  • 342
  • 2
  • 8
0

The problem you have is that your code is returning the original value of the method, not the new path:

def after_sign_up_path_for(resource)
  orders_path(cart_id: @cart)
  super(resource)
end

Returns the overridden method, since you are calling super(resource) as the final expression in the method, and its result gets returned by your defined method.

If it is necessary to call super at all, because the overridden method does something more than just return a string, then either return the order of the calls, or return the value of orders_path with a variable.

def after_sign_up_path_for(resource)
  res = orders_path(cart_id: @cart)
  super(resource)
  res
end

You should check the method being called by super though, as it is possible you can just remove this completely.

Without seeing the rest of your code it is impossible to say whether @cart actually has a value, or is in fact just nil. You should check that it has been set in the controller prior to the after_sign_up_path_for method being called.

Phil
  • 2,797
  • 1
  • 24
  • 30
0

You could use Devise::Controllers::StoreLocation#store_location_for which is used to redirect back after signing in.

store_location_for(current_user, new_order_path(cart_id: @cart))

This stores the location is the session. You can fetch the value with stored_location_for.

def after_sign_up_path_for(resource)
  stored_location_for(resource) || super
end

However you can't/should not redirect to the create action. Create responds to POST requests. Redirects are always GET requests. Before you go adding a GET /posts/create route remember that GET should not be used for non-idempotent actions. Meaning that a GET request should not create or alter any resources.

If you break this rule your users could have a really bad day when they hit the back button.

And no you don't want to store and repeat the post action. Have the user submit the form - save the record and then redirect. Add a flag to the record if needed to mark it as "not finshed". Redirect the user back to the edit action and then "finish the order" by performing a PUT or PATCH request (update).

max
  • 96,212
  • 14
  • 104
  • 165