two controllers in one view in Rails - ruby-on-rails

I have two controllers, one for the users and one for the microposts.
What I'm trying to do, is to put on the same view, the users informations and the microposts form.
The issue is the url of this page is www.mywebsite.com/users/18 so I have only access to the users controller.
I tried to do:
render :file => '/posts/new' but it doesn't work.
The only solution I found is to put everything on one controller but it will be a mess.
I really want a separate controllers for each.
So I don't know how to do this. Any idea?

What I'm trying to do, is to put on the same view, the users informations and the microposts form
I think you are trying to create a new micropost inside the user show view. If so, you can include the below:
<%= form_tag(controller: :microposts, action: :create) do %>
...
<% end %>
When you submit the form, it will call the action create in the microposts controller.

Are you working through Hartl's tutorial? Well, anyway, all you have to do is initialize all instance variables you need in the view in the controller action:
users/18 is probably pointing to users#show, thus you need to add something like
class UsersController < ApplicationController
...
def show
#micropost = current_user.microposts.build
...
end
...
end
in controllers/users_controller.rb (or something that fits your model) to initialize a new micropost for the micro post form, or just
#micropost = Micropost.new
if you don't have/need an association to the user. (Michael Hartl is doing it on the home page, though.)

Related

How to call a mailer action in a controller from a link_to post

I'm trying to call a mailer I have in the create action in my interests controller:
def create
if #interest.save
UserMailer.interest_to_seller(#interest).deliver_now
etc....
This works great when I'm going to the CRUD /interest/new form and creating it there, but I'm actually trying to call this from a post method in a different place. It's as if the create actions is being skipped all together, but that's impossible because the record is being created, right?
My routes.rb:
resources :seller_listings do
post :add_interest, on: :member
end
And in my view:
<%= link_to add_interest_seller_listings_path(m), method: :post do %>
Add Interest & Fire Email!
<%end%>
And in the interest controller, I have this action that that the post in the routes file calls:
def add_interest
current_user.mark_buyer_interest(seller_listing)
Which references this in the user.rb:
def mark_buyer_interest(listing)
buyer_interests.create(seller_listing: listing.id, accepted_by_seller: 0)
end
This works well, records are created, but the Mailer is skipped. Any suggestions? I've never done it this way before so any advice would be great. Thank you!
I think you should call UserMailer.interest_to_seller(#interest).deliver_now again in mark_buyer_interest.
def mark_buyer_interest(listing)
new_interest = buyer_interests.create(seller_listing: listing.id, accepted_by_seller: 0)
UserMailer.interest_to_seller(new_interest).deliver_now
end

Rails 4 - Couldn't find User without an ID

I'm new to rails, so any explanation & advise would much appreciated.
i have a webpage in which i would like any user to view that page not just the current_user, but i am unsure how to correctly define the instance variable #user in my controller
in my static_pages_controller.rb i have the below action recruiterpg
static_pages_controller.rb
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
in my controller, i have defined user as #user = User.find(params[:user_id]) but this breaks my code in the views; views/static_pages/recruiterpg.html.erb
when i define user as #user = current_user my code in the views works perfectly fine
what am trying to do is: for my views, the recruiterpg.html.erb, i would like
any user to be able to view the page not only the current_user to
view the page. Could one kindly advise me and explain to me how to
define #user correctly in my status_pages_controller.rb. i also
tried #user = User.find(params[:id]) but my code still breaks in the
views - i get the error message
Couldn't find User without an ID
You need to make sure you are passing a user_id to the recruiterpg action. For example, try this url in your browser (set user_id to a known id in the users table):
http://localhost:3000/dashboard?user_id=1
A suggested modification to your action:
def recruiterpg
#user = User.find params.require(:user_id)
#adverts = #user.adverts
#applications = #user.forms
end
If params[:user_id] isn't defined, you want to find a way to make visible what is being defined.
If you throw the following statements into your controller...
def recruiterpg
...
puts params
...
end
...you should see something like the following get spit out in your console when you load the page...
{"controller"=>"static_pages", "action"=>"recruiterpg", "id"=>"49"}
Take a look at the Rails guide for parameters. They can get defined in one of three ways.
One: As a query string similar to Sean's answer above.
Two: Routing parameters. See section 4.3 in the Rails guide. In your case, that would mean you should have something like the following in routes.rb:
get '/dashboard/:user_id' => 'staticpages#recruiterpg'
Note that there's nothing magic about :user_id in that string.
Three: From a form which it doesn't seem like applies here, since a user isn't submitting data.
Since you're new, here is some information for you:
User Story
Firstly, the best way to resolve errors is to identify your user story.
A "user story" is a software principle in which you put the "user's perspective" first -- explaining how the software should work in conditions defined from how the user engages with it.
One of the main issues you have with your question is your user story is very weak; it's hard to decifer what you're trying to achieve.
Next time you ask a question, you should try your hardest to describe how the user should see your app, before providing code snippets :)
Controller
Your main issue is an antipattern.
An antipattern is basically a "hack" which will likely break another part of your app in future. Kind of like duct tape for software):
#app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def recruiterpg
#user = User.find(params[:user_id])
#adverts = #user.adverts
#applications = #user.forms
end
end
So you're showing a static page but yet you want to populate it with data?
Hmm...
What you should be doing is something like the following:
#config/routes.rb
resources :users do
resources :recruiters, only: :index #-> url.com/users/:user_id/recruiters
end
#app/controllers/recruiters_controller.rb
class RecruitersController < ApplicationController
def index
#user = User.find params[:user_id]
#adverts = #user.adverts
#applications = #user.forms
end
end
This will allow you to populate the following view:
#app/views/recruiters/index.html.erb
<%= #adverts %>
--
It's important to note the structure of the controller / routes here.
The issue you have is that you're calling a "static page" and expecting to have params available to find a User. This can only happen if you have params available...
Params
Rails is not magic, and as such if you want to look up a user, you have to provide the parameters to do so.
This is why you're able to look up current_user -- the params are already set for this user.
As such, you'll need to use something called nested routes in order to attain a user ID other than that of current_user:
#config/routes.rb
resources :users do
resources :recruiters #-> url.com/users/:user_id/recruiters
end

Action show to find value from hash instead of id

I am still learning rails and have done a lot of readings, but I am not very clear about how params, 'show' actions work yet.
For example we have UsersController, 'index' action is showing all the users with the code #user = User.all, and 'show' action is looking into each users, by using the code #user = User.find(params[:id])
I understand that they are all from the database, where User is a model.
However in my scenario, what if the data I am showing in views, doesn't go through database, instead in the 'index' action it is something like this -
#user = [{name => "alex"}, {name => "peter"}, {name => "john"}]
and in my 'show' action, how can I write the code so that it finds the users by name?
In your Rails app, the data that you show in your views, do not necessarily have to come from/through the database. You can always show any data you want in your views.
For example, in your index action, if you have this:
#users = [{name => "alex"}, {name => "peter"}, {name => "john"}]
Then, in your index view, you can show only those users by looping through the #users instance variable.
Same for show page as well.
If you want to show the users by name in your show page, you have to set the users by name in an instance variable e.g. #users_by_name:
#users_by_name = User.find_by(name: user_name)
# or you can hard code the values if you want like index action
and then this #users_by_name instance variable will be available in your show view so that you can loop through that and show the user names.
Originally, the show page is designed for showing a particular user related information, but you can show whatever information you want going against the conventions.
To be able to have a route like this: localhost:3000/users/alex that will show the user alex's information, you can add a route in your routes.rb file:
get 'users/:name', to: "users#show"
And, in your controller's show action, something like this:
def show
#user = User.find(params[:name])
end
Then, show the #user information in your view page.
P.S. This is not a good idea to find user by name as there might be more than one user with same name in the database and it will create conflict/ or give wrong data in such situations.
In show action , we search the user specific record not all.
So , we have to provide some unique identifiers as parameters to find the specific record.
For eg. Your view should be similar to the params we are passing as below:
<% #user.each do |user| %><br>
<%= link_to user.name, user_show_path+"?name="+user.name %><br>
<% end %><br>
In show action , write the code
def show
#user = User.find_by(:name => params[:name])
end
Also in routes.rb , write the below code:
get 'users/:name', to: "users#show"
For the above solution, make sure that name field will be unique.
My original question is that if it is possible for 'show' action not to go through database
Sure.
Your show action can be the following if you wanted it to:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
#user = "me"
end
end
You really don't have to do anything specific in your application, Rails is just a framework and has certain conventions if you want it to work efficiently.
What you're asking is if you can populate your #user object from a third party set of data...
... Yes you can ...
The way to do it would be in the model, not the controller:
#app/models/user.rb
class User < ActiveRecord::Base
# populates from Hash
end
You'd then be able to populate the data in the controller from the model again:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
#user = User.__________ #-> pull from your hash
end
end
finds the users by name
That's simple - just pass the name through the url: url.com/users/marine_lorphelin
This will set the :id parameter to marine_lorphelin, with which you'll be able to look up the name through your model:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def show
#user = User._______
end
end
If you were using a database with your user model, you'd be able to use the following:
def show
#user = User.find_by name: params[:id]
end
Since you're not, you'll have to attach your XML hash to your model somehow. This, I don't know without specifics such as where you're getting your data from, how you're accessing it, and which routes you're going to send to invoke it.

Rails controller view bug

Alright total noob, tried to find out what was happening but I haven't seen this come up with anyone else as far as I could find.
I'm going through a very basic blog tutorial with rails for the second time. made the home controller and the index view for it. all good. Next i made the 'posts' controller with basic classes and then views for index, edit, new, and show.
class PostsController < ApplicationController
def index
end
def new
end
def create
end
def edit
end
def update
end
def show
end
def destroy
end
end
The html for the views in their respective files
<h1>New Posts</h1>
<h1>Edit Posts</h1>
<h1>Show Posts</h1>
PROBLEM: In the browser when I go to localhost:3000/posts/edit it displays "Show Posts" instead of "Edit Posts". /show also displays "Show Posts". /new works fine and displays "New Posts".
I checked and it looks like localhost:3000/posts/literallyanything will display "Show posts".
I know this is potato level but I thought it was really odd and can't figure out why that would happen
Assuming you have a resource route for posts defined in your routing file like this:
resources :posts
you need to hit
localhost:3000/posts/<post id>/edit
More docs on routing here:
http://guides.rubyonrails.org/routing.html
You can always run rake:routes from your terminal to find out what route you need to hit for any given controller action.

Rails - Partial in another view can't access its controller

I'm trying to build a profile page that displays posts sent only to the requested user, and allows the visitor to write a post of their own. Because this simplified example should have two distinct controllers: users and posts, I made partials for each post action to render within the user's show action.
Directory structure for my views directory looks like this:
- posts
- _index.html.erb
- _new.html.erb
- users
- show.html.erb
... (etc.)
Section that displays these partials within the user's show.html.erb:
<section>
<h3>Posts:</h3>
<%= render '/posts/new', :post => Post.new %>
<%= render '/posts/index', :posts => Post.where(target_id: params[:id]) %>
</section>
I eventually found out that you could pass variables into the partial in this render line, and though this works, it's very messy and probably doesn't follow the best practices.
Ideally, I'd want these partials to be connected with the posts controller so I can write more complex database queries in a place that isn't the view:
class PostsController < ApplicationController
def new
#post = Post.new
end
def index
#posts = Post.where(target_id: params[:id])
end
def create
#post = Post.new(post_params)
#post.user_id = current_user.id
#post.target_id = params[:post][:target_id]
if #post.save
redirect_to :back, notice: 'You published a post!'
else
render new
end
end
private
def post_params
params.require(:post).permit(:body)
end
end
Currently, I haven't found a way of doing this. I know this is a newb question, but thanks for any help in advance.
You are attempting to treat your controllers like models: doing the post work in post controller and the user work in user controller. But controllers are task-oriented, not model-oriented.
Since you want posts info in your user form, it's typical to gather it in the user controller. E.g.
class UsersController < ApplicationController
def show
...
#posts = Post.where(user_id: user.id)
end
end
That #posts instance variable is visible in the show template and any partials it calls. But many coders prefer to send it explicitly through render arguments, as more functional:
<%= render '/posts/post_list', posts: #posts %>
For one thing it's easier to refactor when you can see at a glance all of the partial's dependencies.
I agree somewhat with #Mori's advice. As he said, you are trying to put too much logic into the controller. I think this was a result of you trying to get it out of the view, which is the right idea, but you want business logic to be in the model.
Also, those index and new actions for PostsController are never going to be called. When you are calling the render posts/new for example, that is rendering the view, not the controller action. So, those controller actions have no reason to exist.
I would implement the fix in perhaps a different way than Mori described. It's a recommended practice to try and pass as few instance variables from the controller to the view as possible (see 3rd bullet in the linked section).
Since it's really the show action of the UsersController we are talking about here, I as someone trying to understand your code would assume the instance variable you are passing to the show view is something like #user.
You may want to use an includes method when instantiating the #user object. The includes statement will allow you to load the additional models you will need to instantiate using the minimum number of queries possible (preventing an N+1 query situation). You probably don't want to load every single one if there are thousands of matching posts, so I put an arbitrary limit of 10 on that.
UsersController
def show
#user = User.find(params[:id]).includes(:received_posts).limit(10)
end
#....
View
<section>
<h3>Posts:</h3>
<% unless #user.id == current_user.id %>
<%= render 'posts/form', post: Post.new(user_id: #user.id) %>
<% end %>
<%= render #user.received_posts %>
</section>
Putting the partial for a new post instead as a view called posts/form will allow you to reuse that form if you want to render an edit action (form_for knows which action to use on submit by calling the passed model's persisted? method).
Note that this code assumes the User model has the second relationship with posts set up to be called received_posts, but you can change it to whatever reflects the reality. By passing the received_posts collection to the render method, Rails is smart enough to know that if you want to render a collection of Post models to look for a posts/_post partial and render one for each Post. It's a little cleaner looking IMO. Just make sure to move your posts/show code into that. posts/show implies this is its own action and not something used as a partial for something else.

Resources