How to pass params with devise_invitable? - ruby-on-rails

Goal
I would like to let a user.admin invite a user to only one of its hotels with the devise_invite gem.
=> Users and Hotels are connected via a Join_table UserHotel.
Issue
I am not able to create the user_hotel Join_table and/ord add the specific hotel params to the invited user.
controller
>> #user.hotels => #<ActiveRecord::Associations::CollectionProxy []>
console:
pry(main)> User.invitation_not_accepted.last.hotels
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" IS NOT NULL AND "users"."invitation_accepted_at" IS NULL ORDER BY "users"."id" DESC LIMIT $1 [["LIMIT", 1]]
Hotel Load (0.4ms) SELECT "hotels".* FROM "hotels" INNER JOIN "user_hotels" ON "hotels"."id" = "user_hotels"."hotel_id" WHERE "user_hotels"."user_id" = $1 [["user_id", 49]]
=> []
UPDATE
The issue seems to be in the many-to-many relationship between user and hotel. When I break my controller 'new' action after hotel.user.new and test it I het the following:
>> #user.hotels => #<ActiveRecord::Associations::CollectionProxy []>
>> #hotel.users => #<ActiveRecord::Associations::CollectionProxy [#<User id: 2, email: "test#hotmail.com", created_at: "2019-11-05 14:17:46", updated_at: "2019-11-05 15:04:22", role: "admin">, #<User id: nil, email: "", created_at: nil, updated_at: nil, role: "admin">]>
Note
I set up users with devise, such that my users controller is build up as:
users/confirmations_controller.rb
users/invitations_controller.rb
users/omniauth_callbacks_controller.rb
users/password_controller.rb
users/registrations_controller.rb
users/sessions_controller.rb
users/unlocks_controller.rb
Code
routes
Rails.application.routes.draw do
devise_for :users
resources :hotels do
devise_for :users, :controllers => { :invitations => 'users/invitations' }
end
end
models
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
has_many :user_hotels, dependent: :destroy
has_many :hotels, through: :user_hotels
accepts_nested_attributes_for :user_hotels
enum role: [:owner, :admin, :employee]
after_initialize :set_default_role, :if => :new_record?
def set_default_role
self.role ||= :admin
end
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :invitable
end
class UserHotel < ApplicationRecord
belongs_to :hotel
belongs_to :user
end
class Hotel < ApplicationRecord
has_many :user_hotels, dependent: :destroy
has_many :users, through: :user_hotels
accepts_nested_attributes_for :users, allow_destroy: true, reject_if: ->(attrs) { attrs['email'].blank? || attrs['role'].blank?}
end
views/hotels/show
<%= link_to "invite new user", new_user_hotel_invitation_path(#hotel)%>
controllers/users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
def new
#hotel = Hotel.find(params[:hotel_id])
#user = #hotel.users.new
end
def create
#hotel = Hotel.find(params[:hotel_id])
#user = #hotel.users.new(hotel_user_params)
#user.invite!
end
private
def hotel_user_params
params.require(:user).permit(:email, :role,
hotel_users_attributes: [:hotel_id])
end
end
views/invitations/new.html.erb
<h2><%= t "devise.invitations.new.header" %></h2>
<%= simple_form_for(resource, as: resource_name, url: user_hotel_invitation_path(#hotel), html: { method: :post }) do |f| %> <%= f.error_notification %>
<% resource.class.invite_key_fields.each do |field| -%>
<div class="form-inputs">
<%= f.input field %>
</div>
<% end -%>
<%= f.input :role, collection: [:owner, :admin, :employee] %>
<div class="form-actions">
<%= f.button :submit, t("devise.invitations.new.submit_button") %>
</div>
<% end %>

Related

Nested form won't display for has_one relationship Simple_Form (Rails 4)

I set my user model up to build the user_profile before being created. The user_profile itself is being shown in the console when I load the user object. #user = User.find(1) -> #user.user_profile -> UserProfile Load. The relationship works correctly. The issue is I am unable to make changes to the user profile with the edit user_profile action. The edit page is empty, even though the attributes for the user_profile exist. How can I get the form to load in the edit view, so that the user is able to make changes to his/her profile after logging into their account? Side note: The user does not edit their normal profile during registration.
user.rb
class User < ApplicationRecord
# Include default devise modules.
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable
include DeviseTokenAuth::Concerns::User
before_create :build_user_profile
extend FriendlyId
after_validation :geocode
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
friendly_id :username, use: :slugged
has_one :user_profile
accepts_nested_attributes_for :user_profile
protected
def confirmation_required?
false
end
end
user_profile.rb
class UserProfile < ApplicationRecord
mount_uploader :avatar, UserAvatarUploader
belongs_to :user, :dependent => :destroy
validates_integrity_of :avatar
validates_processing_of :avatar
end
_form.html.erb
<%= simple_form_for(#user_profile, :html => {:multipart => true}) do |f| %>
<%= f.error_notification %>
<div class="form-group">
<% f.simple_fields_for :user_profile do |user_profile| %>
<div class="col-md-4">
<% if current_user.user_profile.avatar.url.present? %>
<%= image_tag(current_user.user_profile.avatar.url) %>
<%= user_profile.label :remove_avatar do %>
<%= user_profile.input :remove_avatar, as: :boolean, checked_value: true, unchecked_value: false %>
<% end %>
<%= user_profile.input :avatar, as: :file, class: 'form-control' %>
<%= user_profile.input :birthday, label: false, id: 'datepicker', class: 'form-control' %>
<% end %>
<div class="form-group">
<div class="col-md-4">
<%= f.button :submit %>
</div>
</div>
<% end %>
<% end %>
schema.rb
create_table "user_profiles", force: :cascade do |t|
t.date "birthday"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "avatar"
t.index ["user_id"], name: "index_user_profiles_on_user_id"
end
user_profiles_controller.rb
class UserProfilesController < ApplicationController
#before_action :set_user_profile, only: [:show, :edit, :update]
before_action :authenticate_user!
before_action :load_profile, only: [:edit, :update]
def show
end
# GET /user_profiles/1/edit
def edit
end
# PATCH/PUT /user_profiles/1
# PATCH/PUT /user_profiles/1.json
def update
respond_to do |format|
if #user_profile.update(user_profile_params)
format.html {redirect_to #user_profile, notice: 'User profile was successfully updated.'}
format.json {render :show, status: :ok, location: #user_profile}
else
format.html {render :edit}
format.json {render json: #user_profile.errors, status: :unprocessable_entity}
end
end
end
private
def load_profile
#user_profile = current_user.user_profile
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_profile_params
params.require(:user).permit(user_profile: [:birthday, :avatar, :user_id])
end
end
Rails Console
#user = User.find(5)
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2 [["id", 5], ["LIMIT", 1]]
#<User id: 5, email: "colejohn#hotmail.com", username: "johnny", first_name: "John", last_name: "Woo">
>> #user.user_profile
UserProfile Load (2.0ms) SELECT "user_profiles".* FROM "user_profiles" WHERE "user_profiles"."user_id" = $1 LIMIT $2 [["user_id", 5], ["LIMIT", 1]]
#<UserProfile id: 2, birthday: nil, user_id: 5, created_at: "2017-08-17 20:47:12", updated_at: "2017-08-17 20:47:12", avatar: nil>
The edit page is empty, even though the attributes for the
user_profile exist.
This line
<% f.simple_fields_for :user_profile do |user_profile| %>
should be
<%= f.simple_fields_for :user_profile do |user_profile| %>
You are missing =, which is the reason for the form being empty.
Misplaced if statement termination <% end %> in simple_form
<% if current_user.user_profile.avatar.url.present? %>
<%= image_tag(current_user.user_profile.avatar.url) %>
<%= user_profile.label :remove_avatar do %>
<%= user_profile.input :remove_avatar, as: :boolean, checked_value: true, unchecked_value: false %>
<% end %>
<% end %>

Devise: Could not find a valid mapping for User id

The following form in a partial called locationpicker:
<%= simple_form_for(current_user, url: registration_path(current_user), method: :put ) do |f| %>
<% f.association :location, collection: Location.where(category: 'Country'), label: false, input_html: {onchange: "this.form.submit()"} %>
<% end %>
brings up this error:
Could not find a valid mapping for #<User id:...
It's the registration_path(current_user) which is causing it. My suspicions were that something has changed in the routes or the users model, but I can't for the life of me think what or know where to start looking. I thought it might be the recent inclusion of ActiveModel::Dirty but removing that doesn't solve the problem.
Routes.rb:
scope ":locale", locale: /#{I18n.available_locales.join("|")}/ do
devise_for :users, :controllers => {:registrations => "registrations"}
...
end
User.rb
class User < ActiveRecord::Base
include ActiveModel::Dirty
devise :database_authenticatable, :registerable, #:encryptable,
:recoverable, :rememberable, :trackable, :validatable, :lockable
has_one :assignment
has_one :role, :through => :assignment
has_many :changerequests, inverse_of: :created_by, foreign_key: "created_by_id"
belongs_to :location, required: true
belongs_to :person, inverse_of: :user, required: true
has_many :people_details, through: :person
scope :inclusive, -> {includes(:person).includes(:people_details).includes(:location).includes(:assignment).includes(:role)}
after_update :update_locale
after_save :expire_caches
def role?(role_sym)
role_name.to_sym == role_sym
end
def role_group?(role_sym)
role_group_name.to_sym == role_sym
end
def send_on_create_confirmation_instructions
true
end
def update_locale
if locale_changed?
I18n.locale = self.locale || "en"
self.expire_caches
end
end
def country
if self.location.category == "Country"
self.location
elsif self.location.ancestors.where(category: "Country")
self.location.ancestors.where(category: "Country").first
elsif self.location.children.where(category: "Country")
self.location.children.where(category: "Country").first
end
end
def expire_caches
#Admin users can change their location so the caches need expiring
if location_id_changed? or locale_changed?
ctrlr = ActionController::Base.new
#Clear fragments
ctrlr.expire_fragment("#{id}_#{locale}_location_picker")
etc...
end
if location_id_changed?
#Clear other caches
Rails.cache.delete("#{id}_locations_scope")
.... etc
end
end
Try adding as: in your simple_form_for call:
<%= simple_form_for(current_user, as: :user, url: registration_path(current_user), method: :put ) do |f| %>
<% f.association :location, collection: Location.where(category: 'Country'), label: false, input_html: {onchange: "this.form.submit()"} %>
<% end %>
Normally, the devise registrations edit form looks like:
= simple_form_for(resource, as: resource_name, url: user_registration_path, html: { method: :patch }) do |f|
In other words, using "resource" not "current_user". You might try that as well, if your form partial is being rendered within the devise controller context.

Couldn't find Photo with id=16

Not sure why this isn't working but I've been mis-guided I think....I'd rather not re-route but simply have the photo uploaded in the current landing_welcome page.. not be transfered to an update template.
error:
Couldn't find Photo with id=16 [WHERE "photos"."attachable_id" = $1 AND "photos"."attachable_type" = $2]
def update
#user = current_user.photos.find(params[:id])
#user.update_attributes!(person_params)
redirect_to #user
end
Users_controller.rb
class UsersController < ApplicationController
def update
#user = current_user.photos.find(params[:id])
#user.update_attributes!
redirect_to #user
end
def show
end
end
landing_welcome.html.erb
<div class="tab-pane" id="tab2">
<%= nested_form_for current_user, :html=>{:multipart => true } do |f| %>
<%= f.fields_for :photos do |p| %>
<p>
<%= image_tag(p.object.file.url) if p.object.file? %>
<%= p.label :file %><br />
<%= p.file_field :file %>
</p>
<%= p.link_to_remove "Remove this attachment" %>
<% end %>
<%= f.link_to_add "Add photos to your profile", :photos %>
<br /><br />
<p><%= f.submit %></p>
<% end %>
</div>
routes.rb
root to: "home#landing"
devise_for :users, :controllers => {:registrations => "users/registrations",
:sessions => "users/sessions",
:passwords => "users/passwords"}
get "welcome", to: "home#landing_welcome"
devise_scope :user do
# get "edit/edit_account", :to => "devise/registrations#edit_account", :as => "account_registration"
get 'edit/edit_account' => 'users/registrations#account_registration', as: :edit_account
end
patch '/users/:id', to: 'users#update', as: 'user'
photo.rb
class Photo < ActiveRecord::Base
attr_accessible :file
belongs_to :attachable, :polymorphic => true
mount_uploader :file, FileUploader
end
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email,
:password,
:password_confirmation,
:zip,
:gender,
:remember_me,
:first_name,
:last_name,
:birthday,
:current_password,
:occupation,
:address,
:interests,
:aboutme,
:profile_image,
:photos_attributes
has_many :photos, as: :attachable
accepts_nested_attributes_for :photos
mount_uploader :profile_image, ProfileImageUploader
validates :gender, :presence => true
def number_of_users
User.all.count
end
end
For lack of a better answer, I think your woes lie in the query generated by your app:
Couldn't find Photo with id=16 [WHERE "photos"."attachable_id" = $1 AND "photos"."attachable_type" = $2]
Two factors are present here:
Why is your attachable_id being called as $1?
Why is your attachable_type a number, not a string?
Polymorphic Association
Your query is trying to load a Photo with ID=16, however, your query is also trying to validate the model, to satisfy the polymorphic association. This is where the error is coming from
As you've not stated which route / page this error is showing, I can only speculate as to the cause of the problem:
#user = current_user.photos.find(params[:id])
This query is really bad for a number of reasons:
You're using the current_user object directly. I might be wrong here, but this is used by Devise / Warden to store relative information about the logged-in user, and is not a real ActiveRecord object (with relational data etc)
You're trying to .find on top of a relation (current_user.photos)
Although this might be incorrect, I would look at doing this for that query:
#photo = User.joins(:photos).find(current_user.id).where("photos.id = ?", params[:id])
Then you can perform the updates you require

Controller not creating with association

I've got two models, Users and Organizations, which have a has_many relationship using an assignments table. I have a nested resource form when the user is created, which creates an associated organization just fine. However, when creating an organization, it doesn't associate it with the user.
Here's my relevant Organizations controller code:
def new
#organization = current_user.organizations.build
end
def create
#organization = current_user.organizations.build(params[:organization])
#organization.save
end
And my models:
Organizations Assignments
class OrganizationAssignment < ActiveRecord::Base
belongs_to :user
belongs_to :organization
attr_accessible :user_id, :organization_id
end
Organizations:
class Organization < ActiveRecord::Base
validates :subdomain, :presence => true, :uniqueness => true
has_many :organization_assignments
has_many :people
has_many :users, :through => :organization_assignments
attr_accessible :name, :subdomain
end
Users:
class User < ActiveRecord::Base
has_many :organization_assignments
has_many :organizations, :through => :organization_assignments
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
accepts_nested_attributes_for :organizations
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :organizations_attributes
# attr_accessible :title, :body
end
form view:
= form_for #organization, :html => { :class => 'form-horizontal' } do |f|
- #organization.errors.full_messages.each do |msg|
.alert.alert-error
%h3
= pluralize(#organization.errors.count, 'error')
prohibited this user from being saved:
%ul
%li
= msg
= f.label :name
= f.text_field :name
= f.label :subdomain
= f.text_field :subdomain
.form-actions
= f.submit nil, :class => 'btn btn-primary'
= link_to t('.cancel', :default => t("helpers.links.cancel")), organizations_path, :class => 'btn'
I'm able to associate the organizations fine after the fact in the console, so I'm pretty sure the relationships are set up correctly in the model. Is there anything else I'm missing?
From my experience with Rails, you can't expect the relation to be made that way. Try something like this.
def create
#organization = Organization.build(params[:organization])
#organization.save
current_user.organizations << #organization
end
You might alternatively keep your code as-is, but save current_user instead of #organization.
def create
#organization = current_user.organizations.build(params[:organization])
current_user.save
end

Displaying a link in microposts and comments that links to the author's profile?

I created a Micropost model that have the following attributes:
<Micropost id: 1, content: "test", user_id: 1, created_at: "2012-01-25 15:34:30", updated_at: "2012-01-29 11:07:53", title: "asdasdad">
an User model with the following attributes:
<User id: 1, email: "alex#gmail.com", username: nil, etc...>
and a Comment model with the following attributes:
<Comment id: 1, content: "sdf", micropost_id: 1, user_id: nil, created_at: "2012-01-29 11:10:42", updated_at: "2012-01-29 11:10:42">
So far, I've only accomplished this:
Display the username of the author of a micropost
Display the ID of the authors of the microposts' comments
<h2>Micropost Index</h2>
<% #microposts.each do |micropost| %>
<%= micropost.title %></td>
<%= micropost.content %></td>
<%= link_to 'Show', micropost %></td>
<%= link_to 'Edit', edit_micropost_path(micropost) %></td>
<%= link_to 'Destroy', micropost, confirm: 'Are you sure?', method: :delete %>
<h2>Comments</h2>
<% #micropost.comments.each do |comment| %>
<p>
<b>Comment:</b>
<%= comment.content %>
</p>
<p>
<b>Commenter</b>
<%= comment.user_id %>
</p>
<% end %>
I don't know how to create a link to the authors profile (e.g. mysite.com/users/1).
I don't how to retrieve the name of the author of a comment and the link to his/her profile
EDIT:
Models:
user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, :username
has_many :microposts
has_many :comments
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else # Create a user with a stub password.
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
end
micropost.rb:
class Micropost < ActiveRecord::Base
attr_accessible :title, :content
belongs_to :user
has_many :comments
end
comment.rb:
class Comment < ActiveRecord::Base
attr_accessible :content, :user_id
belongs_to :micropost
belongs_to :user
end
Micropost controller:
controllers/microposts.rb
def show
#micropost = Micropost.find(params[:id])
end
def new
#micropost = Micropost.new
end
def create
#user = current_user
#micropost = #user.microposts.new(params[:micropost])
#micropost.save
redirect_to #micropost
end
Any suggestions to accomplish this?
To make a link to the user you can use
<%= link_to comment.user.username, comment.user %>
In general part of the "Rails Magic" is, that you if you set up an association correctly, that you can access the related objects through the dot notation. That means, you don't need to say comment.user_id but instead go directly for the associated user object, e.g. comment.user.username or comment.user.email ... you get the idea :)
In order to to this you should have set up your models like this:
class User < ActiveRecord::Base
validates_presence_of :username #username should obviously not allow nil values
has_many :microposts
has_many :comments
end
class MicroPost < ActiveRecord::Base
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :user
end
# Link:
=link_to "User profile", user_path(comment.user)
# Name of the author
=comment.user.username
or since you have user_id in both Micropost and Comment
# Link:
=link_to "User profile", (#micropost.user)
# Name of the author
=#micropost.user.username

Resources