I am newbie to rails ,
I am using devise from ryan bates video tutorial , and , i am stuck at one point
I have created user , role relationship
and in the sign up page , i need to provide select option group for existing roles ,
in my sign up view page i am writing
<%= collection_select(:user,:roles,Role.find(:all),:id,:name) %>
i dont precisely understand collection_select method , kindly help what i might be doing wrong
my models 1 : user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me , :roles
has_and_belongs_to_many :roles
def role?(role)
return !!self.roles.find_by_name(role.to_s.camelize)
end
end
my model 2 : role.rb
class Role < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :users
end
my user migration file
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, :default => 0
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.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
end
end
my role migration file
class CreateRoles < ActiveRecord::Migration
def change
create_table :roles do |t|
t.string :name
t.timestamps
end
end
end
my join table migration file
class UsersHaveAndBelongToManyRoles < ActiveRecord::Migration
def up
create_table :roles_users, :id => false do |t|
t.references :role, :user
end
end
def down
drop_table :roles_users
end
end
the ERROR COMING is
undefined method `each' for "2":String
2 being the id of the role selected
In a has_and_belongs_to_many relationship like you have between User and Role, there is no role_id on the User object.
The second parameter of the collection_select is the attribute you're updating with the selection(s), in your case it's not role_id, it's role_ids and seeing as it's a has_and_belongs_to_many relationship you probably want to allow the user to select multiple options, so try something like this:
<%= collection_select(:user, :role_ids, Role.all, :id, :name, {}, { selected: #user.role_ids, multiple: true }) %>
If you attach it to a form_for on your #user object you can use:
<%= f.collection_select(:role_ids, Role.all, :id, :name, {}, multiple: true) %>
Related
I'm new in ruby on rails
I use gem "devise" for authentication users and I have trouble with update assosiated models in account update action. At account update, keys 'user_id' in assosiated records set to nil
database queries that occurs:
UPDATE "user_information" SET "user_id" = ? WHERE "user_information"."user_id" = ? [["user_id", nil], ["user_id", 1]]
UPDATE "user_settings" SET "user_id" = ? WHERE "user_settings"."user_id" = ? [["user_id", nil], ["user_id", 1]]
ie request reset key user_id to nil in associated models. What am I doing wrong?
My code:
migrations
create_table(:users) do |t|
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
# etc
end
create_table :user_settings, :id => false do |t|
t.references :user, null: false, index: true, unique: true
# etc
end
create_table :user_information, :id => :user_id do |t|
t.references :user, null: false, index: true, unique: true
# etc
end
user model
class User < ActiveRecord::Base
has_one :setting, :class_name => 'User::Setting', inverse_of: :user
has_one :information, :class_name => 'User::Information', inverse_of: :user
accepts_nested_attributes_for :setting
accepts_nested_attributes_for :information'
# devise
end
registrations controller
class Users::Devise::RegistrationsController < Devise::RegistrationsController
before_filter :configure_permitted_parameters
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:account_update) do |u|
u.permit(:name, :email, :password, :password_confirmation, :current_password,
information_attributes: [:name, :contacts], setting_attributes: [:monetization])
# etc
end
end
def build_resource(hash=nil)
super
self.resource.build_setting
self.resource.build_information if hash.empty?
end
end
I am new to Rails and I am trying to get my application running.
My UserMigration.rb file contains:
class UserMigration < ActiveRecord::Migration
def self.up
if (!ActiveRecord::Base.connection.table_exists? :users)
drop_table :users
create_table :users do |t|
t.string :email
t.string :first
t.string :last
t.string :password
t.string :username
t.integer :created_by
t.string :bio #Newly added
t.integer :id #Newly added
t.string :photoURL #Newly added
end
add_index :users, :email
end
if (Models::Persistence::User.count == 0)
Models::Persistence::User.create({:email => 'testuser#my.com',
:username => 'testuser#my.com',
:first => 'Test',
:last => 'User',
:password => 'Test123',
:bio => "User, student",
:id => "1",
:photoURL => "http://link",
:created_by => 1})
And my models User.rb file contains:
module Models
module Persistence
class User < ActiveRecord::Base
validates :bio, :presence => true, length: {minimum: 150}
I am working off an example and I just recently added the three extra fields in the users table (bio, id and photoURL). I keep getting this error when I run my tests with the newly added fields on the db:
undefined method `bio' for #<Models::Persistence::User:0x007f8c04ae6838>
Any pointers on where I could start debugging this from? Thanks!
I'm trying to implement the devise gem into my web app - everything with user login and registration is working, but when a user tries to actually leave a prediction (the only thing you need to be logged in for), I get this error message: undefined method `user=' for nil:NilClass. I can't seem to figure out what's causing this problem. Might anyone know?
_login_items.html.erb:
<ul>
<% if user_signed_in? %>
<li>
<%= link_to('Logout', destroy_user_session_path, :method => :delete) %>
</li>
<% else %>
<li>
<%= link_to('Login', new_user_session_path) %>
</li>
<% end %>
<% if user_signed_in? %>
<li>
<%= link_to('Edit registration', edit_user_registration_path) %>
</li>
<% else %>
<li>
<%= link_to('Register', new_user_registration_path) %>
</li>
<% end %>
</ul>
prediction model:
class Prediction < ActiveRecord::Base
belongs_to :student
belongs_to :user
end
user model:
class User < ActiveRecord::Base
has_many :predictions
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
predictions migration:
class CreatePredictions < ActiveRecord::Migration
def change
create_table :predictions do |t|
t.string :prediction
t.belongs_to :student, index: true
t.belongs_to :user
t.timestamps
end
end
end
user migration:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
t.integer :sign_in_count, default: 0, null: false
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.inet :current_sign_in_ip
t.inet :last_sign_in_ip
## Confirmable
# t.string :confirmation_token
# t.datetime :confirmed_at
# t.datetime :confirmation_sent_at
# t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
predictions controller:
class PredictionsController <ApplicationController
def create
#prediction.user = current_user
Prediction.create(prediction_params)
redirect_to :back
end
def new
#prediction = Prediction.new(student_id: params[:student_id])
end
def destroy
Prediction.find(params[:id]).destroy
redirect_to :back
end
private
def prediction_params
params.require(:prediction).permit(:prediction) #, :student_id
end
end
The problem is in your PredictionsController. You have to create the prediction and assign it to a variable first. Change your create action to this:
def create
#prediction = Prediction.create(prediction_params)
#prediction.user = current_user
redirect_to :back
end
You were trying to assign a user to a non existing prediction. This correction creates the prediction first then assigns the user to it.
Edit: I noticed one other problem: Your prediction_params method is incorrect. The arguments you pass to the permit method must be the attributes of your prediction model which you want to mass assign. The :prediction key in the params is the one you want to require but within that nested hash you want to permit attributes of the prediction model. So, hypothetically, if your prediction model has :name, :value and :student_id attributes, your prediction_params method should look like this:
def prediction_params
params.require(:prediction).permit(:name, :value, :student_id)
end
This will work with a params hash that looks like:
{
prediction: {
name: 'something',
value: 'stuff',
student_id: 1
}
}
I have read the posts here, here, and here, but I'm still having trouble with implementing Single Table Inheritance.
Ideally I would like to have two registration paths (one for clients and one for providers) with the common fields name, email, password, and confirm_password, and the provider registration having an extra radiobutton field to specify a provider type. I am doing the registration through devise. Upon clicking submit on the registration form a user would then be redirected to a second form which is totally different for clients and providers (I have been doing this using the edit page for a resource).
As it stands, everything works if I am just doing it through User, but as soon as I add single table inheritance the registration forms tell me they are missing the requirements of the second forms.
Here is my config/routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => {:sessions => "sessions"}, :skip=> :registrations
devise_for :clients, :providers, :skip=> :sessions
resources :clients
resources :providers
root :to=>'pages#home'
match '/home', to: 'pages#home', via: 'get'
end
My models look as follows:
User:
class User < ActiveRecord::Base
before_save {self.email = email.downcase}
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :name, presence: true, length: {maximum: 50}
validates :email, presence: true, :email => {:ban_disposable_email => true, :message => I18n.t('validations.errors.models.user.invalid_email')}, uniqueness: { case_sensitive: false }
validates :password, presence: true, length: {minimum: 6},:if=>:password_validation_required?
LOGO_TYPES = ['image/jpeg', 'image/png', 'image/gif']
has_attached_file :avatar, :styles => {:medium => "300x300>",:square=>"200x200>", :thumb => "100x100>" }, :default_url => '/assets/missing_:style.png'
validates_attachment_content_type :avatar, :content_type => LOGO_TYPES
def password_validation_required?
!#password.blank?
end
end
Client:
class Client < User
validates :industry, presence: true
validates :city, presence: true
validates :state, presence: true
validates :description, presence: true, length: {minimum: 50, maximum: 300}
end
Provider:
class Provider < User
validates :ptype, presence: true
validates :city, presence: true
validates :state, presence: true
validates :education, presence: true
validates :biography, presence:true, length: {minimum: 50, maximum: 300}
validates_format_of :linkedin, :with => URI::regexp(%w(http https))
validates :resume, presence: true
has_many :disciplines
end
and here are my controllers:
class SessionsController < Devise::SessionsController
def create
rtn = super
sign_in(resource.type.underscore, resource.type.constantize.send(:find,resource.id)) unless resource.type.nil?
rtn
end
end
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
if resource.is_a?(User)
if current_user.is_a?(Client)
edit_client_path(current_user.id)
elsif current_user.is_a?(Provider)
edit_provider_path(current_user.id)
end
else
super
end
end
end
class ClientsController < ApplicationController
def show
#client = Client.find(params[:id])
end
def edit
#client = Client.find(params[:id])
end
def update
#client = Client.find(params[:id])
if #client.update_attributes(client_params_edit)
flash[:success] = "Profile Updated"
redirect_to #client
else
flash[:failure] = "Profile Information Invalid"
render 'edit'
end
end
def client_params_edit
params.require(:client).permit(:avatar,:industry,:city,:website, :description)
end
end
the provider controller is quite similar.
Finally, here is my schema.rb:
ActiveRecord::Schema.define(version: 20140628213816) do
create_table "disciplines", force: true do |t|
t.integer "years"
t.string "description"
t.integer "user_id"
end
create_table "users", force: true do |t|
t.string "name"
t.string "email"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.string "password_digest"
t.string "industry"
t.string "city"
t.string "state"
t.string "website"
t.string "description"
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 "type"
t.string "ptype"
t.string "education"
t.string "resume_file_name"
t.string "resume_content_type"
t.integer "resume_file_size"
t.datetime "resume_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
You need to specify which model should be instantiated inside your custom registrations controller (that one which inherits from Devise::RegistrationsController).
You have to override the protected method called resource_class to somewhat like this:
def resource_class
# for example you pass type inside params[:user]
klass = params[:user].try(:[], :type) || 'Client'
# we don't want wrong class to be instantiated
raise ArgumentError, 'wrong user class' unless ['Client', 'Provider'].include?(klass)
# transform string to class
klass.constantize
end
Also you might want to override sign_up_params to specify allowed params based on user type too.
Just a thought.
Have you considered allowing registration as a user and holding the type parameter back until later in the workflow.
i.e.
Registration page:
Creates User (with a parameter that decides which Type the user will end up being)
Second page (to which you are automatically redirected upon creating user, or even logging in as user having not gone through part 2):
Adds the appropriate required information and changes type from User to your appropriate STI type upon submit.
Other option would be to swap your first "submit" button for a button which simply reveals the relevant extra fields (and the real submit button) via JS.
I am counting many user generated actions and for the most part it's easy, but with regard to one, more complex query, I am having trouble.
I have an invitations model and a user model and I can easily count the number of invitations the user sent, but I want to count the number of new members that signed up based on the invitations the existing member sent out.
In invitations, the invitees email is saved as recipient_email
Then, I know I can check that against new members email some how, but am not clear on the syntax.
Any help will be greatly appreciated. More information below.
Invitation Model:
class Invitation < ActiveRecord::Base
attr_accessible :recipient_email, :sender_id, :sent_at, :token
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
validates_presence_of :recipient_email
validates_uniqueness_of :recipient_email, :message => '%{value} has already been invited'
validate :recipient_is_not_registered
validate :sender_has_invitations, :if => :sender
default_scope order: 'invitations.created_at DESC'
before_create :generate_token
before_create :decrement_sender_count, :if => :sender
after_create do |invitation|
InvitationMailer.delay.invitation_email(self)
end
def invitee
User.find_by_email(self.recipient_email)
end
def invitee_registered?
!invitee.blank?
end
def invitee_submitted?
!invitee.try(:submissions).blank?
end
private
def recipient_is_not_registered
errors.add :recipient_email, 'is already registered' if User.find_by_email(recipient_email)
end
def sender_has_invitations
unless sender.invitation_limit > 0
errors.add_to_base "You have reached your limit of invitations to send.
You can contact Lumeo if you'd like to request more."
end
end
def generate_token
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
def decrement_sender_count
sender.decrement! :invitation_limit
end
end
User Model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :mailchimp
attr_accessible :name, :email, :password, :password_confirmation,
:remember_me, :role_id, :role_ids, :image_attributes,
:terms, :profile_attributes, :current, :image, :roles,
:invitation_token, :join_mailing_list, :affiliate_id,
:invitation_affiliate_token, :affiliation, :referrer_id
validates_uniqueness_of :email
VALID_NAME_REGEX = /[\w]+([\s]+[\w]+){1}+/
validates :name, presence: true,
format: {with: VALID_NAME_REGEX}
#invitation
has_many :sent_invitations, :class_name => 'Invitation', :foreign_key => 'sender_id'
belongs_to :invitation
def invitation_token
invitation.token if invitation
end
def invitation_token=(token)
self.invitation = Invitation.find_by_token(token)
end
before_create :set_invitation_limit
has_one :invitation_affiliate, :class_name => "Affiliate", :foreign_key => 'token', :primary_key => 'invitation_affiliate_token'
private
def set_invitation_limit
self.invitation_limit = 100
end
end
Invitation and User Tables:
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
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.integer "role_id"
t.string "name"
t.integer "invitation_id"
t.integer "invitation_limit"
end
create_table "invitations", :force => true do |t|
t.integer "sender_id"
t.string "recipient_email"
t.string "token"
t.datetime "sent_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
I could think of two different ways:
Add accepted field in invitations
You could add a boolean field for invitations named accepted, the default value will be false and you set it to true when the receipent accepts the invitation. Then you create a scope named accepted that returns only accepted invitations
scope :accepted, where(accepted: true)
You get what you want by #user.sent_invitations.accepted.count
2 . Do the following query
User.where(email: #user.sent_invitations.map(&:recipient_email)).count