I'm working along with Railscast 407 (it's a pro/paid episode) where Ryan Bates adds public activity functionality to a site, namely, each activity (create or update recipes or comments on recipes) is displayed in a public feed. At one point in creating the app, he uses the code below to render out a partial from the /views/activities folder, however, he also adds a comment subfolder so the create partial path is like this views/activities/comment/_create.html.erb and the link to it is like this
<%= render "activities/#{activity.trackable_type.underscore}/#{activity.action}", activity: activity %>
<h1> Activities </h1>
<% #activities.each do |activity| %>
<%= div_for activity do %>
<%= link_to activity.user.name, activity.user %>
<%= render "activities/#{activity.trackable_type.underscore}/#{activity.action}", activity: activity %>
<% end %>
<% end %>
I'm pretty certain I copied his code correctly, but I'm getting this error when I try to follow along, which
The partial name (activities/comment/) is not a valid Ruby identifier; make sure your partial name starts with a letter or underscore, and is followed by any combinations of letters, numbers, or underscores.
However, he doesn't get that error in the episode. Here's a screenshot Here's a screenshot of his directory
Fyi, the actual partial at this stage of the episode looks like this
commented on <%= link_to activity.trackable.recipe.name, activity.trackable.recipe %>
Can you explain why this isn't working for me?
At one point in the episode, Ryan Bates sets the default action to nil in the method that tracks activity (in the application controller)
def track_activity(trackable, action = nil)
current_user.activities.create! action: action, trackable: trackable
end
However, he later changes the default to the parameter action like this
def track_activity(trackable, action = params[:action])
current_user.activities.create! action: action, trackable: trackable
end
If you use the former code, then unless you explicitly declare the action, it'll be nil in the db, and then the view code in the OP will trigger the error. Therefore, it's safer to use the second version of track_activity.
Thanks to comment by #jvnill for pointing this out
Related
I have a controller for customer. In the new action, I redirect to another page, which belong to the pages controller
class CustomersController < ApplicationController
def new
redirect_to register_path
end
Would it be possible to create the object in the registration action like this?
class PagesController < ApplicationController
def registration
#customer = Customer.new
end
end
I believe the setup is something like this: you have models in your application for a Customer and, say, an Agent. Your website users register as either and the desire is to have a single HTML page (URL) with both options available. They choose which one they are and submit some fields, say name/email/password. To keep it simple, without bothering with JavaScript to hide things behind tabs, you have something like:
**Customer**
Name: ___________
Email: __________
Password: _______
[Submit]
**Agent**
Name: ___________
Email: __________
Password: _______
[Submit]
You have a few options here to avoid your guilty feeling in the Rails controllers:
Go heavy client-side JavaScript. Don't have the new actions on the controllers. The JavaScript creates the page elements. The create action becomes a JSON API endpoint, thereby avoiding the problem in the Ruby application. This is obviously a significant architectural deviation from where I think you are today.
Use a little bit of JavaScript to dynamically load the correct 'partial' into the DOM when the user switches between the options. Avoids the underlying problem in the question by effectively separating the 'pages' out to the two controllers. The Pages→registration action does not need to set any instance variables for the view. The JavaScript deals with the partial loading. (see 'link_to' and the 'remote' option)
Don't include both forms in the same HTML page, just default to one, say the Customer one, and provide a link to navigate to the Agent one, e.g. a link in a tab, or a plain link like "Not a Customer? Register as an Agent." In this scenario, you have a neat mapping to the Ruby MVC design, each of the pages are just the new action of its relevant controller. The downside is a page load to change between the two options. This is the simplest, plainest choice … if you can get the boss to agree to the UX. PS: if you are using turbolinks, then the 'feel' of this option in the browser will be not far from option (2).
Stick to your design
Keep in mind that you will have difficultly dealing with error conditions and messages with option (4). You can do it, but the code won't be simple or easy to maintain.
If option (4) is a must, one simplification can be the create actions on each of the controllers rendering their own new in case of an error. If you submit the 'Agent' form from your starting page, with errors, to the Agents→create action, that action finishes with a render 'new' to show the user the Agents→new page. No 'customer' form is visible. You could then add a sprinkle of option (3) in there with a "Not an Agent? Register as a Customer." link under the form. Doing this greatly simplifies your error handling.
Which then leads to a suggestion for your original problem. Cheat. Don't have an #customer instance variable for the new actions (or the registration action). Use partials for the customer and agent forms, and pass in a new object to form_for, e.g.
pages/registration.html.erb
<%= render 'customers/new_form' %>
<%= render 'agents/new_form' %>
customers/new.html.erb
<%= render 'customers/new_form' %>
customers/_new_form.html.erb
<% form_for Customer.new do |f| %>
<%# include the inputs shared with the edit action %>
<%= render 'fields', f %>
<%= f.submit %>
<% end %>
customers/_fields.html.erb
<%# 'f' is one of the locals passed to the partial %>
<% f.input_field :name %>
<% f.email_field :email %>
<% f.password_field :password %>
customers/edit.html.erb
<% form_form #customer do |f| %>
<%= render 'fields', f %>
<%= f.submit %>
<% end %>
… then you would follow the same pattern for:
agents/new.html.erb
agents/_new_form.html.erb
agents/_fields.html.erb
agents/edit.html.erb
I have ideas controller and static_pages controller. The latter has home action which displays all ideas and which i also use as root path.
I want the user be able to Edit the displayed ideas. So far i have this:
<% if #ideas.empty? %>
<p>Share your ideas! See what people think about it.</p>
<% else %>
<% #ideas.each do |idea| %>
<div class="panel panel-default">
<div class="panel-heading"><%= idea.name %></div>
<div class="panel-body"><%= idea.description %> <br>
<%= link_to 'Edit', edit_idea_path(idea.id) %>
</div>
</div>
<% end %>
<% end %>
I had an issue with an empty idea id which i solved by adding idea.id inside edit_idea_path
Now my question is, is that the proper, Rails way of doing it? In what other way can i fetch the idea object from this index page and use it in my ideas controller instead of static_pages controller?
I tried playing around with routing, but I have very vague understanding of it despite reading the guides and others code. I'd appreciate any insight about this matter.
First you need to understand that the requirement of your project defines what you should do in the code, whithout of concerning about the proper way to do something. You just need to follow the rails conventions.
http://guides.rubyonrails.org/active_record_basics.html#convention-over-configuration-in-active-record
Now, back to your question. You just need to create an action (that will handle a view) in your ideas_controller that will manage the edition of the data sended by de static_pages_controller, i will call it (just for example) edit_static_ideas and receive the data with params:
In your ideas_controller : app/controllers/ideas_controller.rb
def edit_static_ideas
#idea = Idea.find(params[:id])
end
Then you need to create the view in your views->ideas folder. An name it, just to continue my example i'll name it edit_static_idea.html.erb. And set the load of the data you get in #idea as a form or a form_for. Then you can submit that edited data and upload it into other action.
Then you have to configure your routes file and add
config/routes.rb
get 'edit_static_idea/:id', to: 'ideas#edit_static_idea', as: 'edit_ideas'
After that, if you run "rake routes" in your console (inside your rails project), you should see your new route (yay!)
Now you have to take the path in your route and use it in you static_pages_controller's view to redirect it to the edit_idea's view handle it by ideas_controller. And be sure that you also send the id of the selected item.
app/views/static_pages/home.html.erb:
<%= link_to 'Edit Idea', insert_your_edit_idea_obtainedinrakeroutes_path(id: idea.id) %>
At last, you only need to configure the form in your edit_static_idea.html.erb and assign it an upload/save route and redirect it to the view that you want.
for example:
In your routes file: config/routes.rb
patch 'save_edited_idea', to: 'ideas#save_edited_idea', as: 'save_edited_idea'
In your ideas_controller: app/controllers/ideas_controller.rb
def save_edited_idea
#idea = Idea.find(params[:id])
respond_to do |format|
if #idea.update(idea_params)
format.html { redirect_to the_view_that_you_want_path(id: #idea.id), notice: 'Data saved without problems.' }
else
flash.now[:alert] = "error"
format.html { render :offer }
end
end
end
I didn't wanted to be so detailed, because i wanted to help you to understand what you have to do. I hope it helps :P
Ok, So i'm trying to place the form found in the "_form.html.erb" in the "index.html.erb" of my ruby project crashes with the error
"First argument in form cannot contain nil or be empty"
<%= form_for(#customer) do |f| %>
I know that changing the #customer to Customer.new could fix this but I would like to know why this isn't necessary in one file and it is in another
Why is this happening and how do I make a form that will update the sqlite db on the index page.
#customer is a variable that must be created somewhere in the corresponding controller action. If your #index controller action defines a variable by that name, then you'll be able to use it in the view template; otherwise you'll need to create it like this:
#customer = Customer.new # (or whatever the value is)
When Rails processes a request, it just executes a big (and complex) lump of code that's created from a bunch of different files. First it executes the appropriate controller action, then it executes any Ruby code found inside the corresponding view template. So any variable (or any method name) that is used in the view template, was first defined at some point before that: either in the controller action, or in one of Rails' countless built-in helper files.
When I am using form_for in a index or show page I like to do is set it to new
<%= form_for Customer.new, url: {controller: "customers", action: "create"} do |f| %>
...
<%= f.submit %>
<% end %>
that way there is a object to be created, I also I like to pass in the controller and the action.
Fresh learning rails, be gentle. No programming experience, but learning.
Building simple app: An app that asks "what's the most important thing you can do right now," gives you an answer field, submits it, and then displays the stored important things.
Ideally, they won't be stored on the index page, but for learning purposes, I'm trying to get them to do this.
Controller code:
class FacilitatesController < ApplicationController
def index
#facilitate = Facilitate.all
end
def new
#facilitate = Facilitate.new
end
def create
#facilitate = Facilitate.new(params[:facilitates])
#facilitate.save
redirect_to #facilitate
end
private
def facilitate_params
params.require(:facilitate).permit(:answer)
end
def show
#facilitate = Facilitate.find(params[:id])
end
end
Index View code:
<h1>Impactful Task Elicitation</h1>
<h1>Listing Stored To dos</h1>
<table>
</table>
<%= link_to 'Store impactful tasks', new_facilitate_path %>
NEW view code:
<h1>What is the most impactful task?</h1>
<p>Store below, motherbiatch</p>
<%= form_for :facilitate, url: facilitates_path do |f| %>
<p>
<%= f.label :answer %><br>
<%= f.text_area :answer %>
</p>
<p>
<%=f.submit 'Save Task' %>
</p>
<% end %>
So far, I can navigate from index, to facilitates/new, and answer the question, to store my important to do. It then takes me to facilitates/33 (ID I'm assuming, or the number that I'm on, task wise)
I'd like to display these tasks both on the facilitates/33 (or whatever number it ends up being) page, as well as the index page.
I've followed directions on a similar type of form here: http://guides.rubyonrails.org/getting_started.html but, I still can't get my stored To do's to display anywhere.
Any help would be awesome.
In your controller, you have the show method below the private line. That means that it can only be called from inside the controller, so you are being sent to the show template without that method ever being called (#facilitate will be nil).
Move the def show method up above the private line.
It then takes me to facilitates/33 (ID I'm assuming, or the number that I'm on, task wise)
The line redirect_to #facilitate, means that after the facilitate is created, go to it's show method and page. The 33 is just a database reference for that particular facilitate, that it can be looked up again with Facilitate.find(params[:id]).
You didn't post what app/views/facilitates/show.html.erb looks like, but if you want to display the newly created facilitate, then it should have a line like this:
<%= #facilitate.answer %>
I'd like to display these tasks both on the facilitates/33 (or whatever number it ends up being) page, as well as the index page.
If you only care about the listing, and not individual facilitates pages, then after creation you can redirect back to the index in the create method by changing redirect_to #facilitate to redirect_to facilitates_path (which translates to '/facilitates').
EDIT:
The <%= #facilitate.answer %> example was meant for the show view, not index.
On index, you'd do something more like this:
<% #facilitate.each do |facilitate| %>
<%= facilitate.answer %><br>
<% end %>
To list them all.
I've been stuck on this problem for days. First off, I now know this code is horribly wrong. I've been trying to fix it, but it's way more important in the short term that this link is created. In my view (I'm so sorry), I call the create method like this, if a certain condition is met:
index.html.erb (controller: subjects_controller)
<%= Baseline.create(subject_id: sub.subject_id) %>
I do this several times on the page, from several controllers (i.e., FollowUp3Week.create(subject_id: sub.subject_id) works). All of the other controllers work. I've checked, and double checked, every controller action and compared them to each other, and they appear the same.
So instead of creating the record, it leaves something like this instead:
#<Baseline:0x007f944c4f7f80>
I'm at a bit of a trouble shooting loss. Once again, I know how wrong it is to have these database actions in the view. But I didn't know that when I made the page, and I really need this to function before I can take the time to learn how to rearrange everything through the MVC.
Any advice would be greatly appreciated. Let me know what other code you might want to look at.
EDIT 1.
link Creation:
<% if Baseline.where(subject_id: sub.subject_id).first != nil %>
<%= link_to "edit", baseline_path(Baseline.where(subject_id: sub.subject_id).first) %>
<% else %>
<%= Baseline.create(subject_id: sub.subject_id) %>
<% end %>
First of all, making DB calls in views is a big NO! NO!
Secondly, to answer why you see the output as
#<Baseline:0x007f944c4f7f80>
for
<%= Baseline.create(subject_id: sub.subject_id) %>
You are trying to render an instance of Baseline model. Its just how the instance would be displayed. If you want to display a particular attribute's value in view then just do
<%= Baseline.create(subject_id: sub.subject_id).subject_id %>
Also, this code will not create a link. To create a link you would have to call link_to helper in your view.
What you need to do is, move the Baseline.create call in the controller. Set an instance variable in the action which renders this particular view as below:
def action_name
#baseline = Baseline.create(subject_id: sub.subject_id)
end
After this in you view you can easily access all the attributes of #baseline instance.
For example:
To access subject_id
<%= #baseline.subject_id %>
To create a link for show page of #baseline, provided you have a RESTful route to show action for baselines
<%= link_to "Some Link", #baseline %>