Displaying Associated Objects in Rails Views - ruby-on-rails

I am working on a rails 4 application that currently has two models User and Status. In the user model I defined the association below. Both the status and user tables are populating with information. Statuses are loading with an associated user_id
User Model
class Status < ActiveRecord::Base
belongs_to :user
end
I have the following block in my show status view which will display the user_id and and the content of the status
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user_id%></strong>
<p> <%=status.content%></p>
I would like to display the user's first name instead. According the tutorial i'm taking I should be able to use this code since I have the association defined however it's returning the error below.
<%=#status.user.first_name%>
Error
#==>undefined method `first_name' for nil:NilClass
How can I display first_name in the controller? Do I need to define a new method for user or should the association provide?
Relevant Controller Code for Reference
class StatusesController < ApplicationController
before_action :set_status,:set_user, only: [:show, :edit, :update, :destroy]
# GET /statuses
# GET /statuses.json
def index
#statuses = Status.all
end
# GET /statuses/1
# GET /statuses/1.json
def show
puts "debug msg #{#status.inspect}"
end
# GET /statuses/new
def new
#status = Status.new
end
# GET /statuses/1/edit
def edit
end
# POST /statuses
# POST /statuses.json
...
...
...
private
# Use callbacks to share common setup or constraints between actions.
def set_status
#status = Status.find(params[:id])
puts "in set status"
end
def set_user
#status.user = User.find_by(#status.user_id)
end
# Never trust parameters from the scary internet, only allow the white list through.
def status_params
params.require(:status).permit(:content, :user_id)
end
end

Sees like there is no problem in your code. The error undefined method first_name for nil:NilClass means that the status object not associated with user or user have no field first_name. Try following code:
<% #statuses.each do |status| %>
<div class="status">
<strong> <%=status.user.try(:first_name) %></strong>
<p> <%=status.content%></p>

I am not sure what page you are trying to display <%=#status.user.first_name%> this on, but this should work.
You can use the will_paginate gem:
def show
#statuses = #statuses.paginate(page: params[:page])
end
add this to the view:
<%= will_paginate %>
or this should be the normal way:
def show
#statuses = #statuses.find(params[:id])
end

Related

why do i get NoMethodError?

I want my trade page to show a list of every Item that a user has added to their cart, and I'm having trouble understanding why this implementation is giving me a NoMethodError
So in the #show action of my TradesController I have a trade_ids variable that contains an array of added items returned by $redis.smembers current_user_trade. I then use this to perform a lookup on the id of each item, and loop through the instance variable in my view.
My Trades Controller:
class TradesController < ApplicationController
def show
trade_ids = $redis.smembers current_user_trade
#trade_items = Item.find(trade_ids)
end
def add
$redis.sadd current_user_trade, params[:item_id]
render json: current_user.trade_count, status: 200
end
def remove
$redis.srem current_user_trade, params[:item_id]
render json: current_user.trade_count, status: 200
end
private
def current_user_trade
"trade#{current_user.id}"
end
end
Here's the method I'm using to add items to current_user_trade:
class Item < ActiveRecord::Base
extend FriendlyId
friendly_id :slug, use: [:slugged, :finders]
def slug
[artist.parameterize, title.parameterize].join("-")
end
def trade_action(current_user_id)
if $redis.sismember "trade#{current_user_id}", id
"Remove from"
else
"Add to"
end
end
end
My routes:
resource :trade, only: [:show] do
put 'add/:item_id', to: 'trades#add', as: :add_to
put 'remove/:item_id', to: 'trades#remove', as: :remove_from
end
And then in my trades view I've got a basic:
<% #trade_items.each do |item| %>
<p><%= item.title %></p>
etc
<% end %>
My initial thought is that this had something to do with the fact that I've been using FriendlyId for slug generation. But according to FriendlyId's doc, adding :finders to the slug declaration in my Item model should reenable lookups by id. But it's not.
I also tried refactoring this so that it used my Items :slug, but that too was to no avail.
Fixed it. Turns out it was cached. I switched from storing item_id to item_slug halfway through, and needed to run redis-cli flushall to get it to store and access the right data.

Beginner #Rails: undefined method `title' for nil:NilClass and Couldn't find Decision with 'id'=edit

For your context: This is my first attempt to create a app. I have just started coding:-).
I am trying to get a simple CRUD setup to work.
Now i'm having two problems i can't get my head around:
My entries don't show up on my index page. it gives te following error: 'undefined method `title' for nil:NilClass'. The model contains the following columns:
string :title,text :forecast, date :review_date
If i go to decisions/edit it gives me the following error: 'Couldn't find Decision with 'id'=edit'
This is my code:
Controller:
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update]
def index
# gets all rows from decision table and puts it in #decision variable
#decisions = Decision.all
end
def show
# find only the decision entry that has the id defined in params[:id]
#decision = Decision.find(params["id"])
end
# shows the form for creating a entry
def new
#decision = Decision.new
end
# creates the entry
def create
#decision = Decision.new(decision_params)
if #decision.save
redirect_to #decision
else
render 'new'
end
end
# shows the form for editing a entry
def edit
#decision = Decision.find(params["id"])
end
# updates the entry
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find(params["id"])
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
index view
<h1>Hello World ^^</h1>
<% #decisions.each do |descision| %>
<p><%= #decision.title %></p>
<% end %>
routes.rb
Rails.application.routes.draw do
resources :decisions
root 'decisions#index'
end
I have been working on these two all morning but i can't figure it out. I would be a great help if you guys can take a look for me.
I have just started coding
Welcome!!
My entries don't show up on my index page.
I'm sure you mean decisions, right?
If so, you have to remember that if you're calling a loop in Ruby, you'll need some conditional logic to determine if it's actually populated with any data before trying to invoke it:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |decision| %>
<%= content_tag :p, decision.title %>
<% end %>
<% end %>
This will have to be matched by the appropriate controller code:
#app/controllers/decisions_controller.rb
class DecisionsController < ApplicationController
before_action :find_decision, only: [:show, :edit, :update, :destroy]
def index
#decisions = Decision.all
end
def show
end
def new
#decision = Decision.new
end
def create
#decision = Decision.new decision_params
#decision.save ? redirect_to(#decision) : render('new')
end
def edit
end
def update
end
def destroy
end
private
def find_decision
#decision = Decision.find params["id"]
end
def decision_params
params.require(:decision).permit(:title, :forecast, :review_date)
end
end
This will give you the ability to call #decisions and #decision in your views depending on which route you're accessing.
An important point is that when you say...
decisions/edit it gives me the following error: Couldn't find Decision with 'id'=edit'
... the issue is caused by the way in which Rails routing is handled:
Because Ruby/Rails is object orientated, each set of routes corresponds to either a collection of objects, or a member object. This is why routes such as edit require an "id" to be passed - they're designed to work on member objects.
As such, when you access any "member" route (decisions/:id, decisions/:id/edit), you'll have to provide an id so that Rails can pull the appropriate record from the db:
#app/views/decisions/index.html.erb
<% if #decisions.any? %>
<% #decisions.each do |descision| %>
<%= link_to "Edit #{decision.title}", decision_edit_path(decision) %>
<% end %>
<% end %>
I can explain a lot more - the above should work for you for now.

How to link model to controller action

I have a Slider model in my project and it has a lot of polymorphic associations with other model like Product, Manufacturer, Article and etc.
So, when I use 'show' action with one of the models I also show related Slider. It's ok. But sometimes I need to show Slider with 'index' action.
What is the best way to link some of the sliders to actions, not to other models?
UPDATE
routes
resources :products, :articles, :industries, :manufacturers, only: [:index, :show]
Product controller
class ProductsController < ApplicationController
load_resource
# GET /products
# GET /products.json
def index
#catalog = Product.by_type_and_manufacturer
end
# GET /products/1
# GET /products/1.json
def show
#page_slider = #product.slider
end
end
So in 'show' action I just use product.slider to get related Slider instance. But I want to show another slider for all products by index action.
In that case, what you're trying to do is not possible. You cannot create a relation to a controller action. What you need to do is link the relation's controller action, rather than trying to create a relation to the controller action. A model can only be related to another model (you cannot has_many index, show, delete, etc...)- In other words, call up the data for the relation, and link to that relation's controller action in the view.
example:
#Models:
class Page < ActiveRecord::Base
has_many :sliders
end
class Slider < ActiveRecord::Base
belongs_to :page
end
#Controllers
class PagesController < ApplicationController
def index
#pages = Page.all # lists all pages
end
def show
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
end
# if you would like to show a page that just has all sliders for a specific page and not the page itself...
def show_page_sliders # you will have to create a route and view for this manually
#page = Page.find(params[:id]) # simplified, this will probably make use of strong params in your actual code
#sliders = #page.sliders # all sliders related to the page
# note that this controller action is identical to the show action, because the data we're going to need is the same- the difference comes in the view
end
end
class SlidersController < ApplicationController
def index
#sliders = Slider.all
end
def show
#slider = Slider.find(params[:id])
end
end
# Views
# page#index
<% #pages.each do |p| %>
...
page listing code goes here. if you want to list the sliders for each page on the index...
<% p.sliders.each do |s| %>
...
individual slider info goes here
...
<% end %>
...
<% end %>
# pages#show
<%= #page.name %>
<%= #page.content %> <!-- or whatever data you have for page -->
# since here we are showing a singular page, we can just use our #page instance variable to list out the sliders
<% #page.sliders do |s| %>
...
Slider listing code goes here
...
<% end %>
# pages#show_sliders
<!-- this is identical to the page#show view, minus the actual page info, and with the addition of a link back to the parent page -->
<%= link_to "Back to page", page(s.page_id) %>
<% #page.sliders do |s| %>
...
Slider listing code goes here
<!-- you can link to any path from the slider listing -->
<%= link_to "Show", slider(s.id) %>
<%= link_to "Edit", edit_slider_path(s.id) %>
<%= link_to "Delete", delete_slider_path(s.id) %>
...
<% end %>
#######################UPDATE#############################
# to define one slider per controller action
class PagesController < ApplicationController
def index
#pages = Page.all
# you need to add a "controller_action" column to your Slider model
#slider = Slider.find_where(controller_action: "pages#index")
end
def show
#page = Page.find(params[:id])
#slider = Slider.find_where(controller_action: "pages#show")
end
# etc ...

Does accessing the database in views via view-models or view-helpers break the MVC paradigm?

I believe all of the following break the MVC paradigm but wanted to double check if this was the case. In all cases the view is directly accessing data rather than having the data being passed in. From my understanding of MVC, it should never do that. The controller should get all the data that is necessary to render the view as to not couple the view and model directly. Is my understanding correct?
Accessing the database through a view helper
# in app/helpers/view_helper.hrb
def some_view_helper(person_id)
#person = Person.find(person_id)
end
Accessing another web server through a view helper
# in app/helpers/view_helper.hrb
def another_view_helper(person_id)
# makes http request over the wire to get json back
#json = WebService.get_person(person_id)
end
Accessing the database through a view model
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id])
#page_model = PageModel.new(#person)
end
#in app/views/persons/show.html.erb
<% #page_model.friends.each do |friend| %>
...
<% end %>
#in app/models/person.rb
class Person < ActiveRecord::Base
has_many :friends
end
#in app/models/page_models/page_model.rb
def initialize(person)
#person = person
end
def friends
#person.friends
end
Accessing web server to get data through a view model
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id])
#page_model = PageModel.new(#person)
end
#in app/views/persons/show.html.erb
<% #page_model.friends.each do |friend| %>
...
<% end %>
#in app/models/page_models/page_model.rb
def initialize(person)
#person = person
end
def friends
WebService.get_friends_for_person(person_id)
end
For 1 and 2, you could just set an instance variable (#person) in the controller.
For 3, your view code isn't so bad, but why have a separate page model? You can also load the friends up front in the controller:
# in apps/controller/person_controller.rb
def show
#person = Person.find(params[:id], :include => :friends)
#friends = #person.friends
end
Example 4 is a bit worse, since you're doing external web service calls in a view. Don't do that.
This article has a good example of what an ideal clean view would look like: http://warpspire.com/posts/mustache-style-erb/

Rails - Creating a GettingStarted Controller

Im working to create a getting started controller, that guides a new users through uploading a photo, finding friends, inviting people etc.
GettingStarted has no model itself, it just guides users through a wizard. A user could fully bypass this gettingstarted process without breaking the site. It's just a guide...
What I've done so far is:
Create a Route, Controller and Model:
Route:
resources :getting_started
namespace :getting_started do
resource :users, :only => [:edit, :update]
end
Controller:
class GettingStartedController < ApplicationController
def index
#current_step = current_step
end
protected
def current_step
current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
end
Model
class GettingStarted < ActiveRecord::Base
attr_writer :current_step
attr_accessor :current_step
def current_step
#current_step || steps.first
return 1
end
def steps
%w[step1 step2 step3]
end
def next_step
self.current_step = steps[steps.index(current_step)+1]
end
def previous_step
self.current_step = steps[steps.index(current_step)-1]
end
def first_step?
current_step == steps.first
end
def last_step?
current_step == steps.last
end
end
View:
<%= #current_step.inspect %>
<% form_for #gettingstarted do |f| %>
<table>
<tbody>
<tr>
<td>
<%= link_to image_tag current_user.profile_pic.url(:large), :class => 'getting-started-profile-pic' %>
</td>
<td>
Upload a photo
</td>
</tr>
<table>
<tbody>
<% end %>
Right now I'm stuck on the issue that I need GettingStarted to guide users through existing models, not be a model itself. And I'm getting undefined method `model_name' for NilClass:Class
Suggestions, thoughts on the above?
Thanks
your GettingStarted model doesn't have to inherit from ActiveRecord::Base but it does, you are getting error because Base is expecting a table in your db called GettingStarteds, or something. If you want to keep the content dynamic, meaning saving it in the db so you can change it, then you are pretty close, you could use a natural language model like 'steps' and the steps have an order associated with them, then you can look up the step based on its order in the getting started controller. you can also use a vanilla workflow with a steps controller, and then rename the route in the routes with the :as => option
if the steps are static you might want to explore some of the static page model libraries available like high voltage https://github.com/thoughtbot/high_voltage

Resources