I've spent the last two hours figuring out what is wrong, but could not find the answer anywhere.
Its my first rails application (except Hartl's tutorial) so the solution might be simple.. I'm using Devise to manage my Users, everything is really ok with it up until now.
Trying to test the User model I defined a factory like this:
FactoryGirl.define do
factory :user do
email "g#g.com"
password "123123"
password_confirmation { "123123" }
end
end
and the test is:
describe User do
# pending "add some examples to (or delete) #{__FILE__}"
#user = FactoryGirl.create(:user)
subject(:user)
it { should respond_to(:email) }
it { should respond_to(:password) }
it { should be_valid }
end
But the last line ( it { should be_valid } ) fails the test.
I've printed the value of user/#user (tried both) and it came out nil.
Edit: It's not nil. Its
#<User id: 13, email: "email1#factory.com", encrypted_password: "$2a$04$.lWs6yadJu/Ya67xi.W1F.fd6sWLGkzc/59.lgTi0sA7...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: "2012-08-27 15:48:23", updated_at: "2012-08-27 15:48:23">
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
# attr_accessible :title, :body
validates :email, :presence => true
validates :password, :presence => true
end
What is it that I don't see?
OK, I finally found the problem. It turns out you have to restart "spork" when ever you make a change to the User model, because it preloads it.
Related
I am migrating a Rails 2 app to Rails 3 and had to use a new authentication solution since restful_authentication is no longer supported. I am trying out Devise and am having problems with it.
I have a login page, which then directs you to the app. You can also use the app without logging in but some options are not available until you login.
Once logging in Devise says that I have logged in via a flash message, but any current_user will evaluate to nil, current_user.blank? evaluates to true and any user_signed_in? will evaluate to false. Oddly signed_in? evaluates to true. Checking my session data shows that warden.user.user.id contains the correct user id and a csrf token exists. This is true both in the view and in the controller.
Here is routes.rb
MyApp::Application.routes.draw do
devise_for :users
match "/:controller(/:action(/:id))"
match "/:controller(/:action(/:id))(.:format)"
match "/:controller(/:action(.:format))"
devise_scope :user do
get 'signup', :to => 'devise/registrations#new'
get 'login', :to => 'devise/sessions#new'
get 'signout', :to => 'devise/sessions#destroy'
end
root :to => 'main#design'
end
User.rb
require 'digest/sha1'
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :confirmable, :validatable,
:encryptable, :encryptor => :restful_authentication_sha1
attr_accessible :email, :password, :password_confirmation, :remember_me, :profile_company, :profile_state, :profile_city, :profile_postcode, :profile_country, :profile_phone, :profile_address
validates_length_of :name, :maximum => 100
validates_presence_of :email
validates_length_of :email, :within => 6..100
validates_uniqueness_of :email
def profile_complete?
if !(self.profile_address.blank? || self.profile_city.blank? || self.profile_state.blank? || self.profile_phone.blank? || self.name.blank? || self.state != 'active')
true
else
false
end
end
end
I've seen a lot of similar questions on SO, but none of the answers seem to fit my scenario. Any help would be greatly appreciated, thanks!
EDIT: warden.authenticate(:scope => :user) returns what i would expect current_user to return.
I believe that I have solved this issue, the restful_authentication plugin added a UsersHelper file that I believe was somehow interfering with how Devise was handling the User class.
I am using the acts_as_tenant gem to manage multi-tenancy, and I'm using devise to manage users.
I have only setup devise User model and Account model for tenants.
I can create users against multiple tenants - this is all working fine EXCEPT when I attempt to create two users with the same email against different tenant ID's I get a uniqeness error.
I am using the validates_uniqueness_to_tenant option as described.
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
acts_as_tenant(:account)
validates_uniqueness_to_tenant :email
end
Account model
class Account < ActiveRecord::Base
attr_accessible :name
end
Application Controller
class ApplicationController < ActionController::Base
set_current_tenant_by_subdomain(:account, :subdomain)
protect_from_forgery
end
This looks like it should be working based on all documentation in acts_as_tenant, do I need to override something at the devise level instead?
EDIT: After some head-scratching and a bit of a break, the problem is I believe because by default Devise has added a unique index to the Email column.
This obviously does not gel with what acts_as_tenant wants to do...
I will try removing the index and see whether Devise pukes or not.
EDIT 2: OK, have officially given up on this for now. I have hand-rolled authentication for the main site and this is working properly with acts_as_tenant.
I can only assume some incompatibility between acts_as_tenant and Devise at some layer - beyond me to find it at this stage.
The only way to do this is by removing the validatable module from devise and run your own validations like so:
class User < ActiveRecord::Base
acts_as_tenant :account
attr_accessible :email, :password, :remember_me
#remove :validatable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
#run own validations
#I've omitted any emailformatting checks for clarity's sake.
validates :email,
presence: true,
uniqueness: { scope: :account_id, case_sensitive: false }
validates :password,
presence: true,
length: { :in => 6..20 },
:if => :password_required?
protected
# copied from validatable module
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
end
I haven't tested it, but I wonder if changing the order might help acts_as_tenant do its thing before devise takes over.
class User < ActiveRecord::Base
acts_as_tenant(:account)
validates_uniqueness_to_tenant :email
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
Just came across this question. Sweam's solution is pretty good.
But I prefer to not override the default behaviour. So I came up with this solution:
validate :remove_old_email_validation_error
validates_uniqueness_of :email, :allow_blank => true, :if => :email_changed?, :scope => [:account_id]
private
def remove_old_email_validation_error
errors.delete(:email)
end
We remove the default validation error for email, therefore ignoring the validation check, and we do our own validation again.
What I have added is from the Validatable module, but i have added :scope to it.
Its important to keep the order. Add the above code after the devise command.
I resolved it as:
validate :remove_old_uniquess_email_error
private
def remove_old_uniquess_email_error
errors.delete(:email) if self.company_id.nil? && errors[:email].present? && errors[:email] == ["already taken"]
end
I've got a simple app (Rails 3.2) with a User modal that's set up with devise(version 2.0.4).
My User model has the devise default settings:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :remember_me
end
Everything seems to work fine when I use the default devise routes like so:
devise_for :users
But I didn't to use the default so I changed it to this ones:
devise_for :users, :path => '', :path_names => { :sign_in => "login", :sign_out => "logout", :sign_up => "sign_up" }
After I changed this routes and restarted my server they seem to work fine but for some reason the validation don't work now, and even if I remove the "validatable" feature from my model and add my own custom validation they does not seem to work as well.
Would love your help on this!
- Paz
Solved this. The reason why it didn't work is because devise uses the "users/login" to know on what scope to work on.
So in order to fix this I changed my routes file to this:
devise_for :users
devise_scope :user do
get "/login" => "devise/sessions#new"
get "/logout" => "devise/sessions#destroy"
get "/register" => "devise/registrations#new"
end
Reference: https://github.com/plataformatec/devise/wiki/How-To:-Change-the-default-sign_in-and-sign_out-routes
I'm writing a web service to return a user's details. In the controller, I simply render :xml => user and return. However, not all of the fields of my User model are being returned, and I don't see anything in my model that would indicate which fields to include or exclude.
Model:
class User < ActiveRecord::Base
devise :database_authenticatable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :email, :password, :password_confirmation, :remember_me, :first_name, :last_name
end
Example:
irb(main):003:0> #user = User.find(3)
=> #<User id: 3, email: "me#me.me", encrypted_password: <redacted>, reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 11, current_sign_in_at: "2011-08-24 22:50:44", last_sign_in_at: "2011-08-24 06:18:41", current_sign_in_ip: "1.2.3.4", last_sign_in_ip: "1.2.3.4", created_at: "2011-08-23 17:09:28", updated_at: "2011-08-26 04:01:01", controller: false, admin: false, chargify_customer_id: 1234, chargify_subscription_id: 1234, first_name: "Me", last_name: "Me", chargify_subscription_state: "active">
What my render is currently returning for that same user:
<?xml version="1.0" encoding="UTF-8"?>
<user>
<last-name>Me</last-name>
<email>me#me.me</email>
<first-name>Me</first-name>
</user>
At a minimum, I need to include the id field; overall, I'd like to understand better how you control what gets included and what doesn't.
Serialization happens in the as_json/as_xml methods. By default these methods serialize all of your models attributes into json/xml. However, devise hides certain attributes generated by its ActiveRecord extensions. That's why you don't get the password fields for example.
You can control which attributes get included in your xml by overriding the to_xml method in your user model.
def as_xml(options = {})
default_options = {
:only => [:id, :first_name, :last_name, :email]
}
xml_options = options.blank? ? default_options : options
super xml_options
end
You can also include custom methods of your model.
def as_xml(options = {})
default_options = {
:only => [:id, :first_name, :last_name, :email],
:methods => [:some_custom_method]
}
xml_options = options.blank? ? default_options : options
super xml_options
end
You can read more about serialization here:
http://api.rubyonrails.org/classes/ActiveRecord/Serialization.html
Greetings all,
I'm having a weird problem with a habtm relationship and honestly I'm beginning to think I may have stumbled upon some weird bug in rails 3. Surely I'm crazy though. I've been beating my head against the wall on this for 3 days, have googled everything under the sun I can think of and still can't come up with an answer.
Ok, the situation:
I'm creating a Rails app to replace both a Java app and a PHP app (java application and php front-end). This is going to be a phased operation with the first phase being the Rails application takes over registration and billing. In order to do this, the Rails application must create data in the databases for the Java and PHP apps. The Rails application itself is using Devise for authentication.
In database.yml I have my standard 3 databases defined and also a connection defined for the Java apps database.
Here are pieces of the model definitions for the external object (I'm just creating regular rails models to talk to the external databases):
class Pushbroom::UserAccount < ActiveRecord::Base
require 'digest/md5'
require 'base64'
establish_connection :pushbroom
set_table_name :user_account
set_primary_key :id
has_and_belongs_to_many :user_roles, :join_table => 'pb_prod.users_roles', :class_name => 'Pushbroom::UserRole', :foreign_key => 'user_account_id', :association_foreign_key => 'user_role_id'
belongs_to :user, :dependent => :destroy
attr_accessible :user_roles, :admin_notes, :enabled, :username, :password_hash, :prefStore, :accepted_tos, :do_not_contact
end
class Pushbroom::UserRole < ActiveRecord::Base
establish_connection :pushbroom
set_table_name :user_role
set_primary_key :id
has_and_belongs_to_many :user_accounts, :join_table => 'pb_prod.users_roles', :class_name => 'Pushbroom::UserAccount', :foreign_key => 'user_role_id', :association_foreign_key => 'user_account_id'
end
And finally my Rails application user object:
class User < ActiveRecord::Base
after_create :send_welcome_email
before_save :create_pushbroom_user_data
# Include default devise modules. Others available are:
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :pb_user_account, :class_name => "Pushbroom::UserAccount", :foreign_key => "pb_user_account_id", :dependent => :destroy, :autosave => true
# Setup accessible (or protected) attributes for your model
attr_accessible :first_name, :last_name, :username, :dob, :email, :password, :password_confirmation, :remember_me
validates_presence_of :first_name, :last_name, :username, :dob
validates_date :dob, :on_or_after => lambda { 100.years.ago }, :on_or_after_message => "must be on or after #{100.years.ago.strftime('%m-%d-%Y')}"
validates_date :dob, :on_or_before => lambda { 13.years.ago }, :on_or_before_message => "must be on or before #{13.years.ago.strftime('%m-%d-%Y')}"
def create_pushbroom_user_data
pb_user = create_pushbroom_user
pb_user_account = create_pushbroom_user_account(pb_user)
pb_user_account.user_roles << Pushbroom::UserRole.find_by_name('user')
self.pb_user_account = pb_user_account
end
def create_pushbroom_user
pb_user = Pushbroom::User.new
pb_user.attributes = self.attributes.slice(
"email",
"first_name",
"last_name",
"dob")
pb_user
end
def create_pushbroom_user_account(pb_user)
pb_user_account = Pushbroom::UserAccount.new
pb_user_account.enabled = true
pb_user_account.password_hash = Pushbroom::UserAccount.create_password_digest(#plaintext_password, self.username)
pb_user_account.username = self.username
pb_user_account.user = pb_user
pb_user_account
end
Seems like it should be pretty vanilla. The ONLY weirdness here is that they aren't in the native rails database and one of the fields is named funny in the relations table.
So here's a rails console session where I create a rails user, call the method to create the external objects, then try to save:
ruby-1.9.2-p180 :001 > def user_fred
ruby-1.9.2-p180 :002?> {
ruby-1.9.2-p180 :003 > :first_name => "Fred",
ruby-1.9.2-p180 :004 > :last_name => "Flinstone",
ruby-1.9.2-p180 :005 > :username => "fflint",
ruby-1.9.2-p180 :006 > :dob => "1986-06-01",
ruby-1.9.2-p180 :007 > :email => "fred#mydomain.org",
ruby-1.9.2-p180 :008 > :password => "badpass"
ruby-1.9.2-p180 :009?> }
ruby-1.9.2-p180 :010?> end
=> nil
ruby-1.9.2-p180 :011 > user = User.new(user_fred)
=> #<User id: nil, email: "fred#mydomain.org", encrypted_password: "$2a$10$IiEOEoSnXIrP7VJAQYckfOVXuzm7Y5ZGo20ayLpSkHhz...", reset_password_token: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, created_at: nil, updated_at: nil, first_name: "Fred", last_name: "Flinstone", username: "fflint", dob: "1986-06-01", pb_user_account_id: nil>
ruby-1.9.2-p180 :012 > user.create_pushbroom_user_data
=> #<Pushbroom::UserAccount id: nil, created_by: nil, created_at: nil, updated_by: nil, updated_at: nil, admin_notes: nil, enabled: true, username: "fflint", password_hash: "blah blah", user_id: nil, prefStore: nil, accepted_tos: nil, do_not_contact: nil>
ruby-1.9.2-p180 :013 > user.pb_user_account.user_roles
=> [#<Pushbroom::UserRole id: 1, created_by: "script", created_at: "2008-11-10 12:10:44", updated_by: "script", updated_at: "2008-11-10 12:10:44", admin_notes: "", name: "user", description: "Generic User Role", conditional: false>]
ruby-1.9.2-p180 :014 > user.save!
NoMethodError: undefined method `relation' for nil:NilClass
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activesupport- 3.0.5/lib/active_support/whiny_nil.rb:48:in `method_missing'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/arel- 2.0.9/lib/arel/insert_manager.rb:22:in `insert'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/arel- 2.0.9/lib/arel/crud.rb:26:in `insert'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord- 3.0.5/lib/active_record/associations/has_and_belongs_to_many_association.rb:76:in `insert_record'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord- 3.0.5/lib/active_record/associations/association_proxy.rb:151:in `send'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:306:in `block in save_collection_association'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/associations/association_collection.rb:431:in `block in method_missing'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in `block in method_missing'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in `each'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in `method_missing'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/associations/association_collection.rb:431:in `method_missing'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:297:in `save_collection_association'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:163:in `block in add_autosave_association_callbacks'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activesupport-3.0.5/lib/active_support/callbacks.rb:415:in `_run_create_callbacks'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/callbacks.rb:281:in `create'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/persistence.rb:246:in `create_or_update'
... 18 levels...
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/callbacks.rb:277:in `create_or_update'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/persistence.rb:56:in `save!'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/validations.rb:49:in `save!'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/attribute_methods/dirty.rb:30:in `save!'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/transactions.rb:245:in `block in save!'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord- 3.0.5/lib/active_record/transactions.rb:292:in `block in with_transaction_returning_status'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/transactions.rb:207:in `transaction'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/transactions.rb:290:in `with_transaction_returning_status'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/activerecord-3.0.5/lib/active_record/transactions.rb:245:in `save!'
from (irb):14
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in `start'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in `start'
from /Users/gander/.rvm/gems/ruby-1.9.2-p180#sms2/gems/railties-3.0.5/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'ruby-1.9.2-p180 :015 >
If I remove the role assignment, everything is just peachy (finds, saves, destroys, etc), but the second I try to save roles everything blows sky-high with this message that, frankly, I don't get. It knows its got the roles, there is no nil object that I can tell. . .and basically if I wasn't already bald I'd be pulling my hair out ; )
Any insight into this is EXTREMELY appreciated!
Gerald
P.S. Also asked here http://railsforum.com/viewtopic.php?id=43647 Will duplicate answer if found.
After beating myself silly for 4 days on this I finally found the problem: Rails (habtm) doesn't have the ability to determine the database to use for external relation tables. I also found the answer, and it doesn't even smell bad! There's a whole thread on the process here: http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/c5655d0442039ccd
The answer? has_many :through - something I'd never taken much of a look at, but it is actually a pretty nice feature (even in other circumstances).
Basically this just allows me to create a model class which represents the relationship. And since I have a model class for it I can explicitly specify the database to connect to.
For posterity sake, here's the code:
class Pushbroom::UsersRolesRelationship < ActiveRecord::Base
establish_connection :pushbroom
set_table_name :users_roles
belongs_to :user_account
belongs_to :user_role
end
class Pushbroom::UserAccount < ActiveRecord::Base
establish_connection :pushbroom
set_table_name :user_account
set_primary_key :id
has_many :users_roles_relationships
has_many :user_roles, :through => :users_roles_relationships, :source => :user_role
end
class Pushbroom::UserRole < ActiveRecord::Base
establish_connection :pushbroom
set_table_name :user_role
set_primary_key :id
has_many :users_roles_relationships
has_many :user_accounts, :through => :users_roles_relationships, :source => :user_account
end
And is used thusly:
def add_subscription_plan_roles_to_pb_user_account(pb_user_account)
roles_granted = pb_user_account.user.subscriptions.first.subscription_plan.roles_granted
pb_user_account.user_roles = roles_granted
end
Thanks a ton folks for helping me get this train moving again! All my tests are passing and it seems to be working, but if you see something wrong, please do still let me know.
Thanks!
Gerald
Try doing some manual saves of the objects created in various places, such as the create_pushbroom_user_account(pb_user) method. I have had some issues in the past when relying on the "autosave" system.