I'm creating a newsfeed and have a JSON response set up with Active Model Serializers gem. I'd like to render a regular ERB partial and have it as one of the attributes in the JSON.
controllers/posts_controller.rb:
def index
#posts = Post.all
respond_to do |format|
format.json { render json: #posts.to_a, each_serializer: PostSerializer }
end
end
serializers/post_serializer.rb:
def event_html
ApplicationController.new.render_to_string(:partial => 'events/event', :locals => { :event => object.event }) if object.event
end
Unfortunately, my view has some code that uses helpers and the request object (urls), and I'm getting errors when I try to do this.
events/_event.html.erb:
<div class="event">
<h4 class="title"><%= link_to event.name, event %></h4>
<div class="date"><%= date_range event.start_at, event.end_at %></div>
<div class="description"><%= event.description.html_safe %></div>
</div>
undefined method `host' for nil:NilClass
Any ideas of a good way to solve this problem?
That happens because your controller lacks response attribute.
You may set the request for the controller as follows:
def event_html
ApplicationController.new.tap {|c|
c.request = ActionController::TestRequest.new(host: "localhost")
}.render_to_string(:partial => 'events/event', :locals => { :event => object.event }) if object.event
end
However, for your case I would pass an existing controller as a context to the serializer
controllers/posts_controller.rb:
def index
#posts = Post.all
respond_to do |format|
format.json { render json: #posts.to_a, each_serializer: PostSerializer, context: self }
end
end
# ... or ...
def default_serializer_options
{
context: self
}
end
serializers/post_serializer.rb:
def event_html
context.render_to_string(:partial => 'events/event', :locals => { :event => object.event }) if object.event
end
Related
I am trying to create a page within my application that shows all listings from a specific user. I am using Devise gem for my users. i do not need/want authentication, therefore the page should be open the the general public. I have already created a "Seller" page where a seller can manage their own listings. so how can I create a link on each listing on my homepage that connects the
<p><%= "Sold by #{listing.user.name}" %></p>
the new show page for that user? thanks!
my listings_controller:
class ListingsController < ApplicationController
before_action :set_listing, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, only: [:seller, :new, :create, :edit, :update, :destroy]
before_filter :check_user, only: [:edit, :update, :destroy]
def seller
#listings = Listing.where(user: current_user).order("created_at DESC")
end
# GET /listings
# GET /listings.json
def index
if params[:category].blank?
#listings = Listing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 16)
else
#category_id = Category.find_by(name: params[:category]).id
#listings = Listing.where(category_id: #category_id).order("created_at DESC").paginate(:page => params[:page], :per_page => 16)
end
end
# GET /listings/1
# GET /listings/1.json
def show
end
# GET /listings/new
def new
#listing = Listing.new
end
# GET /listings/1/edit
def edit
end
# POST /listings
# POST /listings.json
def create
#listing = Listing.new(listing_params)
#listing.user_id = current_user.id
if current_user.recipient.blank?
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
recipient = Stripe::Recipient.create(
:name => current_user.name,
:type => "individual",
:bank_account => token
)
current_user.recipient = recipient.id
current_user.save
end
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render :show, status: :created, location: #listing }
else
format.html { render :new }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /listings/1
# PATCH/PUT /listings/1.json
def update
respond_to do |format|
if #listing.update(listing_params)
format.html { redirect_to #listing, notice: 'Listing was successfully updated.' }
format.json { render :show, status: :ok, location: #listing }
else
format.html { render :edit }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /listings/1
# DELETE /listings/1.json
def destroy
#listing.destroy
respond_to do |format|
format.html { redirect_to listings_url, notice: 'Listing was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_listing
#listing = Listing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def listing_params
params.require(:listing).permit(:name, :category_id, :description, :price, :image)
end
def check_user
if current_user != #listing.user
redirect_to root_url, alert: "Sorry, this listing belongs to someone else"
end
end
end
my current routes:
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
resources :categories
devise_for :users
resources :listings do
resources :orders, only: [:new, :create]
end
get 'pages/about'
get 'pages/contact'
get 'seller' => "listings#seller"
get 'sales' => "orders#sales"
get 'purchases' => "orders#purchases"
# or
root 'listings#index'
end
and finally, my listing model:
class Listing < ActiveRecord::Base
if Rails.env.development?
has_attached_file :image, :styles => { :medium => "200x", :thumb => "100x100>" }, :default_url => "404.jpg"
else
has_attached_file :image, :styles => { :medium => "200x", :thumb => "100x100>" }, :default_url => "404.jpg",
:storage => :dropbox,
:dropbox_credentials => Rails.root.join("config/dropbox.yml"),
:path => ":style/:id_:filename"
end
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
validates :name, :category_id, :description, :price, presence: true
validates :price, numericality: { greater_than: 0}
validates_attachment_presence :image
belongs_to :user
belongs_to :category
has_many :orders
end
my index page where I want to link to the seller specific page
<div class="center">
<div class="row">
<% #listings.each do |listing| %>
<div class="col-md-3">
<div class="thumbnail">
<%= link_to image_tag(listing.image.url), listing %>
<div class="caption">
<h3><%= listing.name %></h3>
<p><%= number_to_currency(listing.price) %></p>
<p><%= "Sold by #{listing.user.name}" %></p>
</div>
</div>
</div>
<% end %>
</div>
</div>
<br>
<div class="center">
<%= will_paginate #posts, renderer: BootstrapPagination::Rails %>
</div>
<% if user_signed_in? %>
<div class="right">
<%= link_to new_listing_path, class: "btn btn-primary", data: { no_turbolink: true } do %>
<i class="glyphicon glyphicon-plus"></i> New Listing
<% end %>
</div>
<% end %>
<br>
'User' is a model that you have created: devise only manages sessions, registrations, passwords, unlocks, and confirmations (the gem provides a controller for each one of these).
You should create your own UsersController, in which you can define the show action that you need. You should also declare a different path in your routes, or you'd have a conflict as '/users' is already used by devise. Something like
resources :users, only: [:show], path: 'sellers'
Then you can use
<p><%= "Sold by #{link_to listing.user.name, user_path(listing.user)}" %></p>
i get an error
undefined method `photo' for #<Post::ActiveRecord_Relation:0x00000004ad42e8>
my posts.index.html.erb
<%= image_tag #post.photo.url(:small) %>
gives me the above error and if i iterate
<% #post.each do |image| %>
<%= image_tag image.photo.url %>
<% end %>
i get this new error
SQLite3::SQLException: no such column: position: SELECT "posts".* FROM "posts" ORDER BY position ASC
here is my model, post.rb
Class Post < ActiveRecord::Base
require 'digest/md5'
has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png",
:path => ":rails_root/public/assets/images/:id/:style/:basename.:extension"
validates_attachment_content_type :photo, :content_type => /\Aimage\/.*\Z/
validates_attachment_size :photo, :less_than => 5.megabytes
validates_attachment_content_type :photo, :content_type => ['image/jpeg', 'image/png']
include PublicActivity::Model
tracked
end
and my posts_controller.rb
class PostsController < ApplicationController
before_action :authenticate_user!
def index
##hotels = hotels.new()
#activities = PublicActivity::Activity.order("created_at desc")
#post = Post.order('position ASC')
respond_to do |format|
format.html # show.html.erb
format.json { render json: #picture }
end
end
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #picture }
end
end
def new
#post = Post.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #picture }
end
end
def create
##post = Post.create( user_params )
#post = Post.new(params[:posts].permit(:hotels, :photo, :number, :price, :guest_house, :restaurant,:lodges, :description, :region))
if #post.save
redirect_to(:controller => 'homes',:action=> 'index')
else
render('new')
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
#post = Post.update_attributes()
end
def delete
#post = Post.find(params[:id])
end
#def user_params
#params.require(:post).permit(:photo)
#end
end
i also have the public activity gem installed on my app and when i try to display the photos using
<%= link_to activity.trackable.photo %>
i get a link to where the photo was saved but not the actual image as shown below. help me out
Ah I finally realized my mistake. i used the link_to tag instead of the image_tag
I can't figure out what I am doing incorrectly when trying to load ActiveRecord validation errors with AJAX. I cannot get the ajax:error to trigger unless I have already submitted a valid form and triggered ajax:success. I have tried only binding the ajax:error to see if the success could be blocking it. I have read through the rails documentation on working with javascript in rails, and Googled my problem to no avail. I have tried to use both .bind() and .on() but I haven't seen a difference. I am sure there is something incredibly simple that I am missing, but I have been looking at this too long and I don't seem to be getting anywhere.
Here is the code.
Model
class User < ActiveRecord::Base
before_create :set_activation_key
before_save :encrypt_password
before_save :downcase_username
after_save :clear_password
validates :username,
format: {
with: /\A.*\z/,
message: 'invalid'
},
uniqueness: {message: 'already in use'},
presence: {message: 'cannot be blank'}
validates :email_address,
format: {
with: /\A.+#.+\..+\z/,
message: 'invalid'
},
uniqueness: {message: 'already in use'},
presence: {message: 'cannot be blank'}
validates :password, :confirmation => true,
presence: {message: 'cannot be blank'},
length: {:within => 6..20,
:too_long => 'too long',
:too_short => 'too short',
}
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.password = BCrypt::Engine.hash_secret(password, salt)
end
end
def clear_password
self.password = nil
end
def set_activation_key
self.activation_key = SecureRandom.urlsafe_base64
end
def downcase_username
self.username = self.username.downcase
end
def self.authenticate(username, password)
user = User.find_by_username(username.to_s.downcase)
if user && user.password == BCrypt::Engine.hash_secret(password, user.salt)
user
else
nil
end
end
end
Controller
class Users::UsersController < ApplicationController
# GET /register
def new
#user = User.new
end
# POST /users
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserMailer.registration_email(#user.email_address).deliver
format.html { redirect_to root_path, notice: 'Check your email' }
format.js
else
format.html { render action: 'new' }
format.js { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:username, :email_address, :password, :password_confirmation)
end
end
Partial
<%= form_for User.new, remote: true do |f| %>
<div id="registration_messages"></div>
<div id="registration_errors"></div>
<%= f.label :username %>
<%= f.text_field :username%>
<%= f.label :email_address %>
<%= f.text_field :email_address %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, 'Confirm Password' %>
<%= f.password_field :password_confirmation %>
<%= f.submit 'Register', :class => 'tiny button' %>
<a class="close-reveal-modal">×</a>
<% end %>
create.js.erb
$(document).ready(function() {
$('#new_user')
.bind("ajax:success", function(event, data, xhr, status){
var $form = $(this);
$form.find('input[type=text], input[type=password], textarea').val('');
$("div#registration_messages").append('<div data-alert class="alert-box success">Check your email</div>');
}).bind("ajax:error", function(event, data, xhr, error){
console.log(event);
console.log(data);
console.log(xhr);
console.log(error);
alert("ajax:error");
});
});
This doesn't help you with your current technique, but you could try doing it in pure JQuery and ditch the js.erb file. I've always found this way more straightforward once you get the hang of it. I would expect something like the following code not to produce the issue you're seeing.
controller
# POST /users
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
UserMailer.registration_email(#user.email_address).deliver
format.html { redirect_to root_path, notice: 'Check your email' }
format.js { render nothing: true, status: :created }
else
format.html { render action: 'new' }
format.js { render json: #user.errors, status: :unprocessable_entity }
end
end
end
javascript.js
# could go in application.js while you're testing it, but best to factor it out into it's own file.
$('form#new_user').on('ajax:success', function(event, data, xhr, status) {
var $form = $(this);
$form.find('input[type=text], input[type=password], textarea').val('');
$("div#registration_messages").append('<div data-alert class="alert-box success">Check your email</div>');
});
$('form#new_user').on('ajax:error', function(event, data, xhr, status) {
console.log(event);
console.log(data);
console.log(xhr);
console.log(error);
alert("ajax:error");
});
I am working on a rails application and want to send an email with an inline image to the email address that the user enters. I have been struggling to do it. I checked online and this is what I did.
When I enter an email in development it gives me an error saying,
ActionController::UnknownFormat
This is my ActionMailer Notifier.rb
class Notifier < ActionMailer::Base
default from: "insidespintex#gmail.com"
def enter_email(sending)
#coming = sending
attachments.inline['blank'] = {
:data => File.read("#{Rails.root.to_s + '/app/assets/images/News.png'}"),
:mime_type => "image/png",
:encoding => "base64"
}
mail :to => #coming.email, :subject => 'InsideSpintex is coming soon'
end
end
This is my ApplicationController LandingController.rb
class LandingController < ApplicationController
layout 'landingPage'
def soon
#coming = Coming.new
end
def after
render 'after'
end
def save_email
#coming = Coming.new(soon_params)
respond_to do |format|
if #coming.save
Notifier.enter_email(#coming).deliver
format.html { render action: 'after' }
#render 'after'
else
render 'soon'
end
end
end
#render 'soon'
private
def soon_params
params.require(:coming).permit(:email)
end
end
And this is my View enter_email.html.erb
<p> Hello There,</h3>
<%= image_tag attachments['News.png'] %>
Well, I think that you don't need the encoding part in your attachments.inline hash. I was using something like this on a project:
Mailer method:
attachments.inline['logo.png'] = {
data: File.read(Rails.root.join('app/assets/images/logo.png')),
mime_type: 'image/png'
}
and you are missing the .url in the view:
<%= image_tag attachments['logo.png'].url %>
I've done a simple project about paper_trail, however I encountered a problem. When I created or updated completely there were two message show in the view:
Product was successfully created. undo
Product was successfully created. <a data-method="post" href="/versions/148/revert" rel="nofollow">undo</a>
here is my controller file:
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.order(:name)
respond_to do |format|
format.html
format.csv { send_data #products.to_csv }
end
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: "Product was successfully created. #{undo_link}" }
format.json { render action: 'show', status: :created, location: #product }
else
format.html { render action: 'new' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to product_url, notice: 'Product was successfully updated.' "#{undo_link}" }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: "Successfully destroyed product. #{undo_link}" }
format.json { head :no_content }
end
end
def import
Product.import(params[:file])
redirect_to root_url, notice: "Products imported."
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :price)
end
def undo_link
view_context.link_to("undo", revert_version_path(#product.versions.scoped.last), :method => :post)
end
end
and the layout file here:
<!DOCTYPE html>
<html>
<head>
<title>Store</title>
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
</head>
<body>
<div id="container">
<% flash.each do |name, msg|%>
<%= content_tag :div, raw(msg), :id => "flash_#{name}" %>
<% end %>
<%= yield %>
</div>
</body>
</html>
I expect to show message once, however it show twice, so please tell me where my errors?
You are calling a flash message in both your create action and your view.
Create action:
format.html { redirect_to #product, notice: "Product was successfully created. #{undo_link}" }
View:
<% flash.each do |name, msg|%>
<%= content_tag :div, raw(msg), :id => "flash_#{name}" %>
<% end %>
The first gives:
Product was successfully created. undo
The latter show all raw output because of raw(msg):
Product was successfully created. <a data-method="post" href="/versions/148/revert" rel="nofollow">undo</a>