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
Related
Im buliding an app with users model which can have different privileges, described by user_types model (id and type as a string). As I am running rails test, I am getting error
LoadError: Unable to autoload constant User_type, expected app/models/user_type.rb to define it
Here is my model:
class UserType < ActiveRecord::Base
belongs_to :user
validates :type, presence: true, length: { maximum: 100 }
end
Below is the controller and test file
class UserTypeController < ApplicationController
def index
#user_type = User_type.all
end
def new
#user_type = User_type.build(user_type_params)
end
private
def user_type_params
params.require(:user_type).permit(:type)
end
end
Testing model:
require 'test_helper'
class UserTypeTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
def setup
#user_type = User_type.new(id:2,type:"test")
end
test "is_valid" do
assert #user_type.valid?
end
end
I wanted to do some basic "is_valid" test and I got an error described above. I have also dropped last model "user_type" and created it as "UserType" but It didn't work.
Throughout your controller and test you use User_type.all, User_type.build, User_type.new, but your model is named UserType.
Your model's class name is UserType. User_type is not defined anywhere. You either keep the model name as is and fix your test and controller, or rename the model name. The former is the Rails convention, so I'd suggest you go with that (the CamelCased name).
Change your model name to UserType as defined in class and from that UserType model's table remove type column or rename it to something like user_type since that is a reserved column name, can be used for single table inheritance only.
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 trying to do in a rails console
>> user = User.new(:name => "", :email => "test#example.com")
=> #<User not initialized>
My User class looks like
class User < ActiveRecord::Base
attr_accessor :name, :email
has_many :microposts
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
end
def formatted_email
"#{#name} <#{#email}>"
end
end
I am following along from the rails tutorial. Why am I not able to initialize the object ?
tl;dr: Copy exactly from the book and you should be fine. (Note: I am the author.)
The example in question is from Chapter 4 of the Ruby on Rails Tutorial book and is not an Active Record model. In particular, the User class shown in the question is based on Listing 4.9:
class User
attr_accessor :name, :email
def initialize(attributes = {})
#name = attributes[:name]
#email = attributes[:email]
end
def formatted_email
"#{#name} <#{#email}>"
end
end
This class does not inherit from ActiveRecord::Base, but rather must be included explicitly in the console using require './example_user.rb', as described in Section 4.4.5. The behavior you're seeing is the result of including < ActiveRecord::Base in the first line, but if you copy the code in Listing 4.9 exactly you should see the expected behavior.
are you running your console in the same file directory as your project? I'd also try switching up theĀ notation to the example used in the book and see if that gets you anywhere.
you can also try calling User.new with no attributes and see if it generates an object as listed in 6.1.3 of the tutorial , and then fill in the attributes and see if it works.
also make sure you dont have a validation on your user name in your model.
and a last check you can run user.error to see why it might not be saving
First, I assume that User model persists in your Rails app. That means, that you already have a migrated User model before running rails console.
If that table doesn't exist, you will be instanly prompted with:
=> User(Table doesn't exist)
Now, let's have some fun in rails console:
First things first, don't override initialize method in Rails model; While creating an object initialize method from ActiveRecord takes precedence (I think), so it may create conflicts. Instead use after_initialize callback. In console:
class User < ActiveRecord::Base
attr_accessible :name, :email
def after_initialize(attributes = {})
self[:name] = attributes[:name]
self[:email] = attributes[:email]
end
def formatted_email
"#{self.name} <#{self.email}>"
end
end
Now,
u = User.new({name: "Foo", email: "foo#bar.org"})
#<User name: "Foo", email: "foo#bar.org", created_at:nil updated_at: nil>
u.formatted_email
#=> "Foo <foo#bar.org>"
All done! Sweet.
UPDATE:
As per your recent gist; I see no point of having after_initialize at all. Rails does that on it's own.
First thing first, replace attr_accessor with attr_accessbile.
attr_accessor is ruby method(courtesy, metaprogramming) which creates getter and setter for provided instance variable. Rails uses attr_accessible for that; for security concerns, only instance variables allowed in attr_accessible allowed for mass-assignment (by sending params hash).
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email
#def after_initialize(attributes = {})
# self[:name] = attributes[:name]
# self[:email] = attributes[:email]
#end
def formatted_email
"#{self.name} <#{self.email}>"
end
end
Are you running console using the rails c command to load your environment from the root directory of your project? Typing irb to start a console session does not load the Rails application environment by itself.
Here are some more troubleshooting tips
Check to make sure that the development database specified in config/database.yml is running
Check to make sure a migration exists to create the Users table
Check to make sure the migrations have run with rake db:migrate
Check to make sure that a Users table actually does exist in the database, with columns of type varchar (or text) for fields :name and :email
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
This is a two part question.
Part one if my Model that i am writing the validations for is inhering from ActiveRecord::Base do i need to include ActiveModel::Validations within that class?? the API for rails doesnt say but here in yehudakatz blog seems to hint that?
part two is where would be the best place to put these validator files? under helpers or as a new model or in lib?
my current validator looks like this
class GenderValidator < ActiveModel::validator
def validate(record)
cred = /(\d{6})(\d{4})(\d{1})(\d{2})/.match(record.id_number.to_s) #breaks up the id into the relevent sections namely birthdate, gender, nationality, validator.
unless cred[0][/\d{13}/] #checks to see if the id is a valid length of numbers if it isnt then skip the validation of gender
return true
else
birthdate = cred[1] #returns a 6 digit string 'yymmdd'
parsed_gender = cred[2] #returns a 4 digit string '0001-4999:female 5000-9999:male'
nationality = cred[3] # should return either a 1 or a 0 1 if the person is foreign or 0 if the person is southafrican
validate_gender(parsed_gender, record)
end
end
private
def validate_gender(parsed_gender, record)
calculate_gender = (parsed_gender <= 4999 ? :female : :male)
unless employee.gender == calculate_gender
employee.errors[:gender] << "Your id indicates you have entered the wrong gender"
end
end
end
an valid id number of each person is optional, but if they do specify it it should check to see if the gender is correct.
if i keep it in the same model the employees model then i get this error
ActionController::RoutingError (uninitialized constant Employee::GenderValidator):
app/models/employee.rb:25:in `<class:Employee>'
app/models/employee.rb:1:in `<top (required)>'
lib/role_requirement_system.rb:19:in `inherited'
app/controllers/employees_controller.rb:1:in `<top (required)>'
librato-rails (0.8.1) lib/librato/rack/middleware.rb:12:in `call'
so i take it they cant be in the same file. what is the best practice for validations? i watched all the rails casts and i have read a few blogs and i am quite new still.
EDIT
in my model i include this class like
include ActiveModel::Validations
and my validations look like this
validates_presence_of :name, :position, :gender
validate :instance_validations, :on => :create
def instance_validations
validates_with GenderValidator
end
just incase you wanted to see that too
thanks ahead!
You do not need include ActiveModel::Validations
My preference to to keep the validation objects in the model folder.
So for model Gender you have a file gender.rb
For the validator GenderValidator you have the file gender_validator.rb
So both files site together in the model folder.
Here is a validator for my Newsletter model
class NewsletterValidator < ActiveModel::Validator
def validate(record)
if record.send_test_email
if test_email_address.blank?
record.errors[:test_email_address] << "Test email address is blank"
end
if record.send_email_to_subscribers
record.errors[:send_test_email] << "You cannot send a test and send to subscribers at the same time"
end
end
end
end
In my Newsletter model I simply have
validates_with NewsletterValidator
You have a spelling mistake in your example
You have
class GenderValidator < ActiveModel::validator
It should be
class GenderValidator < ActiveModel::Validator
Note the capital V