Tintin81
Tintin81

Reputation: 10207

Access current_user from within Rails models?

I am building an Invoicing Application that basically follows the following pattern:

Users < Clients < Projects < Invoices

Now in order to generate autoincrementing invoice numbers for each User I put this in my Invoice model:

  before_create :create_invoice_number

  def create_invoice_number
    val = @current_user.invoices.maximum(:number)
    self.number = val + 1
  end

However, it seems that the current_user variable cannot be accessed from within models in Rails?

What can I do to solve this problem?

Upvotes: 2

Views: 1050

Answers (2)

Sandip Ransing
Sandip Ransing

Reputation: 7733

there should not be any arise of such case still if you want then make use of observers in rails

Upvotes: 1

Veraticus
Veraticus

Reputation: 16064

This is due to separation of concerns in Rails and is a somewhat sticky issue to deal with. In the Rails paradigm, models should have no knowledge of any application state beyond what they're passed directly, so most Rails coders will tell you that any model needing to know about a current_user is code smell.

That said, there are three ways to do this, each "more correct" (or at least I would consider them so).

First, try creating an association to the user inside the invoice and link the invoice to the user in the controller:

class InvoicesController < ApplicationController

...

def create
  @invoice = current_user.invoices.create(params[:invoice])
  ...
end

And in your model:

belongs_to :user

def create_invoice_number
  self.user.invoices.maximum(:number) + 1
end

If that doesn't work, do this manually in the controller. It's true that controllers should always be as skinny as you can manage, but since this is clearly an application-level concern the controller is the place to put it:

class InvoicesController < ApplicationController

...

def create
  @invoice = Invoice.create(params[:invoice])
  @invoice.update_attribute(:number, current_user.invoices.maximum(:number))
  ...
end

Lastly, if you really, really want to bridge the controller and model, you can do so with ActionController::Sweepers. They are not intended for this purpose but will certainly get the job done for you.

Upvotes: 10

Related Questions