User model test error from inserting into Group table - ruby

I'm just starting out with tests. When I run this one:
rake test test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase
test "should not save without an email address" do
user = User.new
assert_not user.save
end
end
I get the following error:
1) Error:
UserTest#test_should_not_save_without_an_email_address:
ActiveRecord::StatementInvalid: SQLite3::ConstraintException: NOT NULL constraint failed: groups.name: INSERT INTO "groups" ("created_at", "updated_at", "id") VALUES ('2015-08-11 17:31:07', '2015-08-11 17:31:07', 980190962)
This is user.rb
class User < ActiveRecord::Base
has_many :groups
has_many :user_groups
attr_accessor :password
EMAIL_REGEX = /A[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\z/i
validates :password, :confirmation => true #password_confirmation attr
validates_length_of :password, :in => 6..20, :on => :create
validates :email, :presence => true, :uniqueness => true, :format => EMAIL_REGEX
before_save :encrypt_password
after_save :clear_password
def encrypt_password
if password.present?
self.salt = Digest::SHA1.hexdigest("# We add {self.email} as unique value and #{Time.now} as random value")
self.encrypted_password = Digest::SHA1.hexdigest("Adding #{self.salt} to {password}")
end
end
def clear_password
self.password = nil
end
end
This is test_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Add more helper methods to be used by all tests here...
end
As far as I can tell I don't have any callback or otherwise that would attempt to write to the "groups" table. My "groups.yml" is default, but that shouldn't matter if I'm only testing this one model, correct? Any help as to where I could start looking would be much appreciated. Thanks!

test_helper.rb was setting up all my fixtures and they weren't defined. Commenting "fixtures :all" fixed it.

Related

rom-rb form validation when using multiple relations

I'm trying out http://rom-rb.org/ and can't figure out how to get a presence validation to pass in the presence of multiple source models. I would expect the following script to save a new event and organiser, but instead it says that event_name is not present.
What am I missing?
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rom'
gem 'rom-sql'
gem 'rom-rails'
gem 'activemodel'
gem 'sqlite3'
gem 'activesupport'
end
require 'rom'
require 'rom-rails'
`rm -Rf /tmp/romtest.sqlite`
ROM.setup(:sql, 'sqlite:///tmp/romtest.sqlite')
class Events < ROM::Relation[:sql]
end
class Organisers < ROM::Relation[:sql]
end
class CreateEvent < ROM::Commands::Create[:sql]
relation :events
register_as :create
result :one
associates :organiser, key: [:organiser_id, :id]
end
class CreateOrganiser < ROM::Commands::Create[:sql]
relation :organisers
register_as :create
result :one
end
class CreateEventWithOrganiser < ROM::Model::Form
commands organisers: :create, events: :create
input do
attribute :email
attribute :event_name
end
validations do
validates :event_name, presence: true
end
def commit!
command = organisers.create.with(
email: email,
) >> events.create.with(
name: event_name,
)
command.transaction do
command.call
end
end
end
ROM.finalize
rom = ROM.env
gateway = rom.gateways.fetch(:default)
migration = gateway.migration do
change do
create_table :organisers do
primary_key :id
column :email, String, null: false
end
create_table :events do
primary_key :id
column :name, String, null: false
column :organiser_id, Integer, null: false
end
end
end
migration.apply(gateway.connection, :up)
f = CreateEventWithOrganiser.build(
email: 'test#example.com',
event_name: 'Test Event'
)
# Unexpectedly fails
f.save
puts f.errors.full_messages
# => "Event name can't be blank"
Here's an updated version of your script which works:
require 'rom'
require 'rom-rails'
`rm -Rf /tmp/romtest.sqlite`
ROM.setup(:sql, 'sqlite:///tmp/romtest.sqlite')
class Events < ROM::Relation[:sql]
end
class Organisers < ROM::Relation[:sql]
end
class CreateEvent < ROM::Commands::Create[:sql]
relation :events
register_as :create
result :one
associates :organiser, key: [:organiser_id, :id]
end
class CreateOrganiser < ROM::Commands::Create[:sql]
relation :organisers
register_as :create
result :one
end
class CreateEventWithOrganiser < ROM::Model::Form
inject_commands_for :organisers, :events
input do
attribute :email
attribute :event_name
end
validations do
validates :event_name, presence: true
end
def commit!
validate!
return if errors.any?
command = organisers.create.with(
email: email
) >> events.create.with(
name: event_name
)
command.transaction do
command.call
end
end
end
ROM.finalize
rom = ROM.env
gateway = rom.gateways.fetch(:default)
migration = gateway.migration do
change do
create_table :organisers do
primary_key :id
column :email, String, null: false
end
create_table :events do
primary_key :id
column :name, String, null: false
column :organiser_id, Integer, null: false
end
end
end
migration.apply(gateway.connection, :up)
f = CreateEventWithOrganiser.build(
email: 'test#example.com',
event_name: 'Test Event'
)
puts f.save.result.inspect
# #<ROM::Commands::Result::Success:0x007fa92b589ea0 #value={:id=>1, :name=>"Test Event", :organiser_id=>1}>
The reason why it didn't work with commands is because this method will generate command objects for your form and set provided validations for each command, which will only work correctly if you used a single command. Otherwise same validator is used for each command which doesn't make sense. When you use inject_commands_for it will grab your own commands where validators are not set so you are free to handle validations yourself.
I think we should stop setting validators on commands which would make your original sample work but notice that you need to call validate! yourself.
I hope this helps.
I also created a gist showing how to do the same without a form: https://gist.github.com/solnic/3b68342482cf1414f719

How to avoid to update record in self referenced model due to not refers to itself and also not to be duplicate references

I have self referenced model:
class Component < ActiveRecord::Base
has_and_belongs_to_many :allergens
has_many :cqnames, as: :cqnable
has_many :inclusions
has_many :ingredients, :through => :inclusions
accepts_nested_attributes_for :cqnames, allow_destroy: true
accepts_nested_attributes_for :ingredients
accepts_nested_attributes_for :inclusions, allow_destroy: true
translates :name, :description
validates :name, presence: true, uniqueness: true
def self.get_children(ing)
#tree ||= []
if ing.ingredients.nil?
return #tree
else
#tree << ing.ingredients
get_children(ing.ingredients)
end
end
end
I have to avoid self referencing to the same record and also to the records already referenced when the respecting record is updating.
Experiment with before_validation...:
class Component < ActiveRecord::Base
before_validation :hokus
has_and_belongs_to_many :allergens
has_many :cqnames, as: :cqnable
has_many :inclusions
has_many :ingredients, :through => :inclusions
has_many :inverse_inclusions, :class_name => "Inclusion", :foreign_key => "ingredient_id"
has_many :composites, :through => :inverse_inclusions, :source => :component
accepts_nested_attributes_for :cqnames, allow_destroy: true
accepts_nested_attributes_for :ingredients
accepts_nested_attributes_for :inclusions, allow_destroy: true
translates :name, :description
validates :name, presence: true, uniqueness: true
require 'set'
def self.pokus(ing)
avoid = Set.new([self])
q = true
if ing.ingredients.present?
ing.ingredients.each do |record|
q = false if avoid.include? record
end
end
q
end
def hokus
Component.pokus(self)
end
end
If you want a simpler version that should get the job done then this may do.
require 'set'
def self.get_children(ing)
tree = Set.new
recursive = lambda {|recs|
recs.ingredients.each do |record|
recursive.call(record.ingredients) if tree.add?(record)
end
}
recursive.call(ing.ingredients)
tree.to_a
end
I think this is a bit of a mess. But I'll give you a pointer on how you may build your list without duplicates.
require 'set'
def self.get_children(ing)
tree = Set.new
avoid = Set.new([self])
recursive = lambda {|recs|
recs.ingredients.each do |record|
pass = false
if avoid.include? record
pass = true
else
tree << record
avoid << record
end
recursive.call(record.ingredients) unless pass
end
}
recursive.call(ing.ingredients)
tree.to_a
end
I've done a fare bit of recursive work like this so I believe that will do what you're hoping to do. Although I haven't tested this particular piece of code.
If you'd like to see a depth first record duplication method I've written you can see it in my gem poly_belongs_to . I use a variation on Set I built which basically does the same thing I demonstrated here with the avoid Set. I explain Set in my blog post here Different Collection Types in Ruby

Uninitialized constant (NameError) when using FactoryGirl in module

Here's the error I'm getting when I try to run my tests with RSpec:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/activesupport-3.2.11/lib/active_support/infl
ector/methods.rb:230:in `block in constantize': uninitialized constant User (Nam
eError)
I'm trying to run FactoryGirl with RSpec but without Rails. Here are the files that take part in the testing:
user_spec.rb
require 'spec_helper'
module Bluereader
describe User do
describe 'login' do
user = FactoryGirl.build(:user)
end
describe 'logout' do
end
describe 'create_account' do
end
describe 'delete_account' do
end
end
end
spec/spec_helper
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'rspec'
require 'lib/bluereader'
require 'factory_girl'
FactoryGirl.find_definitions
spec/factories.rb
require 'digest/sha1'
FactoryGirl.define do
sequence(:username) { |n| "user-#{n}" }
factory :user do
username
encrypted_password Digest::SHA1.hexdigest('password')
full_name 'John Doe'
logged_in_at Time.now
logged_out_at 0
end
end
At this point I know that the factories.rb file is being loaded (I tried with the moronic print-debugging). When I remove the user = FactoryGirl.build(:user) line from user_spec.rb I get no errors (and the normal RSpec feedback telling me there are no tests, but no errors). If you are interested, here's my model:
require 'digest/sha1'
module Bluereader
class User < ActiveRecord::Base
has_many :categories, :foreign_key => :user_id
has_many :news, :foreign_key => :user_id
has_many :settings, :foreign_key => :user_id
attr_reader :full_name
class << self
def login(username, password)
encrypted_password = Digest::SHA1.hexdigest(password)
if not User.exists?(:username => username, :encrypted_password => encrypted_password)
user_id = User.id_from_username(username)
update(user_id, :logged_in_at => Time.now, :logged_out_at => 0)
end
end
def logout
update(current_user.id, :logged_out_at => Time.now)
end
def validate_account(username, password, full_name)
if username.empty? or password.empty or full_name.empty?
return 'Please fill in all the fields.'
end
if User.exists?(:username => username)
return 'That username is already in use.'
end
unless username =~ /^\w+$/
return 'Username field should contain only letters, numbers and underscores.'
end
''
end
def create_account(username, password, full_name)
encrypted_password = Digest::SHA1.hexdigest(password)
User.create(:username => username,
:encrypted_password => encrypted_password,
:full_name => full_name,
:logged_in_at => Time.now,
:logged_out_at => 0)
end
def delete_account
current_user.destroy
end
private
def id_from_username(username)
user = where(:username => username).first
user.nil? ? 0 : user.id
end
def current_user
where(:logged_out_at => 0).first
end
end
end
end
SOLUTION
The problem was that the class User was in a module, here's the solution:
factory :user, class: Bluereader::User do
You need to require the rails environment in your spec helper file. Add the following to spec/spec_helper.rb:
require File.expand_path("../../config/environment", __FILE__)
Update
Even if you're not using Rails, you'll still need to require the models in your spec helper.
Taken from the bottom of the question
The problem was that the class User was in a module, here's the solution:
factory :user, class: Bluereader::User do
For anyone clumsy like me, you may have FactoryGirl in your code where you meant to have FactoryBot

Rails -- updating a model in a database

So I ran into a little issue with validations -- I created a validation to ensure that no users in a database share identical email addresses. Then I created a user in the database. Afterward, I said user = User.find(1) which returned the user I had just created. Then I wanted to change its name so I said user.name = "New Name" and then tried to use user.save to save it back into the database. However, this command isn't working anymore (it returns false instead) and I think it has to do with my uniqueness validation test. Can someone help me with this problem?
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime
# updated_at :datetime
#
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, #says that the name and email attributes are publicly accessible to outside users.
:password, :password_confirmation #it also says that all attributes other than name and email are NOT publicly accessible.
#this protects against "mass assignment"
email_regex = /^[A-Za-z0-9._+-]+#[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+[A-Za-z]$/ #tests for valid email addresses.
validates :name, :presence => true,
:length => {:maximum => 50}
validates :email, :presence => true,
:format => {:with => email_regex},
:uniqueness => {:case_sensitive => false}
validates :password, :presence => true,
:length => {:maximum => 20, :minimum => 6},
:confirmation => true
before_save :encrypt_password
def has_password?(submitted_password)
#compare encrypted_password with the encrypted version of the submitted password.
encrypted_password == encrypt(submitted_password)
end
def self.authenticate(email, submitted_password)
user = find_by_email(email)
if (user && user.has_password?(submitted_password))
return user
else
return nil
end
end
private
def encrypt_password
if (new_record?) #true of object has not yet been saved to the database
self.salt = make_salt
end
self.encrypted_password = encrypt(password)
end
def encrypt(string)
secure_hash("#{salt}--#{string}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string) #uses cryptological hash function SHA2 from the Digest library to encrypt the string.
end
def make_salt
secure_hash("#{Time.now.utc}--#{password}")
end
end
try save! and see what the exception tells you

how to autobuild an associated polymorphic activerecord object in rails 3

class ItemSource < ActiveRecord::Base
belongs_to :product, :polymorphic => true
end
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
end
What I'd like to do is is call:
a = RandomProduct.find(1)
a.item_source
and if item_source doesn't already exist (= nil), then build it automatically (build_item_source).
previously, I did this with alias_chain_method, but that's not supported in Rails 3.
oh, and I also tried this to no avail:
class RandomProduct < ActiveRecord::Base
has_one :item_source, :as => :product, :autosave => true, :dependent => :destroy
module AutoBuildItemSource
def item_source
super || build_item_source
end
end
include AutoBuildItemSource
end
In Rails 3, alias_method_chain (and alias_method, and alias) work fine:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
# This works:
#
# def profile_with_build
# profile_without_build || build_profile
# end
# alias_method_chain :profile, :build
#
# But so does this:
alias profile_without_build profile
def profile
profile_without_build || build_profile
end
end
But there's always accept_nested_attributes_for as an alternative, which calls build when profile_attributes are set. Combine it with delegate (optional) and you won't have to worry if the record exists or not:
class User < ActiveRecord::Base
has_one :profile, :inverse_of => :user
delegate :website, :to => :profile, :allow_nil => true
accepts_nested_attributes_for :profile
end
User.new.profile # => nil
User.new.website # => nil
u = User.new :profile_attributes => { :website => "http://example.com" }
u.profile # => #<Profile id: nil, user_id: nil, website: "http://example.com"...>
If the association is always created, delegation isn't necessary (but may be helpful, anyhow).
(Note: I set :inverse_of to make Profile.validates_presence_of :user work and to generally save queries.)
(Rails 4, FYI)
I personally prefer setting it up with after_initialize
after_initialize :after_initialize
def after_initialize
build_item_source if item_source.nil?
end
This also works well because you can automatically use forms with what would otherwise be an empty association (HAML because it's nicer):
= form_for #product do |f|
= f.fields_for :item_source do |isf|
= isf.label :prop1
= isf.text_field :prop1
If you didn't have the item_source built already, the label and text field wouldn't render at all.
How about creating the item_source when the RandomProduct is created:
class RandomProduct << ActiveRecord::Base
after_create :create_item_source
end
Of course, if you need to pass specific arguments to the item source, you could do something like this, instead:
class RandomProduct << ActiveRecord::Base
after_create :set_up_item_source
protected
def set_up_item_source
create_item_source(
:my => "options",
:go => "here"
)
end
end
Not that you really need a gem for this since it's so simple to do yourself, but here's a gem that makes it even easier to declare an auto-build:
https://github.com/TylerRick/active_record_auto_build_associations

Resources