Rails: database not being updated after save is called - ruby-on-rails

I trying to create an association between two objects and save the changes to the database.
I have included in the notice a call on the object, to test if it saves after it passes true to the if stament. When I check to see if the update has actually occurred in the data base nothing has changed.
requests_controller.rb
class RequestsController < ApplicationController
before_filter :load_requestable
def accept
#request = Request.find(params[:id])
#request.profile.send("#{#belongs_to}=",#requestable)
if #request.save
redirect_to [#requestable, :requests], notice: "Request Accepted #{#request.profile.send("#{#belongs_to}").name}"
else
render :new
end
end
private
def load_requestable
klass = [Company, Profile].detect { |c| params["#{c.name.underscore}_id"]}
#requestable = klass.find(params["#{klass.name.underscore}_id"])
#belongs_to = klass.to_s.downcase
end
end

Try saving profile directly (since you're modifying it, not the request object)
if #request.profile.save
# redirect

Related

Rails Relationship (has_many/belongs_to) Completed

So it's been quite a bit of time since I played with relationships and I want to make sure I've done it right.
In my model for Client I have:
class Client < ApplicationRecord
has_many :projects, dependent: :destroy
end
In my model for Projects I have:
class Project < ApplicationRecord
belongs_to :client
end
So I know that's set. Then to grab projects I put in my projects controller:
def create
#client = Client.find(params[:client_id])
#project = #client.project.new(project_params)
flash[:notice] = "Project created successfully" if #client.project << #project
respond with #project, location: admin_project_path
end
Would I need to put something in my show that does the same?
Anything else I'm missing for relationships?
I would think this:
def create
#client = Client.find(params[:client_id])
#project = #client.project.new(project_params)
flash[:notice] = "Project created successfully" if #client.project << #project
respond with #project, location: admin_project_path
end
Would look more like:
def create
#client = Client.find(params[:client_id])
#project = #client.projects.new(project_params)
if #project.save
# do success stuff
else
# do failure stuff
end
end
Note that
#project = #client.project.new(project_params)
should be:
#project = #client.projects.new(project_params)
As Yechiel K says, no need to do:
#client.project << #project
Since:
#project = #client.projects.new(project_params)
will automatically set client_id on the new #project. BTW, if you want to add a project to the client manually, then it's:
#client.projects << #project
(Note projects vs. project.)
In the off chance that there is not a client with params[:client_id], then #client = Client.find(params[:client_id]) will throw an error. You should probably include a rescue block. Alternatively, I prefer:
def create
if #client = Client.find_by(id: params[:client_id])
#project = #client.projects.new(project_params)
if #project.save
# do success stuff
else
# do failure stuff
end
else
# do something when client not found
end
end
Also, respond with isn't a thing. respond_with is a thing. (I believe it's been moved to a separate gem, responders.) It's unclear from your code if you're needing different responses, say, for html and js. If not, then I think it would be more like:
def create
if #client = Client.find_by(id: params[:client_id])
#project = #client.projects.new(project_params)
if #project.save
flash[:notice] = "Project created successfully"
redirect_to [#client, #project]
else
# do failure stuff
end
else
# do something when client not found
end
end
This assumes that your routes look something like:
Rails.application.routes.draw do
resources :clients do
resources :projects
end
end
In which case rails will resolve [#client, #project] to the correct route/path.
As DaveMongoose mentions, you could move #client = Client.find_by(id: params[:client_id]) into a before_action. This is quite common. Here's one discussion of why not to do that. Personally, I used to use before_action like this, but don't any more. As an alternative, you could do:
class ProjectsController < ApplicationController
...
def create
if client
#project = client.projects.new(project_params)
if #project.save
flash[:notice] = "Project created successfully"
redirect_to [client, #project]
else
# do failure stuff
end
else
# do something when client not found
end
end
private
def client
#client ||= Client.find_by(id: params[:client_id])
end
end
Taking this a bit further, you could do:
class ProjectsController < ApplicationController
...
def create
if client
if new_project.save
flash[:notice] = "Project created successfully"
redirect_to [client, new_project]
else
# do failure stuff
end
else
# do something when client not found
end
end
private
def client
#client ||= Client.find_by(id: params[:client_id])
end
def new_project
#new_project ||= client.projects.new(project_params)
end
end
I would replace this line:
flash[:notice] = "Project created successfully" if #client.project << #project
with:
flash[:notice] = "Project created successfully" if #project.save
No need to manually add #project to #client.projects, it gets added automatically when you create it using #client.projects.new, the only thing you missed was that creating something using .new doesn't persist it in the DB, that gets accomplished by calling #project.save.
For your show action, I'm not sure if you mean the client's show page or the project's, but in either case, you would retrieve it using params[:id] (unless you were using some nested routing).

Active Record: Adding "visit" counter on the model

I currently have a Subscriber model that takes in a "phone_number" and a "visit" attribute that is an integer. I want to set up a "check in" view form that will have a subscriber type in their phone_number and it will say if phone_number exists? add 1 to the visit attribute. So it will run a sql query and see if that number is in the database.
To be more clear I have to break the REST actions because the create action is already taken for the new subscribers. I'm pretty new to rails and I'm having a super hard time figuring this feature out. I'm curious if this is possible and how I should go about implementing this?
Here is my controller at the moment:
class SubscribersController < ApplicationController
def index
#subscriber = Subscriber.all
end
def new
#subscriber = Subscriber.new
end
def create
#subscriber = Subscriber.create(subscriber_params)
if #subscriber.save
flash[:success] = "Subscriber Has Been successfully Created"
redirect_to new_subscriber_path(:subscriber)
else
render "new"
end
end
def visit
end
private
def subscriber_params
params.require(:subscriber).permit(:first_name, :last_name, :email, :phone_number)
end
end
Something along those lines?
def visit
subscriber = Subscriber.find_by_phone_number(params[:phone_number])
if subscriber
subscriber.visit += 1
subscriber.save
end
end
Make sure that the default value (via DB/Migration) for visit is set to 0.
You don't need to break REST style controller though. You can create another controller which does that check. For example something like:
class Subscriber::VisitController < ApplicationController
def create
# code here
end
end

How can I restrict someone to access the data he hasn't created? (ruby)

I'm using Ruby on rails with devise.
I generated a scaffold.
tasks_controller.rb:
def index
#tasks= current_user.tasks
end
By using this, I'm able to show the people only what they have created,
but other users can see the data of tasks that they have not entered, like:
GET /tasks/1
GET /tasks/2
Although the tasks with id 1 is not created by the current_user, the user can access that.
Yes you can restrict this using filters e.g.
class TasksController < ApplicationController
before_filter :user_can_view_task, only: :show
def show
#you do not need to set #task here as it will be set by the filter method
end
private
def user_can_view_task
#task = Task.find(params[:id])
unless #task.user_id == current_user.id
flash[:notice] = "You may only view Tasks you have created."
redirect_to(tasks_path)
end
end
end
Every time a User hits the route for the show view it will execute this method prior to rendering the view. (Thus "before")
This method will look up the task and determine if the current_user created the task (assuming associations between User and Task). If the user is not the task creator it will redirect them back to the index view and inform them that they can only view tasks they have created rather than allowing them to access the show.
There is Pundit gem(https://github.com/elabs/pundit) for these cases. Your code will look:
class TaskPolicy
attr_reader :user, :task
def initialize(user, task)
#user = user
#task = task
end
def show?
user.tasks.include? task
end
end
And your controller's action:
def show
#task = Task.find(params[:id])
authorize #task
...
end
This action will raise Pundit::NotAuthorizedError if current user hasn't certaint task
Try:
def show
#task = current_user.tasks.find(params[:id])
# or with error handling
# begin
# #task = current_user.tasks.find(params[:id])
# rescue
# redirect_to [:tasks], flash: { error: 'not authorized' }
# return
# end
end

In a Ruby Rails App, I am getting "project/app/controllers/workouts_controller.rb:43: syntax error, unexpected keyword_end, expecting $end

So I've looked all through Stack Overflow trying to find a reason why I would be hitting a parsing error. This is a relatively new Rails app, but I don't think I've missed any configuration as I've been able to deploy on Heroku successfully.
From previous Stackoverflow posts it would seem that I have an extra end since Rails is complaining that it is finding an end when it is expecting the end of the file. I tried taking out the last end (which is the closer for the controller class itself) as well as commenting various parts of the code in and out and it just simply isn't working. Does anyone have any ideas/advice on how to circumvent/solve this issue?
Here is my controller file
Class WorkoutsController < ApplicationController
def show
id = params[:id] # retrieve workout ID from URI route
#workout = Workout.find(id) # look up workout by unique ID
# will render app/views/workouts/show.<extension> by default
end
def index
#all_workouts = Workout.all_workouts
#workouts = Workout.all
end
def new
# default: render 'new' template
end
def create
#workout = Workout.create!(params[:workout])
flash[:notice] = "#{#workout.title} was successfully created."
redirect_to workouts_path
end
def edit
#workout = Workout.find params[:id]
end
def update
#workout = Workout.find params[:id]
#workout.update_attributes!(params[:workout])
flash[:notice] = "#{#workout.title} was successfully updated."
redirect_to workouts_path(#workout)
end
def destroy
#workout = Workout.find(params[:id])
#workout.destroy
flash[:notice] = "Workout '#{#workout.title}' destroyed."
redirect_to workouts_path
end
end
This probably isn't needed, but here is my model file:
Class Workout < ActiveRecord::Base
attr_accessible :title, :time, :creator, :exercise_list
def self.all_exercises
%w(push-ups pull-ups crunches rest)
end
end
Replace
Class WorkoutsController < ApplicationController
with
class WorkoutsController < ApplicationController
Same for your model. class keyword should be used in lower register.
I assume you misprinted it, thats why parser sees extra end keyword

Rails redirect based on user type

I'm learning Rails by building a shop application and I'm having a bit of trouble with redirects. I have 3 roles in the application:
Buyer
Seller
Administrator
Depending on which type they are logged in as then I would like to redirect to a different page/action but still show the same URL for each (http://.../my-account).
I don't like having to render partials in the same view, it just seems messy, is there another way to achieve this?
The only way I can think of is to have multiple actions (e.g. buyer, seller, administrator) in the accounts controller but that means the paths will look like http://.../my-account/buyer or http://.../my-account/seller etc.
Many thanks,
Roger
I've put my code below:
models/user.rb
class User < ActiveRecord::Base
def buyer?
return type == 'buyer'
end
def seller?
return type == 'seller'
end
def administrator?
return type == 'administrator'
end
...
end
controllers/accounts_controller.rb
class AccountsController < ApplicationController
def show
end
end
controllers/user_sessions_controller.rb
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
if session[:return_to].nil?
# I'm not sure how to handle this part if I want the URL to be the same for each.
redirect_to(account_path)
else
redirect_to(session[:return_to])
end
else
#user_session.errors.clear # Give as little feedback as possible to improve security.
flash[:notice] = 'We didn\'t recognise the email address or password you entered, please try again.'
render(:action => :new)
end
end
def destroy
current_user_session.destroy
current_basket.destroy
redirect_to(root_url, :notice => 'Sign out successful!')
end
end
config/routes.rb
match 'my-account' => 'accounts#show'
Many thanks,
Roger
In UserSessionsController#create (i.e.: the login method) you could continue to redirect to the account path (assuming that goes to AccountsController#show) and then render different views according to the role. I.e.: something like this:
class AccountsController < ApplicationController
def show
if current_user.buyer?
render 'accounts/buyer'
elsif current_user.seller?
render 'accounts/seller'
elsif current_user.administrator?
render 'accounts/administrator
end
end
end
Better yet, you could do this by convention...
class AccountsController < ApplicationController
def show
render "accounts/#{current_user.type}"
end
end
If I understand you question correctly, then the solution is simple.
You can just call the method you want inside your controller. I do this in my project:
def create
create_or_update
end
def update
create_or_update
end
def create_or_update
...
end
In your case it should be:
def action
if administrator? then
admin_action
elsif buyer? then
buyer_action
elseif seller? then
seller_action
else
some_error_action
end
end
You should probably explicitly call "render" with an action name in each of those actions, though.

Resources