Godzilla74 Godzilla74 - 4 months ago 17
Ruby Question

Calling methods in other models in controller

So, I'm pretty new to the concept of offloading functionality that affects the DB into a model instead of a controller and having some difficulty getting it to work.

Basically, from my

Stock
controller, I'm trying to call a method in my
User
model. Instead, I'm getting an error:

NoMethodError (undefined method `stock_relationships' for #<Class:0x007fc0da1a8d60>):
app/models/user.rb:27:in `follow_stock'
app/controllers/stocks_controller.rb:20:in `add_stock'


So, here is what I have:


A user adds a stock in views/stocks/index.html.erb


<% @stocks.each do |s| %>
<tr>
<td><%= s.symbol %></td>
<td><%= s.name %></td>
<td>
<%= link_to raw("<i class='fa fa-plus'></i>"), add_stock_path(id: s.id) %>
</td>
</tr>
<% end %>



Which then triggers the StockController
add_stock
method:


def add_stock
stock = Stock.find(params[:id])
user = current_user.id
User.follow_stock(stock_id: stock, user_id: user)
flash[:success] = "Successfully added stock"
redirect :back
end



The 3rd line in the method is where my problem is, since the
follow_stock
method resides in the
User
model:


class User < ActiveRecord::Base

has_many :stock_relationships
has_many :stocks, through: :stock_relationships

def self.follow_stock(stock)
self.stock_relationships.create(stock_id: stock)
end
end


Can some one help me wrap my head around how to actually call this method or am I totally off base with how I'm trying it?

UPDATE

I've changed some code around and now I am able to create the
StockRelationship
however,
stock_id
is being saved as
nil
:

=> #<StockRelationship:0x007fee03f25f00
id: 17,
user_id: 1,
stock_id: nil,
created_at: Thu, 14 Jul 2016 13:44:52 UTC +00:00,
updated_at: Thu, 14 Jul 2016 13:44:52 UTC +00:00>


I changed the code in
StockController#add_stock
:

def add_stock
stock = Stock.find(params[:id])
current_user.follow_stock(stock)
flash[:success] = "Successfully added stock"
redirect_to :back
end

Answer

You can gradually make your code concise by refactoring.

First, let's write the implementation in controller:

def add_stock
  stock = Stock.find(params[:id])
  current_user.stock_relationships.create(stock: stock)
  flash[:success] = "Successfully added stock"
  redirect :back
end

That's not too much code, and is concise enough. In my opinion, I don't think the code needs refactoring, but if you insist on putting the logic to the User model to enhance the expressiveness, like

def add_stock
  stock = Stock.find(params[:id])
  current_user.follow_stock(stock)
  flash[:success] = "Successfully added stock"
  redirect :back
end

then we need to implement the User#follow_stock. Notice I used # in the notation, which means follow_stock should be an instance method, not a class method.

class User < ActiveRecord::Base
  def follow_stock(stock)
    stock_relationships.create(user: self)
  end
end

I just did some copy and paste (evil? not in refactoring phase.), and replaced current_user with self, and it's done.

Comments