Rails - Associated model's show action & its corresponding view? - ruby-on-rails

I'm building a simple app that has a typical User model & a Profile model. User has_one Profile and Profile belongs_to User. All seems to be working fairly well as I am basically following the Michael Hartl tutorial. However, when I try to render the view of something from the profiles table (show action), I get an error (no id) AND the profile record I created gets deleted!
Questions:
In my ProfilesController, am I defining my show action properly for the simple view I am trying to render?
Why does simply visiting the url localhost/3000/profiles/1 delete the profile record? I think it has something to do with dependent destroy (b/c removing that will stop this behavior), but I think I want to keep dependent destroy, correct?
Routes
resources :users
resources :profiles
Models
Class User < ActiveRecord::Base
has_one :profile, dependent: :destroy
Class Profile < ActiveRecord::Base
belongs_to :user
ProfilesController
def new
#profile = current_user.build_profile
end
def create
#profile = current_user.build_profile(params[:profile])
if #profile.save
flash[:success] = "Profile created dude!"
redirect_to root_path
else
render 'new'
end
end
def show
#profile = Profile.find(params[:user_id])
end
View (profiles/show.html.erb)
<p>Display Name: <%= #profile.display_name %></p>

Check your rake routes. You will see that for your Profile#show, you have URL structure like: /profiles/show/:id.
Thus, params, must be expecting the :id instead of :user_id.
If by /profiles/show/3, you wish to show profile 3, then:
def show
#profile = Profile.find(params[:id])
end

Related

Rails Form Associations - Passing id From Previous Page

Trying to figure our how to set up associations in form.
I have 3 models:
class Request < ActiveRecord::Base
has many :answers
has many :users, through: :answers
end
class Answer < ActiveRecord::Base
belongs to :user
belongs to :request
end
class User < ActiveRecord::Base
has many :answers
has many :requests, through: :answers
end
I am trying to figure out: how to have a User link to Answer#new from Request#Show, and then create an Answer record passing in the Request#Show request_id from the previous page - creating an association between the User's Answer and the Request he was viewing.
My method of doing this now is: I flash the request_id value on Request#Show, and then when a User links to Answer#new, it passes the flashed value into a hidden form tag on Answer#new. This does not seem like the best way to do this.
Any thoughts?
Kudos for the creative approach using flash, however your right there is an easy way. You can pass parameters much between controllers just like passing parameters between methods using the route names.
I didn't quite follow what it was you were trying to achieve in this case but it looks like this blog entry here should get you started..
https://agilewarrior.wordpress.com/2013/08/31/how-to-pass-parameters-as-part-of-the-url-in-rails/
Good luck!
User link to Answer#new from Request#Show
This can be achieved with either sessions or nested resources (or both!). Let me explain:
I would definitely add a nested resource to your requests routes:
#config/routes.rb
resources :requests do
resources :answers, only: [:new, :create] #-> url.com/requests/:request_id/answers [POST]
end
This gives you the ability to call a "nested" route (IE one which sends data to a child controller, and requires "parent" data to be appended to the request).
In your case, you want to create an answer for a request. The most efficient way is to use a routing structure as above; this will allow you to use the following controller method:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
#request = Request.find params[:request_id]
#answer = #request.answers.new
end
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.save
end
private
def answer_params
params.require(:answer).permit(:title, :body)
end
end
The above gives you the ability to create an answer by passing the request_id to the nested route. You must note the corresponding route will require a POST method in order to work.
You don't need the new method. If you wanted it, it can easily be handled with the above structure.
Passing the user is a little more tricky.
You can either use the routes, or set a session.
I would personally set a session (it's cleaner):
#app/controllers/requests_controller.rb
class RequestsController < ApplicationController
def show
session[:user_id] = #user.id #-> I don't know how you populate #user
end
end
This will give you the ability to access this session here:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
user = User.find session[:user_id]
end
end
#app/views/requests/show.html.erb
<%= link_to "New Answer", request_new_answer_path(request) %>
--
If you're using Devise, the user object should be available in the current_user object (which means you don't have to set session[:user_id]):
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
def new
## current_user available here if using devise
end
end
To assign a #user to the new answer record, just do this in answers#create:
#app/controllers/answers_controller.rb
class AnswersController < ApplicationController
...
def create
#request = Request.find params[:request_id]
#answer = #request.answers.new answer_params
#answer.user = current_user
#answer.save
end
end
Something like this worked for me:
I have two models (Formula and FormulaMaterial)
Formula has_many FormulaMaterials, which belongs to Formula
My Formula controller sets #formula like so:
#formula = Formula.find(params[:id])
I list my Formula Materials in my Formula show.html.erb by declaring it in my Formula controller like so:
#formula_materials = FormulaMaterial.where(:formula_id => #formula)
When I want to add a new FormulaMaterial to my Formula, the "New Formula Material" button in my show.html.erb file looks like this:
<%= link_to 'Add Material To Formula', new_formula_material_path(:formula_id => #formula), class: "btn btn-success" %>
In the "new_..._path" I set the associated id to the #formula variable. When it passes through to the new.html.erb for my FormulaMaterial, my URL looks like so:
http://localhost:3000/formula_materials/new?formula_id=2
In my FormulaMaterial new.html.erb file, I created a hidden_field that sets the value of the association by using "params" to access the formula_id in the URL like so:
params[:formula_id] %>
I am not sure if this is the best way to do this, but this way has allowed me to pass through the view id from the previous page as a hidden, associated and set field in the form every time.
Hope this helps!

Trouble creating new nested resource with has_one relationship

I am trying to create a nested resource with a has_one relationship. When the form is submitted all the parameters look right, and it routes correctly to the create action. However, something is making it error out inside the create action.
Error: undefined method 'new' for nil:NilClass
Relevant resources:
#models/employer.rb
class Employer < ActiveRecord::Base
has_one :employment_training_opportunity, dependent: :destroy
end
#models/employment_training_opportunity.rb
class EmploymentTrainingOpportunity < ActiveRecord::Base
belongs_to :employer
end
And the controller:
#controllers/employment_training_opportunities_controller.rb
class EmploymentTrainingOpportunitiesController < ApplicationController
def create
#employer = Employer.find(params[:employer_id])
# error highlights line below
#employment_training_opportunity = #employer.employment_training_opportunity.new(employment_training_opportunity_params)
if #employment_training_opportunity.save
redirect_to #employer, flash: {success: "Employment Opportunities was successfully created"}
else
render :new
end
end
end
Some more clues I got:
When I put in a binding.pry I noticed that it is properly finding the employer resource, so #employer is returning an active record object.
When I call #employer.employment_training_opportunity it returns nil. This makes sense because this employer resource does not yet have an associated employment_training_opportunity resource.
But when I call #employer.employment_training_opportunity.new, that is when it errors out with NoMethodError: undefined method 'new' for nil:NilClass. I just don't get it because this works just fine with has_many associations for nested resources.
#employer.employment_training_opportunity is nil, so you can't call new on it. You could do this:
#employer.employment_training_opportunity = EmploymentTrainingOpportunity.new(employment_training_opportunity_params)
Which will create the new record and set up the relationship. But this is cleaner, IMO:
#employer.build_employment_training_opportunity(employment_training_opportunity_params)

Passing params[:id] to create method in Rails?

I'm trying to write a create method that collects the ID of the profile the user is currently viewing, along with some other information that is irrelevant to this question. However, because the create method POSTs rather than GETs (as I understand it), the value of params[:id] doesn't exist so it's always null. My code is as follows:
class PostsController < ApplicationController
def new
#Post = Post.new
end
def create
#Post = Post.new(post_params)
#Post.user_id = current_user.id
#Post.target_id = params[:id] #this
if #Post.save
redirect_to :back, notice: "You added a post!"
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Is there a way to get the value of params[:id] from elsewhere, perhaps from my Users controller in the show method where it actually exists?
Keep in mind that I was successfully able to create a hidden field in the Posts form, but I didn't like the fact that users were able to edit the value using Developer Tools, allowing them to change what profile the post would go to.
If there is a direct relation between the Target and the Post model, you should express this in the controller and model structure: link
This expresses your intention and it provides all the rails automations like routing, url helpers, form helpers, a.s.o.
In your concrete example, my guess is the Target would have many Posts:
class Target < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :target
end
Which would lead to the following route structure:
resources :targets do
resources :posts
end
To create a new post for the current target you would post to:
targets/:target_id/posts
And the target id would be accessed via params[:target_id]

Issue with PUT in Rails

I'm working on creating a survey application where I create the survey on my rails server and as long as the user hasn't taken the survey the next time they open up my Android app they'll be displayed the survey to take. Once the user takes the survey I'd like to update the survey to contain that user so that they are no longer sent the same survey each time they log in. I'm using a HABTM relationship between users/surveys.
I'm able to get the survey/let the user take it/create the user choices back on the server but I'm struggling with updating the survey with the user that took just the survey. I'm pretty sure my problem is my PUT request (the auth_token is used to identify the user).
Started PUT "/api/v1/surveys/4?auth_token=8d707d9fa2b279f381eb416f1be887c0"
Here's my controller:
module Api
module V1
class SurveysController < ApplicationController
# before_action :restrict_access
respond_to :json
def survey
#survey = Survey.check_survey params[:auth_token]
redirect_to api_v1_survey_path(#survey)
end
def update
#survey = Survey.find(params[:id])
#user = User.find_user_by_token params[:auth_token]
complete_survey #survey, #user
end
My model:
class Survey < ActiveRecord::Base
has_many :surveytizations
has_many :questions, :through => :surveytizations
has_and_belongs_to_many :users, -> { uniq }
def complete_survey (survey, user)
survey.users << user
survey.number_taken += 1
if survey.number_taken == survey.survey_limit
survey.survey_finished = true
end
end
In Using curl to test Rails routes it goes into how to update an attribute of a user. I'm pretty new to curl - if I wanted to test updating an array of users with an additional user, how would I do that? I tried survey[users]=user1 but that doesn't seem to work.
Thanks in advance for your help!

Is there a plugin or gem that can help me do "invite a friend" capability in rails?

I want to add the ability for users to invite a friend.
The email should be generated so that, if someone clicks on the link and register, that person is automatically a friend.
Not sure what the options are, but wanted some ideas and strategies as an alternative to building it from scratch.
I'm not aware of any gems that handle the entire process (user >> email >> signup). If you're just looking to create the relationship when a user comes from a specific link, create a special invitation route (the separate controller isn't necessary but just to make it clear):
# routes.rb
match '/invite/:friend_id' => 'public#invite', :as => :invite
# PublicController
def invite
session[:referring_friend] = params[:friend_id]
redirect_to root_path
end
# UsersController
def create
#user = User.new(params[:user])
if #user.save
#user.create_friendship(session[:referring_friend]) if session[:referring_friend]
...
else
...
end
end
If you want to track conversion metrics, I'd recommend creating a link model and using that to track clicks and signups:
class Link < ActiveRecord::Base
belongs_to :user
attr_accessible :user, :user_id, :clicks, :conversions
def click!
self.class.increment_count(:clicks, self.id)
end
def convert!
self.class.increment_count(:conversions, self.id)
end
end
# routes.rb
match '/invite/:link_id' => 'links#hit', :as => :invite
# LinksController
def hit
link = Link.find(params[:link_id])
link.click!
session[:referring_link_id] = link.id
redirect_to root_path # or whatever path (maybe provided by link...)
end
# UsersController
def create
#user = User.new(params[:user])
if #user.save
if session[:referring_link_id]
link = Link.find(session[:referring_link_id])
link.convert!
#user.create_friendship(link.user_id)
end
...
else
...
end
end
Which method you choose depends on what you'll want to track down the road.
I don't know gem for rails. But there's an extension for Spree, rails based e-commerce project. Check it out & probably you can refer how it's implemented.
https://github.com/spree/spree_email_to_friend
I don't know about some gem to support this, but solution should be rather trivial. I guess you need Friendship model, you can place some status in it like 'waiting_for_approvment' and send in mail link with that Friendship model id. When user accepts either way you just change status to 'approved' or even 'rejected' if you want to track that too.
Start by defining the relationship:
class User < ActiveRecord::Base
has_and_belongs_to_many :friends, :class_name => "User", :join_table => "friends_users"
end
So really, User relates to itself with a different name. Then you can use something along the lines of:
#current_user.friends << #selected_user
in your controller.

Resources