gravatar_for no method error Hartl rails chapter 7 - ruby

Everything was working fine till I tried defining gravatar_for and calling it. user.email.downcase is nil class. Below is what I am working with. Let me know if you need any other information.
I see when running localhost at url http://localhost:3000/users/1
NoMethodError in Users#show
`Showing /Users/nathandewaele/Desktop/workspce/static_pages/app/views/users/show.html.erb where line #6 raised:
undefined method `downcase' for nil:NilClass`
extracted source around line 5
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
`Rails.root: /Users/nathandewaele/Desktop/workspce/static_pages
Application Trace | Framework Trace | Full Trace
app/helpers/users_helper.rb:5:in `gravatar_for'
app/views/users/show.html.erb:6:in `_app_views_users_show_html_erb___4114710587947303689_70365956197180'
Request
Parameters:
{"id"=>"1"}
Toggle session dump
Toggle env dump
Response
Headers:
None`
app/helpers/users_helper.rb
```rb
module UsersHelper
# Returns the Gravatar for the given user.
def gravatar_for(user, options = { size: 80 })
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
size = options[:size]
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
end
```
app/controllers/users_controller.rb
```ruby
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
end
```
app/db/schema.rb
```ruby
ActiveRecord::Schema.define(version: 20171108202327) do
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.index ["email"], name: "index_users_on_email", unique: true
end
end
```
app/controllers/config/routes.rb
```ruby
Rails.application.routes.draw do
# get '/new', to: 'users#new'
get '/show', to: 'users#show'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
resources :users
end
```
app/views/users/show.html.erb
```erb
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
</div>
```
screenshot of error

Had the same issue, resolved when I updated /app/helpers/users_helper with the code:
# Returns the Gravatar for the given user.
def gravatar_for(user)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
(as shown in Listing 7.9)

I was having this same issue, which was hard to trace! As mentioned above, an email was not being sent to the database. My problem appeared to resolve itself after I changed code in my model/users.rb file that I was confusing myself with:
before_save { self.email = email.downcase }.
Before I had used before_save { self.email = email.downcase! }. Validation is located in Chapter 6 of the Rails Tutorial. Somehow I was confusing email.downcase! with self.email = email.downcase.
This still leaves the mystery of why an account was created without an email, instead of an error preventing me from proceeding...

Related

Can't determine why iteration not showing objects - Ruby / ActiveRecord / Sinatra

I have two tables created through ActiveRecord: Users and Paintings. I am able to create instances of both objects. Paintings have a belongs_to relationship to Users. Users have a has_many relationship to Paintings. When I attempt to iterate over a Users list of paintings nothing reflects. When I use a similiar convention for iterating over all paintings I don't run into any issues.
require './config/environment'
class ApplicationController < Sinatra::Base
configure do
set :public_folder, 'public'
set :views, 'app/views'
enable :sessions
set :session_secret, "extra_secret"
end
get '/' do
erb :index
end
helpers do
def logged_in?
!!current_user
end
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
end
end
class UsersController < ApplicationController
get '/users/:slug' do
#user = User.find_by_slug(params[:slug])
erb :'users/user_homepage'
end
get '/signup' do
if !logged_in?
erb :'users/new_user'
else
redirect to '/'
end
end
post '/signup' do
if params[:username] == "" || params[:email] == "" || params[:password] == ""
redirect to '/new_user'
else
#user = User.new(:username => params[:username], :email => params[:email], :password => params[:password])
#user.save
session[:user_id] = #user.id
redirect to '/user_homepage'
end
end
get '/login' do
if !logged_in?
erb :'users/login'
else
redirect '/index'
end
end
post '/login' do
user = User.find_by(:username => params[:username])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect to '/index'
else
redirect to 'users/new_user'
end
end
get '/logout' do
if logged_in?
session.destroy
redirect to '/login'
else
redirect to '/'
end
end
end
class PaintingsController < ApplicationController
get '/index' do
if logged_in?
#paintings = Painting.all
erb :'paintings/index'
else
redirect to '/login'
end
end
get "/new" do #create get
erb :'paintings/new'
end
post "/new" do #create post
Painting.create(params)
redirect "/user_homepage"
end
post '/user_homepage' do #changed from /index to /user_homepage. Did not fix.
if logged_in?
if params[:name] == ""
redirect to "/new"
else
#painting = current_user.paintings.build(name: params[:name])
if #painting.save
redirect to "/index/#{#painting.id}"
else
redirect to "/new"
end
end
else
redirect to '/login'
end
end
get "/paintings/:id/edit" do #update get
if logged_in?
#painting = Painting.find_by_id(params[:id])
if #painting && #painting.user == current_user
erb :'paintings/edit_tweet'
else
redirect to '/user_homepage'
end
else
redirect to '/login'
end
end
post "/paintings/:id" do #update post
#painting = Painting.find(params[:id])
#painting.update(params.select{|p| p=="name" || p=="year" || p=="artist_id"})
redirect "/paintings/#{#painting.id}"
end
get "/paintings/:id" do #read get
#painting = Painting.find(params[:id])
erb :'paintings/show'
end
get '/user_homepage' do
if logged_in?
erb :'users/user_homepage'
else
redirect '/index'
end
end
#Need post /user_homepage to show users paintings?
end
#View that is not showing a Users paintings
<h1> User Homepage </h1>
<h2>Welcome <%= current_user.username %>!</h2>
<p> Below is a list of your favorite paintings: </p>
<% #current_user.paintings.each do |painting| %>
<div>
Name: <%= painting.painting_name %>
Year: <%= painting.year %>
</div>
<% end %>
<p> Add New Painting
<p> Click here to see today's popular paintings! </p>
ActiveRecord::Schema.define(version: 2021_03_03_151621) do
create_table "paintings", force: :cascade do |t|
t.string "painting_name"
t.string "year"
t.integer "artist_id"
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "password_digest"
end
end
class Painting < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_secure_password
has_many :paintings
def slug
username.downcase.gsub(" ","-")
end
def self.find_by_slug(slug)
User.all.find{|user| user.slug == slug}
end
end
It seems that you might be using current_user where you intended to use #user.
This is your "user homepage" action:
#user = User.find_by_slug(params[:slug])
erb :'users/user_homepage'
but in the view you have
#current_user.paintings
Another option to check is that (I'm no Sinatra expert) the helpers are exposed to the view but not the underlying instance variables, so you need to use
current_user.paintings

Stripe webhook error 400 bad request when using Heroku web address

I have integrated Stripe in my Ruby on Rails website. If I test it with ngrok, everything works fine but when I use my heroku website address as stripe webhook, it throws a 400 bad request error. If I look up the documentation it says a required parameter is missing. Could this be because I don't have a ssl certificate? I am on the free tier on Heroku but nevertheless the heroku web address starts with https... is that not secure? I have entered the publishable, secret and signing key on the heroku website.
Routes.rb
Rails.application.routes.draw do
mount StripeEvent::Engine, at: '/stripe-webhooks'
devise_for :users, controllers: {
sessions: 'users/sessions',
passwords: 'users/passwords',
registrations: 'users/registrations'
}
scope '(:locale)', locale: /en|de/ do
root to: 'pages#home'
get '/about', to: 'pages#about', as: 'about'
get '/shipping', to: 'pages#shipping', as: 'shipping'
get '/privacypolicy', to: 'pages#privacypolicy', as: 'privacypolicy'
get '/termsandconditions', to: 'pages#termsandconditions', as: 'termsandconditions'
get '/success', to: 'pages#success'
get 'contact', to: 'contacts#new', as: 'contact'
resources :contacts, only: [:new, :create]
get 'cart', to: 'carts#show', as: 'cart'
delete 'carts', to: 'carts#destroy'
delete 'cart_items/:id', to: 'cart_items#destroy', as: 'cart_items'
resources :cart_items, only: [:create, :destroy] do
member do
get :add_quantity
get :reduce_quantity
end
end
post 'without-login', to: 'orders#without_login'
resources :users
resources :products do
resources :cart_items, only: [:create]
end
resources :categories
resources :orders, only: [:new, :show, :create] do
resources :payments, only: :new
end
end
end
schema:
create_table "orders", force: :cascade do |t|
t.string "product_sku"
t.integer "amount_cents", default: 0, null: false
t.string "checkout_session_id"
t.bigint "user_id"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "first_name"
t.string "last_name"
t.string "street_name"
t.string "house_number"
t.string "postal_code"
t.string "city"
t.string "country"
t.string "email"
t.text "comment"
t.integer "price_cents", default: 0, null: false
t.boolean "termsandconditions"
t.string "status", default: "pending"
t.index ["user_id"], name: "index_orders_on_user_id"
end
class PaymentsController < ApplicationController
skip_before_action :authenticate_user!
def new
if current_user
#order = current_user.orders.where(status: 'pending').find(params[:order_id])
else
#order = Order.find(params[:order_id])
end
gon.order_amount = #order.amount_cents.to_f/100
gon.order_id = #order.id
end
end
class OrdersController < ApplicationController
skip_before_action :authenticate_user!
def new
#order = Order.new
#cart = current_cart
# Storing the two constants in a gon variable to send data to the JS file
gon.ceilings = Product::ORDER_CEILINGS
gon.prices = Product::SHIPPING_PRICES
end
def create
#order = Order.new(order_params)
#cart = current_cart
#cart.cart_items.each { |item| #order.cart_items << item }
#order.amount = #cart.total_price
shipping_costs = calculate_shipping_costs(params[:order][:country], #order.amount)
#order.amount += Monetize.parse(shipping_costs)
#order.user = current_user if current_user
#order.email = current_user.email if current_user
if #order.save
save_user_address if params[:save_address].to_i == 1
trigger_stripe(shipping_costs)
cleanup_cart
redirect_to new_order_payment_path(#order)
else
#cart = #current_cart
render :new
end
end
def show
if current_user
#order = current_user.orders.find(params[:id])
else
#order = Order.find(params[:id])
end
mail = OrderMailer.with(order: #order).confirmation
mail.deliver_now
# may need to change this for guest users- must check that their email address is saved to the database
end
def index
#orders = current_user.orders
end
def without_login
session[:without_login] = true
redirect_to new_order_path
end
def submit
end
private
def order_params
params.require(:order).permit(:first_name, :last_name, :email, :street_name, :house_number, :postal_code, :city, :country, :comment)
end
def trigger_stripe(shipping_costs)
stripe_session = Stripe::Checkout::Session.create(
payment_method_types: ['card'],
customer_email: customer_email,
locale: I18n.locale.to_s,
line_items: stripe_line_items(#order.cart_items, shipping_costs),
success_url: order_url(#order),
cancel_url: order_url(#order)
)
#order.update(checkout_session_id: stripe_session.id)
end
def cleanup_cart
#cart.cart_items.each { |item| item.update(cart_id: nil) }
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
end
def stripe_line_items(order_items, shipping_costs)
all_items = []
order_items.each do |item|
item_hash = {
name: item.product.title,
amount: (item.total_price.amount * 100).to_i / item.quantity,
quantity: item.quantity,
currency: 'eur'
}
all_items.push(item_hash)
end
shipping_item_hash = {
name: "Delivery",
amount: (shipping_costs * 100).to_i,
quantity: 1,
currency: 'eur'
}
all_items.push(shipping_item_hash)
return all_items
end
def customer_email
current_user ? current_user.email : nil
end
def save_user_address
if #order.user != nil
current_user.attributes = #order.attributes.except("id", "email", "status", "comment", "amount_cents", "amount_currency", "checkout_session_id", "user_id", "updated_at", "created_at")
current_user.save
end
end
class StripeCheckoutSessionService
def call(event)
order = Order.find_by(checkout_session_id: event.data.object.id)
order.update(status: 'paid')
end
end
payments new.html.erb:
<script src="https://js.stripe.com/v3/"></script>
<script>
const paymentButton = document.getElementById('pay-stripe');
paymentButton.addEventListener('click', () => {
const stripe = Stripe('<%= ENV['STRIPE_PUBLISHABLE_KEY'] %>');
stripe.redirectToCheckout({
sessionId: '<%= #order.checkout_session_id %>'
});
});
</script>
initializers stripe
Rails.configuration.stripe = {
publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
secret_key: ENV['STRIPE_SECRET_KEY'],
signing_secret: ENV['STRIPE_WEBHOOK_SECRET_KEY']
}
Stripe.api_key = Rails.configuration.stripe[:secret_key]
StripeEvent.signing_secret = Rails.configuration.stripe[:signing_secret]
StripeEvent.configure do |events|
events.subscribe 'checkout.session.completed', StripeCheckoutSessionService.new
end
It looks like StripeEvent responds with a 400 error if it gets back a Signature Verification Error from Stripe (see https://github.com/integrallis/stripe_event/blob/31b948d6afd4a2f82c6ad3cd973211366b48a0d8/app/controllers/stripe_event/webhook_controller.rb#L12).
You should double check your signing secret and make sure it matches the secret for your heroku webhook, and not your ngrok webhook.
I had a similar issue. l mistakenly put the testing key (which was shown when I created the live key in the code sample beside it).
Open your webhooks in stripe and click on the reveal secret key. put this secret key in your webhook handler

Couldn't find product without ID using Stripe

I am trying to complete my payment set up in Ruby but am struggling to gt the order confirmation screen to print. I have set up my partial for the payment as such.
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="<%= Rails.configuration.stripe[:publishable_key] %>"
data-image="<%= asset_path(#product.image_url) %>"
data-name="<%= #product.name %>"
data-description="<%= #product.description %>"
data-amount="<%= #product.price*100.to_i %>">
</script>
My payment controller.
class PaymentsController < ApplicationController
before_action :authenticate_user!
def create
#product = Product.find(params[:product_id])
#user = current_user
token = params[:stripeToken]
# Create the charge on Stripe's servers - this will charge the user's card
begin
charge = Stripe::Charge.create(
amount: #product.price, # amount in cents, again
currency: "eur",
source: token,
description: params[:stripeEmail]
)
if charge.paid
UserMailer.order_confirmation(#user, #product).deliver_now
Order.create!(
:product_id => #product.id,
:user_id => #user.id,
:total => #product.price_show
)
flash[:success] = "Your payment was processed successfully"
end
rescue Stripe::CardError => e
body = e.json_body
err = body[:error]
flash[:error] = "Unfortunately, there was an error processing your payment: #{err[:message]} Your card has not been charged. Please try again."
end
redirect_to product_path(#product), notice: "Thank you for your purchase."
end
end
and my routes file.
Rails.application.routes.draw do
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout' },
controllers: {registrations: "user_registrations"}
resources :products do
resources :comments
end
post 'payments/create'
resources :users
resources :orders, only: [:index, :show, :create, :destroy]
resources :users, except: [:index]
get 'simple_pages/about'
get 'simple_pages/contact'
root 'simple_pages#landing_page'
post 'simple_pages/thank_you'
mount ActionCable.server => '/cable'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
The form within product show.html.erb
<%= form_with(url: '/payments/create') do |form| %>
<%= render partial: "shared/stripe_checkout_button" %>
<%= hidden_field_tag(:product_id, #product.id) %>
<% end %>
However, when I try to complete a test payment, I have the action controller pop up with "Couldn't find Product without an ID". I thought this was defined in the create section but I am not sure how to rectify this. Any suggestions would be greatly appreciated.
The :product_id is not set in your form post,
1. Quick Solution:
your form url could be like this:
<%= form_with(url: "/payments/create?#{product_id=#product.id}") do |form| %>
2. Better Way:
configure you routes for payments already has product id:
# routes.rb
resources :products do
resources :comments
resources :payments
end
form url:
<%= form_with(url: product_payments_path(#product)) do |form| %>

rails 5 strong params updating record sets other columns to nil

For some reason, when I submit my form and update my user profile record, my user profile records have all the other columns updated to nil for some reason.
Lets say User.last.user_profile has an attribute "linkedin_url" and it is set to "www.yahoo.com". Once I submit the form, the User :phone number, and UserProfile :work_address, :work_zipcode gets updated, but the "linkedin_url" gets set to nil. IDK why!
class UserProfilesController < ApplicationController
def update
#user = User.last
if #user.update(user_profile_params)
respond_to do |format|
format.html {
redirect_to user_profile_path(#user)
}
format.json {
render :show, status: :ok, location: #preference
}
end
end
end
def user_profile_params
params.require(:user).permit( :phone_number, user_profile_attributes: [:work_address, :work_zipcode])
end
form
= form_for #user, url: user_profile_path(#user), method: :put do |f|
= f.fields_for :user_profile do |ff|
= ff.label :work_address
%br
= ff.text_field :work_address
%br
= ff.label :work_zipcode
%br
= ff.text_field :work_zipcode
%br
= f.label :phone_number
%br
= f.text_field :phone_number
%br
= f.submit "Submit", class: "btn"
def user_profile_params
params.require(:user).permit(
:phone_number,
user_profile_attributes: [:work_address, :work_zipcode] # ⇐ HERE
)
end
In the method above you should explicitly list :linkedin_url:
def user_profile_params
params.require(:user).permit(
:phone_number,
user_profile_attributes: [:work_address, :work_zipcode, :linkedin_url]
)
end
Just solved my own problem, it was because i didn't whitelist :id in the strong parameters.

Add a record in Many to many relation fails

I have a many to many connection in Rails applications, it looks like this:
class Workspace
has_and_belongs_to_many :users, dependent: :destroy
end
class User
has_and_belongs_to_many :workspaces
end
class UserWorkspace
belongs_to :user
belongs_to :workspace
end
Schema:
create_table :users_workspaces do |t|
t.integer :user_id
t.integer :workspace_id
t.integer :role, default: 0
t.timestamps null: false
end
Then I want to create a new record like this:
#user.workspaces.create(:workspace_id => #workspace.id, :role => 1)
or this
#user.workspaces << #workspace
and have an error in logs:
(0.0ms) begin transaction
(0.0ms) begin transaction
(0.1ms) rollback transaction
(0.1ms) rollback transaction
Completed 500 Internal Server Error in 207ms (ActiveRecord: 5.5ms)
Completed 500 Internal Server Error in 207ms (ActiveRecord: 5.5ms)
ActiveRecord::UnknownAttributeError (unknown attribute 'workspace_id' for Workspace.):
app/controllers/sessions_controller.rb:10:in `block in sign_up'
app/controllers/sessions_controller.rb:4:in `sign_up'
What am I doing wrong?
PS Controller:
def sign_up
respond_to do |format|
#user = User.new(user_params)
if #user.save
#workspace = Workspace.new(title: "#{#user.name}'s workspace")
#workspace.save
puts "workspace id: #{#workspace.id}"
#user.workspaces.create(:workspace_id => #workspace.id, :role => 1)
puts "workspaces count: #{#user.workspaces.count}"
#user.workspace = #workspace
#user.update_attributes(user_params)
flash.now[:success] = 'Welcome! Please check activation letter in your email box.'
format.js { render 'signup_message' }
else
format.js { render 'render_signup_errors' }
end
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :name, :workspace_id)
end
There are couple of problems with your code. For example, you are creating workspaces that are already created (#user.workspaces.create), or permitting a :workspace_id that is not used, etc.
Pleas see code below:
def sign_up
respond_to do |format|
#user = User.new(user_params)
if #user.save
#workspace = Workspace.new(title: "#{#user.name}'s workspace")
if #workspace.save
# Like this
UserWorkspace.create(user: #user, workspace: #workspace, role: 1)
# Or, like this
#user.user_workspaces.create!(workspace_id: #workspace.id, role: 1)
end
flash.now[:success] = 'Welcome! Please check activation letter in your email box.'
format.js { render 'signup_message' }
else
format.js { render 'render_signup_errors' }
end
end
end
private
# You don't need :workspace_id since you are not using it anywhere
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :name)
end

Resources