ruby on rails should i query to db from controller - ruby-on-rails

I am just getting started with Ruby on Rails and i am not sure how i should do the queries to database in order to get data, add data or edit data...
Is it better to find for example a user from the controller or its better to add the queries into the model?
currently, my user homepage controller looks like this with some simple functionality
class HomeController < ApplicationController
#get current_user variable
helper_method :current_user
def index
if user_signed_in?
#user = User.find_by_id(current_user.id)
else
render_404
end
end
end
it simply checks if the user is logged in and finds the user...
Should i move the db calls to a model for best practice or using the above method is also fine?

The way you did it is fine in my opinion. The controller's job is to set the data for the view and part of setting the data is fetching it from the DB.
For more complex DB query you could use scope which are located in the model. This way the controller does not hold too much logic of DB queries on the model.

You usually want to use a repository pattern which rails gives us by default with active record. I'd say that's perfectly fine. If you had more complex business logic or were pulling from multiple tables, then I'd probably move it into it's own class.

Related

How to validate presense of a value based on an API call

I've got a model #record_request that takes in a unique identifier and saves it to the database - the model can be later used to fire a bunch of API requests off to an external database to do it's work.
Thing is, I don't want the user to be able to save the value in the Rails app, if it can't be queried in the remote database.
I've tried creating validations and creating Rspec tests, but all of them require my #current_user variable with all the api_token and referesh_token and other OmniAuth goodies needed to make an autenticated API call...
So I'm kind of stumped. Where is the best practice place to put a validation rule of this kind.
I'm thinking I'll have to put something in my controller on :create that makes the call there and raises errors/flashes that way... but then it seems like I've got a load of code on my controller, and I've fallen for a fat controller anti-pattern, no?
What is the best practice?
Here is an example from a project I'm working on, this code is written in the model
after_validation :should_validate_match?, on: [:create, :update]
def should_validate_match?
if self[:type] == 'Match'
self.answers.each{|ans| self.errors.add(:answer, "match cannot be null") if ans.match.blank?}
end
self.errors.empty?
end
You could test the authentication in custom method triggered by a before_save callback in your model.
Something like:
before_save :do_this
def do_this
if name_of_attribute.blank?
return
else
# api call to test authentication
end
end
If a record request has a reference to user you shouldn't need access to the current_user helper.

Using current user in Rails in a model method

I'm currently trying to implement simple audit for users (just for destroy method). This way I know if the user has been deleted by an admin or user deleted itself. I wanted to add deleted_by_id column to my model.
I was thinking to use before_destroy, and to retrieve the user info like described in this post :
http://www.zorched.net/2007/05/29/making-session-data-available-to-models-in-ruby-on-rails/
module UserInfo
def current_user
Thread.current[:user]
end
def self.current_user=(user)
Thread.current[:user] = user
end
end
But this article is from 2007, I'm not sure will this work in multithreaded and is there something more up to date on this topic, has anyone done something like this lately to pass on the experience?
Using that technique would certainly work, but will violate the principle that wants the Model unaware of the controller state.
If you need to know who is responsible for a deletion, the correct approach is to pass such information as parameter.
Instead of using callbacks and threads (both represents unnecessary complexity in this case) simply define a new method in your model
class User
def delete_user(actor)
self.deleted_by_id = actor.id
# do what you need to do with the record
# such as .destroy or whatever
end
end
Then in your controller simply call
#user.delete_user(current_user)
This approach:
respects the MVC pattern
can be easily tested in isolation with minimal dependencies (it's a model method)
expose a custom API instead of coupling your app to ActiveRecord API
You can use paranoia gem to make soft deletes. And then I suggest destroying users through some kind of service. Check, really basic example below:
class UserDestroyService
def initialize(user, destroyer)
#user = user
#destroyer = destroyer
end
def perform
#user.deleted_by_id = #destroyer.id
#user.destroy
end
end
UserDestroyService.new(user, current_user).perform

ActiveRecord callback access to session data

I am trying to add current user information to record using ActiveRecord callback, but I don't see way how to do that.
I tried Thread.current[:user], but in results I see that thread value is accessed from another user sessions.
I am using Passanger in production, but in the same time I am using acts_as_audited who get user value correctly.
Whats the best/safest way how to do that?
The current user is not accessible from within an ActiveRecord model. This is due to the separation of concerns in Rails--the current user and the session are concepts that pertain to the realm of the controller.
You need to get the relevant data and pass it into the model in order for your callback to work. One way to do this is with an accessor method:
# model
attr_accessor :current_user
def my_callback
# do something with current_user
self.some_attribute = current_user
end
# controller
#model = MyModel.find(params[:id])
#model.current_user = current_user # assuming you have a controller method that does this
#model.save!
You should rename the current_user accessor to be meaningful: eg. if you are tracking the author of a blog post, call it author or responsible_user or some such, since current_user means nothing once the model is saved.

Rails scope model data by user_id

I have a Rails application where a user longs in and I have the user_id in the session. The next step is to create a scope for all model data shown to the user where data.user_id = session[:user_id].
I know I can do the following in each of my controllers
Controller.find_all_by_user_id(session[:user_id])
Yet to me it seems there is probably a better solution. I found the possibility to add a scope to the model, yet the session is not known here and MVC pattern wise it is probably not a good idea to have it there. Is there a solution to apply such a user_id restriction to all data coming from the models or should I just use the find_all_by_user_id for every controller function that has userdata in it?
If I understand you correctly you want to access some data by user_id. Which means that you can define relationship in the user model as has_many :this_and_that or something like that. It it is right, then you can create a before_filter or even better a function in your application controller in which you get your current user instance. Trough this instance, you can access all available data to that user. You can even make that function a helper function, and you can use that in a view.
#User.erb
has_many :other_data
#ApplicationConroller.erb
def current_user
#current_user ||= User.find_by_id(session[:user_id])
end
#OtherControllers
def index
#other_datas = current_user.other_datas
end
You can have before_filter :load_user_data, :if => current_user or something like that.
Method will look like that:
def load_user_data
#data = ModelName.where(user_id: session[:user_id])
end
#data will be ActiveRecord::Relation and will be chainable. Also using find_all_by_user_id in each controller not that bad, if you really need it.

Rails3 - Permission Model Before_Save Check?

I have a permission model in my app, that ties (Users, Roles, Projects) together.
What I'm looking to learn how to do is prevent a user for removing himself for their project...
Can you give me feedback on the following?
class Permission < ActiveRecord::Base
.
.
.
#admin_lock makes sure the user who created the project, is always the admin
before_save :admin_lock
def before_save
#Get the Project Object
project = Find(self.project_id)
if project.creator_id == current_user.id
# SOME HOW ABORT OR SEND BACK Not Allowed?
else
#continue, do nothing
end
end
end
Is that look like the right approach?
Also, I'm not sure how to do the following two things above:
How to abort prevent the save, and send back an error msg?
Get the devise, current_user.id in the model, that doesn't seem possible, so how do Rails gurus do stuff like the above?
Thanks for reading through
How to abort prevent the save, and send back an error msg?
return false during the callback chain tells activemodel to stop (similar to how adding errors to the model during a validation tells it to stop at that point)
self.errors.add_to_base "msg" will add an error to the model, which can then be rendered on the view.
Get the devise, current_user.id in the model, that doesn't seem possible, so how do Rails gurus do stuff like the above?
Models shouldn't really know about things like the current request, if at all possible, you should be locking things down at the controller/action level.
EDIT:
So, the role of controllers is to deal with everything involved in getting the correct information together based on the request, and passing it to the view (which becomes the response). People often say "make your models fat and your controllers skinny", but that could be said of any system that embraces object oriented design -- your logic should be in objects when possible.
That being said, the whole point of controllers is to deal with routing the right things to the right places, and authentication is definitely a concern of routing.
You could easily move the line comparing creator_id to user id in the action, and react based on that.
Now, sometimes you genuinely need that stuff in the model and there is no way around it. That becomes a problem, because you need to fight rails to get it there. One way would be to attr_accessor a current_user field on your model, and pass that in on initialize. Another would be to remove the fields from the params hash that a user is not allowed to change in the action. Neither is really that nice though.
Agreed with Matt that you should try to use the controller for the redirect. The model should have the logic to determine if the redirect is appropriate. Maybe something like
class ProjectsController < ApplicationController
def update
redirect_to(projects_url, :alert => "You can't remove yourself from this project.") and return if Role.unauthorized_action?(:update, params[:project])
#project = Project.find(params[:id])
if #project.update_attributes(params[:project])
...
end
class Role
def self.unauthorized_action?(action, params)
# your logic here
end
You should check out CanCan for some ideas.
In permission model take one field project_creater as boolean
In project modelbefore_create :set_project_ownership
def set_project_ownership
self.permissions.build(user_id: User.current.id, project_creater: true)
end
In project controllerbefore_filter :set_current_user
In Application controllerdef set_current_user
User.current = current_user
end

Resources