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
Related
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 have a weird behaviour when using User.find_or_create_by! in before_action filter as follows:
class ApplicationController < ActionController::API
before_action :authorize_request
attr_reader :current_user
private
def authorize_request
#current_user = (AuthorizeApiRequest.new(request.headers).call)[:user]
end
end
Then in AuthorizeApiRequest I'm checking for existence or creating a new User by name:
class AuthorizeApiRequest
def initialize(headers = {})
#headers = headers
end
def call
{
user: user
}
end
def user
if decoded_auth_token && decoded_auth_token[:sub]
#user ||= User.find_or_create_by!(username: decoded_auth_token[:sub])
Rails.logger.silence do
#user.update_column(:token, http_auth_header)
end
#user
end
rescue ActiveRecord::RecordInvalid => e
raise(
ExceptionHandler::InvalidToken,
("#{Message.invalid_token} #{e.message}")
)
end
end
Example of UsersController:
class UsersController < ApplicationController
def me
if user_info_service.call
json_response current_user, :ok, include: 'shop'
else
raise AuthenticationError
end
end
private
def user_info_service_class
#user_info_service_class ||= ServiceProvider.get(:user_info_service)
end
def user_info_service
#user_info_service ||= user_info_service_class.new(user: current_user)
end
end
What is weird is that sometimes the User is created twice with the same username, sometimes not.
I'm using Ember JS in the front and another call is made to shops right after the authentication with JWT. All the routes are protected. I have the impression that calling current_user is not always in the same thread or sth like that and it results in having 2 identical users:
- the first one with just a username attribute set
- another one with all the others User attributes.
Here is the User model:
class User < ApplicationRecord
validates :username, presence: true, uniqueness: { case_sensitive: false }, on: :create
validates :shop_identifier, numericality: { only_integer: true, greater_than: 0 }, on: :update
validates :first_name, presence: true, on: :update
validates :last_name, presence: true, uniqueness: { case_sensitive: false, scope: :first_name }, on: :update
before_update do |user|
user.first_name = first_name.strip.capitalize
user.last_name = last_name.strip.upcase
end
Any ideas ? Thank you
I am working through Michael Hartl's Rails book and I am about halfway through chapter 10-working on account activation.
I had everything working with the mailers but then when I tried to add a new user, I got the following error message: "undefined method `activation_digest=' for #"
I have been trying to follow along in the book the best that I can. I have my users_controller.rb here:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update]
before_action :correct_user, only: [:edit, :update]
def new
#user = User.new
end
def index
#users = User.paginate(page: params[:page], :per_page => 10)
end
def show
#user = User.find(params[:id])
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
else
render 'edit'
end
end
def edit
#user = User.find(params[:id])
end
#confirms if a user is logged in
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please Log In."
redirect_to login_url
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
Here is my Model/user.rb:
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token
before_save :downcase_email
before_create :create_activation_digest
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }
# 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 random token
def User.new_token
SecureRandom.urlsafe_base64
end
#Remembers a user in the database for use in persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
#Returns true if the given token matches the digest
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
#forgets a user
def forget
update_attribute(:remember_digest, nil)
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
The routes I have this:
root 'static_pages#home'
get 'sessions/new'
get 'users/new'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
Please let me know if anything more is needed to be seen. I do have my App up on Github under the name sample_app, my username is ravenusmc.
Looking at your project on Github, your User model doesn't have an activation_token or activation_digest column, nor does the model define them as attributes.
Your User model is trying to write to these columns in the User#create_activation_digest function which is most likely causing the issue.
You'll need to write a migration to add those columns to your User model or add them is attributes (ie attr_accessor) if they are not meant to be persisted.
I'm working on a web app that has users model. You either sign up or try the app and be a 'guest'. I had the sign up functionality working well until I added the guest option. I based on Railscast to do this (http://railscasts.com/episodes/393-guest-user-record) and that's when I ran into troubles
Here is my user model code
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :bookings
has_one :account, dependent: :destroy
# before_save :downcase_email, allow_nil: true
before_save :create_remember_token
validates_presence_of :name, :email, :password, :password_confirmation, unless: :guest?
validates_uniqueness_of :email, case_sensitive: false , allow_blank: true
validates :name, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, format: { with: VALID_EMAIL_REGEX }, unless: :guest?
validates :password, length: { minimum: 6 }, unless: :guest?
def downcase_email
{ |user| user.email = email.downcase }
end
# has_secure_password
# override has_secure_password to customize validation until Rails 4.
require 'bcrypt'
attr_reader :password, :password_confirmation
include ActiveModel::SecurePassword::InstanceMethodsOnActivation
##def to_param
## "#{id}.#{name.parameterize}"
##end
def self.new_guest
new { |u| u.guest = true }
end
def name
guest ? "Guest" : name
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
and here is my User Controller code:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:edit, :update]
before_filter :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = params[:user] ? User.new(params[:user]) : User.new_guest
if #user.save
sign_in #user
#user.create_account.accountPlan = "Free"
##flash[:success] = "Welcome to the HighTide!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
private
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
you may notice some code is commented out. I wasn't able to store in the database a user without an email (and for have a quick and dirty thing working now I comment the callback before_save, I also might add that I use the guidelines of Michael Hartl tut)
What happens is, when I run this code I get a unknown attribute: password_confirmation error, but when I comment out
require 'bcrypt'
attr_reader :password, :password_confirmation
include ActiveModel::SecurePassword::InstanceMethodsOnActivation
and use has_secure_password instead
I get a stack level too deep
I haven't figured out what to do
I have an Item model that I do not need a separate show view for. Instead, when the item is updated I would like to return the user to the index. When the form is submitted to edit an item, you get an error like this : No route matches [PUT] "/items/1"
Here is the routes file
Order::Application.routes.draw do
root to: 'static_pages#home'
resources :static_pages
resources :customers
resources :demands
resources :items, only: [:new, :create, :destroy, :index, :edit]
end
Here is the controller
class ItemsController < ApplicationController
def index
#items = Item.all
end
def new
#item = Item.new
end
def create
#item = Item.new(params[:item])
if #item.save
flash[:success] = "Item saved!"
redirect_to items_path
else
render new_item_path
end
end
def destroy
Item.find(params[:id]).destroy
redirect_to items_path
end
def edit
#item = Item.find(params[:id])
end
def update
#item = Item.find(params[:id])
if #item.update_attributes(params[:item])
redirect_to 'items#index'
flash[:success] = "Item updated!"
else
render 'edit'
end
end
end
Here is the model
class Item < ActiveRecord::Base
attr_accessible :name, :price
validates :name, presence: true
VALID_PRICE_REGEX = /^[+-]?[0-9]{1,3}(?:,?[0-9]{3})*\.[0-9]{2}$/
validates :price, presence: true, format: {with: VALID_PRICE_REGEX}
end
You're missing the update action for items in your routes file.
resources :items, only: [:new, :create, :destroy, :index, :edit]
should be
resources :items, only: [:new, :create, :destroy, :index, :edit, :update]
or, more concisely,
resources :items, except: [:show]