multi-tenant rails app user-account relationship issue - ruby-on-rails

My application main gems: rails 4.1.1, pg, acts_as_tenant, devise and activeadmin.
Application description: saas multi-tenant app with subdomains (not using postgresql schemas).
I have 2 models: Account and User with:
account belongs_to :owner a class_name for User
user belongs_to :account and also acts_as_tenant(:account)
Note: if using acts_as_tenant, I am not sure I need to declare belongs_to?
On public domain (www.myapp.dev) user can create a new account and choose own subdomain. This user has the status of ‘owner’ for this particular subdomain/account. After account creation, user is redirected to custom subdomain for sign in. Once signed in, owner can create/invite other users to join his account.
On account creation, owner_id is correctly saved to account record. In ActiveAdmin I can filter my accounts by owners.
The problem is the account_id is not saved in the user record. Also in ActiveAdmin, when I try to edit the users records to add the account, the data is not saved…
I am learning Rails so please be easy :)
My Account model :
class Account < ActiveRecord::Base
RESTRICTED_SUBDOMAINS = %w(www)
belongs_to :owner, class_name: 'User'
validates :owner, presence: true
validates :name, presence: true
validates :subdomain, presence: true,
uniqueness: { case_sensitive: false },
format: { with: /\A[\w\-]+\Z/i, message: 'contains invalid characters' },
exclusion: { in: RESTRICTED_SUBDOMAINS, message: 'restricted' }
accepts_nested_attributes_for :owner
before_validation :downcase_subdomain
def self.current_id=(id)
Thread.current[:account_id] = id
end
def self.current_id
Thread.current[:account_id]
end
private
def downcase_subdomain
self.subdomain = subdomain.try(:downcase)
end
end
My User model :
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :account
acts_as_tenant(:account)
validates :lastname, presence: true, allow_nil: false
validates :firstname, presence: true, allow_nil: false
validates :password, presence: true, allow_nil: false
validates :email, presence: true, allow_nil: false #uniqueness: true,
def to_s
"#{firstname} #{lastname} (#{email})"
end
end
Application Controller :
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :authenticate_user! #, :set_mailer_host
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:accept_invitation).concat([:firstname])
end
private
def current_account
#current_account ||= Account.find_by(subdomain: request.subdomain)
end
helper_method :current_account
def after_sign_out_path_for(resource_or_scope)
new_user_session_path
end
def after_invite_path_for(resource)
users_path
end
end
Account controller :
class AccountsController < ApplicationController
skip_before_filter :authenticate_user!, only: [:new, :create]
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
if #account.valid?
#account.save
redirect_to new_user_session_url(subdomain: #account.subdomain)
else
render action: 'new'
end
end
private
def account_params
params.require(:account).permit(:subdomain, :name, owner_attributes: [:lastname, :firstname, :email, :password, :password_confirmation, :account_id])
end
end
User controller :
class UsersController < ApplicationController
def index
#users = User.all
end
end
And my db schema :
ActiveRecord::Schema.define(version: 20140619202210) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "accounts", force: true do |t|
t.string "name"
t.string "subdomain"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "owner_id"
end
create_table "active_admin_comments", force: true do |t|
t.string "namespace"
t.text "body"
t.string "resource_id", null: false
t.string "resource_type", null: false
t.integer "author_id"
t.string "author_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "active_admin_comments", ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id", using: :btree
add_index "active_admin_comments", ["namespace"], name: "index_active_admin_comments_on_namespace", using: :btree
add_index "active_admin_comments", ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id", using: :btree
create_table "admin_users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "admin_users", ["email"], name: "index_admin_users_on_email", unique: true, using: :btree
add_index "admin_users", ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true, using: :btree
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "account_id"
t.string "lastname"
t.string "firstname"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end

Try:
class Account < ActiveRecord::Base
has_one :account
...
class User < ActiveRecord::Base
acts_as_tenant(:account)
...
Without belongs_to for User model.

Related

User create a team or join a team after sign up / log in

I'm currently working on a Ruby on Rails application where a devise user when successfully logged in / signed up can create a team or join a team. The user who creates the team will become the owner and can't belong to any other teams, a user that decides to join a team will see all teams that have been made and are able to join them. I'm having a lot of trouble understanding the relations I need to use for this
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
validates_presence_of :phone, :city, :state, :street, :zip, presence: true, on: :create
has_one :team
end
schema.rb
create_table "teams", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "team_name"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "firstname"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
team.rb
class Team < ApplicationRecord
belongs_to :user, class_name: 'User'
end
routes.rb
devise_for :users, path: 'users' , controllers: { sessions: "users/sessions", confirmations: 'users/confirmations', registrations: 'users/registrations' }
Am I using the right approach?

NoMethodError in Account::PostsController#index

I'm the newbie, my rails version is 4.2.0 and I install three gem devise, cancancan, rolify. When I sign up success, I got this message
NoMethodError in Account::PostsController#index
undefined method `role' for #<User:0x007fd84c2afba8>
def admin?
self.role.name == "admin"
end
end
I guess the problem is users, roles the connection is miss. And I try to modify my user.rb but not work.
This is my app/models/ability.rb code
class Ability
include CanCan::Ability
def initialize(user)
if user.admin?
can :manage, :all
else
can :update, Post do |post|
post.user == user
end
can :destroy, Post do |post|
post.user == user
end
can :create, Post
end
end
end
app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
############################################################
# not work
# belongs_to :users_roles
# has_and_belongs_to_many :roles, :join_tabe => :users_roles
# has_one :users_role
############################################################
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
def admin?
self.role.name == "admin"
end
end
app/models/role.rb
class Role < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :users_roles
belongs_to :resource, :polymorphic => true
validates :resource_type,
:inclusion => { :in => Rolify.resource_types },
:allow_nil => true
scopify
end
When I sign up I will add a role to first sign up user, this is my application_controller.rb
def after_sign_in_path_for(resource)
if resource.is_a?(User)
if User.count == 1
resource.add_role 'admin'
else
resource.add_role 'normal'
end
resource
end
root_path
end
schema.rb
ActiveRecord::Schema.define(version: 20160225105659) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "ckeditor_assets", force: :cascade do |t|
t.string "data_file_name", null: false
t.string "data_content_type"
t.integer "data_file_size"
t.integer "assetable_id"
t.string "assetable_type", limit: 30
t.string "type", limit: 30
t.integer "width"
t.integer "height"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "ckeditor_assets", ["assetable_type", "assetable_id"], name: "idx_ckeditor_assetable", using: :btree
add_index "ckeditor_assets", ["assetable_type", "type", "assetable_id"], name: "idx_ckeditor_assetable_type", using: :btree
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "roles", force: :cascade do |t|
t.string "name"
t.integer "resource_id"
t.string "resource_type"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "roles", ["name", "resource_type", "resource_id"], name: "index_roles_on_name_and_resource_type_and_resource_id", using: :btree
add_index "roles", ["name"], name: "index_roles_on_name", using: :btree
create_table "travel_events", force: :cascade do |t|
t.string "title"
t.datetime "start_date"
t.datetime "end_date"
t.text "note"
t.integer "travel_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "travels", force: :cascade do |t|
t.string "title"
t.datetime "start_date"
t.datetime "end_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
create_table "users_roles", id: false, force: :cascade do |t|
t.integer "user_id"
t.integer "role_id"
end
add_index "users_roles", ["user_id", "role_id"], name: "index_users_roles_on_user_id_and_role_id", using: :btree
end
I think you forgot to add
has_one :users_role
in user.rb
I find the answer, but I'm not sure the question is table's relationship.
I remove my user.rb's admin method
user.rb
class User < ActiveRecord::Base
rolify
has_many :posts
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# def admin?
# self.role.name == "admin"
# end
end
And modify ability.rb
ability.rb
class Ability
include CanCan::Ability
def initialize(user)
# if user.admin?
if user.has_role?(:admin) # modify this line
can :manage, :all
else
can :update, Post do |post|
post.user == user
end
can :destroy, Post do |post|
post.user == user
end
can :create, Post
can :read, Post
end
end
end

Ruby on Rails SQLite3::ConstraintException: NOT NULL constraint failed:

I am developing a simple app where a user can add a subject to a cart. Before I add the authentication I was able to add a subject to the cart but as I want a user to be able to has access to just his/her cart I used Devise to create User with authentication. Now, when I click on the button to add a subject to the cart I have the following error:
This is a snapshot of the error I get: 1
SQLite3::ConstraintException: NOT NULL constraint failed: carts.user_id: INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?)
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_cart
Cart.find(params[:user_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
params[:user_id] = cart.id
cart
end
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :cart
end
class Cart < ActiveRecord::Base
belongs_to :user
has_many :line_items, dependent: :destroy
scope :user_carts, ->(user) { where(['user_id= ?', user.id]) }
end
class AddUsersToCarts < ActiveRecord::Migration
def up
add_reference :carts, :user, index: true
Cart.reset_column_information
user = User.first
Cart.all.each do |cart|
cart.user_id = user.id
cart.save!
end
change_column_null :carts, :user_id, false
add_foreign_key :carts, :users
end
def down
remove_foreign_key :carts, :users
remove_reference :carts, :user, index: true
end
end
Edit: I added the schema.rb below:
ActiveRecord::Schema.define(version: 20151210213408) do
create_table "carts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", null: false
end
add_index "carts", ["user_id"], name: "index_carts_on_user_id"
create_table "line_items", force: :cascade do |t|
t.integer "subject_id"
t.integer "cart_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "subjects", force: :cascade do |t|
t.string "title", null: false
t.string "code", null: false
t.text "description", null: false
t.integer "credits", null: false
t.string "lecturer", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "subjects", ["title"], name: "index_subjects_on_title", unique: true
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
You current_cart method does not make much sense.
You cannot find the user's cart by calling Cart.find(params[:user_id]), because that looks for a cart by an id (not by an user_id).
Cart.create fails, because you do not provide an user_id that is required (your database migrations says that the filed cannot be null).
Furthermore, params[:user_id] = cart.id changes the params hash, but not the new cart.
Change that method to something like this (using find_or_create_by) and use the current_user.id instead of params[:user_id]:
def current_cart
Cart.find_or_create_by(user_id: current_user.id)
end

Issue with belongs_to and user (devise)

I'm new to RoR, and I would like to develop an app, but I have an issue with the belongs_to association. I am using devise for the authentication of my users, and I have an object called timesheet, I followed several tutorials and read a lot of forums but unfortunately user_id remains null in my db, so I do not know where does the problem come from.
If you can tell how to fix it, any links that can helps me, that would be great.
Schema.rb:
ActiveRecord::Schema.define(version: 20150128160116) do
create_table "timesheets", force: true do |t|
t.date "date"
t.time "working_start_time"
t.time "working_end_time"
t.integer "breaks"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "timesheets", ["user_id"], name: "index_timesheets_on_user_id"
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.integer "current_sign_in_ip"
t.integer "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
timesheets_controller.rb
class TimesheetsController < ApplicationController
layout 'application'
def show
#timesheet=Timesheet.find(params[:id])
end
def index
#timesheet = Timesheet.all
end
def new
#timesheet = Timesheet.new
end
def create
#timesheet = Timesheet.create(timesheet_params)
redirect_to new_timesheet_path
end
def edit
#timesheet=Timesheet.find(params[:id])
end
def update
#timesheet = Timesheet.find(params[:id])
#timesheet.update_attributes(timesheet_params)
redirect_to student_table_path
end
user.rb model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :timesheets
end
Timesheet.rb model
class Timesheet < ActiveRecord::Base
belongs_to :user
validates :user_id, :presence => true
end
Thanks in advance.
It will stay null because you are not using it in the timesheetsController, your create action should be like this :
def create
#timesheet = current_user.timesheets.build(timesheet_params)
redirect_to new_timesheet_path
end
You have to use that build method to reference the current_user, so the timesheet will have the current_user in the user_id field.

User id is not saving in the Events table. This is a rails project using postgresql database

I used devise to create my users and I am trying to get the user_id to save in the events table when I create an event, I keep getting the error "cant find user without id". Not sure what the problem is any pointers would be greatly appreciated.
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :email
validates_presence_of :encrypted_password
validates_presence_of :sign_in_count
has_many :events
end
class Event < ActiveRecord::Base
validates_presence_of :name
validates_presence_of :description
belongs_to :user
has_many :reviews
end
class EventsController < ApplicationController
def index
#events = Event.all
end
def new
#event = Event.new
end
def create
#user = User.find(params[:user_id])
#event = #user.events.build(event_params)
end
def show
#event = Event.find(params[:id])
end
protected
def event_params
params.require(:event).permit(:name, :event_key, :location, :date, :event_url, :description, :time,
:event, :user_id)
end
end
Here is my schema
ActiveRecord::Schema.define(version: 20131226220750) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "events", force: true do |t|
t.string "name", null: false
t.string "location"
t.string "event_url"
t.string "description", null: false
t.integer "user_id"
t.datetime "time"
t.datetime "event_date"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "event_key", null: false
end
add_index "events", ["event_key"], name: "index_events_on_event_key", unique: true, using: :btree
create_table "reviews", force: true do |t|
t.string "feedback_comments"
t.integer "rating", null: false
t.integer "event_id"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.string "first_name", null: false
t.string "last_name", null: false
t.string "twitter_handle"
t.string "linked_in_url"
t.string "phone_number"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
Have you looked at params[:user_id] to see if it is what you think it is? I'm pretty sure params[:user_id] will be nil.
Let us look at your params handling:
def event_params
params.require(:event).permit(:name, :event_key, :location, :date, :event_url, :description, :time, :event, :user_id)
end
The require method will look for params[:event] and return it if found. Then permit will look for :name, :event_key, ..., :user_id within params[:event]. So your :user_id is probably params[:event][:user_id] rather than params[:user_id]. The result is that you're saying:
#user = User.find(nil)
and find doesn't want to hear about nils.
This should work better:
def create
event = event_params
#user = User.find(event[:user_id])
#event = #user.events.build(event)
end
If #user is supposed to be the current user all the time then you'd want to get that out of current_user rather than params:
def create
#user = current_user
#event = #user.events.build(event_params)
end
#...
def event_params
params.require(:event).permit(:name, :event_key, :location, :date, :event_url, :description, :time, :event)
# No more :user_id up there
end

Resources