ActiveModel::Model error when initializing class with no params - ruby-on-rails

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.

Related

How do disable email validation in Clearance

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

Trying to understand why validates_presence_of test fails

I'm just starting to have a play with Rails (using Rspec and Shoulda Matchers) to build a demo blog.
I've literally just started and my first test is failing but I can't understand why.
I think I've set up everything correctly, but when I try to validate that a title is present on my Article model but it returns a failure
Shoulda::Matchers::ActiveModel::AllowValueMatcher::AttributeDoesNotExistError:
The matcher attempted to set :title on the Article to nil, but that
attribute does not exist.
My model looks like this...
class Article < ApplicationRecord
# attr_accessor :title
validates_presence_of :title
end
and my test...
require 'rails_helper'
RSpec.describe Article do
it { should validate_presence_of :title }
end
If I uncomment out the attr_accessor then the test passes but I understand that it's not required with Rails.
What am I doing wrong?
The attr_accessor is not required, as long as your articles database table has a title column.
You can check that in db/schema.rb.

Is it bad to create ActiveRecord objects in a constant?

I have some code where I create ActiveRecord objects as constants in my model like so:
class OrderStage < ActiveRecord::Base
validates :name, presence: true, uniqueness: true
DISPATCHED = find_or_create_by(name: 'Dispatched')
end
Each Order has an OrderStage:
class Order < ActiveRecord::Base
belongs_to :order_stage
end
Now this seems to work fine throughout the site, and in my integration tests. However it is breaking in my unit test. The following test
it 'lists all order stages' do
# using FactoryGirl
create(:order, order_stage: OrderStage::DISPATCHED)
expect(Order.all.map(&:order_stage)).to eq [OrderStage::DISPATCHED]
end
passes fine when I run it individually, or when I run just order_spec.rb. But when I run the whole test suite, or even just spec/models I get this error:
Failure/Error: expect(Order.all.map(&:order_stage)).to eq [OrderStage::DISPATCHED]
expected: [#<OrderStage id: 1, name: "Dispatched">]
got: [nil]
That error goes away if I write my test like so:
it 'lists all order stages' do
order_stage = OrderStage.find_or_create_by(name: 'Dispatched')
create(:order, order_stage: order_stage)
expect(Order.all.map(&:order_stage)).to eq [order_stage]
end
So it must be something to do with creating the ActiveRecord object in the constant, it this a bad thing to do?
You should use class method.
attr_accessible :name
def self.dispatched
#dispatched ||= find_or_create_by_name('Dispatched')
end
private
def self.reset!
#dispatched = nil
end

Unable to initialize ActiveRecord object

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

how attr_accessor works in ActiveResource rails 3?

How does attr_accessor works in ActiveResource?
class User < ActiveResource::Base
attr_accessor :name
end
How its different from attr_accessor in ActiveRecord?
attr_accessor is built into Ruby, not rails. You may be confusing it with attr_accessible, which is part of ActiveRecord. Here's the difference:
attr_accessor
Take a class:
class Dog
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
self.first_name = first_name
self.last_name = last_name
end
end
attr_accessor creates a property and creates methods that allow it to be readable and writeable. Therefore, the above class would allow you to do this:
my_dog = Dog.new('Rex', 'Thomas')
puts my_dog.first_name #=> "Rex"
my_dog.first_name = "Baxter"
puts my_dog.first_name #=> "Baxter"
It creates two methods, one for setting the value and one for reading it. If you only want to read or write, then you can use attr_reader and attr_writer respectively.
attr_accessible
This is an ActiveRecord specific thing that looks similar to attr_accessor. However, it behaves very differently. It specifies which fields are allowed to be mass-assigned. For example:
class User
attr_accessible :name, :email
end
Mass assignment comes from passing the hash of POST parameters into the new or create action of a Rails controller. The values of the hash are then assigned to the user being created, e.g.:
def create
# params[:user] contains { name: "Example", email: "..."}
User.create(params[:user])
#...
end
For the sake of security, attr_accessible has to be used to specify which fields are allowed to be mass-assigned. Otherwise, if the user had an admin flag, someone could just post admin: true as data to your app, and make themselves an admin.
In summary
attr_accessor is a helper method for Ruby classes, whereas attr_accessible is an ActiveRecord thing for rails, to tighten up security.
You don't need to have attr_accessor to work with ActiveResource.
The base model (ActiveResource::Base) contains the #attributes hash in which you can 'dump' properties as you wish. (you should be careful though on what params you allow)
The way it does this, is by handling the method_missing? method.
You can take a look here
If you define attr_accessor, what ruby does is that it creates a setter and a getter method, so it will break the method_missing functionality since it will never get to execute that code.
If you still want to use attr_accessor, you should create a Concern something like this:
module Attributes
extend ActiveSupport::Concern
module ClassMethods
def attr_accessor(*attribs)
attribs.each do |a|
define_method(a) do
#attributes[a]
end
define_method("#{a}=") do |val|
#attributes[a] = val
end
end
end
end
end

Resources