Active Storage is outputting image metadata in HTML - html

I am creating a form where a user can change the images they uploaded. The problem is the form to edit the images is displaying the image metadata along with the images that have been uploaded. Alongside the images, in the HTML page it outputs the following:
[#<ActiveStorage::Attachment id: 3, name: "image", record_type: "Space", record_id: 1, blob_id: 3, created_at: "2018-08-20 00:52:57">,
How do I prevent it from displaying the above data?
Here is the relevant code
html.erb file that displays the images
<div>
<% if #space.image.attached? %>
<%= #space.image.each do |image| %>
<%= image_tag image %>
<% end %>
<% end %>
</div>
Model
class Space < ApplicationRecord
belongs_to :user
has_many_attached :image
end
Controller
class SpacesController < ApplicationController
before_action :authenticate_user!, except: [:show]
def index
#spaces = current_user.spaces
end
def new
#space = current_user.spaces.build
end
def create
#space = current_user.spaces.build(space_params)
if #space.save
redirect_to listing_space_path(#space), notice: "Saved..."
else
flash[:alert] = "Something went wrong..."
render :new
end
end
def show
end
def update
if #space.update(space_params)
flash[:notice] = "Saved!"
else
flash[:notice] = "Something went wrong. Please check your submission and try again."
end
redirect_back(fallback_location: request.referer)
end
private
def space_params
params.require(:space).permit(:name, :description, image: [])
end
end

On:
<%= #space.image.each do |image| %>
Remove the = after the <%.
On an ERB tag, the equals sign will print the result of that line. It is printing the .inspect on the image object.

For people using haml and running into this issue:
= #user.images.each do |image|
= image_tag image.variant(resize_to_limit: [300, 220])
becomes
# replace = sign
- #user.images.each do |image|
= image_tag image.variant(resize_to_limit: [300, 220])

Related

Rails - Save client model into the database

Hey here! I'm kinda new to Rails and I've been trying to find some answers but no luck yet so here we go.
I've set up a basic Rails app and just trying to save a Client to my database with a validation but nothing seems to be coming together. Anyone could point me to the right direction please or let me know what I've been doing wrong in my code.
I keep getting errors like this:
NoMethodError in Clients#new
Showing /Users/******/Documents/******/*****/app/views/clients/_form.html.erb where line #1 raised:
undefined method `clients_path' for #<ActionView::Base:0x00000000064a50>
Did you mean? clients_new_path
Even if I remove #client = Client.new from the new method I can view the page but nothing gets saved.
I'm stuck really now so any help much appreciated!
Thanks!
My Routes.rb file:
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
get 'dashboard/index'
root to: "home#index"
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations'
}
get '/clients/index'
get '/clients/new'
get '/clients/edit'
get '/clients/delete'
get '/clients/:id', to: 'clients#show'
post '/clients/new', to: 'clients#create'
end
My Dashboard file:
<% if user_signed_in? %>
<nav class="subnav">
<ul>
<li><%= link_to('My Clients', clients_index_path) %></li>
<li><%= link_to('Add Client', clients_new_path) %></li>
<li><%= link_to('Edit registration', edit_user_registration_path) %></li>
<li><%= link_to('Logout', destroy_user_session_path, :method => :delete) %></li>
</ul>
</nav>
<% else %>
<%= link_to('Register', new_user_registration_path) %>
<%= link_to('Login', new_user_session_path) %>
<% end %>
My ClientsController file:
class ClientsController < ApplicationController
def index
#clients = Client.all
end
def new
#client = Client.new
end
def show
#client = Client.find(params[:id])
end
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render :new
end
end
def edit
end
def delete
end
private
def client_params
params.require(:client).permit(:name, :provider)
end
end
My form:
<%= form_with model: #client do |form| %>
<div>
<%= form.label :name %><br>
<%= form.text_field :name %>
<% client.errors.full_messages_for(:name).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :provider %><br>
<%= form.text_field :provider %>
</div>
<div>
<%= form.label :business_type %><br>
<%= form.select :business_type, ["Partnership", "Sole Trader", "Limited Company"] %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
Finally my new.html.erb file:
<h1>Clients#new</h1>
<%= render 'form', client: #client %>
clients_path is generated by resources :clients, only: :index or you probably need to give your route the name you want. Try this
get '/clients/index', as: :clients
or, if you want to specify non default paths as you're doing, your index is probably called clients_index_path, but you can check that with a rake routes or rails routes, because I'm not sure.
That said, I suggest you to go with the resources method in your routes file and use the default paths as you're trying to do. Something like
resources :clients
but now you don't have a path like /clients/index no more, just /clients for the index action.
If you're in doubts with routes try to read the guide about routing
The Rails way to declare the routes to Create, Update, Read and Destroy (CRUD) a resource is just:
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
get 'dashboard/index'
root to: "home#index"
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations'
}
resources :clients
end
As you can see by the annotions above each method Rails does not add the "action" to the path except for the new and edit routes:
class ClientsController < ApplicationController
before_action :set_client, only: [:show, :edit, :update, :destroy]
# GET /clients
def index
#clients = Client.all
end
# GET /clients/1
def show
end
# GET /clients/new
def new
#client = Client.new
end
# POST /clients
def create
#client = Client.new(client_params)
if #client.save
redirect_to #client
else
render :new
end
end
# GET /clients/edit
def edit
end
# PATCH /clients/1
def update
if #client.update(client_params)
redirect_to #client
else
render :edit
end
end
# DELETE /clients/1
def delete
#client.destroy
redirect_to action: :index,
notice: 'Client deleted'
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(:name, :provider)
end
end
Thus you don't create resources with post '/clients/new' - You use POST /clients. Also when you use the "bare-bones" routing methods such as match, get, post etc Rails does not automatically add routing helper. If you actually wanted to generate the equivilent routes you would need to use:
post '/clients',
to: 'clients#create',
as: :clients
But you're much better off embracing the conventions and learning to use them to be productive.

Ruby on Rails - hidden tag not passing into the database

Really new to RoR, and ran into an issue I can't find the answer to anywhere.
I have two tables, user and books, and I'm trying to use a join table to match up the user and the books and add a rating and review column in the join table.
The problem is everything passes into the join table EXCEPT the book_id
View
<%= form_for(current_user.user_book_collections.build) do |f| %>
<div>
<%= hidden_field_tag :book_id, current_book.id %>
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :review %> (optional):
<%= f.text_area :review, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
</div>
<p></p>
<%= f.submit "Add to your collection", class:"btn btn-primary" %>
<% end %>
current_user and current_book are methods where the users and books are assigned respectively.
Controller
def create
#user_book_collection = current_user.user_book_collections.build(user_book_collection_params)
if #user_book_collection.save
flash[:success] = "Added to your collection"
redirect_to mybooks_path
else
flash[:danger] = "Add was unsuccessful"
redirect_to bookcollection_path
end
end
private
def user_book_collection_params
params.require(:user_book_collection).permit( :book_id, :user_id, :review, :rating )
end
end
This is what shows up in the console:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"85gmzUO7ldQrevh/qnKwYO9mkd9lX77sG3xxJQV8Y46xZrkl5ifk665abPr79nOT91rO3oLcMSDgYL7BtR+/XQ==", "book_id"=>"6", "user_book_collection"=>{"rating"=>"s", "review"=>"asd"}, "commit"=>"Add to your collection"}
but then checking the record also in the console i can see the book_id did not pass through even though its been assigned in the parameters:
=> #<ActiveRecord::Associations::CollectionProxy [#<UserBookCollection id: 10, user_id: 7, book_id: nil, review: "asd", rating: "s", created_at: "2016-02-07 06:36:33", updated_at: "2016-02-07 06:36:33">
Any help would be appreciated, thanks.
It is because your form is constructed in a way the book_id is passed in params[:book_id], not in params[:user_book_collection][:book_id] thus not present in user_book_collection_params
Use:
f.hidden_field :book_id, value: current_book.id
<%= f.hidden_field :book_id, current_book.id %>
When using form_for, you need to bind the input method to the form object (as above).
In addition to Rich Peck and Vasfeds excellent answers I would suggest you consider what you are doing from a REST point of view.
You want users to be able to add reviews to a book. Note that here UserBookCollections is just an ugly piece of plumbing - not really a resource which you would expose with REST. And calling it UserBookCollection is a bit misleading since each UserBookCollection is really just a link between one user and one book.
Also you want to avoid calling anything Collection in Rails since ActiveRecord uses the concept to denote a collection of linked records which may or may not be loaded - you're going to confuse others.
This is one way to solve the same problem
class User
has_many :reviews
has_many :books, through: :reviews
end
class Review
belongs_to :user
belongs_to :book
end
class Book
has_many :reviews, through: :reviews
end
Nice, now we have an object that we don't even have to explain what it does. So lets turn it into a RESTful resource:
Rails.application.routes.draw do
resources :reviews, only: [:show, :edit, :update, :destroy]
resources :books do
resources :reviews, only: [:new, :create, :index], module: :books
end
resources :users do
resources :reviews, only: [:index], module: :users
end
end
This gives us routes with context:
Prefix Verb URI Pattern Controller#Action
edit_review GET /reviews/:id/edit(.:format) reviews#edit
review GET /reviews/:id(.:format) reviews#show
PATCH /reviews/:id(.:format) reviews#update
PUT /reviews/:id(.:format) reviews#update
DELETE /reviews/:id(.:format) reviews#destroy
book_reviews GET /books/:book_id/reviews(.:format) books/reviews#index
POST /books/:book_id/reviews(.:format) books/reviews#create
new_book_review GET /books/:book_id/reviews/new(.:format) books/reviews#new
user_reviews GET /users/:user_id/reviews(.:format) users/reviews#index
Cool - now we have an API for Review with a built in context. The RESTful routes themselves tell us when we are creating a review for a certain book or looking at reviews by a certain user.
module: :books tells rails to link the nested routes to a "namespaced" controller instead of shoving everything into ReviewsController.
We setup our controllers as follows:
class ReviewsController < ApplicationController
before_action :set_review, only: [:edit, :show, :update, :destroy]
# GET /reviews/:id
def show
end
# GET /reviews
def index
#reviews = Review.all
end
# GET /reviews/:id/edit
def edit
end
# PATCH /reviews/:id
def update
#review.update(review_params)
respond_with(#review)
end
# DELETE /reviews/:id
def destroy
#review.destroy
respond_with(#review)
end
private
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :body) # note that you don't need book_id in here.
end
end
# app/controllers/books/reviews_controller.rb
class Books::ReviewsController < ReviewsController
before_action :set_book
# GET /books/:book_id/reviews
def index
#reviews = Review.eager_load(:user, :book).where(book_id: params[:book_id])
end
# GET /books/:book_id/reviews/new
def new
#review = #book.find(params[:book_id]).reviews.new
end
# POST /books/:book_id/reviews
def create
#review = #book.reviews.new(review_params) do |review|
review.user = current_user
end
respond_with(#review)
end
private
def set_book
#book = Book).find(params[:book_id])
end
end
# app/controllers/users/reviews_controller.rb
class Users::ReviewsController < ApplicationController
# GET /users/:user_id/reviews
def index
#reviews = Review.joins(:user, :book).where(user_id: params[:user_id])
end
end
You might question why you would want to use 3 controllers? It allows for a very clean mechanism of both code sharing and customizations. You can have different logic and views without creating a bunch of if or switch code paths. Having many branches in a single action makes testing really messy and violates the skinny controllers paradigm.
The forms would look something like this:
# app/views/reviews/_fields.html.erb
<%= f.label :rating %>
<%= f.text_field :rating, class:"form-control" %>
<%= f.label :body %> (optional):
<%= f.text_area :body, size: "24x8", placeholder: "Please enter a brief review... ", class:"form-control" %>
# app/views/books/reviews/new.erb
<%= form_for([#book, #review]) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>
# app/views/books/edit.erb
<%= form_for(#review) do |f| %>
<%= render partial: 'reviews/fields', f: f %>
<% end %>

index outputs content that isn't in the .html.erb file

I'm following a ruby on rails tutorial to build a simple application using database tables and for some reason my home page outputs more than it should. It creates this array that looks like a database query made with prompt and puts it on the page. I'm pretty sure the problem is with my index.html.erb file (if I empty the file and reload the weird content isn't there), but I can't figure out what's going on. Here's a screenshot of what happens.
controller code:
class BooksController < ApplicationController
def new
#page_title = 'Add Book'
#book = Book.new
#category = Category.new
#author = Author.new
#publisher = Publisher.new
end
def create
#book = Book.new(book_params)
#book.save
redirect_to books_path
end
def update
end
def edit
end
def destroy
end
def index
#books = Book.all
end
def show
end
private
def book_params
params.require(:book).permit(:title, :category_id, :author_id, :publisher_id, :isbn, :price, :buy, :format, :excerpt, :pages, :year, :coverpath)
end
end
html:
<div id= "books-index">
<% #books.each_slice(4) do |book| %>
<div class = "row">
<%= book.each do |book| %>
<div class="col-md-3 col-sm-3">
<h3><%= book.title %></h3>
<%= image_tag(book.coverpath) %>
<%= link_to 'Read More', book_path(book), class:'btn btn-primary' %>
</div>
<% end %>
</div>
<% end %>
</div>
I am new to ruby and ruby on rails, so if I need to post any more resources or info in order to make my question clearer please let me know. Thanks for the help
You should put book.each call inside of <% tag, instead of <%= tag:
<% books.each do |book| %>
Now, you print the result of books.each (which is books array) into your HTML.
<%= book.each do |book| %>
The equal after the opening syntax prints the command(s) that follow. That should be the case, try removing it
remove= from the line <%= book.each do |book| %>

Does anyone know how to use conditional statements inside image_tags in ruby on rails.

Hello I am building a daily horoscope fortune telling app in ruby on rails. I am able to get the page to display a random image however I want a message to be associated with a certain picture when the user clicks on the button to tell their future. So far I have
<h1>What Does The Shadows Say About Your Heart</h1>
<%= image_tag "#{rand(21)}.jpg", size: "300x300" %>
<% if image_tag "0.jpg", size: "300x300" %>
<p>You have a thirst for vengeance.</p>
<% end %>
<% if image_tag "1.jpg" , size: "300x300"%>
<p>Soon you will face a great adversary.</p>
<% end %>
<% if image_tag "2.jpg" , size: "300x300"%>
<p>The road is not always clear.</p>
<% end %>
and my controller
class PagesController < ApplicationController
def home
end
def show
#dark = ["0" , "1", "2", "3", "4", "5", "6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"]
end
end
However the messages display on all the pictures. Please help it would be great. If you need to see more code just let me know. Note: I am not using carrierwave active admin nor paperclip.
I guess this is what you want
Controller
def show
#random = rand(21)
end
show.html
<h1>What Does The Shadows Say About Your Heart</h1>
<%= image_tag "#{#random}.jpg", size: "300x300" %>
<p><%= image_message(#random)%></p>
page_helper.rb
def image_message(random)
message = {1=> 'You have a thirst for vengeance.', 2=> 'Soon you will face a great adversary.', 3=> 'The road is not always clear.'}
message[random]
end
I'm not sure exactly what you want to achieve but one thing is for sure: move the logic out of the view; it will allow you to be more flexible as well.
class ImageRandomizer
MAPPING = [
[0, 'Financial good'], [1, 'Love life awesome'],...
]
def image
random_image_mapping.first
end
def message
random_image_mapping.last
end
private
def random_image_mapping
#random_image_mapping ||= MAPPING[rand(1..21)]
end
end
controller.rb
def show
#random_image = ImageRandomizer.new
end
show.html.erb
<h1>What Does The Shadows Say About Your Heart</h1>
<%= image_tag "#{#random_image.image}.jpg", size: "300x300" %>
<p><%= #random_image.message %></p>

ActionController::UrlGenerationError in Images#index

newbie here stuck on nested resources. I have a image.rb model, which stores images onto the cloudinary CDN. After getting all that working, I decided I needed to categorize the images, so i created a category.rb model which just has a name really, for now anyways.
I have been slowly working through errors after adding a category model and controller/views and all was going okay, until this one. I'm stumped lol. Have been googling around for a few hours now and decided I'm at a wall... so, naturally I come here, hoping someone could enlighten me. Thanks in advance! <3 SO
Here is the error (it is currently throwing it on the "edit" link, but i think it will throw it on the "delete" as well after i get edit working, so I have been changing them there as well):
No route matches {:category_id=>nil, :id=>#<Image id: 16, title: "mario", description: "i belong to category 2!", upload_date: nil, created_at: "2014-03-15 01:13:54", updated_at: "2014-03-15 01:13:54", category_id: 2>} missing required keys: [:category_id]
<td><%= link_to "Show", [#category, image] %></td>
<% if admin? %>
<td><%= link_to "Edit", edit_category_image_path(#category, image) %></td>
<td><%= link_to "Delete", [#category, image], confirm: "Are you sure you want to delete \"#{image.title}\"?", method: :delete %></td>
<% end %>
</tr>
Here is my Image model:
class Image < ActiveRecord::Base
has_attachment :image_file, accept: [:jpg, :png, :gif]
validates :title, presence: true
belongs_to :category
end
Here is the Category model:
class Category < ActiveRecord::Base
has_many :images
end
Here are the controllers for each:
class ImagesController < ApplicationController
before_filter :authenticate_admin, except: [:index, :show]
def index
#images = Image.all
end
def show
#image = Image.find(params[:category_id])
end
def new
#category = Category.find(params[:category_id])
#image = #category.images.build(image_params)
end
def create
#category = Category.find(params[:category_id])
#image = #category.images.build(image_params)
if #image.save
render "show"
else
render "new"
end
end
def destroy
#image = Image.find(params[:id])
#image.destroy
redirect_to images_path
end
def edit
#category = Category.find(params[:category_id])
#image = #category.images.find(params[:id])
end
def update
#category = Category.find(params[:category_id])
#image = #category.images.find(params[:id])
if #image.update(image_params)
render "show"
else
render "edit"
end
end
private
def image_params
params.require(:image).permit(:title, :description, :image_file, :category_id)
end
end
and the Category controller
class CategoriesController < ApplicationController
def index
#categories = Category.all
end
def show
redirect_to category_images_path(params[:id])
end
def new
#category = Category.new
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
else
render "new"
end
end
def destroy
#category = Category.find(params[:id])
#category.destroy
redirect_to categories_path
end
def edit
#category = Category.find(params[:id])
end
def update
#category = Category.find(params[:id])
if #category.update(category_params)
redirect_to #category
else
render "edit"
end
end
private
def category_params
params.require(:category).permit(:title)
end
end
Here is the routes file:
Portfolio::Application.routes.draw do
devise_for :users
resources :blogs
resources :categories do
resources :images
end
root "index#index"
#attachinary
mount Attachinary::Engine => "/attachinary"
end
From googling around I kept running into people having problems with it being related to form partials so here is the _form.html.erb I am using for Image just in case it's needed:
<%= form_for([#category, #image]) do |f| %>
<%= render "error" %>
<p>
<%= f.label :title %>
<br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :description %><br>
<%= f.text_area :description %>
</p>
<p>
<%= f.label "Upload image" %><br>
<%= f.attachinary_file_field :image_file %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
ImagesController
class ImagesController < ApplicationController
before_action :load_category
before_action :set_image, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_admin, except: [:index, :show]
def index
#images = #category.images.all
end
def show
end
def new
#image = #category.images.new
end
def create
#image = #category.images.new(image_params)
if #image.save
render "show"
else
render "new"
end
end
def destroy
#image.destroy
redirect_to images_path
end
def edit
end
def update
if #image.update(image_params)
render "show"
else
render "edit"
end
end
private
def load_category
#category = Category.find(params[:category_id])
end
def set_image
#image = #category.images.find(params[:id])
end
def image_params
params.require(:image).permit(:title, :description, :image_file, :category_id)
end
end
Try to use the before_action callback to set your necessary instance variables. Here I loaded the category and the images via a private method. This saves a lot time. You can even see this when you generate scaffolding.
before_filter has been aliased as before_action to make it clearer of its function.
The root path is usually defined on the second line of routes so its clear.
Your named paths doesn't seem like the problem. It is mainly your images controller
I suggest you generate a new rails app using scaffolding just to look at it's controllers.
rails g scaffolding images