I am working on a test application in which someone who favors something becomes a subscriber of that person.
The controller:
def favorite_subscribe
#favorite_subscription = FavoriteSubscription.add_favorite_subscription(current_user, #user)
#user.favorite_subscriber_total = #user.favorite_subscriber_total + 1
#user.save
redirect_to :back, :notice => "Congratulations, you have favorited #{#user.username}."
end
The model:
def self.add_favorite_subscription(user, favorite_subscribe)
user.favorite_subscriptions.where(:subscribe_id => subscribe.id).first_or_create
end
# Remove the favorite of the user with the other user
def self.remove_favorite_subscription(user, favorite_subscribe)
user.favorite_subscriptions.where(:subscribe_id => subscribe.id).destroy_all
end
# Get the user with the subscription
def favorite_subscribe
User.find(subscribe_id)
end
I get an error that it cannot autoload constant, and that it expects my model to define it. If anybody can help that would be very appreciated.
Error is here, sorry about that:
Unable to autoload constant FavoriteSubscription, expected /home/jakxna360/rails/test/app/models/favorite_subscription.rb to define it
This usually means that Rails is unable to find the file in which some class is defined and therefore autoloading fails. Rails is very strict about its conventions.
In the context of the posted code, I suggest double checking that
the class FavoriteSubscription (singular) is defined in a file named app/models/favorite_subscription.rb (singular), that a database table is named favorite_subscriptions (plural) and that is exists.
the FavoriteSubscriptionsController (plural) is defined in a file named app/controllers/favorite_subscriptions_controller.rb (plural).
Related
I'm trying to get a variable from the controller over to the model. Its the value of a checkbox passed up from the View. I cant see it in the controller (0 or 1) and just need to get it to the model. I have seen tons of examples on here but none of them seem to work for me. Maybe its something about the Ruby environment we have setup? Its a Rails 5 environment.
Here is what I have tried so far and the errors it gives in the logs:
Controller:
#vin = Vehicle.create!
#results = #vin.myvin!(params[:vehicle][:vincheck])
Model:
def myvin!(localvin)
logger.info "MADE IT INTO VIN"
end
Error in development.log:
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.
I'm not calling render or redirect!! Just trying to call the method in the model.
I have also tried a variation like:
Controller:
Vehicle.myvin(params[:vehicle][:vincheck])
Model:
def self.myvin(localvin)
logger.info "MADE IT INTO VIN"
end
But regardless... I always get that render error in the logs!
I have tried instantiating a new object:
Controller:
Vehicle.new(params[:vehicle][:vincheck])
Model:
attr_reader :localvin
def initialize(localvin)
logger.info "MADE IT INTO VIN"
end
When I do that I get:
500wrong number of arguments (given 0, expected 1)
Short Stack
app/models/vehicle.rb:25:in 'initialize'
app/controllers/vehicles_controller.rb:84:in 'new'
Anyone have any thoughts on how I can get this data over? Maybe it has something to do with the Vehicle Model itself? In the controller it gets instantiated at different places.
For example:
def index
#vehicles = Vehicle.active
end
def create
#this is where I am putting all my code
#not really sure how this is getting created since there was no
#initialize in the code before I added one
#vehicle = Vehicle.new(vehicle_params.merge(
created_by_id: current_user.id,
created_by_name: current_user.name,
deleted: false))
end
thanks!
Checks_controller
class Checkscontroller < ApplicationController
def show
#check= Tester.find(params[:id])
end
def new
end
def create
#check = Tester.new(check_params)
#check.save
redirect_to #check
end
def check_params
params.require(:check).permit(:title, :description)
end
end
I am trying to save the data in 'checks' controller to 'Tester' model, getting "NoMethodError in ChecksController#create", undefined method tester_url' for#` while trying to save the data to my DB. There seems to be some issue on this line: "redirect_to #check".
Routes.rb
Rails.application.routes.draw do
get 'home/screen'
resources :checks
root 'home#screen'
end
EDIT: I see this answer got accepted. To anyone else looking at this: PLEASE DO NOT DO THIS WITHOUT A REALLY GOOD REASON.
Ok, so since you want to use the ChecksController for your Tester model, you'll have to add this to your routes: note that I'm assuming that you do not have a Check model, since I don't see it anywhere and youre using Tester as a check?
resources :testers, as: 'checks' controller: 'checks'
This line will make it so that /checks/1 goes to a Tester object with ID: 1, and use the ChecksController show method to show it
Old answer, for posterity
You're getting this error because you're missing routes for your Tester model in your routes.rb file.
You could add resources :testers to it and it will work. Of course you also already need AT LEAST your TestersController to exist with a show action
This error is occurring because when you redirect_to #check, Rails knows it's a Tester object and expects a route called tester to route to TestersController#show. It's attempting to use a helper method that rails creates for routes, called tester_url
I'm using Rails 4.1.0.beta1's new Action Mailer previews and have the following code:
class EventInvitationPreview < ActionMailer::Preview
def invitation_email
invite = FactoryGirl.create :event_invitation, :for_match, :from_user, :to_user
EventInvitationMailer.invitation_email(invite)
end
end
This is all good until I actually try to preview my email and get an error saying that validation on a User object failed due to duplicate email addresses. Turns out that ActionMailer::Preview is writing to my development database.
While I could work around the validation failure or use fixtures instead of factories, is there any way to avoid ActionMailer::Preview writing to the development database, e.g. use the test database instead? Or am I just doing it wrong?
Cleaner/Easier (based on other answers) and tested with Rails 7: Do not change Rails' classes but create your own. Id addition to not change the controller but the call method of ActionMailer::Preview.
# app/mailers/preview_mailer.rb
class PreviewMailer < ActionMailer::Preview
def self.call(...)
message = nil
ActiveRecord::Base.transaction do
message = super(...)
raise ActiveRecord::Rollback
end
message
end
end
# inherit from `PreviewController` for your previews
class EventInvitationPreview < PreviewController
def invitation_email
...
end
end
OLD:
You can simply use a transaction around email previews, just put this inside your lib/monkey_mailers_controller.rb (and require it):
# lib/monkey_mailers_controller.rb
class Rails::MailersController
alias_method :preview_orig, :preview
def preview
ActiveRecord::Base.transaction do
preview_orig
raise ActiveRecord::Rollback
end
end
end
Then you can call .create etc. in your mailer previews but nothing will be saved to database. Works in Rails 4.2.3.
A cleaner way to proceed is to prepend a module overriding and wrapping preview into a transaction:
module RollbackingAfterPreview
def preview
ActiveRecord::Base.transaction do
super
raise ActiveRecord::Rollback
end
end
end
Rails.application.config.to_prepare do
class Rails::MailersController
prepend RollbackingAfterPreview
end
end
TL;DR -- The original author of the ActionMailer preview feature (via the MailView gem) provides three examples of different supported approaches:
Pull data from existing fixtures: Account.first
Factory-like pattern: user = User.create! followed by user.destroy
Stub-like: Struct.new(:email, :name).new('name#example.com', 'Jill Smith')
~ ~ ~ ~ ~ ~ ~ ~ ~ ~
To elaborate on the challenge faced by the OP...
Another manifestation of this challenge is attempting to use FactoryGirl.build (rather than create) to generate non-persistent data. This approach is suggested by one of the top Google results for "Rails 4.1" -- http://brewhouse.io/blog/2013/12/17/whats-new-in-rails-4-1.html?brewPubStart=1 -- in the "how to use this new feature" example. This approach seems reasonable, however if you're attempting to generate a url based on that data, it leads to an error along the lines of:
ActionController::UrlGenerationError in Rails::Mailers#preview
No route matches {:action=>"edit", :controller=>"password_resets", :format=>nil, :id=>nil} missing required keys: [:id]
Using FactoryGirl.create (rather than build) would solve this problem, but as the OP notes, leads to polluting the development database.
If you check out the docs for the original MailView gem which became this Rails 4.1 feature, the original author provides a bit more clarity about his intentions in this situation. Namely, the original author provides the following three examples, all focused on data reuse / cleanup / non-persistence, rather than providing a means of using a different database:
# app/mailers/mail_preview.rb or lib/mail_preview.rb
class MailPreview < MailView
# Pull data from existing fixtures
def invitation
account = Account.first
inviter, invitee = account.users[0, 2]
Notifier.invitation(inviter, invitee)
end
# Factory-like pattern
def welcome
user = User.create!
mail = Notifier.welcome(user)
user.destroy
mail
end
# Stub-like
def forgot_password
user = Struct.new(:email, :name).new('name#example.com', 'Jill Smith')
mail = UserMailer.forgot_password(user)
end
end
For Rails 6:
#Markus' answer worked for me, except that it caused a nasty deprecation-soon-will-be-real error related to how Autoloading has changed in Rails 6:
DEPRECATION WARNING: Initialization autoloaded the constants [many constants seemingly unrelated to what I actually did]
Being able to do this is deprecated. Autoloading during initialization is going to be an error condition in future versions of Rails.
[...]
Well, that's no good!
After more searching, this blog and the docs for
to_prepare helped me come up with this solution, which is just #Markus' answer wrapped in to_prepare. (And also it's in initializer/ instead of lib/.)
# /config/initializers/mailer_previews.rb
---
# Wrap previews in a transaction so they don't create objects.
Rails.application.config.to_prepare do
class Rails::MailersController
alias_method :preview_orig, :preview
def preview
ActiveRecord::Base.transaction do
preview_orig
raise ActiveRecord::Rollback
end
end
end
end
If you have a complicated object hierarchy, you can exploit transactional semantics to rollback the database state, as you would in a test environment (assuming your DB supports transactions). For example:
# spec/mailers/previews/price_change_preview.rb
class PriceChangeMailerPreview < ActionMailer::Preview
#transactional strategy
def price_decrease
User.transaction do
user = FactoryGirl.create(:user, :with_favorited_products) #creates a bunch of nested objects
mail = PriceChange.price_decrease(user, user.favorited_products.first)
raise ActiveRecord::Rollback, "Don't really want these objects committed to the db!"
end
mail
end
end
#spec/factories/user.rb
FactoryGirl.define do
factory :user do
...
trait :with_favorited_products do
after(:create) do |user|
user.favorited_products << create(:product)
user.save!
end
end
end
end
We can't use user.destroy with dependent: :destroy in this case because destroying the associated products normally doesn't make sense (if Amazon removes me as a customer, they don't remove all the products I have favorited from the market).
Note that transactions are supported by previous gem implementations of the preview functionality. Not sure why they aren't supported by ActionMailer::Preview.
I'm new to Rails and Rspec and I'm using Rspec to test this controller method which includes exception handling:
def search_movies_director
#current_movie = Movie.find(params[:id])
begin
#movies = Movie.find_movies_director(params[:id])
rescue Movie::NoDirectorError
flash[:warning] = "#{#current_movie} has no director info"
redirect_to movies_path
end
end
I can't figure out how to correctly test the said path: after invalid search (when error is received) it should redirect to the homepage. I tried something like this:
describe MoviesController do
describe 'Finding Movies With Same Director' do
#some other code
context 'after invalid search' do
it 'should redirect to the homepage' do
Movie.stub(:find)
Movie.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
get :search_movies_director, {:id => '1'}
response.should redirect_to movies_path
end
end
end
end
After running the test fails with an error: NameError: uninitialized constant Movie::NoDirectorError
How to fake raising an error in this test so it actually checks whether redirect happens?
Thanks!
UPDATE:
As nzifnab explained, it couldn't locate Movie::NoDirectorError. I forgot to define this exception class. So I added it to app/models/movie.rb :
class Movie < ActiveRecord::Base
class Movie::NoDirectorError < StandardError ; end
#some model methods
end
This solved my problem and this test passes.
You're very close. You need to add any_instance in there.
Movie.any_instance.stub(:find_movies_director).and_raise(Movie::NoDirectorError)
edit: I misread the post. The above would work given an instance of Movie, but not for OP's question.
The error indicates it doesn't know where that Movie::NoDirectorError exception class is defined. You might need to specifically require the file where that class is defined or the test may not be able to find it.
Rails will automatically attempt to locate any constant missing constants using a conventional file directory format. It will look for a file in the load_path at movie/no_director_error and movie based on the name of the constant. If the file is not found or the file doesn't define the expected constant than you'll need to specifically require the file yourself.
In Rails 4.1:
verse_selector = double("VerseSelector", select_verses: ActiveRecord::RecordNotFound.new("Verses not found"))
verse_selector.select_verses will now return an ActiveRecord::RecordNotFound
Rails newbie here, trying to get a new controller working.
When I try to show ann existing instance, I get an undefined method error on a helper method.
Code follows.
Any idea why getRecipes would be undefined?!
Controller:
def show
id = params[:id]
recipe_ids = ConcreteMenu.getRecipes(id)
respond_to do |format|
format.html
end
end
Model
require 'json/objects'
class ConcreteMenu < ActiveRecord::Base
has_many :menu_recipes
has_many :recipes, :through => :menu_recipes
belongs_to :menu
def self.getRecipes(id)
recipes = MenuRecipe.find(:all, :conditions => {:concrete_menu_id => id}, :select => 'id')
end
end
It would help if you pasted the error text, because your explanation leaves a lot of possibilities for what could be wrong. BUT, there is an easier way to get what you want. The value of defining "has_many" relationships is that instead of calling a class method and passing the id of a concrete menu to get its associated recipes, you can just do this:
def show
#concrete_menu = ConcreteMenu.find(params[:id], :include => :recipes)
end
Now you'll have the menu object, and #concrete_menu.recipes returns an array of recipes you need. This feature is already built in, no need to reinvent the wheel.
Also, I noticed you were attempting to collect id's in the controller instead of the objects themselves. This suggests that you're going back and actually retrieving the records in the view itself. This is less efficient, and more difficult to troubleshoot when things go wrong. My example above will do what you need in a better (and more rails-accepted) way.
As you have it defined there, it should be available. Is there a chance you have something else called ConcreteMenu defined, but in a different context?
To be sure you're calling the correct one, where there may be ambiguity, you can refer to the top-level class:
recipe_ids = ::ConcreteMenu.getRecipes(id)
The other way to check that the method is defined correctly via script/console:
ConcreteMenu.methods.grep(/getRecipe/)
# => ["getRecipes"]
This is presuming, of course, you're having trouble with the getRecipes method. There's a possibility you're mistaking how controller variables are passed to the view:
def show
#id = params[:id]
#recipe_ids = ConcreteMenu.getRecipes(#id)
respond_to do |format|
format.html
end
end
Any instance variables defined (#...) will be available within the context of the view, but any local variables will no longer be defined as they are out of scope.