Ok so I have this method of an application I am working with and it works in production. My question why does this work? Is this new Ruby syntax?
def edit
load_elements(current_user) unless current_user.role?(:admin)
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
rescue ActiveRecord::RecordNotFound
respond_to_not_found(:json, :xml, :html)
end
rescues do not need to be tied to an explicit begin when they're in a method, that's just the way the syntax is defined. For examples, see #19 here and this SO question, as well as the dupe above.
rescue can work alone . no need of begin and end always .
You can use rescue in its single line form to return a value when other things on the line go awry:
h = { :age => 10 }
h[:name].downcase # ERROR
h[:name].downcase rescue "No name"
rescue word is part of method definition
But in controllers better to rescue errors with rescue_from
try this
def edit
begin
load_elements(current_user) unless current_user.role?(:admin)
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
rescue ActiveRecord::RecordNotFound
respond_to_not_found(:json, :xml, :html)
end
end
Related
How do I go about making this code block dry?
I understand that dry means dont repeat yourself but I don't see any obvious opportunity for refactoring.
Index, show, edit, and create seem like basic/necessary methods. They appear to be pretty dry to me.
The methods after I am not sure about.
I haven't tried anything other than googling so far.
class UsersController < ApplicationController
def index
#users = User.all
end
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Slack.notify_channel
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
Slack.notify_channel
end
private
def user_params
params.require(:user).permit(:username, :email)
end
end
There is no rails backend attached to this code snippet. I am assuming it is just theoretical - wanting us to refactor the code to make it shorter.
IMHO you can do something like that.
class UsersController < ApplicationController
include ExceptionHandling
before_action :load_user, only: [:show, :edit, :update, :destroy]
after_action :slack_notify_channel, only: [:create, :update, :destroy]
def index
#users = User.all
end
def new
#user = User.new
end
def create
#user = User.create!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
end
end
def update
#user.update!(user_params)
respond_to do |format|
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
end
end
def destroy
#user.destroy!
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def load_user
#user = User.find(params[:id])
end
def slack_notify_channel
Slack.notify_channel
end
def user_params
params.require(:user).permit(:username, :email)
end
end
I would recommend you to create a concern to manage exceptions and render each specific error by exception. Then you can avoid having two ways in each action to render a good and bad case.
I have to guess a bit about what's in your ApplicationController and User classes. But the obviously repeated code is #user = User.find(params[:id]). Your show and edit methods both just run this line. So, they do exactly the same thing, which they oughtn't to do. One method for one thing.
Also, once you resolve show and edit into a single method, your create, update and destroy methods should call it instead of repeating the line.
Next, I wouldn't use new as a method name, since it is already used by BasicObject::new. I tested a few things and it gets pretty unclear:
class Test
attr_reader :test
def initialize
#test = 'test'
end
end
class Test2
attr_reader :test2
def new
p 'test2new'
#test2 = Test.new
end
end
testx = Test2.new
p testx.new.test
p testx.test2.test
=> "test2new"
=> "test"
=> "test"
It takes extra effort to see when you're calling your own new method and when you're calling BasicObject::new. So, I'd change that to new_user or something, if you even need it at all — I don't see why you need both it and your create method. (If you don't need it, get rid of it.)
Finally, #user = User.find(params[:id]) doesn't imply either showing or editing, so understanding what you're trying to do and coming up with a name that reflects it (e.g. set_user_id) is something that ought to be done as well.
I'm working on building out my mailer, but I keep running into:
wrong number of arguments (0 for 1)
Call my crazy, but I feel like I defined everything correctly:
Controller (truncated for brevity):
def create
#cms484 = Cms484.new(cms484_params)
respond_to do |format|
if #cms484.save
SendLink.message(#cms484).deliver_later
format.html { redirect_to cms484s_path, notice: 'Cms484 was successfully created.' }
format.json { render :show, status: :created, location: #cms484 }
else
format.html { render :new }
format.json { render json: #cms484.errors, status: :unprocessable_entity }
end
end
SendLink.rb:
class SendLink < ApplicationMailer
def message(cms484)
#cms484 = cms484
mail(
:subject => 'Hello from Postmark',
:to => #cms484.recipient ,
:from => 'info#mysite.com',
:html_body => '<strong>Hello</strong> user!.',
end
end
Can anybody else see the needle in the haystack or am I missing something else entirely?
I'm using Postmark for delivery if that matters, and have those parameters defined in my application.rb file as per the documentation. Think this is a simpler matter though.
Edit
The complete error:
Completed 500 Internal Server Error in 76ms
ArgumentError (wrong number of arguments (0 for 1)):
app/mailers/send_link.rb:2:in `message'
app/mailers/send_link.rb:4:in `message'
app/controllers/cms484s_controller.rb:38:in `block in create'
app/controllers/cms484s_controller.rb:36:in `create'
I had a similar issue where I named my ActionMailer method "message" it turns out it was a reserved word in Rails and threw an error.
I would assume that "mail" was a reserved word where "email" was not.
mail ... line in SendLink.rb looks wrong , change it to,
mail(
:subject => 'Hello from Postmark',
:to => #cms484.recipient ,
:from => 'info#mysite.com',
:html_body => '<strong>Hello</strong> user!.')
Ok, so I decided to re-write it and behold - it works. Why or what's different than the previous version(other than the method email vs mail, surely that can't be it?), I have no idea. If you can see what it is, Please point it out to me!
Send_link.rb:
class SendLink < ApplicationMailer
def email(cms484)
#cms484 = cms484
mail(
:subject => 'Hello from Postmark',
:to => #cms484.recipient ,
:from => 'info#mysite.com',
)
end
end
Controller:
def create
#cms484 = Cms484.new(cms484_params)
respond_to do |format|
if #cms484.save
SendLink.email(#cms484).deliver_later
format.html { redirect_to cms484s_path, notice: 'Cms484 was successfully created.' }
format.json { render :show, status: :created, location: #cms484 }
else
format.html { render :new }
format.json { render json: #cms484.errors, status: :unprocessable_entity }
end
end
end
I am new to rails and working on a rails application.
I have installed the Devise gem and find it helpful in many areas, however I cannot create a new user unless I do it on the Rails Console. I find this especially frustrating. Does anyone have any suggestions or solutions?
When I sign up as an employee I get the following error "CanCan::AccessDenied in JobsController#index"
Also please note that when I verified my user model in devise and I have already included "include Devise::Models::DatabaseAuthenticatable"
My jobs_controller.rb file
def index
#users = User.all
#user = current_user
#jobs = Job.all
#jobs = Job.paginate(:page => params[:page], :per_page => 3)
end
# GET /jobs/1
# GET /jobs/1.json
def show
end
# GET /jobs/new
def new
#job = Job.new(:date => Time.now, :date_of_loss => Time.now, :sign_date => Time.now, :time_called_in => Time.now)
end
# GET /jobs/1/edit
def edit
end
# POST /jobs
# POST /jobs.json
def create
#job = Job.new(job_params)
respond_to do |format|
if #job.save
format.html { redirect_to #job, notice: 'Job was successfully created.' }
format.json { render action: 'show', status: :created, location: #job }
else
format.html { render action: 'new' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /jobs/1
# PATCH/PUT /jobs/1.json
def update
respond_to do |format|
if #job.update(job_params)
format.html { redirect_to #job, notice: 'Job was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #job.errors, status: :unprocessable_entity }
end
end
end
# DELETE /jobs/1
# DELETE /jobs/1.json
def destroy
#job.destroy
respond_to do |format|
format.html { redirect_to jobs_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_job
#job = Job.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def job_params
params.require(:job).permit(:date, :time_called_in, :date_of_loss, :contact, :job_address, :city, :postal_code, :site_phone, :work_phone, :cell_phone, :email, :referred_by, :insurance_company, :policy_number, :claim_number, :broker, :insurance_address, :insurance_city, :insurance_province, :insurance_postal_code, :insurance_phone, :insurance_contact, :insurance_email, :customer_name, :customer_address, :authorized, :signed_by_name, :sign_date, :signature, :contact_first_name, :contact_last_name)
end
end
My ability model looks like this
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.role == "admin"
can :manage, :all
elsif user.role == "manager"
can [:read, :update, :create], [Job, Equipment]
#can [:read, :update, :create], [Equipment]
elsif user.role == "employee"
can [:read, :update, :create], [Equipment, Job]
cannot [:create], [Job]
#can [:read, :update], [Equipment]
end
end
If you are getting this error: CanCan::AccessDenied in JobsController#index it's because your user is not authorized to access the JobsController#index method and has nothing to do with creating users.
Can you explain what you mean when you say you "Sign up as an employee"? Do you have a roles table or a boolean on the users table to indicate that a user is an employee?
Please post your Ability.rb class (possibly in app/models/ability.rb) as that's where the access controls for cancan are defined.
Try changing this:
user ||= User.new # guest user (not logged in)
to this:
user ||= User.new # guest user (not logged in)
puts "user.role = #{user.role}"
and then check your logs to see what the value of user.role is. I'm wondering if your user does not have the role that you think it does.
I want to log the current backtrace (stacktrace) in a Rails 3 app without an exception occurring. Any idea how?
Why do I want this? I'm trying to trace the calls that are made when Rails looks for a template so that I can choose a part of the process to override (because I want to change the view path for a particular subclassed controller of mine).
I'd like to call it from the file: gems\actionpack-3.2.3\lib\action_dispatch\middleware\templates\rescues\missing_template.erb. I know that's not best practice, but I know it's downstream of the stack from where the search for templates occurs.
You can use Kernel#caller:
# /tmp/caller.rb
def foo
puts caller # Kernel#caller returns an array of strings
end
def bar
foo
end
def baz
bar
end
baz
Output:
caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'
Try using
Thread.current.backtrace
I use this to show a custom error page when exception are raised.
rescue_from Exception do |exception|
logger.error exception.class
logger.error exception.message
logger.error exception.backtrace.join "\n"
#exception = exception
# ExceptionNotifier::Notifier.exception_notification env, #exception
respond_to do |format|
if [AbstractController::ActionNotFound, ActiveRecord::RecordNotFound, ActionController::RoutingError, ActionController::UnknownAction].include?(exception.class)
format.html { render :template => "errors/404", :status => 404 }
format.js { render :nothing => true, :status => 404 }
format.xml { render :nothing => true, :status => 404 }
elsif exception.class == CanCan::AccessDenied
format.html {
render :template => "errors/401", :status => 401 #, :layout => 'application'
}
# format.js { render :json => { :errors => [exception.message] }, :status => 401 }
# format.js { render :js => 'alert("Hello 401")' }
format.js { render :template => 'errors/401.js.erb' }
else
ExceptionNotifier::Notifier.exception_notification(env, exception).deliver
format.html { render :template => "errors/500", :status => 500 } #, :layout => 'im2/application' }
# format.js { render :nothing => true, :status => 500 }
format.js { render :template => 'errors/500.js.erb' }
end
end
end
I got this Rails3 action:
def export
respond_to do |format|
format.tdl { render :xml => #template.export_as_tdl and return }
format.json { render :json => #template.export_as_json }
end
end
and filter before the export:
def find_environment
#environment = KTEnvironment.find(params[:environment_id])
raise HttpErrors::NotFound, _("Couldn't find environment '#{params[:environment_id]}'") if #environment.nil?
#environment
end
and this rspec:
describe "export" do
it "should call export_as_json" do
#tpl.should_receive(:export_as_json)
get :export, :id => TEMPLATE_ID
end
it "should call export_as_tdl" do
#tpl.should_receive(:export_as_tdl)
get :export, :id => TEMPLATE_ID, :format => 'tdl'
end
end
I also defined the following MIME type:
Mime::Type.register "application/tdl-xml", :tdl
When I try to run my rspec tests, I am constantly getting:
1) Api::TemplatesController export should call export_as_tdl
Failure/Error: get :export, :id => TEMPLATE_ID, :format => 'tdl'
AbstractController::DoubleRenderError:
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
# ./app/controllers/api/api_controller.rb:135:in `render_exception'
# ./app/controllers/api/api_controller.rb:133:in `render_exception'
# ./app/controllers/api/api_controller.rb:22:in `__bind_1314974553_619675'
# ./spec/controllers/api/templates_controller_spec.rb:178
I have no clue what is happening there. This is my exception rendering code:
def render_wrapped_exception(status_code, ex)
logger.error "*** ERROR: #{ex.message} (#{status_code}) ***"
logger.error "REQUEST URL: #{request.fullpath}"
logger.error pp_exception(ex.original.nil? ? ex : ex.original)
orig_message = (ex.original.nil? && '') || ex.original.message
respond_to do |format|
format.json do
render :json => {
:errors => [ ex.message, orig_message ]
}, :status => status_code
end
format.all do
render :text => "#{ex.message} (#{orig_message})",
:status => status_code
end
end
end
Ah so the render_exception general method in my api_controller is called. It looks like:
def render_exception(status_code, exception)
logger.error pp_exception(exception)
respond_to do |format|
format.json { render :json => {:errors => [ exception.message ]}, :status => status_code }
format.all { render :text => exception.message, :status => status_code }
end
end
Try disabling your error handler, the root cause should come up on its own.