undefined method `model_name' for #<User:XXXXXXXX> - ruby-on-rails-4

I have a dashboard containing 2 partials. One partial works fine and does what its suppose to (bill). The other partial is setup similar to bill but wont work(request). When I look at my log it shows that the tenant(user) is being queried, also, the 1st partial is queried but the 2nd partial doesn't query. when I went to my dashboard controller and changed the instance of the partial to (Request.new) it works but I can't seem to get it to work right thru the controller. I don't want to have the method in the model. I am using mongoid.
SO here is my render in the dashboard...
<%= render partial: "request", locals: {request: #request} %>
In the partial I have...
<%= form_for [:tenants, request] do |f| %>
And on the dashboard controller I have...
def show
#dashboard = current_tenant
#bill = current_tenant.bill || current_tenant.build_bill
#request = current_tenant.request || current_tenant.build_request
end
(if I change #request = Request.new it works fine but I know that's not right)
The bill partial works and the dashboard finds the tenant but I keep getting
"undefined method `request' for #
Any idea of what I am missing? I compared the bill controller to the request controller and I cant find any differences. When I put the Model.new into the dashboard controller it works but I know this isn't right, its as if the app wont recognize the Request controller...
The error is saying it doesn't recognize "request" method.
Also here is my controller for request...
class Tenants::RequestsController < ApplicationController
before_filter :authenticate_tenant!
def index
#requests = Request.all
end
def show
#request = Request.find(params[:id])
end
def create
if #request = current_tenant.create_request(authorization_params)
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} has been Authorized!"
else
redirect_to tenants_dashboard_path, :error => "#{request.manager_name} has NOT been Authorized, please try again."
end
end
def edit
end
def update
if current_tenant.request.update_attributes(authorization_params)
redirect_to tenants_dashboard_path, :notice => "You have approved #{request.manager_name} to review your report"
else
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} is NOT allowed to review your report"
end
end
def destroy
#request = Request.find(params[:request_id])
name = #request.name
if #request.destroy
flash[:notice] = "\"#{name}\" was successfully removed from your profile."
redirect_to #dashboard
else
flash[:error] = "There was an error deleting this managers access."
render :show
end
end

Well it looks like
current_tenant.request has an issue. That means that the method is not available. Assuming you're not trying to access the http request , then you have an issue with the request method.
So your issue is with how you defined the request method (maybe in your model). e.g. is it a class method or a instance method etc.
Without knowing your goal, that's the general answer I can give you. Creating a Request.new could be right depending on your goal, but if your goal is to call the request method, you must make it available to current_tenant
One controller shouldn't be calling your other controller as you have suggested...

Related

Crud operation "Delete" don't works in my first project in ruby on rails

I'm implementing in my first project the crud operation "delete", but the system take the command as a "get", whitout errors.
this is the controller:
class MoviesController < ApplicationController
def index
#movies = Movie.all
end
#GET /movies/:id
def show
id = params[:id]
#movie = Movie.find(id)
#render plain: 'show'+params[:id]
end
#POST /movies/
# skip_before_action :verify_autenticity_token:
def create
#movie = Movie.create!(params[:movie].permit(:title, :rating, :description, :realease_date))
flash[:notice] = "#{#movie.title} was successfully created."
redirect_to movies_path
end
#new
def new
end
def edit
id = params[:id]
#movie = Movie.find(id)
end
def update
id = params[:id]
#movie = Movie.find(id)
if #movie.update_attributes!(params[:movie].permit(:title,:rating,:realease_date))
flash[:notice] = "#{#movie.title} has been edited."
redirect_to movies_path
end
end
def destroy
id = params[:id]
#movie = Movie.find(id)
#movie.destroy
flash[:notice] = "#{#movie.title} has been deleted."
redirect_to movies_path
end
end
this is the view where is possible call "Delete" operation:
%h2 Details about #{#movie.title}
%ul#details
%li
Rating:
= #movie.rating
%li
Released on:
= #movie.realease_date#.strftime("%B %d, %Y")
%h3 Description:
%p#description= #movie.description
%h4 Reviews:
- if #movie.reviews.empty?
%p
No reviews for this movie...
-else
- #movie.reviews.each do |r|
- u = Moviegoer.find(r.moviegoer_id)
<b>#{r.vote}</b> (<i>#{Moviegoer.find(r.moviegoer_id).name}</i>) #{r.message} <br />
= link_to 'Add review', new_movie_review_path(#movie)
<br/><br/>
#{link_to 'Edit info', edit_movie_path(#movie)} - #{link_to 'Delete', movie_path(#movie), :method => :delete} - #{link_to 'Back to movie list', movies_path}
The problem probably is in the last line, because in server command line "Delete" isn't calls.
this is the server response (is the last GET that is execute when i press "Delete" on the browser) :
enter image description here
file routes.rb:
Rails.application.routes.draw do
resources :movies do
resources :reviews, only: [:new, :create, :destroy]
end
resources :moviegoers
root :to => redirect('/movies')
end
Result of command "rake routes":
Prefix Verb URI Pattern Controller#Action
movie_reviews POST /movies/:movie_id/reviews(.:format) reviews#create
new_movie_review GET /movies/:movie_id/reviews/new(.:format) reviews#new
movie_review DELETE /movies/:movie_id/reviews/:id(.:format) reviews#destroy
movies GET /movies(.:format) movies#index
POST /movies(.:format) movies#create
new_movie GET /movies/new(.:format) movies#new
edit_movie GET /movies/:id/edit(.:format) movies#edit
movie GET /movies/:id(.:format) movies#show
PATCH /movies/:id(.:format) movies#update
PUT /movies/:id(.:format) movies#update
DELETE /movies/:id(.:format) movies#destroy
moviegoers GET /moviegoers(.:format) moviegoers#index
POST /moviegoers(.:format) moviegoers#create
new_moviegoer GET /moviegoers/new(.:format) moviegoers#new
edit_moviegoer GET /moviegoers/:id/edit(.:format) moviegoers#edit
moviegoer GET /moviegoers/:id(.:format) moviegoers#show
PATCH /moviegoers/:id(.:format) moviegoers#update
PUT /moviegoers/:id(.:format) moviegoers#update
DELETE /moviegoers/:id(.:format) moviegoers#destroy
root GET / redirect(301, /movies)
rails_service_blob GET /rails/active_storage/blobs/:signed_id /*filename(.:format) active_storage/blobs#show
rails_blob_representation GET /rails/active_storage/representations/:signed_blob_id/:variation_key/*filename(.:format) active_storage/representations#show
rails_disk_service GET /rails/active_storage/disk/:encoded_key/*filename(.:format) active_storage/disk#show
update_rails_disk_service PUT /rails/active_storage/disk/:encoded_token(.:format) active_storage/disk#update
rails_direct_uploads POST /rails/active_storage /direct_uploads(.:format) active_storage/direct_uploads#create
It's hard to say exactly what's going on without seeing the corresponding routes. You might try running rake routes to see what the available routes are.
Based on the comments in your controller, I'm guessing you probably have to do something like pluralize the path link_to 'Delete', movies_path(id: #movie.id), method: :delete, but that would only fix the problem if your resource routes are misconfigured.
In order for the link to work as you have it configured your route file would either have to contain resources :movies, or a specially configured route to map to the delete action.

Ruby on Rails - Cocoon gem, Don't Pre-populate existing/added posts in edit action

My application has models Campaign & Post, I have:
class Campaign < ActiveRecord::Base
has_many :posts, inverse_of: :campaign
accepts_nested_attributes_for :posts, reject_if: :all_blank, allow_destroy: true
class Post < ActiveRecord::Base
belongs_to :campaign
My form:
= simple_form_for(#campaign) do |f|
= f.error_notification
= f.input :title
#posts
= f.simple_fields_for :posts do |post|
= render 'post_fields', f: post
.links
= link_to_add_association 'Add Post', f, :posts, wrap_object: Proc.new {|post| post.user_id = current_user.id; post }
I use Cocoon gem for nested_forms.
When I go to my campaigns#edit view, I can see all posts that were already added to a campaign (natural behavior of the gem), and I can add new posts to my campaign and/or edit existing posts .
I have also a button that has this param: add_to: 'existing_campaign' and what I am trying to achieve is, if my link has ?add_to=existing_campaign, I don't want to show/Pre-populate any of the posts that were already added to campaign, so user can only add new posts to the campaign
My link_to looks like:
= link_to 'Add Post', edit_campaign_path(campaign, add_to: 'existing_campaign'),
short explain: if edit link has param ?add_to=existing_campaign, I don't Pre-populate already added posts, if param doesn't exists, I Pre-populate posts
How can I achieve this?
Set up an attr_accessor in campaign to control whether or not existing posts should be seen...
class Campaign < ActiveRecord::Base
attr_accessor hide_posts
...
end
Set the value in your edit method
class CampaignsController < ApplicationController
def edit
#campaign.hide_posts = params[:add_to] == 'existing_campaign'
...
end
Ensure the temporary variable is in your strong parameters (so that redisplay after failed update remembers to hide posts)
def campaign_params
params.require(:campaign).permit( :hide_posts, ...
Now on your view you can do...
= f.hidden_field :hide_posts
= f.simple_fields_for :posts do |post|
= render('post_fields', f: post) unless #campaign.hide_posts && post.object.persisted?
Updating with more specifics ...
You as the design need to decide if you want to move the logic to a helper. If you aren't aware of skinny controller / fat model or another strategy for organizing your codebase read this ... Link. For now, I'm going to assume you will use a helper.
The theory & some nitpicky things ...
What's going to happen with skinny controller is you need that parameter accessible in the view logic (but that would be insecure and violate the rails way). Since you shouldn't just expose params to the view, you instead are passing the message containing the parameter's value. Object orientation teaches us to use pass messages. Rails says to use instances variables (#something) in the controller and the view has access to those.
We need to setup the conditional logic next. One of the Rails ways is to use helpers to remove extraneous logic from the view or make it readable. This qualifies as something that unless you have another reason I'm not aware of - this should be in a helper. I would be tempted here to just test for boolean and call another partial for the extra view you make.
Which means I have to assume your tests will change too (if not done in a standard way, you have to include to get access to that method/object).
Specific to your question
link_toon the page calling the nested form should be true/false...
= link_to 'Add Post', edit_campaign_path(campaign, show_posts: false)
Your controller will have #show_posts in whatever action of the campaign controller you are using (edit or new usually). You need to set #show_posts = params[:show_posts]
Write the helper ...
helpers/campaign_helper.rb
def showPosts?(show_posts)
testPart == true ? 'only_comment' : 'post_fields'
end
A new partial ... which is basically the same, but drops the simple_fields_for loop which populates the other posts.
Your _form partial will change from what you had above to ...
= f.simple_fields_for :posts do |post|
= render 'posts_fields', f: post
To ...
...
#posts
= render showPosts?(#show_posts), f: post
...
Update, I tested all the parts & got it working with your exact syntax - I ended up using ternary operator in the helper.

Assign nested attributes records to current user when using Cocoon gem

In my application I have models Post & Slides & I have:
class Post < ActiveRecord::Base
has_many :slides, inverse_of: :post
accepts_nested_attributes_for :slides, reject_if: :all_blank, allow_destroy: true
Everything works fine, only thing I need (because of how my application will work), is when a slide is created, I need to assign it to current_user or user that is creating the record.
I already have user_id in my slides table and:
class User < ActiveRecord::Base
has_many :posts
has_many :slide
end
class Slide < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
My PostsController looks like this:
def new
#post = current_user.posts.build
// This is for adding a slide without user needing to click on link_to_add_association when they enter new page/action
#post.slides.build
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Was successfully created.' }
else
format.html { render :new }
end
end
end
Any help is appreciated!
There are two ways to accomplish this:
First option: when saving the slide, fill in the user-id, but this will get pretty messy quickly. You either do it in the model in a before_save, but how do you know the current-user-id? Or do it in the controller and change the user-id if not set before saving/after saving.
There is, however, an easier option :) Using the :wrap_object option of the link_to_add_association (see doc) you can prefill the user_id in the form! So something like:
= link_to_add_association ('add slide', #form_obj, :slides,
wrap_object: Proc.new {|slide| slide.user_id = current_user.id; slide })
To be completely correct, you would also have to change your new method as follows
#post.slides.build(user_id: current_user.id)
Then of course, we have to add the user_id to the form, as a hidden field, so it is sent back to the controller, and do not forget to fix your strong parameters clause to allow setting the user_id as well :)
When I'm looking at this I see three ways to go about it, but since you're on cocoon already, I would drop the connection between user & slides - as it kind of violates good database practices (until you hit a point where you page is so popular you have to optimize of course, but that would be done differently).
You are using cocoon, but you're not utilizing the nesting of the relationship fully yet ...
The best practice would be to have cocoon's nesting create both & instead of trying to assign to current_user you call something like:
#slides = current_user.posts.find_first(param[:id]).slides
The #slides saves all the results, the .Post.find(param[:id]) finds a specific post for current_user.
Note: this is not the most optimized way & I haven't tested this, but it shows you the format of one way you can think about the relationships. You will need to hit rails console and run some tests like ...
(rails console)> #user = User.first
Next we test that there are posts available, as it's frustrating to test blanks & not get the results ...
(rails console)> #posts = #user.posts
Then we use the find method & I'm going to use Post.first just to get a working id, you can easily put "1" or any number you know is valid ...
(rails console)> #post = #posts.find(Post.first)
Finally, we go with either all slides to make sure its a valid dataset
(rails console)> #post.slides
If you want a specific slide later & have a has_many relationship just tag that find method on the .slides after.
Also one last thing - when you state earlier in there you need the current_user to be related, you can use an entry in your model.rb to create a method or a scope to get the data & allow you to link it to the current_user more easily & even drop some directed SQL query with the .where method to pull that information up if performance is an issue.
I spotted a second optimization in there ... if everything really is working - don't worry about this!
And don't forget about the strong_parameters nesting to do this fully ... Strong Param white listing
Basic format ... `.permit(:id, :something, slide_attributes: [:id, :name, :whatever, :_destroy])

Rails controllers, rendering view from different controller, saving form inputs and error messages

Two controllers: Users and Tasks.
Main page for Users = Users/user_id.
Form on main page used to input data into tasks model.
This process handled by the Tasks Controller.
Successful input: redirect and load tasks from database, OK, all working.
Unsuccessful input, just need to refresh main page so we keep form input and specialised (non-flash) error messages
I can't seem to get the Tasks Controller to deliver the original page. Error is Missing template users/1(which is the correct syntax if I were to visit in my browser).
Should I be calling an action and passing params? Any help for this beginner would be really appreciated.
def create
#task = current_user.tasks.build(task_params)
if #task.save
flash[:success] = "New task created!"
redirect_to user_url(current_user)
else
flash[:error] = "Task not saved! Please see guidance by form labels"
render "users/#{current_user.id}"
end
end
private
def task_params
params.require(:task).permit(:label, :address, :content)
end
end
users/1 is not a template, it's a path. users/show is the template in this case.
mind that the only reason to use render is to render a template in the scope of your current controller action rather than the normal one.
i.e. you probably need to have #user etc set, or the users/show template will be upset about missing variables.
In this case it might be easier just to redirect_to user_path(id) and allow the users/#show controller action set up the #user variable etc.

Rails 4, not saving #user.save when registering new user

When I try to register an user, it does not give me any error but just cannot save the user.
I don't have attr_accessible. I'm not sure what I am missing. Please help me.
user.rb
class User < ActiveRecord::Base
has_secure_password
validates :email, presence: true,
uniqueness: true,
format: { with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i }
validates :password, presence: true, length: {minimum: 6}
validates :nickname, presence: true, uniqueness: true
end
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params) # Not saving #user ...
if #user.save
flash[:success] = "Successfully registered"
redirect_to videos_path
else
flash[:error] = "Cannot create an user, check the input and try again"
render :new
end
end
private
def user_params
params.require(:user).permit(:email, :password, :nickname)
end
end
Log:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"x5OqMgarqMFj17dVSuA8tVueg1dncS3YtkCfMzMpOUE=", "user"=>{"email"=>"example#example.com", "password"=>"[FILTERED]", "nickname"=>"example"}, "commit"=>"Register"}
(0.1ms) begin transaction
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = 'example#example.com' LIMIT 1
User Exists (0.1ms) SELECT 1 AS one FROM "users" WHERE "users"."nickname" = 'example' LIMIT 1
(0.1ms) rollback transaction
Regarding our short discussion in the comments, it appears that one or two things are happening to cause #user.save to return false:
One of the validation rules are failing
A callback within your model is returning false, thus halting processing and rolling back the transaction
There are a few quick ways to debug the validations. I figured I could describe them so you could learn a few options.
A. Change the call within the if statement to instead use the bang method of save:
if #user.save!
This will cause the app to raise an exception if validation fails, displaying the validation errors within the browser on your screen. In this particular scenario, you'd want to remember to remove the ! after you're done debugging because you probably don't want the final version of your app doing that.
Or...
B. Within the else statement, add this line:
raise #user.errors.to_yaml
This will display the validation errors within the browser on the screen. Of course, remember to remove this line after you're done debugging.
Or...
C. Within the else statement, add this line and then run the form post:
puts #user.errors.to_yaml
This will display the validation errors within your console. You'll want to remember to remove this line after you're done debugging, but it's "less worse" if you happen to forget because at least the extra info is only output to STDOUT.
You may want to try each of these just to get a little practice and to see what your options are in simple debugging scenarios like this.
High chances that error is in password confirmation. You use has_secure_password from Rails, which automagically handles password confirmation for you. And here is the problem - you don't have it before user creation. Thus just add. For details check out similar question on has_secure_password
And check, that you have password_digest:string in users table :)