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
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
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
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| %>
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.
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