I'm making a twitter-copy and right now I'm trying to show all the posts from the users an other user is following. I'm new at ruby and rails, so i might be doing this a really weird way..
These are the files I have:
session#home.html.erb
<h2 class='User_Header'> Home <h2>
<%= link_to "New Post", controller: "posts", action: "new" %>
<%= link_to "Log Out", :controller => "sessions", :action => "logout" %>
<%= show_tweets("following") %>
sessions_helper
module SessionsHelper
def show_tweets(opt)
if opt == "following"
#sub = Subscription.where("userID = ?", #current_user.id)
#post = Post.where("user_id = ?", #sub.followingID)
render partial: 'shared/follower_tweets'
end
end
def show_tweet(s)
#post = Post.where("user_id = ?", s.id)
render partial: 'shared/tweet'
end
def tweet_username(p)
#username = User.where("id = ?", p.user_id)
Rails.logger.debug #username.inspect
render partial: 'shared/user'
end
end
_follower_tweets.html.erb
<h2>Tweets</h2>
<table>
<tr>
<th>Username</th>
<th>Tweet</th>
</tr>
<% div_for(#post, class: 'post') do %>
<td><%= tweet_username(#post) %></td>
<td><%= #post.content %></td>
<% end %>
</table>
_user.html.erb
<%= #username %>
session.rb
class Session < ActiveRecord::Base
attr_accessible :content, :user_id, :followingID, :userID
end
Error
app/views/sessions/home.html.erb where line #9 raised:
undefined method `followingID' for #<ActiveRecord::Relation:0x007fd74b66f8a8>
What is happening is that you have followingID on your Session model instead of Subscription model. Should be something like the following:
class Subscription < ActiveRecord::Base
attr_accessible :followingID
end
However, the problem is bigger than that. You must read about Active Record Associations, then you would be able to do something like
#subs = #current_user.subscriptions
#posts = #current_user.posts
check if your model's association is correct. the messages indicates that there's an error about this.
Related
I have a view with a form and a table that displays some data from the database. Whenever I try to access the object from my controller in my view I get undefined method domain for "https://www.lookagain.co.uk/":String. But if do <%#savedHTML = ScrapedPage.all%> everything works fine. I know the I should not do that in the view as it defeats to purpose of MVC but I don't seem to fin a fix.
View:
<%= stylesheet_link_tag "masterstyles.css" %>
<% #url = 'default' %>
<%= form_for #url, :url => {:controller => "page_scraper", :action => "scrape"} do |f| %>
<%= f.text_field (:url) %>
<%= f.submit "Scrape" %>
<% end %>
<%#domain ='default'%>
<%#date ='default'%>
<%= form_for #domain, :url => {:controller => "page_scraper", :action => "compare"} do |f| %>
<%=select_tag 'domain', options_for_select(#savedHTML.collect{ |u| [u.domain, u.domain] })%>
<%=select_tag 'date', options_for_select(#savedHTML.collect{ |u| [u.created_at, u.created_at] })%>
<%= f.submit "compare" %>
<% end %>
<div class="subjects index">
<h2>FGH Page Scraper</h2>
<table class="listing" summary="Links list">
<tr class="header">
<th>ID</th>
<th>link</th>
<th>Created at</th>
<th>Updated at</th>
</tr>
<% #savedHTML.each do |page| %>
<tr>
<td><%= page.id %></td>
<td><%= page.domain %></td>
<td class="center"><%= page.created_at %></td>
<td class="center"><%= page.updated_at %></td>
<td class="actions">
<%= link_to("Delete", {:controller => 'page_scraper', :action => 'delete', :id => page.id}, :class => 'action delete') %>
</td>
</tr>
<% end %>
</table>
</div>
Controller:
class PageScraperController < ApplicationController
require 'nokogiri'
require 'open-uri'
require 'diffy'
require 'htmlentities'
def scrape
#url = watched_link_params[:url].to_s
puts "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOG#{#url}"
#page = Nokogiri::HTML(open(#url))
coder = HTMLEntities.new
#encodedHTML = coder.encode(#page)
create
end
def index
#savedHTML = ScrapedPage.distinct.pluck(:domain)
end
def show
#savedHTML = ScrapedPage.distinct.pluck(:domain)
end
def new
#savedHTML = ScrapedPage.new
end
def create
#savedHTML = ScrapedPage.create(domain: #url, html: #encodedHTML, css: '', javascript: '')
if #savedHTML.save
puts "ADDED TO THE DATABASE"
redirect_to(root_path)
else
puts "FAILED TO ADD TO THE DATABASE"
end
end
def edit
end
def upadate
end
def delete
#savedHTML = ScrapedPage.find(params[:id])
end
def destroy
#savedHTML = ScrapedPage.find(params[:id])
#savedHTML.destroy
redirect_to(root_path)
end
def compare
#domain = params[:domain].to_s
puts #domain
redirect_to(root_path)
#timestamp
end
def watched_link_params
params.require(:default).permit(:url)
end
def compare_params
params.require(:domain).permit(:domain)
end
end
The problem is that in your controller you are saving only string-values to #savedHTML variable (pluck will give you only an array of attributes from given objects). Therefore you cant ask "some_string".domain because String class doesn't have a domain method.
If you have a domain method on ScrapedPage object then in your controller action (index or show - whatever you are dealing with) you should replace
#savedHTML = ScrapedPage.distinct.pluck(:domain)
with
#savedHTML = ScrapedPage.select(:domain).distinct
The latter will give you unique ScrapedPage objects based on domain value. Look here for further info and examples.
NB! also a tip for refactoring:
Use strong parameters under private section. Also, if you have the same query in your controller twice in different actions then it is better to make it in before_action like this:
class PageScraperController < ApplicationController
before_action :set_saved_html, only: %i[index show]
def index
end
def show
end
private
def watched_link_params
params.require(:default).permit(:url)
end
def compare_params
params.require(:domain).permit(:domain)
end
def set_saved_html
#savedHTML = ScrapedPage.select(:domain).distinct
end
end
Now i am tried by following to this article http://www.rymcmahon.com/articles/2
My product.rb file is
class Product < ActiveRecord::Base
def self.search(search)
where("name LIKE ?","%#{search}%")
end
end
my index file is
Data
<%= form_tag(products_path, :method => "get") do %>
<%= text_field_tag :search, params[:search], placeholder: "products" %>
<%= submit_tag "Search", :name => nil %>
<% end %>
my index method is
def index
#product = Product.all
if params[:search]
#recipes = Product.search(params[:search]).order("created_at DESC")
else
#recipes =Product.all.order("created_at DESC")
end
end
But it didn't filtering the data
my output is:
dinesh 200 2016-09-21 06:10:40 UTC
reddyc 300 2016-09-21 06:16:31 UTC
Goods 200 2016-09-21 09:33:56 UTC
That article doesn't seem to be entirely correct, because the first collection assignment seems redundant. I'd write it like this:
def index
#products = search_products.order(created_at: :desc)
end
private
def search_products
if params[:search]
Product.search(params[:search])
else
Product.all
end
end
Then in your views you use #products variable instead of #recipes. It should work
You have taken two different variables for product, also #product = Product.all is not required, as your taking the product variable again in both if and else cases.
def index
if params[:search].present?
#products = Product.search(params[:search]).order("created_at DESC") //I recommend taking plural(products)
else
#products = Product.all.order("created_at DESC")
end
end
Your index.html.erb can be,
<% #products.each do |product| %>
<tr>
<td><%= product.field1 %></td>
<td><%= product.field2 %></td>
<td><%= product.field3 %></td>
</tr>
<% end %>
My controller is:
def show
#product = Product.find(params[:id])
end
def search
end
def searchable
searchword = params[:search]
#data = Product.where(name: searchword)
end
and my searchable.html.erb is:
<h1>searching for '<%= params[:search] %>'</h1>
<% if #data.empty? %>
<h1>no results found</h1>
<% end %>
<% #data.each do |s| %>
<%= s.name %>
<%= s.created_at %><br>
<% end %>
And search.html.erb
<%= form_tag searchable_path, method: 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", name: nil %>
<% end %>
And my routes are assigned as
get 'search' => 'products#search'
get 'searchable' => 'products#searchable'
By this I am able to search data in my database.And it is working fine.
I am a noob on Ror. Been looking for my problem answers for 3 days now, I have been looking for the answers but can’t find one with my specific problem. ( I even found it hard to write the right title)
So I have been trying to build a nested form in RoR. I have a simple order form that enable users to specify the quantity they ordered in that form. The form will only store the value into the database if the quantity text field is not empty.
The Order form is simply look like this:
I am storing the quantity data into the join table between order and inventory which has many to many relationship through Inventory_orders table. now in the Inventory_orders table instead of only having orders_id and inventories_id , I also add the quantity column.
now I have been able to get the form working with the code below:
Controller:
def new
#order = Order.new
#customer = Customer.all
#inventories_list = Inventory.all
#inventory_order = #order.inventory_orders.build
end
def create
#order = Order.new(order_params)
#inventories_list = Inventory.all #controller can call any model
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render :show, status: :created, location: #order }
else
format.html { render :new }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
def order_params
params.require(:order).permit(:customer_id, :order_ids => [],:inventory_orders_attributes => [:id, :quantity, :inventory_id ])
end
View:
<%= form_for(#order) do |f| %>
<div id = “Main_Container">
*** Some Code ***
<table id = "inventory_table">
<tr>
<td class = "prodCodeTitle"><h3>Prod Code</h3></td>
<td class = "prodResult"><h3>Quantity</h3></td>
<td class = "prodResult"><h3>Size</h3></td>
<td class = "prodResult"><h3>Price</h3></td>
</tr>
//Here display all the inventories list
<% #inventories_list.each do |t| %>
<tr>
<td class ="prodResult"><%= link_to t.pName, inventory_path(t), :remote => true %></td>
<td class = “prodResult">
//nested form for the join table
<%= f.fields_for :inventory_orders do |qty| %>
<%= qty.hidden_field :inventory_id , value: t.id %>
<%= qty.number_field :quantity %>
<%end%>
</td>
<td class = "prodResult"><%= t.pMeter %></td>
<td class = "prodResult"><%= t.pSellPrice %></td>
</tr>
<% end %>
*** Some Code***
<% end %>
Model:
class Order < ActiveRecord::Base
belongs_to :customer
has_many :inventory_orders
has_many :inventories, through: :inventory_orders
validates :customer_id, :presence => true
accepts_nested_attributes_for :inventory_orders, :reject_if => lambda { |a| a[:quantity].blank?}
end
class InventoryOrder < ActiveRecord::Base
belongs_to :inventory
belongs_to :order
validates :quantity, :presence => true
end
Now when creating new Orders form , the application works and store the data that I want in the inventory_orders table.
The problem occurs when I try to edit the form. When trying to click on edit button I get this output in my View file:
for example this is what I Input into the form:
when I try to edit the form this is what I get:
this is my controller for edit:
def edit
#order = Order.find(params[:id])
#customer = Customer.all
#inventories_list = Inventory.all
end
I have been looking at the psql database schema by manual do sql query as follow:
select * from inventory_orders where inventory_orders.order_id = 63;
and get this as result:
now it seems that the fields_for Inventory_orders get the number of rows returned , but I don't get why all the quantity also get displayed 4 times for each product. Also how can I ensure that when I try to edit quantity for product “aaa” it will only display one number_field with what users has input before.
Sorry for the long post,otherwise I am not sure how to clearly convey my meaning.
EDITED
this to show my Inventory Model:
Class Inventory < ActiveRecord::Base
has_many :inventory_orders
has_many :orders, through: :inventory_orders
end
You need to use the following:
//Here display all the inventories list
<% #inventories_list.each do |t| %>
<%= link_to t.pName, inventory_path(t), :remote => true %>
<%= f.fields_for :inventory_orders, t do |qty| %>
<%= qty.hidden_field :inventory_id , value: t.id %>
<%= qty.number_field :quantity %>
<% end %>
<%= t.pMeter %>
<%= t.pSellPrice %
<% end %>
The issue is that since f.fields_for populates a form based on the built associated objects, if you're passing 4 fully constructed objects through the edit action, fields_for is going to populate all of them each time.
What you need is to use the instance of the associated data.
I think your code could be improved a lot:
#app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def new
#order = Order.new
#inventory = Inventory.all
#inventory_order = #order.inventory_orders.build
end
def edit
#order = Order.find params[:id]
#inventory = Inventory.all
end
end
#app/views/orders/new.html.erb
<%= form_for #order do |f| %>
<% #inventory.each do |inventory| %>
<%= f.fields_for :inventory_orders, item do |item| %>
<%= item.text_field :quantity %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
#app/views/orders/edit.html.erb
<%= form_for #order do |f| %>
<%= f.fields_for #order.inventory_orders do |item| %>
<%= item.text_field :quantity %>
<% end %>
<%= f.submit %>
<% end %>
I am building an app with voting (a post has many votes, a user has many votes). It was working when I didn't limit the number of votes per user, but after adding a validation, it gets the following error when I try to vote up a post that the same user voted up previously:
>undefined method `model_name' for NilClass:Class
>Extracted source (around line #28):
>25: <% end %>
>26:
>27: <table class="posts" summary="User posts">
>28: <%= render #posts %>
>29: </table>
>Trace:
>app/views/posts/index.html.erb:28:in `_app_views_posts_index_html_erb__978098650233580665_2485618500'
>app/controllers/votes_controller.rb:23:in `create'
It works when voting for a post which the user has not voted up previously. How do I get rails to flash the error when the user has already voted for the post, but then redirect without an error? I'm sure I've just missed something obvious. The validation works when I try to add a vote through the rails console (ie, rails doesn't add the new vote to the database if that user has already voted for it). Thanks -
The related code in vote.rb:
class Vote < ActiveRecord::Base
attr_accessible :user_id, :post_id
validate :only_one_user_per_post
belongs_to :user
belongs_to :post, :counter_cache => true
private
def only_one_user_per_post
if Vote.where("user_id = ? AND post_id = ?", self.user_id, self.post_id).all.any?
errors.add(:user_id, "Can only post once per post!")
end
end
end
the create from VotesController.rb
def create
#vote = Vote.new(params[:vote])
post = params[:vote][:post_id]
uid = params[:vote][:user_id]
if #vote.save
flash[:success] = "You voted for the article"
redirect_to root_path
else
flash[:failure] = "You did not vote"
render 'posts/index'
end
relevant code from index.html.erb:
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
<table class="posts" summary="User posts">
<%= render #posts %>
</table>
And here's the relevant snippet from _post.html.erb:
<%= form_for #vote do |f| %>
<div class="right"> <%= image_submit_tag "plus32.png", :size => "16x16", :class => "squareicon" %>
<span class="label success"><%= post.votes_count %></span>
</div>
In your controller, in the else-branch you have the following code:
else
flash[:failure] = "You did not vote"
render 'posts/index'
end
You also will need to retrieve the #posts there to alleviate the error. Or use redirect_to posts_path instead.
Hope this helps.
I just picked up Agile Web Development with Rails 3rd Ed., and I'm going thru the Depot Application chapters, I'm attempting to create a simple Edit quantity function, and delete function. I've had luck with the delete function but no luck with the edit quantity function.
I'm going to provide a lot of information, so please don't feel overwhelmed. I've found this to be a challenging problem.
To add_to_cart.html.erb
<div class="cart-title">Your cart</div>
<table>
<% for item in #cart.items %>
<tr>
<td><% form_for #cart.items, :url => {:action => "cart_update", :id => "#{item.getinventoryid}"} do |f| %>
<%= f.text_field :quantity, :size => '3' %>
<%= f.hidden_field :id, :value => "#{item.getinventoryid}" %>
<%= f.submit 'cart_update' %>
<% end %></td>
<td><%=h item.quantity %> ×</td>
<td><%=h item.title %></li></td>
<td><%=h item.description %></td>
<td class="item-price"><%= number_to_currency(item.price) %></td>
<td><%= link_to 'remove', {:controller => 'inventories', :action => 'remove_cart_item', :id => "#{item.getinventoryid}"} %></td>
</tr>
<% end %>
<tr class="total-line">
<td colspan="4">Total</td>
<td class="total-cell"><%= number_to_currency(#cart.total_price) %></td>
</tr>
</table>
<%= button_to "Checkout", :action => 'checkout' %>
<%= button_to 'Empty cart', :action => 'empty_cart' %>
inventories_controller:
def cart_update
#inventory = Inventory.find(params[:id])
#cart = find_cart
#cart.increment_inventory_quantity(params[:inventory])
end
def remove_cart_item
inventory = Inventory.find(params[:id])
#cart = find_cart
#cart.remove_inventory(inventory)
redirect_to_index("The item was removed")
end
Cart.rb model
attr_accessor :items
def increment_inventory_quantity(id, quantity)
inventory_to_increment = #items.select{|item| item.inventory == inventory}
# We do this because select will return an array
unless product_to_increment.empty?
inventory_to_increment = inventory_to_increment.first
else
# error handling here
end
inventory_to_increment.quantity = quantity
end
def remove_inventory(inventory)
#items.delete_if {|item| item.inventory == inventory }
end
cart_item.rb model
attr_accessor :inventory, :quantity
def getinventoryid
#inventory.id
end
This produces strange results:
Notice the quantity 16 appears in both items from my loop (#Fail). When I submit the form a ArgumentError in InventoriesController#cart_update wrong number of arguments (1 for 2) error is returned. Parameters being passed:
{"commit"=>"cart_update",
"_method"=>"put",
"authenticity_token"=>"sH1tWXTJPltpSq5XaAkww7259IR5ZiflnqSFB2Zb0IY=",
"id"=>"50",
"cart_item"=>{"quantity"=>"16",
"id"=>"50"}}
You are getting the wrong number of arguments error because you are passing one argument to #cart.increment_inventory_quantity in the controller method. That method requires two arguments.
# In your controller:
def cart_update
#inventory = Inventory.find(params[:id])
#cart = find_cart
#cart.increment_inventory_quantity(params[:inventory]) # you are passing one thing
end
# Then in your model:
def increment_inventory_quantity(id, quantity) # the method wants two things
# ...
Possibly you intended to do something like this:
def cart_update
#inventory = Inventory.find(params[:cart_item][:id])
#cart = find_cart
#cart.increment_inventory_quantity(#inventory.id, params[:cart_item][:quantity])
end
Are you sure it's form_for( #cart.items ) and not form_for( item )?