I recently switched my app from a gem to a by-hand shopping cart. I created a new orders table, yet when I try to get into my app I get an error saying:
ActiveRecord::RecordNotFound at /
Couldn't find Order with 'id'=5
The error is called on this bit of my application_controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_order
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id]) <<<<< THIS LINE
else
Order.new
end
end
end
I tried order.delete_all in my controller, but it is still looking for id=5 for some reason. I would try db:drop db:create but I have data on the production app I don't want to lose.
Is there any way to either:
Fix this error without messing up the database?
Drop/recreate just the orders table without destroying the rest of the database?
Also, I could be reading the problem wrong, so let me know if you think the solution lies elsewhere.
The problem has nothing to do with database at all. Well, almost. Certainly, nothing that will require dropping/recreating db structures.
Your session[:order_id] has a value that is not in the db. That's what you have to fix, the session. Or your handling of not-found records. For example, this will not raise an error.
Order.where(id: session[:order_id]).first
Related
I am trying to catch database connection issues on a specific request and take a different action when the database is down.
for example:
config/routes.rb
get 'my_route' => 'my_controller#my_action'
app/controllers/my_controller.rb
class MyController < Public::ApplicationController
def my_action
begin
url = database_lookup
rescue Mysql2::Error => e
url = fallback_lookup
end
redirect_to url
end
def database_lookup
# get info from db
end
def fallback_lookup
# lookup info in redis cache instead
end
end
This might work in certain situations, however if the database goes down and a new request comes in, active record middleware raises an exception long before ever reaching the controller.
I have been messing with Middleware to try and catch the error and do something else, but its not looking too promising.
What i'm trying is:
application.rb
config.middleware.insert_after 'ActionDispatch::RemoteIp', 'DatabaseExceptionHandler'
app/middleware/database_exception_handler.rb
class DatabaseExceptionHandler
def initialize app
#app = app
end
def call env
#status, #headers, #response = #app.call(env)
[#status, #headers, #response]
rescue Mysql2::Error => e
[#status, #headers, #response]
end
end
This is allowing me to catch connection exceptions that are raised when the request runs, but it doesn't help me much. I need to somehow get to a controller action still.
I think a simpler approach would be to skip all the active record connection nonsense for a specific controller action.
It seems silly to force a database connection for something that might not even need the database.
Anyone have any better ideas than what i've come up with so far?
I think a simpler approach would be to skip all the active record
connection nonsense for a specific controller action.
No doable or even a good idea. Rails does not process the configuration and middleware on a per request basis and that would not work with any kind of server that speeds the process up by booting up rails in the background.
This is allowing me to catch connection exceptions that are raised
when the request runs, but it doesn't help me much. I need to somehow
get to a controller action still.
This is probably a fools errand. If Rails bailed in the initialization process the integrity of the system is probably not great and you can't just continue on like its business as usual.
What you can do is set config.exceptions_app to customize the error pages. And get a less flaky database.
I ran into a problem, when PG fails out of sync (well known problem)(example).
PG fails out of sync, sequence of id stops incrementing and raises ActiveRecord::RecordNotUnique error.
But all solutions proposed here (all I found) propose some manual solutions - either do some operations in console, either run custom rake task.
However, I find this unsatisfying for production: each times it happens, users get 500, while someone administrating server should operatively save the day. (And according to test data for some reason it possible will occur frequently in my case).
So I would like rather to patch ActiveRecord Base class to catch this specific error and rescue it.
I use this logic sometimes in controller:
class ApplicationController < ActionController::Base
rescue_from ActionController::ParameterMissing, ActiveRecord::RecordNotFound do |e|
# some logic here
end
end
However, here I don't need retry. Also, I would like to not to go deep in monkey patching, for example, without overriding Base create method.
So I was thinking of something like this:
module ActiveRecord
class Base
rescue ActiveRecord::RecordNotUnique => e
if e.message.include? '_pkey'
table =e.message.match(//) #regex to define table
ActiveRecord::Base.connection.reset_pk_sequence!(table)
retry
else
raise
end
end
end
But it most likely doesn't work, as I'm not sure if Rails/Ruby will understand what exactly it asked to retry.
Is there any solution?
P.S. Not related solution for overall problem of sequence which will work without manual command line commands and having unserved users are also appreciated.
To answer the question you're asking, no. rescue can only be used from within a begin..end block or method body.
begin
bad_method
rescue SomeException
retry
end
def some_method
bad_method
rescue SomeException
retry
end
rescue_from is just a framework helper method created because of how indirect the execution is in a controller.
To answer the question you're really asking, sure. You can override create_or_update with a rescue/retry.
module NonUniquePkeyRecovery
def create_or_update(*)
super
rescue ActiveRecord::RecordNotUnique => e
raise unless e.message.include? '_pkey'
self.class.connection.reset_pk_sequence!(self.class.table_name)
retry
end
end
ActiveSupport.on_load(:active_record) do
include NonUniquePkeyRecovery
end
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'm trying to install Vanity A/B Testing in my Rails App, but I can't even get the example from the GitHub page working. I've generated the vanity models and run the migrations and made the experiment files, but as soon as I include a test like
<%= ab_test :price_options %>
the program throws an error:
invalid value for Integer(): "{:conditions=>{:experiment_id=>\"price_options\""
In my controllers/application_controller.rb I have just:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
use_vanity
end
I didn't include a user identifier because I haven't built one into this app yet, but the Vanity docs say that if no argument is supplied Vanity will just use cookies instead, so that shouldn't be an issue. If anyone has any clue why this cryptic error is being thrown, I would be very appreciative!
Edit: I should add that I actually started a new rails app from scratch just to try and debug this. Literally all I did was start an app and install vanity following the readme instructions and I got this same error. This has happened on two different machines as well, so I suspect it's something obvious that I'm missing, but I can't be sure (else I wouldn't be here!).
EDIT: I've since decided to use the Split gem instead, and am having no troubles with it.
This error is thrown because the current release version of Vanity uses the deprecated Rails find(:first) method, specifically:
VanityParticipant.first(:conditions=>{ :experiment_id=>experiment.to_s, :identity=>identity.to_s })
This no longer works with Rails 4.1 but is fixed in the master branch of Vanity, so you can get round this in your Rails 4.1 app by adding:
gem 'vanity', :git => 'git#github.com:assaf/vanity', :branch => 'master'
to your Gemfile.
With
class ApplicationController < ActionController::Base
protect_from_forgery
use_vanity
end
and
# experiments/price_options.rb
ab_test "Price options" do
description "Mirror, mirror on the wall, who's the best price of all?"
alternatives(19, 25, 29)
end
and
class StaticController < ApplicationController
def home
end
end
the following view loads and sets a vanity_id cookie:
<h1>Static#home</h1>
<p>Find me in app/views/static/home.html.erb</p>
<h2>Get started for only $<%= ab_test :price_options %> a month!</h2>
<%= link_to 'Make a new account', new_account_path %>
I would like to allow the users to 'create' and 'update' their submissions (bets) until a specific date and time at which point their bets are final (can no longer be created or updated). This process would repeat each week.
I'm fairly new to Rails and I'm not sure if there is a term for this or what to search for.
Can anyone point me in the right direction?
Probably the easiest way to achieve this is just to add a before_filter (Rails 3.x) or before_action (Rails 4.x) to your controller. You can do so like this:
Assume you have submissions_controller.rb with create/update actions like so - add a before filter that will only apply to the create and update actions. You can then implement a private method in the controller to redirect the user back to your root_path or elsewhere and give a flash message as to why.
class PagesController < ApplicationController
before_filter :check_if_bets_are_final, :only => [:create, :update]
def create
...
end
def update
...
end
private
def check_if_bets_are_final
if Time.now >= Time.new(2014, 02, 20)
flash[:error] = "You can no longer modify or submit new bets!"
redirect_to root_path
end
end
end
Aside from your controller action though, it will probably be safer to implement a model-level validation/check to reject it if the date is past, just to be safe (or if you have other ways to update that object in the future). You can do this through the model hook before_save, in which you can pretty much do a similar check that I have given above.
Also, the other caveat is that comparing Time.now could be in a different timezone depending on where your server is. Just be cognisant of this when you do your checks, and cast the time properly with this in mind.
Since you didn't provide a specific implementation, I'm not quite sure if you're having trouble specifically with Ruby or Rails. However, given your question, I would store a datetime variable in your database when the user creates the bet. Every time the user tries to 'update' the bet, check in the database whether or not it's been past that specific time away from the bet creation. Hope this helps.