I'm working on a multi-user, multi-account App where 1 account can have n users. It is very important that every user can only access info from its account. My approach is to add an account_id to every model in the DB and than add a filter in every controller to only select objects with the current account_id. I will use the authorization plugin.
Is this approach a good idea?
What is the best way to always set the account_id for every object that is created without writing
object.account = #current_account
in every CREATE action? Maybe a filter?
Also I'm not sure about the best way to implement the filter for the select options. I need something like a general condition: No matter what else appears in the SQL statement, there is always a "WHERE account_id = XY".
Thanks for your help!
This is similar to a User.has_many :emails scenario. You don't want the user to see other peoples emails by changing the ID in the URL, so you do this:
#emails = current_user.emails
In your case, you can probably do something like this:
class ApplicationController < ActionController::Base
def current_account
#current_account ||= current_user && current_user.account
end
end
# In an imagined ProjectsController
#projects = current_account.projects
#project = current_account.projects.find(params[:id])
I know, I know, if you access Session-variables or Instance variables in your Model you didn't understand the MVC pattern and "should go back to PHP". But still, this could be very useful if you have - like us - a lot of controllers and actions where you don't always want to write #current_account.object.do_something (not very DRY).
The solution I found is very easy:
Step 1:
Add your current_account to Thread.current, so for example
class ApplicationController < ActionController::Base
before_filter :get_current_account
protected
def get_current_account
# somehow get the current account, depends on your approach
Thread.current[:account] = #account
end
end
Step 2:
Add a current_account method to all your models
#/lib/ar_current_account.rb
ActiveRecord::Base.class_eval do
def self.current_account
Thread.current[:account]
end
end
Step 3: Voilá, in your Models you can do something like this:
class MyModel < ActiveRecord::Base
belongs_to :account
# Set the default values
def initialize(params = nil)
super
self.account_id ||= current_account.id
end
end
You could also work with something like the before_validation callback in active_record and then make with a validation sure the account is always set.
The same approach could be used if you always want to add the current_user to every created object.
What do you think?
To answer your second question, check out the new default_scope feature in Rails 2.3.
I understand that you don't want to bother about scoping you account all time. Lets be honest, it's a pain in the a**.
To add a bit magic and have this scoping done seamlessly give a look at the following gem
http://gemcutter.org/gems/account_scopper
Hope this helps,
--
Sebastien Grosjean - ZenCocoon
Related
I've got STI like this:
class Post
end
class Post::Confirmed < Post
end
class Post::Draft < Post
def confirm!
becomes Post::Confirmed
end
end
...# somewhere in controller
# POST /posts/1/confirm
# POST /posts/1/confirm.json
def confirm
#post = Post::Draft.first
#post = #post.confirm! # this is the only way I can reload #post with Post::Confrmed
end
Is it somehow possible to make:
#post.confirm! # I want this #post(Post::Draft) to become Post::Confirmed without reassigning
Or is it just nor RoR way?
Thanks in advance!
The pattern I've found that works best here is having a datetime type field that records when the record was flagged.
For example:
def confirm!
self.confirmed_at = DateTime.now
self.save!
end
Then you can tell when something was confirmed. This comes in especially handy for when you have a situation where something will be flagged but isn't yet, such as setting a publishing date in the future.
Although it might seem a little annoying to not have your STI bag of tricks available, STI is not always the appropriate tool. Generally STI is to differentiate between similar but different models that have a lot of commonality or are used in a common context. It's not supposed to be used to handle different states of a singular model.
What you want in that case is a state-machine type pattern.
I am using Rails 4.0.2 and Devise 3.2.2 to handle user registration / authentication.
I would like to write a custom method for Devise's current_user , this method is for checking how many times does the current_user sign in. I will be using sign_in_count
Do I write the method in the User model or should I define the method in Users Controller ?
Is it possilbe to write something like the below
def count
user = current_user
user.sign_in_count
end
and call current_user.count ?
Thanks
----edited----
What if I need to add other methods, am I able to add something like the below
#app/controllers/post_controller.rb
before_action :check_time
def check_time
time = User.last_sign_in_at(current_user)
if # something
# do bla bla
end
end
Do I write the method in the User model or should I define the method in Users Controller ?
It depends where (& when) you want to use the method
If you're going to use it as part of the "controller-level" interactivity, you'll want to put it into the UsersController, but if it's going to be used on "model-level" (by multiple controllers / models), you may wish to put it into the model
Something you need to be aware of is that current_user is a helper, and is not available at model level:
#app/controllers/products_controller.rb
def lookup
sign_ins = User.sign_in_count(current_user)
if sign_ins > 10
#do something
end
end
#app/models/user.rb
Class User < ActiveRecord::Base
def self.sign_in_count(user)
user = find(user.id)
user.sign_in_count
end
end
But as stated by #apneadiving, a far more efficient way to do this is to reference the current_user.sign_in_count attribute directly
Update
In reference to your update, you'll be best reading up about class & instance methods
You could perform the method like this:
#app/controllers/post_controller.rb
before_action :check_time
private
def check_time
time = current_user.last_sign_in_at
if # something
# do bla bla
end
end
In my references to model / controller methods - you'd use model methods if you wanted to give standard functionality on an app-wide level (such as User.weight_gain?). If you're using controller-centric data, you're best to keep it all in the controller
I have an app where there are different layers of permissions that can manage, or just simply create/view different objects. An example of my breakdown:
A host can view a reservation for a restaurant, and make an edit, but cannot create
A doorman can create a new reservation, and edit.
A customer service rep (on our side), can do pretty much everything.
A superadmin can do everything.
Is there a gem or mountable engine that I can use to take care of this? What would be the best practice?
Cancan is a good choice but lately I've been looking at Pundit as a better alternative. In your case you would have something like this:
# app/policies/reservation_policy.rb
ReservationPolicy = Struct.new(:user, :reservation) do
def create?
user.service_rep? || user.doorman?
end
end
Then in your controller:
# app/controllers/reservations_controller.rb
class ReservationsController < ApplicationController
def create
#reservation = Reservation.new(reservation_params)
authorize #reservation
#reservation.save
respond_with(#reservation)
end
end
This isn't tested and will need to be adapted to your exact situation but I hope it's a starting point.
I'm using Devise for authentication in my Rails app. I'd like to eager load some of a users associated models in some of my controllers. Something like this:
class TeamsController < ApplicationController
def show
#team = Team.includes(:members).find params[:id]
current_user.includes(:saved_listings)
# normal controller stuff
end
end
How can I achieve this?
I ran into the same issue and although everyone keeps saying there's no need to do this, I found that there is, just like you. So this works for me:
# in application_controller.rb:
def current_user
#current_user ||= super && User.includes(:saved_listings).find(#current_user.id)
end
Note that this will load the associations in all controllers. For my use case, that's exactly what I need. If you really want it only in some controllers, you'll have to tweak this some more.
This will also call User.find twice, but with query caching that shouldn't be a problem, and since it prevents a number of additional DB hits, it still is a performance gain.
Override serialize_from_session in your User model.
class User
devise :database_authenticatable
def self.serialize_from_session key, salt
record = where(id: key).eager_load(:saved_listings, roles: :accounts).first
record if record && record.authenticatable_salt == salt
end
end
This will however, eager load on all requests.
I wanted to add what I think is a better solution. As noted in comments, existing solutions may hit your DB twice with the find request. Instead, we can use ActiveRecord::Associations::Preloader to leverage Rails' work around loading associations:
def current_user
#current_user ||= super.tap do |user|
::ActiveRecord::Associations::Preloader.new.preload(user, :saved_listings)
end
end
This will re-use the existing model in memory instead of joining and querying the entire table again.
Why not do it with default_scope on the model?
like so:
Class User < ActiveRecord::Base
...
default_scope includes(:saved_listings)
...
end
In my rails app I would like to track who changes my model and update a field on the model's table to reflect.
So, for example we have:
class Foo < ActiveRecord::Base
before_create :set_creator
belongs_to :creator, :class_name => "User"
protected
def set_creator
# no access to session[:user_id] here...
end
end
What's a good testable way for me to get at the user_id from my model? Should I be wacking this data in Thread.current ?
Is it a better practice to hand this information from the controller?
Best practice in MVC is to have your Models be stateless, the controller gets to handle state. If you want the information to get to your models, you need to pass it from the controller. Using a creation hook here isn't really the right way to go, because you are trying to add stateful data, and those hooks are really for stateless behavior.
You can pass the info in from the controller:
Foo.new(params[:foo].merge {:creator_id => current_user.id})
Or you can create methods on User to handle these operations:
class User
def create_foo(params)
Foo.new(params.merge! {:creator_id => self.id})
end
end
If you find yourself writing a lot of permissions code in the controller, I'd go with option 2, since it will let you refactor that code to the model. Otherwise option 1 is cleaner.
Omar points out that it's trickier to automate, but it can still be done. Here's one way, using the create_something instance method on user:
def method_missing(method_sym, *arguments, &block)
meth = method_sym.to_s
if meth[0..6] == "create_"
obj = meth[7..-1].classify.constantize.new(*arguments)
obj.creator_id = self.id
else
super
end
end
You could also override the constructor to require user_ids on construction, or create a method inside ApplicationController that wraps new.
There's probably a more elegant way to do things, but I definitely don't like trying to read state from inside Model code, it breaks MVC encapsulation. I much prefer to pass it in explicitly, one way or another.
Yeah, something like that would work, or having a class variable on your User model
cattr_accessor :current_user
Then in your controller you could have something like:
User.current_user = current_user
inside a before filter (assuming current_user is the logged in user).
You could then extend AR:Base's create/update methods to check for the existence of a created_by/updated_by field on models and set the value to User.current_user.
I'd create new save, update, etc methods that take the user_id from everything that calls them (mainly the controller).
I'd probably extend ActiveRecord:Base into a new class that handles this for all the models that need this behaviour.
I wouldn't trust Thread.current, seems a bit hackish. I would always call a custom method which takes an argument:
def create_with_creator(creator, attributes={})
r = new(attributes)
r.creator = creator
r.save
end
As it follows the MVC pattern. The obviously inherient problem with this is that you're going to be calling create_with_creator everywhere.
You might find PaperTrail useful.
Probably you could check out usertamp plugins, found two in github
http://github.com/delynn/userstamp/tree/master
http://github.com/jnunemaker/user_stamp/tree/master