I'm new to rails, having just finished Michael Hartl's excellent Rails Tutorial. I'm building out another project app loosly following that framework, but with differences.
I'm partway in and have already run into an issue where many parts of my app seem unusually slow, and I don't see an obvious reason why. I've put details of my users index page below, because that is the slowest. As shown below, it takes 9.1s to load, of which ~8.7s is loading the views. This is on the dev environment (AWS Cloud9), but even when deployed on Heroku it takes ~5s to render the page with only 13 users in the database!
5s is pretty bad at this limited scale, and the rails tutorial sample app renders the users index page in 447ms total with 50 users shown! (running on a similar AWS EC2 instance) I don't see where my app is different enough to cause a 20x increase in load time.
Render /users server output:
Started GET "/users" for 24.85.170.222 at 2023-01-29 21:52:03 +0000
Cannot render console from 24.85.170.222! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by UsersController#index as HTML
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:12:in `current_user'
Rendering layout layouts/application.html.erb
Rendering users/index.html.erb within layouts/application
User Count (1.1ms) SELECT COUNT(*) FROM "users"
↳ app/views/users/index.html.erb:5
User Load (0.6ms) SELECT "users".* FROM "users" LIMIT ? OFFSET ? [["LIMIT", 30], ["OFFSET", 0]]
↳ app/views/users/index.html.erb:8
Rendered users/index.html.erb within layouts/application (Duration: 7719.3ms | Allocations: 280179)
Rendered layouts/_rails_default.html.erb (Duration: 7.6ms | Allocations: 6444)
Rendered layouts/_shim.html.erb (Duration: 0.0ms | Allocations: 9)
Company Load (0.1ms) SELECT "companies".* FROM "companies" INNER JOIN "company_users" ON "companies"."id" = "company_users"."company_id" WHERE "company_users"."user_id" = ? AND "company_users"."employee" = ? LIMIT ? [["user_id", 2], ["employee", 1], ["LIMIT", 1]]
↳ app/views/layouts/_header.html.erb:35
Rendered layouts/_header.html.erb (Duration: 963.3ms | Allocations: 1473)
Rendered layouts/_footer.html.erb (Duration: 0.2ms | Allocations: 73)
Rendered layout layouts/application.html.erb (Duration: 8693.9ms | Allocations: 289996)
Completed 200 OK in 9147ms (Views: 8697.1ms | ActiveRecord: 2.2ms | Allocations: 291862)
users_controller.rb:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy, :show]
before_action :correct_user, only: [:edit, :update]
def index
#users = User.paginate(page: params[:page])
end
end
/models/user.rb:
class User < ApplicationRecord
before_save :downcase_email
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: true
#Returns true if given token matches the digest
def authenticated?(token)
#debugger
return false if session_digest.nil?
BCrypt::Password.new(session_digest).is_password?(token)
end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
#returns a session token to prevent session hijacking
def set_session_token
#session_token = SecureRandom.urlsafe_base64
update_attribute(:session_digest, User.digest(#session_token))
return #session_token
end
private
def downcase_email
email.downcase!
end
end
sessions_helper.rb:
module SessionsHelper
def log_in(user)
session[:user_id] = user.id
#guard against session replay attacks
session[:session_token] = user.set_session_token
end
def current_user
if (user_id = session[:user_id])
#sess_user ||= User.find_by(id: user_id)
if #sess_user && #sess_user.authenticated?(session[:session_token])
#current_user = #sess_user
end
end
end
def current_user?(user)
user && user == current_user
end
def logged_in?
!current_user.nil?
end
#confirms a logged in user
def logged_in_user
unless logged_in?
store_location #store requested URL in a cookie for friendly forwarding
flash[:danger] = "Please log in"
redirect_to root_url, status: :see_other
end
end
end
/views/users/index.html.erb:
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<% #users.each do |user| %>
<li>
<%= link_to user.display_name, user %>
<% if current_user.superadmin? && !current_user?(user) %>
| <%= link_to "delete", user, data: { "turbo-method": :delete, turbo_confirm: "Delete #{user.email}?" } %>
<% end %>
</li>
<% end %>
</ul>
<%= will_paginate %>
views/layouts/application.html.erb:
<!DOCTYPE html>
<html>
<head>
<title><%= full_title(yield(:title)) %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta charset="utf-8">
<%= render 'layouts/rails_default' %>
<%= render 'layouts/shim' %>
<%= javascript_importmap_tags %>
</head>
<body>
<% flash.each do |message_type, message| %>
<%= content_tag(:div, message, class: "alert alert-#{message_type}") %>
<% end %>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
<%= debug(params) if Rails.env.development? %>
</div>
</body>
</html>
My views/layouts/_header.html.erb file also hits is_logged_in? and current_user a few times.
I am using all the same Gems as the railstutorial.org sample app, so a Gem shouldn't be the issue.
At first I thought that current_user was hitting the database multiple times based on the log. So I memoized it with the #sess_user ||= User.find_by(id: user_id) line. But this didn't affect load times hardly at all, and when I compared with the sample I app I realized this wasn't it as the db hits were cached: CACHE User Load (0.0ms)
I feel like there's something obvious that I'm missing. I've read up on N+1 queries and using .includes, especially as there are some other models on my site for which that might be relevant, but the fact that the issue occurs even on the very plain users index page perplexes me.
I'm still only partway through development, but I want to try and figure out this issue before I get too far ahead and things get only more complex. Would be very appreciative of any insights!
Alex's first comment on this question drove straight to the heart of the issue. I refactored current_user as shown below to memoize the return value so that is_password? is only called once per view. Instant relief - page load times are now sub-400ms.
All credit to Alex. He nailed it. His comment should really be "the answer", but I feel like I should post this here so the question shows as "answered".
def current_user
#memoize current user so authenicated? only runs once per page (its very slow # 200ms ea)
#current_user ||= begin
if (user_id = session[:user_id])
user ||= User.find_by(id: user_id)
if user && user.authenticated?(session[:session_token])
user
end
end
end
end
Related
I'm following along with the rails tutorial by Michael Hartl Chapter 13 but instead of microposts, I'm creating animals.
My view of animals shows a "delete" hyperlink that is supposed to delete an animal record from my list but the view action doesn't seem to get to the point where it uses the destroy method at all.
I read through all the answers in a similar post here but did not find those answers to help in my case.
I'm currently in a development environment on AWS cloud9 as instructed in the tutorial. I really appreciate any pointers as I have been struggling with this one for days.
Here is my code from the view:
<li id="animal-<%= animal.id %>">
<%= link_to gravatar_for(animal.user, size: 50), animal.user %>
<span class="user"><%= link_to animal.user.name, animal.user %></span>
<span class="ear_tag"><%= animal.ear_tag %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(animal.created_at) %> ago.
<% if current_user?(animal.user) %>
<%= link_to "delete", animal, method: :delete, data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>
This view is called from a feed view:
<% if #feed_items.any? %>
<ol class="animals">
<%= render #feed_items %>
</ol>
<%= will_paginate #feed_items %>
<% end %>
Which comes from the home page view:
<div class="col-md-8">
<h3>Animal Feed</h3>
<%= render 'shared/feed' %>
</div>
I have reviewed the <%= link_to... line many times and it seems to be correct. My controller code is:
class AnimalsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def destroy
#animal.destroy
flash[:success] = "Animal deleted"
redirect_to request.referrer || root_url
end
def correct_user
#animal = current_user.animals.find_by(id: params[:id])
redirect_to root_url if #animals.nil?
end
end
I noticed that I never see the flash "Animal deleted" so that tells me I probably don't get to that point in the controller method.
My model code is:
class Animal < ApplicationRecord
belongs_to :user
default_scope -> {order(created_at: :desc) }
validates :user_id, presence: true
validates :ear_tag, presence: true, length: {maximum: 20}
end
Here is my application.js file from app/assets/javascripts:
//= require jquery
//= require bootstrap
//= require rails-ujs
//= require turbolinks
//= require_tree
Here is what the server log says after I click on the "delete" tag in my rendered view in the browser:
Started DELETE "/animals/308" for 23.25.133.17 at 2018-09-06 21:11:06 +0000
Processing by AnimalsController#destroy as HTML
Parameters: {"authenticity_token"=>"vwF6cWow+6B3BxOiJElVY0aQmMGr4WLWDOCxgB0C03nRLcQDKC3YCUqBr4ahVwlSKN7bEYRrmGytyI1fPgvavw==", "id"=>"308"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 102], ["LIMIT", 1]]
Animal Load (0.2ms) SELECT "animals".* FROM "animals" WHERE "animals"."user_id" = ? AND "animals"."id" = ? ORDER BY "animals"."created_at" DESC LIMIT ? [["user_id", 102], ["id", 308], ["LIMIT", 1]]
Redirected to https://cabfa13dd4f34e67b634d4f52a7a046f.vfs.cloud9.us-west-2.amazonaws.com/
Filter chain halted as :correct_user rendered or redirected
Completed 302 Found in 4ms (ActiveRecord: 0.3ms)
I also found one of my tests to fail:
FAIL["test_animal_interface", AnimalsInterfaceTest, 1.5248641059999954]
test_animal_interface#AnimalsInterfaceTest (1.53s)
"Animal.count" didn't change by -1.
Expected: 38
Actual: 39
test/integration/animals_interface_test.rb:33:in `block in <class:AnimalsInterfaceTest>'
Filter chain halted as :correct_user rendered or redirected
The problem is you have #animals(which is not defined) instead of #animal, so redirect_to root_url if #animals.nil? always succeeds which results in destroy action failing always. You should change #animals to #animal
def correct_user
#animal = current_user.animals.find_by(id: params[:id])
redirect_to root_url if #animal.nil?
end
In my RoR application, I have an update_multiple method that updates multiple records with the user's inputs. However, for some reason I get the error ActiveModel::ForbiddenAttributesError despite using strong params. Can someone please help me fix this?
The update_multiple method in Recipients_Controller is as follows:
def update_multiple
#email = Email.find_by_id(params[:email_id])
if Recipient.update(params[:recipient].keys, params[:recipient].values)
#listofcontacts = Recipient.where("id in (?)", params[:recipient].keys)
#account = Account.find_by_id(#email.account_id)
#listofcontacts.each do |f|
recipient_message = #email.message
recipient_message = recipient_message.gsub("VAR1", f.var1)
contact = Contact.find_by_id(f.contact_id)
#unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
UserEmails.send_email(#email, #account, contact.email, #unsubscribe, recipient_message).deliver_now
end
flash[:notice] = "recipients were updated"
redirect_to root_path
else
render 'edit_multiple'
end
end
private
def recipient_params
params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end
This method takes the user input from this form:
<%= form_for :recipient, :url => update_multiple_recipients_path, :html => { :method => :put } do %>
<fieldset>
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" id="example">
<thead>
<tr>
<th>Contact</th>
<% if #email_message.upcase.include? "VAR1" %><th>VAR1</th><% end %>
</tr>
</thead>
<tbody>
<%= hidden_field_tag :email_id, #email %>
<% #recipients.each do |recipient| %>
<tr class="odd gradeX">
<%= fields_for "recipient[]", recipient do |recipient_fields| %>
<td><%= recipient_fields.label recipient.contact.firstname %> <%= recipient_fields.label recipient.contact.surname %></td>
<% if #email_message.upcase.include? "VAR1" %><td><%= recipient_fields.text_field :var1, :required => true, :maxlength => 20 %></td><% end %>
<% end %>
</tr>
<% end %>
</tbody>
</table></br>
<%= submit_tag 'Send Email', {:class => 'btn btn-primary'} %></br>
<%= link_to "Back", edit_email_path(#email) %>
</fieldset>
<% end %>
The development.log reads this:
Started PUT "/recipients/update_multiple" for ::1 at 2017-03-03 09:33:10 +0000
Processing by RecipientsController#update_multiple as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"BJtQ56CW169tJ0Yqlc7BZNZk8SiTCauvkpNkXRUqVv4WESSS/DGFVDe3uQnfTxxDgif8lbg8THtmxHT9bOh0zw==", "email_id"=>"292", "recipient"=>{"635"=>{"var1"=>"ben"}}, "commit"=>"Send Email"}
[1m[36mEmail Load (0.0ms)[0m [1mSELECT "emails".* FROM "emails" WHERE "emails"."id" = ? LIMIT 1[0m [["id", 292]]
[1m[35mRecipient Load (1.0ms)[0m SELECT "recipients".* FROM "recipients" WHERE "recipients"."id" = ? LIMIT 1 [["id", 635]]
[1m[36m (0.0ms)[0m [1mbegin transaction[0m
[1m[35m (0.0ms)[0m rollback transaction
Completed 500 Internal Server Error in 5ms (ActiveRecord: 1.0ms)
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/recipients_controller.rb:15:in `update_multiple'
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_source.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (4.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (1459.1ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_markup.html.erb (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/console.js.erb within layouts/javascript (1449.1ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/main.js.erb within layouts/javascript (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.0ms)
Rendered C:/RailsInstaller/Ruby2.2.0/lib/ruby/gems/2.2.0/gems/web-console-2.3.0/lib/web_console/templates/index.html.erb (3305.2ms)
Where it says app/controllers/recipients_controller.rb:15:in 'update_multiple', it is pointing to the line if Recipient.update(params[:recipient].keys, params[:recipient].values)
I really cannot figure out why I am getting this error. Can someone please help me?
I have looked at various other SO questions and they seem to have been solved by strong_params, but mine already has strong_params declared and isn't working.
The problems is this line:
if Recipient.update(params[:recipient].keys, params[:recipient].values)
You are passing params directly to the update method. You need to pass recipient_params into update:
if Recipient.update(recipient_params.keys, recipient_params.values)
Update
However, from your logs, it is apparent that params[:recipient] is returning a hash containing id/attribute pairs, not a single set of attributes. So you'll need to permit the attributes for each recipient id passed in params. I think this code should do it:
private
def recipient_params
params.require(:recipient).permit(permit_for_each_recipient)
end
def permit_for_each_recipient
params[:recipient].keys.inject({}){|h,k| h[k] = attributes_to_permit; h}
end
def attributes_to_permit
[:contact_id, :group_id, :email_id, :var1, :var2, :var3]
end
EDITING ANSWER AFTER COMMENTS
First of all, some good docs To help you to understand better how to use strong parameters.
Now, let's try to refactor a bit your code. Remember, methods larger than 4 lines are more risky to hide mistakes, avoid them if you can!
This is your code, I changed nothing except I moved some chunks of code into sub-methods
def update_multiple
#this cannot work because there is no instance to update
if Recipient.update(params[:recipient].keys, params[:recipient].values)
send_unsuscribe_emails
flash[:notice] = "recipients were updated"
redirect_to root_path
else
render 'edit_multiple'
end
end
private
def recipient_params
params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end
def send_unsuscribe_emails
#email = Email.find_by_id(params[:email_id])
#this cannot work because params[:recipient].keys does not return a list of ids (probably you want womethink like recipients_params[:contact_id])
#listofcontacts = Recipient.where("id in (?)", params[:recipient].keys)
#account = Account.find_by_id(#email.account_id)
#listofcontacts.each do |f|
send_unsuscribe_email(f)
end
end
def send_unsuscribe_email(f)
recipient_message = #email.message.gsub("VAR1", f.var1)
contact = Contact.find_by_id(f.contact_id)
#unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
UserEmails.send_email(#email, #account, contact.email, #unsubscribe, recipient_message).deliver_now
end
and now the solution could be like this
def update_multiple
#listofcontacts = Recipient.where("id in (?)", recipients_params[:contact_id])
if #listofcontacts.update(recipient_params)
send_unsuscribe_emails
flash[:notice] = "recipients were updated"
redirect_to root_path
else
render 'edit_multiple'
end
end
private
def recipient_params
params.require(:recipient).permit(:contact_id, :group_id, :email_id, :var1, :var2, :var3)
end
def send_unsuscribe_emails
#email = Email.find_by_id(params[:email_id])
#account = Account.find_by_id(#email.account_id)
#listofcontacts.each do |f|
send_unsuscribe_email(f)
end
end
def send_unsuscribe_email(f)
recipient_message = #email.message.gsub("VAR1", f.var1)
contact = Contact.find_by_id(f.contact_id)
#unsubscribe = Rails.application.message_verifier(:unsubscribe).generate(contact.id)
UserEmails.send_email(#email, #account, contact.email, #unsubscribe, recipient_message).deliver_now
end
Of course I cannot test it so, probably it will crash somewhere, but more or less this is the idea.
I've been trying to deal with this to no avail. It's driving me nuts. Usually, I can sleep on something like this and realize it was a typo in the morning. But I can't sleep until I figure this out - catch 22. Anyway, I have an index of questions being displayed, the title of each question is a link to itself. Nothing surprising there. And it works on local host. When I deploy to heroku, it shows that it's leading me to the correct link and I have it routed correctly when I hover the links. (ie, myapp.herokuapp.com/questions/2). I definitely have the links set to question_path(question) in the block. yet, when I click the link, it takes me to the questions_path, right back to the index of questions. It's driving me mad. Anyone have a problem like this before?
index.html.erb View:
<% if #quests.present? %>
<% #quests.each do |question| %>
<div class="booyah-box col-xs-10 col-xs-offset-1">
<center>
<bptitle>
<%= link_to question.qtitle.html_safe, question_path(question) %>
</bptitle>
<br />
<bpauthor> A question by <%= question.user.firstname %> <%= question.user.lastname %>, from
<%= question.user.organization.name %></bpauthor>
</center>
<br />
<%= question.qbody.split[0..10].join(" ").html_safe %>... <br /><br />
Answers: <%= question.answers.count %><br />
</div>
<% end %>
<% else %>
<p>There are no posts containing the term(s) <%= params[:search] %>.</p>
<% end %>
</center>
questions_controller:
def index
if params[:search] != nil
#quests = Question.search(params[:search]).order("created_at DESC")
else
#quests = Question.all.order('created_at DESC')
end
end
def show
#question = Question.find(params[:id])
#answer = Answer.new
#faveanswer = Favanswer.new
#ans = Answer.find(params[:id])
#qids = []
#qaids = []
Favanswer.all.each do |favanswer|
#qids << favanswer.question_id
#qaids << [favanswer.question_id, favanswer.answer_id]
end
end
Question model:
class Question < ActiveRecord::Base
include Bootsy::Container
mount_uploader :bootsy, BootsyUploader
belongs_to :user
has_many :answers
has_one :favanswer
def self.search(search)
where("qtitle LIKE ? OR qbody LIKE ?", "%#{search}%", "%#{search}%")
end
end
Please tell me I have a typo. I will kiss your feet if you help. I need sleep.
Editing with my heroku logs per Andrew's comment below:
2016-08-01T13:54:20.800626+00:00 app[web.1]: Started GET "/questions/3" for 162.244.49.116 at 2016-08-01 13:54:20 +0000
2016-08-01T13:54:20.803208+00:00 app[web.1]: Processing by QuestionsController#show as HTML
2016-08-01T13:54:20.803238+00:00 app[web.1]: Parameters: {"id"=>"3"}
2016-08-01T13:54:20.806102+00:00 app[web.1]: User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
2016-08-01T13:54:20.809813+00:00 app[web.1]: Question Load (0.8ms) SELECT "questions".* FROM "questions" WHERE "questions"."id" = $1 LIMIT 1 [["id", 3]]
2016-08-01T13:54:20.833346+00:00 app[web.1]: Redirected to http://mypretendapp.herokuapp.com/questions
Depending on your Heroku configuration, you can either face a 404 error or being redirected (to home by default if I remember well) when the page you're looking for does not exist (either the routing is bad or your controller raises a ActiveRecordNotFound error). If it works in localhost, I would rather explore my second guess. You should check your question object that you are passing to your url helper. Is it correct ? Does it have an id ?
Have you tried replacing the variable question in
<%= link_to question.qtitle.html_safe, question_path(question) %>
with #question? If I'm reading it correctly, it would be pointing it back at #quests because of your do loop instead of #question.
I created an ajax button to create a transaction, but when I click on it, the Transaction.count doesn't increment.
I have three models : User, Product and Transaction
User and Product have a many to many association through transaction, that belongs to both.
Here is the create method in my app/controllers/transactions_controller.rb
def create
#product = Product.find(params[:transaction][:product_id])
#transaction = Transaction.new(transaction_params)
#transaction.save!
respond_to do |format|
format.html { redirect_to #product }
format.js
end
end
def transaction_params
params.require(:transaction).permit(:product_id, :start_date, :end_date)
end
Here is my button view app/controllers/transactions/buttons/_wants.html.erb
<div class="wants_button product_<%=product.id%>">
<% if current_user %>
<%= form_for(current_user.transactions.build(product_id: product.id),
remote: true) do |f| %>
<div><%= f.hidden_field :product_id %></div>
<%= f.submit t('button.ownership.create'), class: "btn btn-success btn-small" %>
<% end %>
<% end %>
</div>
And here my app/views/transactions/create.js.erb
$(".wants_button.product_<%=#product.id%>").html("<%= escape_javascript(render('transactions/ownership/buttons/wants_not', product: #product)) %>")
At least, what returns the server :
Started POST "/transactions" for 127.0.0.1 at 2013-07-22 16:05:52 +0200
Processing by TransactionsController#create as JS
Parameters: {"utf8"=>"✓", "transaction"=>{"product_id"=>"239"}, "commit"=>"Acquisition"}
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."remember_token" = 'A0J7Ii29W7NPO0ECCccm6g' LIMIT 1
Product Load (0.2ms) SELECT "products".* FROM "products" WHERE "products"."id" = ? ORDER BY products.created_at DESC LIMIT 1 [["id", "239"]]
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 422 Unprocessable Entity in 12ms
Do you know what could interfere with the creation of a transaction ?
Ran into a similar problem. Make sure you are sending the CSRF authenticity token - using rails' form_tag or what have you automatically includes it for you, but manual AJAX request do not.
Hi I've set set of an appreciation controller that handles user's liking different posts. Each post has a like button and when I click it looks like the request goes through but the page doesn't refresh and update the button.
When I click like this is the log, it shows the unlike partial being returned but nothing changes:
Started POST "/appreciations" for 127.0.0.1 at 2011-04-22 05:47:28 -0700
Processing by AppreciationsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zQQJeXZiAPFeQ/7AEy9hvQac01+jq929XUXHrd6eSOE=", "appreciation"=>{"liked_id"=>"3"}, "commit"=>"Like"}
User Load (1.1ms) SELECT "users".* FROM "users" WHERE ("users"."id" = 4) LIMIT 1
Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 3) ORDER BY posts.created_at DESC LIMIT 1
SQL (0.5ms) INSERT INTO "appreciations" ("created_at", "liked_id", "liker_id", "updated_at") VALUES ('2011-04-22 12:47:28.642264', 3, 4, '2011-04-22 12:47:28.642264')
Redirected to http://localhost:3000/posts/3
Completed 302 Found in 185ms
Started GET "/posts/3" for 127.0.0.1 at 2011-04-22 05:47:28 -0700
Processing by PostsController#show as HTML
Parameters: {"id"=>"3"}
Post Load (0.4ms) SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 3) ORDER BY posts.created_at DESC LIMIT 1
User Load (1.2ms) SELECT "users".* FROM "users" WHERE ("users"."id" = 4) LIMIT 1
Appreciation Load (0.3ms) SELECT "appreciations".* FROM "appreciations" WHERE ("appreciations".liker_id = 4) AND ("appreciations"."liked_id" = 3) LIMIT 1
CACHE (0.0ms) SELECT "appreciations".* FROM "appreciations" WHERE ("appreciations".liker_id = 4) AND ("appreciations"."liked_id" = 3) LIMIT 1
Rendered posts/_unlike.html.erb (49.4ms)
Rendered users/_like_form.html.erb (77.7ms)
Rendered posts/show.html.erb within layouts/application (208.9ms)
Completed 200 OK in 248ms (Views: 212.4ms | ActiveRecord: 5.2m
appreciations controller
class AppreciationsController < ApplicationController
before_filter :authenticate_user!
def create
#post = Post.find(params[:appreciation][:liked_id])
current_user.like!(#post)
redirect_to #post
end
def destroy
#post = Appreciation.find(params[:id]).liked
current_user.unlike!(#post)
redirect_to #post
end
end
_like_form.html.erb
<% unless current_user?(#user) %>
<div id="like_form">
<% if current_user.likes?(#post) %>
<%= render 'posts/unlike' %>
<% else %>
<%= render 'posts/like' %>
<% end %>
</div>
<% end %>
_like.html.erb
<%= form_for(current_user.appreciations.
build(:liked_id => #post.id),
:remote => true) do |f| %>
<div><%= f.hidden_field :liked_id %></div>
<div class="actions"><%= f.submit "Like" %></div>
<% end %>
_unlike.html.erb
<%= form_for(current_user.appreciations.find_by_liked_id(#post),
:html => { :method => :delete },
:remote => true) do |f| %>
<div class="actions"><%= f.submit "Unlike" %></div>
<% end %>
Should the result of POST "/appreciations" be a redirect to http://localhost:3000/posts/3? You're using the :remote => true option of the form so I believe you need to change your controller to this:
respond_to do |format|
format.html { redirect_to #post}
format.js
end
And create a *.js.erb partial that does something like this:
<% unless current_user?(#user) %>
<% if current_user.likes?(#post) %>
$("#post_form").html("<%= escape_javascript(render('posts/unlike'))%>");>
<% else %>
$("#post_form").html("<%= escape_javascript(render('posts/like'))%>");
<% end %>
<% end %>
Basically I believe you're seeing no change because you're making an AJAX POST, but not doing anything with the result. You would need JavaScript that updates the innerHTML of a DOM element with the result of your post.
This post might be helpful: http://www.stjhimy.com/posts/7-creating-a-100-ajax-crud-using-rails-3-and-unobtrusive-javascript