I'm developing a Rails 3.2.13 app using Devise and Rolify, and I need to have 3 user types as follows:
class User < ActiveRecord::Base
rolify
...
end
class UserTypeOne < User
....
end
class UserTypeTwo < User
....
end
class UserTypeThree < User
....
end
When I try to seed my db, que creation of users works ok, but it gives an error when trying to add a role to any of them:
user = UserTypeOne.find_or_create_by_email :name => 'User one', :email => 'userone#test.com', :password => 'userone', :password_confirmation => 'userone'
user.confirm!
user.add_role :admin
rake aborted!
undefined method `find_or_create_by' for nil:NilClass
But the user is inserted correctly... What am I doing wrong?
Thanks in advance!
Try adding
self.adapter = User.adapter
To each of the inherited classes.
Example:
class UserTypeOne < User
self.adapter = User.adapter
....
end
This worked for me.
When using STI in ActiveRecord, Rolify only knows to look in the current class
and not the parents.
Here is the issue:
https://github.com/EppO/rolify/issues/138
Ethans answer above should work but Here is the answer in the Rolify FAQ.
Personally, I don't like either of those solutions.
Perhaps Rolify could pass the adapter along when the class is inherited. Something like:
def self.inherited(klass)
klass.adapter = self.adapter
super # EDIT: added super call to prevent ActiveRecord error (NoMethodError: undefined method `synchronize' for nil:NilClass)
end
Related
I'm trying to get Clearance to work with AWS Dynamo as the back-end store. The problem I'm having is that I can't get Clearance to not do the email-uniqueness validation, which it can't do because it's not able to do standard ActiveRecord uniqueness validations via a SQL query.
According to the comments in the code, I should be able to have my User object return email_optional? true, and that should disable the uniqueness validation on emails. So I have:
class User < ApplicationRecord
include Dynamoid::Document
include Clearance::User
field :name
field :email
def email_optional?
puts 'yes, email is optional'
true
end
end
But, when I try to create a user I get an error, and, more to the point, the puts is not executed:
$ rails c
Running via Spring preloader in process 18665
Loading development environment (Rails 5.1.3)
irb(main):001:0> u = User.new(name: 'ijd', email: 'ian#fu.bar', password: 'test')
ActiveRecord::StatementInvalid: Could not find table 'editor_development_users'
from (irb):1
Update: the reply from #spickermann reminded me that I should have noted that I also tried without subclassing ActiveRecord::Base (via ApplicationRecord). It gives a different error:
class User
include Dynamoid::Document
....
irb(main):002:0> reload!
Reloading...
=> true
irb(main):003:0> u = User.new(name: 'ijd', email: 'ian#fu.bar', password: 'test')
ArgumentError: Unknown validator: 'UniquenessValidator'
from app/models/user.rb:4:in `include'
from app/models/user.rb:4:in `<class:User>'
from app/models/user.rb:2:in `<top (required)>'
from (irb):3
User.new does not trigger validations. Therefore the error cannot be connected to the validations itself.
At the moment your User model is kind of both: A subclass of ActiveRecord::Base and it behaves like a Dynamoid::Document.
class User < ApplicationRecord
include Dynamoid::Document
# ...
end
ActiveRecord::Base reads the table definition from the database when an instance is initialized. This leads to your exception, because the table does not exist. Just remove the inheritance from ApplicationRecord.
class User
include Dynamoid::Document
# ...
end
The second issue when you remove the inheritance is more complex. Usually, I would suggest to just include ActiveModel::Validations when you want to validate models that do not inherit from ActiveRecord::Base. But the UniquenessValidator isn't defined in ActiveModel::Validations but in ActiveRecord::Validations (what makes kind of sense). This makes Clearance incompatible with models that do not inherit from ActiveRecord::Base.
I would probably define a dummy implementation of a UniquenessValidator as a work-around:
class User
include Dynamoid::Document
class UniquenessValidator
def initialize(_options); end
def def validate_each(_record, _attribute, _value); end
end
# ...
end
When running a test, if I try to create a new object using User.new I get an error. If instead I use User.new({}), it works fine.
Isn't params supposed to be defaulted to empty if not passed in?
$ rails -v
Rails 5.0.0.1
user.rb
class User
include ActiveModel::Model
attr_accessor :name, :email, :country
end
user_test.rb
require 'test_helper'
class User < ActiveSupport::TestCase
test "should create an empty user when initialized with no params" do
user = User.new
assert_not_nil(user, 'user cannot be empty')
end
end
test result
Error:
User#test_should_create_an_empty_user_when_initialized_with_no_parameters:
ArgumentError: wrong number of arguments (given 0, expected 1)
test/models/user_test.rb:7:in `new'
test/models/user_test.rb:7:in `block in <class:User>'
Generally, attr_accessor is used on a model for columns that are not actual columns in the SQL table.
So if your model has columns :name, :email and :country, then declaring attr_accessor is unnecessary. I think rails is waiting for you to declare them now.
Try commenting out the attr_accessor :name, :email, :country line, then re-run your test.
This may help
Instead of including model class extend it like:
class Crime < ApplicationRecord
//do your stuff here
end
Then you can use #user = User.new
The User you're referencing in the test is the test itself.
The easiest solution is to follow Ruby convention and name the test class UserTest.
I'm using Devise and Papertrail and would like to display which user made the most recent update (documentation link).
In controller:
def show
# #history = #person.versions.last
#last_change = #person.versions.last
#user_who_made_the_change = User.find #last_change.whodunnit.to_i
end
In show page
<%= #user_who_made_the_change %>
However I get the resulting error:
undefined method `whodunnit' for nil:NilClass
in app/controllers/people_controller.rb:15:in `show'
Any help would be much appreciated, thanks
I prefer to modify initializer to kept template clean.
config/initializers/paper_trail.rb
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version < ::ActiveRecord::Base
belongs_to :user, foreign_key: :whodunnit
end
end
history.html.erb
<% #versions.each do |version| %>
<%= version.user.email %>
<% end %>
There are many reasons for omit save version data (configuration, save without callbacks, save from console ...)
I think you must check if you have the information to show:
def show
#last_change = #person.versions.last
#user_who_made_the_change = (User.find(#last_change.whodunnit.to_i) if #last_change && #last_change.whodunnit) || nil
end
This must do the trick.
I've done a bit of googling this evening about this topic. I simply wanted to be able to write something like #post.versions.last.user and get the devise User record (ie the person whodunnit that version).
The way that worked for me was creating the paper_trail.rb initializer with
PaperTrail::Rails::Engine.eager_load!
module PaperTrail
class Version < ActiveRecord::Base
include PaperTrail::VersionConcern
belongs_to :user, foreign_key: :whodunnit
end
end
Some solutions gave me errors including json col not set or something and/or undefined methodtimestamp_sort_order'`.
This is just what worked for me.
Other options included creating this in your app/models which I liked the idea of but never tried cause this 'worked' and I have other code I want to write :)
Wondering if someone can help me find this issue. I'm using rails 4, ruby 2, and have spent alot of time trying different accessors, etc and nothing has worked.
The whole plan model:
class Plan < ActiveRecord::Base
has_many :users
end
Some of the user model:
class User < ActiveRecord::Base
...
validate :plan_type_valid
belongs_to :plan
...
def plan_type_valid
if free_ok
# the following line causes error
valid_plans = Plan.where(price_tier: plan.price_tier).pluck(:id)
else
valid_plans = Plan.where(price_tier: plan.price_tier).where.not(stripe_id: 'free').pluck(:id)
end
unless valid_plans.include?(plan.id)
errors.add(:plan_id, 'is invalid')
end
end
end
Here's a pastebin of the whole users controller:
http://pastebin.com/GnXz3R8k
the migration was all messed up because of a superuser issue and it wasn't able to create the extensions for hstore field type.
I've read this article, but it's for Rails 1.x.
I'd really like to create my own association methods:
user = User.find(1)
# Example of a normal association method
user.replies.create(:body => 'very informative. plz check out my site.')
# My association method
user.replies.find_by_spamminess(:likelihood => :very)
In Rails 3, what's the proper way of doing this?
The Rails 3 way of doing things is often to not use find methods, but rather scopes, which delays the actual database call until you start iterating over the collection.
Guessing at your first example, I would do:
in class Reply ...
scope :spaminess, lambda {|s| where(:likelyhood => s) }
and then using it:
spammy_messages = user.replies.spaminess(:very)
or to use it in a view
spammy_messages.each do |reply|
....
end
I think I found it!
If you search for "association extensions" the Rails API page for ActiveRecord::Assications, you'll see that this is the syntax (copied from that link):
class Account < ActiveRecord::Base
has_many :people do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by_first_name_and_last_name(first_name, last_name)
end
end
end