First off, the exception in question:
undefined method '_view_paths' for nil:NilClass`
The related routes:
get 'payments/index' => 'payments#index'
get 'payments/class' => 'payments#class'
get 'payments/kids' => 'payments#kids'
get 'payments/donate' => 'payments#donate'
The associated controller:
class PaymentsController < ApplicationController
def index
end
def class
end
def kids
end
def donate
end
end
So, the exception occurs every time I try to access one of the routes. The views for the routes described above are the simple ones generated with scaffolding and use no other rails API calls. I can't seem to find any other information on this '_view_paths' method. The only assumption I can make thus far is that the proper view isn't being found, but all views reside exactly where expected according to rails conventions (app/views/payments/*).
Has anyone stumbled upon this issue and found a solution?
You can't define a method named "class" as it's already a reserved method to refer to the object's class, for example:
Object.new.class #=> Object
Technically I suppose you can override it (as you have), but doing so is mostly likely going to have some bizarre consequences unless you know what you're doing.
The error is probably happening when the code tries to call something like self.class._view_paths. It expects to be calling PaymentsController._view_paths. However, you've overridden the instance method class with an empty method returning nil, hence the nil exception.
Related
I am trying to figure out a graceful way to handle a ActiveRecord::RecordNotUnique exception globally for all my ActiveRecord code. I know about the validates_uniqueness_of validation but I want to rescue the exception directly as I have a constraint on the database in order to avoid bad data due to race conditions. I also don't want to create a bunch of custom methods that directly handle the exception every time I want to save or update an object where this constraint can be violated.
I would prefer not to monkey patch ActiveRecord methods like save() but I am beginning to think that achieving graceful exception handling for all ActiveRecord objects in my code might require that. Below is some code that demonstrates what a solution would look like:
class Photo < ActiveRecord::Base
belongs_to :post
def save(*args)
super
rescue ActiveRecord::RecordNotUnique => error
errors[:base] << error.message
false
end
end
While this works if I call save directly on a Photo object it won't work if I save the object through another model using accepts_nested_attributes_for with validates_associated.
Any help would be greatly apprecaited.
Thanks
Update
The desired outcome is to handle the exception and just add a key/value pair to the object's errors hash and then display form errors back to the user telling them that the email has been taken.
This is covered in the Action Controller Overview Rails Guide. In short, you can use the rescue_from method to register a handler for exceptions. If you use it in ApplicationController then it'll be inherited by all other controllers.
Here's the example from the Guide:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
end
Go take a look for more information and an additional example.
What I was looking for was the inverse_of option when defining the association on each of the models. What inverse_of does is it causes rails to use the in memory instance of the associated object as opposed to going to the db to fetch the record. I created a save method in the Photo model that looks like this:
def save(*args)
super
rescue ActiveRecord::RecordNotUnique => error
post.errors[:base] << "You can only have one photo be your header photo"
false
end
In the rescue block, when I call post.errors I am getting the unsaved, associated post object rather than rails looking for one in the db based on photo.post_id which at this point is nil because the photo object is invalid which caused the post not to be persisted to the db.
Here are the docs for inverse_of
http://guides.rubyonrails.org/association_basics.html#bi-directional-associations
My gem activerecord-transactionable allows you to do all of the following (a la carte):
rescue
retry with alternate logic (switch from create to find, for example)
add errors to the record that failed to save
use locking
use nested transactions
handle different kinds of database errors in different ways
I have a ruby/rails/hobo system that someone wrote a couple years ago, that I need to port to the latest version of ruby/rails/hobo. It seems that ruby doesn't care about backward compatibility, so code that used to work in the old app doesn't work anymore:
In the observation.rb model file, the old app has this:
belongs_to :survey
has_one :site, :through => :survey
def create_permitted?
acting_user == self.survey.contact or acting_user.administrator?
end
survey.rb model file has this:
belongs_to :contact, :class_name => 'User', :creator => true
Unfortunately the code in observation.rb doesn't work under the new ruby/rails/hobo, and it gives me the error:
NoMethodError in Observations#index
Showing controller: observations; dryml-tag: index-page where line #1 raised:
undefined method `contact' for nil:NilClass
Extracted source (around line #1):
0
Rails.root: /home/simon/ruby/frogwatch2
Application Trace | Framework Trace | Full Trace
app/models/observation.rb:48:in `create_permitted?'
How should the "create_permitted" method be changed? I'm finding that the documentation for ruby/rails/hobo is pretty atrocious (which is fair enough as it is free software). Also I don't even know how to begin searching for this on google (i've been trying for days).
Please help! :)
Apart from differing views on the docs around Rails, you are calling contact on a survey which does not exist in this case, resulting in a call nil.contact.
An alternative would be to check for the presence of the survey before calling contact, e.g in such a way.
def create_permitted?
acting_user == (survey && survey.contact) or acting_user.administrator?
end
You are getting the error because reference column(survey_id) may contain a null or invalid reference id.
If the null or invalid reference is allowed then, change the code to handle it
( self.survey and acting_user == self.survey.contact ) or acting_user.administrator?
I'm going to echo what the other two have said. The survey you are trying to reference is nil, and nil does not have a method called contact. I am going to offer up a slightly different solution:
def create_permitted?
acting_user == survey.try(:contact) or acting_user.administrator?
end
The #try method exists on nil and on survey. It basically wraps the method call in a rescue. Conceptually, it looks like:
def try(method_name, *args)
self.send(method_name, args) rescue nil
end
This may reduce the amount of code that you have to write to catch conditions where a relationship may not be present, preventing a NoMethodError exception.
#try is part of the Rails core extensions for Object. In reality, it does not work as I have above, since exceptions arising from calls to Object#try will still happen as they normally should. Instead, it extends Object by calling send. It extends NilClass by returning nil, so it does not try to send any method to NilClass, preventing a NoMethodError. As tadman points out in the comments, a catch-all exception handler is normally not a good idea.
https://github.com/rails/rails/blob/6ef9fda1a39f45e2d18aba4881f60a19589a2c77/activesupport/lib/active_support/core_ext/object/try.rb
Update
A better solution, and one that I forgot about, is to use delegate.
For example:
class User < ActiveRecord::Base
delegate :contact, to: :survey, prefix: true, allow_nil: true
end
Then you would call user.survey_contact, and would fail gracefully if the survey is nil.
I have a "event" model that has many "invitations". Invitations are setup through checkboxes on the event form. When an event is updated, I wanted to compare the invitations before the update, to the invitations after the update. I want to do this as part of the validation for the event.
My problem is that I can't seem to access the old invitations in any model callback or validation. The transaction has already began at this point and since invitations are not an attribute of the event model, I can't use _was to get the old values.
I thought about trying to use a "after_initialize" callback to store this myself. These callbacks don't seem to respect the ":on" option though so I can't do this only :on :update. I don't want to run this every time a object is initialized.
Is there a better approach to this problem?
Here is the code in my update controller:
def update
params[:event][:invited_user_ids] ||= []
if #event.update_attributes(params[:event])
redirect_to #event
else
render action: "edit"
end
end
My primary goal is to make it so you can add users to an event, but you can't not remove users. I want to validate that the posted invited_user_ids contains all the users that currently are invited.
--Update
As a temporary solution I made use for the :before_remove option on the :has_many association. I set it such that it throws an ActiveRecord::RollBack exception which prevents users from being uninvited. Not exactly what I want because I can't display a validation error but it does prevent it.
Thank you,
Corsen
Could you use ActiveModel::Dirty? Something like this:
def Event < ActiveRecord::Base
validates :no_invitees_removed
def no_invitees_removed
if invitees.changed? && (invitees - invitees_was).present?
# ... add an error or re-add the missing invitees
end
end
end
Edit: I didn't notice that the OP already discounted ActiveModel::Dirty since it doesn't work on associations. My bad.
Another possibility is overriding the invited_user_ids= method to append the existing user IDs to the given array:
class Event < ActiveRecord::Base
# ...
def invited_user_ids_with_guard=(ids)
self.invited_user_ids_without_guard = self.invited_user_ids.concat(ids).uniq
end
alias_method_chain :invited_user_ids=, :guard
end
This should still work for you since update_attributes ultimately calls the individual attribute= methods.
Edit: #corsen asked in a comment why I used alias_method_chain instead of super in this example.
Calling super only works when you're overriding a method that's defined further up the inheritance chain. Mixing in a module or inheriting from another class provides a means to do this. That module or class doesn't directly "add" methods to the deriving class. Instead, it inserts itself in that class's inheritance chain. Then you can redefine methods in the deriving class without destroying the original definition of the methods (because they're still in the superclass/module).
In this case, invited_user_ids is not defined on any ancestor of Event. It's defined through metaprogramming directly on the Event class as a part of ActiveRecord. Calling super within invited_user_ids will result in a NoMethodError because it has no superclass definition, and redefining the method loses its original definition. So alias_method_chain is really the simplest way to acheive super-like behavior in this situation.
Sometimes alias_method_chain is overkill and pollutes your namespace and makes it hard to follow a stack trace. But sometimes it's the best way to change the behavior of a method without losing the original behavior. You just need to understand the difference in order to know which is appropriate.
I have the following in my application controller:
before_filter :set_current_subdomain
protected
def set_current_subdomain
Thread.current[:current_subdomain] = current_subdomain
#account = Account.find_by_subdomain(current_subdomain)
end
def current_subdomain
request.subdomain
end
and then the following in some of my models:
default_scope :conditions => { :account_id => (Thread.current[:account].id unless Thread.current[:account].nil?) }
Now, this works - some of the time. I for instance load up an index method and get back a list of records with the scope applied, but also sometimes get an empty list as Thread.current[:account_id] is coming out as nil, even though queries earlier in the request are working using the same value.
Question is, why is this not working, and is there a better way to set a variable that's global to the current request?
Manipulating the Thread local variables is a really bad idea and is going to lead to nothing but sadness, heartache, and pain. There's no guarantee that different parts of the request processing will be handled by the same thread, and because of this, your variables might end up getting lost.
The Rails convention is to create instance variables in the context of ApplicationController. In simple terms, all you really do is this:
class ApplicationController < ActionController::Base
before_filter :set_current_subdomain
attr_reader :current_subdomain
helper_method :current_subdomain
protected
def set_current_subdomain
#current_subdomain = request.subdomain
#account = Account.find_by_subdomain(#current_subdomain)
end
end
Any #... type variables you create will be attached to the instance of the ApplicationController associated with the current request. It's important to note that each request will be issued a brand-new instance of the appropriate controller class.
You're free to create whatever instance variables you want provided they don't somehow conflict with those used by Rails itself but in general terms this doesn't happen very often and conflicts typically occur on method names instead.
Class-level instance variables will persist between requests in environments where the "cache classes" flag is enabled. In the development environment your controller class is re-loaded each time a request is made to ensure it reflects the current state of your source files.
I'm trying to follow along with the thread on implementing an achievement system (located at How to implement an achievement system in RoR), and am running into a TypeError when the object is saved and the method awarded? gets called. The error looks like:
TypeError (can't dump anonymous class Class):
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/yaml/rubytypes.rb:6:in `to_yaml'
app/models/user.rb:18:in `awarded?'
The model logic is exactly as defined in that previous question:
def awarded?(achievement)
achievements.count(:conditions => { :type => achievement }) > 0
end
Any idea what's happening here?
UPDATE:
Based on the info below, I ended up removing the abstraction and placing the award methods directly in each controller for now. Not very DRY and when I have time to play around with it more I'll try to abstract it out again, but works for now. An example:
if #user.achievements.count(:conditions => { :type => "CommentAchievement" }) < 1 and #comments > 1000
#user.achievements << CommentAchievement.new(:group_id => #group)
end
Not sure of the exact problem but it looks like whatever is being provided to the the method that is assigning the achievement is wrong, which is causing the value of 'type' to be stored incorrectly. Check your database and make sure that the value being written to 'type' is a subclass of your parent, and not the parent (or any other incorrect value).
If that value is stored incorrectly rails will not be able to instantiate an object, and it must look that value up before it attempts to create the object which is why its throwing that error.