Sinatra Partial with data? - ruby

I am making a super small Sinatra blog application, how could I take entries from a database, format them, and insert them into my layout?

class Blog < Sinatra::Base
helpers do
def partial (template, locals = {})
erb(template, :layout => false, :locals => locals)
end
end
get "/list" do
#posts = Post.all
erb :list
end
end
list.erb:
<% #posts.each do |post| %>
<%= partial(:post, :post => post) %>
<% end %>
post.erb:
<h1><%= post.title %></h1>
<p><%= post.body %></p>

<% #posts.each do |post| %>
<%= erb :"_partial_name", :locals => {} %>
<% end %>
the partial template need start with _

Related

Ruby on Rails guide (blog) error in 5.10

I'm learning Ruby on Rails and to begin I'm making this little blog application via http://guides.rubyonrails.org/getting_started.html#showing-articles .
Now I'm at 5.10 where I need to add validation to the form so if the user adds a tittle with a length shorter than 5.
So this is my articles_controller.rb:
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
end
def create
#render plain: params[:article].inspect
##article = Article.new(params[:article])
#article = Article.new(article_params)
##article.save
if #article.save
redirect_to #article
else
render 'new'
end
redirect_to #article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
And in this view I have an error (new.html.erb):
<%= form_for :article, url: articles_path do |f| %>
<% if #article.errors.any? %>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
This is the error I get:
I'm new to Ruby and rails so I hope I can get some help.
You didn't initialize #article instance variable, but you try tu use it in new view. You should have:
def new
#article = Article.new
end

How to save many items on one form rails?

I need to save many items to Cart on form, user enter quantity one form, and selected items goes to db, but now save only first entered quantity of item. Why?
my form
<%= form_for #cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
And controller
cart_items_controller.rb
class CartItemsController < ApplicationController
before_action :set_cart, only: [:create]
def create
#cart_items = CartItem.create(cart_items_params)
#cart_items.cart_id = #cart.id
if #cart_items.save
redirect_to :back
else
render root_path
end
end
private
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
def set_cart
#cart = Cart.find(session[:cart_id])
rescue ActiveRecord::RecordNotFound
#cart = Cart.create
session[:cart_id] = #cart.id
end
end
There are a few problems here. I'll give you a little bump:
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, :value => item.id %>
<%= f.text_field :qty %>
<% end %>
For each CartItem, this is going to create an input like this
<input name="qty">
This is problematic because only one (the last one in the DOM) will be submitted. You need to research fields_for and incorporate that into your loop in order to get unique names for each Item in the form.
This same issue follows through into your controller
def cart_items_params
params.require(:cart_item).permit(:id, :qty, :item_id, :cart_id)
end
This is going to look for a single :id, :qty, :item_id, and :cart_id, when in reality you're looking to accept multiple :item_id and :qty fields. You need to research Strong Parameters with nested has_many associations.
Finally you have this
#cart_items = CartItem.create(cart_items_params)
which is going to attempt to create a single CartItem when you're really trying to create multiple items and associate them back to the Cart. You need to research accepts_nested_attributes_for as well as more generally "rails form save has_many association". It's a widely covered topic here on SO and elsewhere.
I do this:
def create
#cart_items = params[:cart_items]
#cart_items.each do |c|
#cart_item = CartItem.new(c)
if #cart_item.qty.present?
#cart_item.cart_id = #cart.id
#cart_item.save
end
end
and form
<%= form_tag cart_items_path do %>
<% #cart_items.each do |cart_item| %>
<%= fields_for "cart_items[]", cart_item do |f| %>
<% #category.items.each do |item| %>
<%= item.name %>
<%= f.hidden_field :item_id, value: item.id %>
<%= f.text_field :qty %>
<% end %>
<%= f.submit %>
<% end %>
<% end %>
<% end %>

skipping(going straight to the show page) the index page if there is only 1 item in the database

How can I skip the index page and go straight to the show page if there is exactly only 1 project update in my database for that project(while also making sure no button gets displayed if there is no update(zero) in the database?
I tried this:
<% if #project.updates.any? %>
<%= button_tag type: "button", :class => "radius" do %>
<% if #project.updates=1 %>
<%= link_to 'Project Update', project_update_path(#project), :style => "color: white" %>
<% else %>
<%= link_to 'Project Updates', all_project_updates_path(#project), :style => "color: white" %>
<% end %>
<% end %>
<% end %>
but i get this error:
undefined method `each' for 1:Fixnum
On this line:
<% if #project.updates=1 %>
What is the proper syntax for this?
Below is the relevant code:
My button:
<% if #project.updates.any? %>
<%= button_tag type: "button", :class => "radius" do %>
<%= link_to 'Project Updates', all_project_updates_path(#project), :style => "color: white" %>
<% end %>
<% end %>
These is my custom route:
get 'all_project_updates/:id' => 'project_updates#index', as: 'all_project_updates'
These are the final generated routes:
project_updates_path GET /project_updates(.:format) project_updates#index
project_update_path GET /project_updates/:id(.:format) project_updates#show
This is my projects controller(show action)
def show
#project = Project.find(params[:id])
#comments = Comment.all.where(:project_id => #project.id)
#updates = ProjectUpdate.all.where(:project_id => #project.id)
end
And this is my project updates controller index action:
def index
#projectUpdates = ProjectUpdate.where(:project_id => params[:id])
respond_to do |format|
format.html
end
end
And this is my project updates controller show action:
def show
#projectUpdate = ProjectUpdate.find(params[:id])
respond_to do |format|
format.html
end
end
You probably meant:
<% if #project.updates.count == 1 %>
== is for comparison, = is usually for assignment. Also, you need to compare updatesnumber to1(you can get number withcountmethod), notupdates` themselves.
Instead of comparison, you can use Enumerable#one? method:
<% if #project.updates.one? %>

Like button does not update with AJAX

I am passing a collection (#feed_items) to a _feed_item partial via the :collection option and converting it to dailypost with :as => :dailypost.
Inside the _feed_item partial I rendered another partial for _like_button, and i used :locals to continue using dailypost.
Everything works fine with the database. Likes get added and taken out :) BUT
I am trying to use (AJAX) to create.js.erb & destroy.js.erb the like button.
For some reason only the top post gets updated correctly and i have to refresh the page to see the ones below it.
I know the solution involves assigning a unique post id to each "like" element, say "like-123", but that is where i am stuck.......
I also know that the problem may be in _feed_items.html.erb because i am passing two ids....any suggestions??
Views
_feed.html.erb
<% if #feed_items.any? %>
<ol>
<%= render partial: 'shared/feed_item', collection: #feed_items, :as => :dailypost %>
</ol>
<% end %>
_feed_items.html.erb
<li id="<%= #dailypost.id %>">
<span class="user">
<%= link_to dailypost.user.name, dailypost.user %>
<span class="content"><%= dailypost.content_html %></span>
<div id="like">
<%= render :partial => 'likes/like_button', :locals =>{:dailypost => dailypost} %>
</div>
</li>
_like_button.html.erb
<% if like = current_user.likes.find_by_dailypost_id(dailypost.id) %>
<%= form_for like, :html => { :method => :delete }, :remote => true do |f| %>
<%= f.submit "Unlike" %>
<% end %>
<% else %>
<%= form_for current_user.likes.build, :remote => true do |f| %>
<div><%= f.hidden_field :dailypost_id, value: dailypost.id %></div>
<%= f.hidden_field :user_id %>
<%= f.submit "Like" %>
<% end %>
<% end %>
create.js.erb
$("#like").html("<%= escape_javascript(render :partial => 'like_button', :locals => {:dailypost => #dailypost}) %>");
destroy.js.erb
$("#like").html("<%= escape_javascript(render :partial => 'like_button', :locals => {:dailypost => #dailypost}) %>");
Controller
class LikesController < ApplicationController
respond_to :html, :js
def create
#like = Like.create(params[:like])
#dailypost = #like.dailypost
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
def destroy
like = Like.find(params[:id]).destroy
#dailypost = like.dailypost
respond_to do |format|
format.js
format.html { redirect_to :back }
end
end
end
Hmmm.Replace the first-line in _feed_items.html.erb with this.
<li id="dailypost<%= dailypost.id %>">
and In create.js.erb and destroy.js.erb, make changes
$("#like").html(...)
To
$("#dailypost<%= dailypost.id%> #like").html(...)
This should Work.

What is the correct way to pass multiple arguments to a ruby == operator?

I'm a noob trying to do this:
<% if #page[:title] == "Portraits" %>
<%= render :partial => "/shared/slideshow" %>
<% elsif #page[:title] == "Escapes" %>
<%= render :partial => "/shared/slideshow" %>
<% elsif #page[:title] == "Articulos pa Web" %>
<%= render :partial => "/shared/slideshow" %>
<% end %>
There has to be a concise way to do this, but I just can't figure it out.
Avoid putting the logic you currently have, in a view.
Put this in a helper method instead and use it in the view:
def get_partial_to_render
if ["Portraits","Escapes","Articulos pa Web"].include? #page[:title]
"shared/slideshow"
else
"some_other_template"
end
end
#Note that the partial should not have a leading `/` in the path to it.
And in your view:
<%= render :partial => get_partial_to_render %>
Or, if you do not want to render a partial if a name is not in the array:
def render_my_partial?
["Portraits","Escapes","Articulos pa Web"].include? #page[:title]
end
<%= render :partial => "shared/slideshow" if render_my_partial? %>
Note that the ? is part of the method name. Isn't Ruby wonderful? :D
<% if ["Portraits", "Escapes", "Articulos pa Web"].include?(#page[:title]) %>
<%= render :partial => "/shared/slideshow" %>
<% end %>

Resources