This should be easy but I'm obviously missing something. Working through a testing exercise, and attempting to create a method on the User model. Seems like it should be incredibly straightforward, but Rails is throwing method missing whenever I attempt to access the method.
Here's the code for the file app/models/user.rb:
class User < ActiveRecord::Base
has_secure_password
validates :email, presence: true, uniqueness: true
def administrator?
end
end
At this point I'm working through TDD process, and only attempting to satisfy the test exception by creating the method (I'll add code for the administrator? method later). Here's the line from the spec:
expect(admin.administrator?).to be_true
And the output from the test:
1) User management adds a new user
Failure/Error: expect(admin.administrator?).to be_true
expected to respond to `true?`
However, I can't seem to access the freaking method! Whenever I call the #administrator? method on an object of the User class I get:
NoMethodError: undefined method `administrator?' for #<User:0x007f9ac6929358>
Here's the pry response to 'ls -M' when I'm in the User class, showing the methods available:
User#methods: administrator password password_confirmation
Here's the output from a pry console session:
[73] pry(User):1> admin = User.first
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, email: "test#yahoo.com", password_digest: "secret123", created_at: "2015-06-25 17:33:25", updated_at: "2015-06-25 17:33:25", admin: true, firstname: nil, lastname: nil>
[74] pry(User):1> admin.valid?
User Exists (0.2ms) SELECT 1 AS one FROM "users" WHERE ("users"."email" = 'test#yahoo.com' AND "users"."id" != 1) LIMIT 1
=> true
[75] pry(User):1> admin.instance_of?(User)
=> true
[76] pry(User):1> admin.administrator?
NoMethodError: undefined method `administrator?' for #<User:0x007f9ac6929358>
from /Users/me/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activemodel-4.1.1/lib/active_model/attribute_methods.rb:435:in `method_missing'
I've tried defining the method as self.administrator as well, with the same results. I went back and looked at the model methods defined in the Rails Tutorial, and I can't see anything about Hartl's model that helps me understand why my method isn't working.
Any help would be appreciated! I'm missing something basic here it seems...
**** EDIT/UPDATE ****
After getting a response about how to return the boolean :admin column from the user object I realized that it's worth clarifying the real question. The real issue I was trying to understand isn't so much whether the administrator? method is the best way to determine whether a given user is an admin or not (as #sajinmp very helpfully points out below, the object will respond to admin? because of the corresponding column in the database).
My real question/frustration was with why I couldn't get the object to respond to that or any other methods created in the model.
If all you are doing in the method is to check whether a user is an admin or not you do not need such a specific method. Since in your user model the administrator is represented by admin simply calling admin? is enough.
current_user.admin (<admin.admin> in this case)
will answer your call. But when admin is nil this will be a problem so it is better to use
admin.try(:admin)
This will not throw an error even if admin is nil.
I restarted the Rails server, and that seemed to fix it. After cycling the server and starting a new console session, the user object now responds to the methods in user.rb.
Should have done that sooner (DUH!), but I'd assumed that it wouldn't be necessary. Within the console session I could use reload! and then the Pry show-source method would confirm that the methods were appearing in the console as written. I didn't think that the console relied on the server, but I must be wrong..?
Appreciate any confirmation on this.
Related
I have an object that saves a model and runs a background job.
Class UseCase
...
def self.perform
#account.save
BackgroundJob.perform_later(#account.id)
end
end
In my spec I'd like to test separately that both messages are sent.
I started with something like
it 'saves the account' do
expect_any_instance_of(Account).to receive(:save)
UseCase.perform(account)
end
And this worked fine when I was just saving the account in the perform.
But when I have added the background job the spec doesn't pass anymore since now Couldn't find Account without an ID.
How can I verify (in RSped 3.5) separately that both messages are sent?
UPDATE
it 'runs the job' do
expect(BackgroundJob).to receive(:perform_later).with(instance_of(Fixnum))
UseCase.perform(account)
end
passes so I suppose the account is correctly saved.
However, when I try to inspect #account
def self.perform
#account.save
byebug
BackgroundJob.perform_later(#account.id)
end
In 'saves the account', I get
(byebug) #account
#<Account id: nil, full_name: "john doe" ...>
In 'runs the job', I get
(byebug) #account
#<Account id: 1, full_name: "john doe" ...>
The expectation makes #account a test double so in the first spec the job cannot get the id.
Thanks
The error Couldn't find Account without an ID is actually pretty helpful considering the code that you have inside your perform method.
The issue is mentioned in the comments but I'll elaborate a bit further.
You are using #account.save (I'm assuming #account is an ActiveRecord object) which by definition will return true/false when run (see documentation)
What you probably want is to use save! instead since it will raise a ActiveRecord::RecordInvalid error and stop execution rather than triggering the error you noted earlier. (toss a binding.pry into the method and note what #account is when attempting to call .id)
When you change to save! you can add a test for a case where save might fail (missing attribute, etc). Might look something like this
it 'should raise error when trying to save invalid record' do
# do something to invalidate #account
#account.username = nil
expect { UseCase.perform(#account) }.to raise_error(ActiveRecord::RecordInvalid)
#confirm that no messages were sent
end
Hope this helps you out! GL and let me know if you have any questions / need more help with rspec
I'm developing my thesis on Padrino, using Active Record as ORM and FactoryGirl as Mocking framework.
I'm facing a strange behavior.
I've two models: User and Rate.
- User has a 'has_many :rates' association;
- Rate has a 'belongs_to :user' association;
- 'rates' table has an integer attribute named 'user_id' (not created with 'references' on migration, but directly with 'integer').
My association is working well, but only after performing a reload on parent object.
Here are the snippets related to this issue:
https://bitbucket.org/snippets/cuscos/MbdAK
If I start a 'Padrino Console' and create a user manually, this is the current behavior:
$ user = FactoryGirl.create(:user_with_rates)
$ user.rates.length # Received '0', but expected '1'
$ user.rates.all.length # Received '1', OK
$ user.reload!
$ user.rates.length # Now I'm receiving '1' correctly
It seems that ActiveRecord isn't performing the Lazy Loading for any reason.
Does anyone know why is this happening?
Thanks for all support so far.
For those who it may interest, here is the solution I'm adopting to solve this problem:
In User factory, instead:
after(:create) do |user, evaluator|
create_list(:rate, evaluator.rates_count, user: user)
end
Do:
after(:create) do |user, evaluator|
user.rates << create_list(:rate, evaluator.rates_count, user: user)
end
It's not a proper solution, but solved my problem for now.
Cheers o/
Rails 4.0.4, Ruby 2.1.2
I want to use STI like so:
User < ActiveRecord::Base
Admin < User
But currently I have:
User < ActiveRecord::Base
Info < ActiveRecord::Base
So I changed my models, and then start writing my migration. In my migration, I first add a column to allow STI:
add_column :users, :type, :string
Then I want to update the Users currently in the database to be Admin
# Place I'm currently stuck
Then I move all my Info records into the Users table
Info.all.each { |info| User.create(name: info.name, email: info.email) }
Everything seems to work except turning the previous Users into Admins. Here are some things I've tried:
# Seems to work, but doesn't actually save type value
User.each do |user|
user.becomes!(Admin)
user.save! # evaluates to true, doesn't have any errors
end
# Seems to work, but doesn't actually save type value
# I've also tried a combo of this one and the above one
User.each do |user|
user.type = "Admin"
user.save! # evaluates to true, doesn't have any errors
end
User.each do |user|
user = user.becomes!(Admin)
user.save! # evaluates to true, doesn't have any errors
end
# Seems to work, but doesn't actually save type value
User.each do |user|
user.update_attributes(type: "Admin")
end
Each time the local user variables seems to have the correct type ("Admin"), along with save evaluating to true, but when I check Admin.count or check Users type value, it is always nil. I know you're not supposed to change them, but this is just to migrate the data over to STI and then I'll be able to start creating Users or Admin with the proper class.
At the very least I think Rails should raise an error, set an error or somehow let the developer know it's failing the save calls.
It turns out that while update_attributes doesn't work for type (I haven't researched why yet), update_column does work.
So the migration simply becomes:
User.each do |user|
user.update_columns(type: "Admin")
end
The reason this works and other updates don't can probably be traced back to either callbacks or validations not being run. I have no callbacks that would prevent it, but maybe there are default Rails ones for type
http://apidock.com/rails/ActiveRecord/Persistence/update_columns
If you had more rows in the database User.each would become quite slow as it makes an SQL call for each user.
Generally you could use User.update_all(field: value) to do this in one SQL call but there is another reason to avoid this: if the User model is later removed the migration will no longer run.
One way to update all rows at once without referencing the model is to use raw SQL in the migration:
def up
execute "UPDATE users SET type = 'Admin' WHERE type IS NULL"
end
I'm working on a Padrino application that has a main App and an Admin app. When a user logins through session, I just run the following two lines to log him in.
account = Account.authenticate(params[:email], params[:password])
set_current_account(account)
With debugger in any controllers in Admin
current_account
#<Account #id=1 #name="John" #surname="Davies" #email="john.davies#gmail.com" #crypted_password="3456789" #role="admin">
With debugger in any models
current_account
*** NameError Exception: undefined local variable or method `current_account' for #<Post #id=1 #question="Why is this not working?" #answer="I have no idea">
I am able to access current_account to find out which user is logged in in the Admin app, but this variable is not accessible in the main App.
What I am trying to do is that I am creating an Activity Feed, or Audit Trail, for all my models, so when a record is created/updated/destroyed in any model, a new record is created in model Activity. That being said, I need to access the current_account variable in my models.
I have searched for solutions, and came across a suggestion:
In admin/app.rb
enable :sessions
set :session_id, "my-global-session"
In app/app.rb
register Padrino::Admin::AccessControl
register Padrino::Admin::Helpers
enable :sessions
set :session_id, "my-global-session"
It did not work for me. Is there a way I can access current_account in my models?
Thank you for any guide or suggestion.
I'm not exactly sure what are you trying to do, but I solved a similar problem like this:
class Account
# ...
class << self
attr_accessor :current
end
# ...
end
class Admin < Padrino::Application
#...
before do
Account.current = current_account
end
# ...
end
And then use Account.current to access it.
I'm learning RoR at the moment, and I think I must be misunderstanding something.
I have an ActiveRecord class call User, with simple validations on :name and :email such as presence: true, length: { maximum: 15 }, etc. I thought I'd check the validations in the console. I go into rails console (development env), and create a new instance with a name that is too long, such as
user_instance = User.new (name:"aaaaabbbbbcccccddddd", email:"").
The validation doesn't throw up any errors. When I try user_instance.save, the record won't write to the DB, so it's obviously working fine at that stage. What am I doing wrong?
When you want to get an exception raised on record saving, use save! instead of save (same with update/update!, create/create!).
With save you won't have an exception raised if there are validation errors, it will just return false. You can also check if there are errors on an instance with user_instance.valid? and get the errors with user_instance.errors.
See When Does Validation Happen?.
the validation won't throw errors if you try to set invalid data on your model, however the save will fail.
if you wanna check out if the validation is working correctly, just check user.valid? and it should return false
after calling valid?, you can check user.errors for the specific errors set on your model.