FactoryGirl Association giving 'uninitialized constant' error - factory-bot

I have a problem with factory girl and associations.
NameError: uninitialized constant User::DefaultDeviceX
The users factory:
FactoryGirl.define do
factory :user do
name "MyString"
email "MyString"
password "MyString"
association :default_device_x, factory: :device
association :default_device_y, factory: :device
end
end
Executing FactoryGirl.create(:user) raises the mentioned error above.
So, what am i doing wrong?
User model references device:
belongs_to :default_device_x
belongs_to :default_device_y
Device factory
FactoryGirl.define do
factory :device do
name "Device 1"
comments "Comment"
end
end

Related

How can Accessing an Association change a field value

Today, I had a bug crop up in our Ruby-on-Rails application.
Somehow, the awsaccount_id field of our Device model is getting "marked for change" whenever we access the awsvpc association through the Device model.
To debug the problem, I added several log entries and several extra lines of code (as seen below) to diagnose how the device model instance is getting marked for change.
You may notice that the "dev.changes" method returns a pending change between logging "changes12" and "changes2". The only code that occurs between those log entries is accessing the association with dev.awsvpc.id
Under what circumstances can this ruby code:
Rails.logger.info "Update_Device changes0: #{dev.changes}"
sample_vpc = dev.awsvpc_id
Rails.logger.info "Update_Device changes1: #{dev.changes}"
sample_vpc = Awsvpc.find(dev.awsvpc_id)
Rails.logger.info "Update_Device changes12: #{dev.changes}"
sample_vpc = dev.awsvpc.id
Rails.logger.info "Update_Device changes2: #{dev.changes}"
Cause log entries that look like this:
Update_Device changes0: {}
Update_Device changes1: {}
[1m[36mAwsvpc Load (0.2ms)[0m [1m[34mSELECT "awsvpcs".* FROM "awsvpcs" WHERE "awsvpcs"."id" = $1 LIMIT $2[0m [["id", 644], ["LIMIT", 1]]
↳ app/lib/api/api_get_asset_result_builder.rb:53:in `populate_awssubnet'
Update_Device changes12: {}
[1m[36mCACHE Awsvpc Load (0.0ms)[0m [1m[34mSELECT "awsvpcs".* FROM "awsvpcs" WHERE "awsvpcs"."id" = $1 LIMIT $2[0m [["id", 644], ["LIMIT", 1]]
↳ app/lib/api/api_get_asset_result_builder.rb:55:in `populate_awssubnet'
Update_Device changes2: {"awsaccount_id"=>[66, 644]}
For anyone else who has this problem, I found the problem.
In the models, we had:
Device.rb
belongs_to :awsaccount, inverse_of: :devices, optional: true
belongs_to :awsvpc, inverse_of: :devices, optional: true
Awsvpc.rb
has_many :devices, :class_name => 'Device', dependent: :nullify, inverse_of: :awsaccount
Awsaccount.rb
has_many :devices, :class_name => 'Device', dependent: :nullify, inverse_of: :awsaccount
As you can see, the Awsvpc model should have used inverse_of :awsvpc
Once changed, everything works as expected.

Postgres foreign key violation error: insert or update on table... violates foreign key constraint

I have a web app in which I have the following relationship between two tables, a Topics and Categories table in which a Category has many Topics and a Topic belongs to a Category.
class Topic < ActiveRecord::Base
has_many :comments, dependent: :destroy
belongs_to :category
belongs_to :user
validates :subject, :body, :user_id, :category_id, presence: true
private
def self.find_by_id(params)
if params[:topic_id]
find(params[:topic_id])
else
find(params[:id])
end
end
def self.build_topic_comment(params, comment_params)
#topic = Topic.find_by_id(params)
#topic.comments.build(comment_params)
end
def self.load_comments(topic)
topic.comments.build
end
end
class Category < ActiveRecord::Base
has_many :topics, dependent: :destroy
belongs_to :user
validates :name, :user_id, presence: true
private
def self.find_by_id(params)
if params[:category_id]
find(params[:category_id])
else
find(params[:id])
end
end
def self.load_topics_desc(category)
category.topics.order(created_at: :desc)
end
def self.build_category_topic(params, topic_params)
#category = Category.find_by_id(params)
#category.topics.build(topic_params)
end
end
I added the functionality to move a topic to a different category by updating a topic's foreign key called category id. This is done trough an admin panel I coded myself and it is working correctly. When I checked the topics index page he category column is indeed updated.
The issue is that the rspec test I created is failing and giving me the following error:
Admin::TopicsController with administrator access PATCH #update with valid attributes updates a topic
Failure/Error: if #topic.update(topic_params)
ActiveRecord::InvalidForeignKey:
PG::ForeignKeyViolation: ERROR: insert or update on table "topics" violates foreign key constraint "fk_rails_d5d593e6f0"
DETAIL: Key (category_id)=(3) is not present in table "categories".
: UPDATE "topics" SET "subject" = $1, "body" = $2, "category_id" = $3, "updated_at" = $4 WHERE "topics"."id" = $5
# ./app/controllers/admin/topics_controller.rb:19:in `update'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:19:in `block in process'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:75:in `catch'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:75:in `_catch_warden'
# /Users/Beno/.rvm/gems/ruby-2.2.2/gems/devise-4.1.1/lib/devise/test_helpers.rb:19:in `process'
# ./spec/controllers/admin/topics_controller_spec.rb:79:in `block (5 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# PG::ForeignKeyViolation:
# ERROR: insert or update on table "topics" violates foreign key constraint "fk_rails_d5d593e6f0"
# DETAIL: Key (category_id)=(3) is not present in table "categories".
# ./app/controllers/admin/topics_controller.rb:19:in `update'
The rspec test file:
require 'rails_helper'
RSpec.describe Admin::TopicsController, type: :controller do
describe 'with administrator access' do
let(:valid_attributes) { attributes_for(:topic) }
let(:invalid_attributes) { attributes_for(:topic, category_id: nil) }
let(:updated_attributes) { attributes_for(:topic, category_id: 3) }
before(:each) do
#topic = create(:topic)
admin = create(:admin)
sign_in admin
end
describe 'GET #index' do
it 'renders the index template' do
get :index
expect(response).to render_template(:index)
end
it 'loads all the topics in the database' do
get :index
expect(assigns(:topics)).to eq([#topic])
end
end
describe 'GET #show' do
it 'renders the show template' do
get :show, id: #topic
expect(response).to render_template(:show)
end
it 'retrieves a topic from the database' do
get :show, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
it 'loads the topics comments' do
topic = create(:topic_with_comments)
get :show, id: topic
expect(topic.comments.length).to eq(5)
end
end
describe 'GET #edit' do
it 'renders the edit template' do
get :edit, id: #topic
expect(response).to render_template(:edit)
end
it 'retrieves a topic from the database' do
get :edit, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
end
describe 'PATCH #update' do
context 'with valid attributes' do
it 'finds a topic in the database' do
get :edit, id: #topic
expect(assigns(:topic)).to eq(#topic)
end
it 'updates a topic' do
patch :update, id: #topic, topic: updated_attributes
#topic.reload
expect(assigns(:topic)).to eq(3)
end
it 'redirects to admin topic path' do
patch :update, id: #topic, topic: updated_attributes
expect(response).to redirect_to(admin_topics_path)
end
end
context 'with invalid attributes' do
it 'does not update a topic'
it 're-renders the edit template'
end
end
describe 'GET #new' do
it 'renders the new template'
it 'creates a new topic'
end
describe 'PUT #create' do
context 'with valid attributes' do
it 'saves a topic in the database'
it 'redirects to created topic'
end
context 'without valid attributes' do
it 'does not save a topic in the database'
it 're-renders the new template'
end
end
describe 'DESTROY #delete' do
it 'finds a topic in the database'
it 'deletes a topic from the database'
it 'redirects to admin topics index'
end
end
end
The admin topics controller:
class Admin::TopicsController < Admin::BaseController
def index
#topics = Topic.all
end
def show
#topic = Topic.find_by_id(params)
end
def edit
#topic = Topic.find_by_id(params)
#topic_options = Topic.all.collect { |topic| [ topic.category.name, topic.category_id ] }.uniq
end
def update
#topic = Topic.find_by_id(params)
if #topic.update(topic_params)
redirect_to admin_topic_path(#topic), notice: 'Topic updated successfully'
else
render :edit
end
end
private
def topic_params
params.require(:topic).permit(:subject, :body, :category_id)
end
end
Can someone point me in the right direction in regards to why the tests could be failing and the app is working correctly?
Thanks!

Operator does not exist: jsonb = integer

I am getting the following postgres error:
"operator does not exist: jsonb = integer"
I have my application hosted on Heroku and am using a Postgres 9.4 DB. Below is what my accounts controller looks like with the ahoy_matey analytics gem.
Accounts Controller
class AccountsController < ApplicationController
before_filter :authenticate_user!
def index
if current_user.business_id
#business = Business.find(current_user.business_id)
# analytics
#monthly_business_views = ahoy_event_sum("Page Viewed", #business.id)
#monthly_get_websites = ahoy_event_sum("Visited Website", #business.id)
#monthly_get_directions = ahoy_event_sum("Directions Viewed", #business.id)
#monthly_get_fb_shares = ahoy_event_sum("Shared via Facebook", #business.id)
#monthly_get_tweets = ahoy_event_sum("Tweeted via Twitter", #business.id)
#reviews = Review.where(:business_id => #business).all
end
end
...
def ahoy_event_sum(event, business_id)
Ahoy::Event.where(name:event, properties: business_id).count
end
end
Ahoy Events Model
module Ahoy
class Event < ActiveRecord::Base
self.table_name = "ahoy_events"
belongs_to :visit
belongs_to :user
serialize :properties, JSON
end
end
I have intermediate skills and apologize if this question has a simple fix, please provide any details on how I would solve this issue. Thanks!

ChangeSet Ecto.Model all fields of Struct are nil

I'm working on some Phoenix framework and i have encountered a weird problem (as usual). Whenever I try to create some Users, i get User with all fields set to nil. I'm using Mongo.Ecto/
def post_login(conn, %{"login" => login, "password" => password}) do
# IO.inspect Plug.Conn.read_body(conn)
a = User.changeset(%User{}, %{"login" => "login", "password" => "password"})
IO.inspect a
Repo.insert( a )
redirect conn, to: "/default"
end
And the model:
defmodule HelloWorld.User do
use HelloWorld.Web, :model
#primary_key {:id, :binary_id, autogenerate: true}
schema "users" do
field :login, :string
field :password, :string
end
#required_fields ~w()
#optional_fields ~w()
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
end
end
And the screen from console:
As you can see in the picture, both login and password fields are nils which makes me feel I've done something incredibly stupid.
The fields need to exist in the options to the cast/4 function:
#required_fields ~w()
#optional_fields ~w(login password)
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
end
Anything that is in required_fields but not in the params will add an error to that field on the changeset. If you want the fields to be required just move them to the required_fields list.

Rails 4, devise, omniauth, facebook: problems when regular sign up involves nested forms

I am trying to set up facebook login integration with omniauth and devise.
Note that I have a MEMBER model which contains the devise info and a USER model that contains the member profile info (to keep them separate)
On the sign up page, the user has a nested form in the member sign up form and receives all the user data at that time. Once the member is saved, the email value for the saved member is also entered into the user table like so
class Member < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :omniauthable
after_save :update_user
def update_user
user.email = self.email
user.save
end
I have the standard model method for the facebook data processing.....
def self.process_omniauth(auth)
where(provider: auth.provider, uid: auth.uid, email: auth.info.email).first_or_create do |member|
member.provider = auth.provider
member.uid = auth.uid
member.email = auth.info.email
end
end
But of course, in this case, there is no nested form so the 'user' is not instantiated and it throws an error when the update_user method is called on saving the member.
How can I modify this method above to instantiate a new user, when signing up via the facebook pathway?
EDIT: This works.....
def update_user
if User.find_by_member_id(self.id).present?
user.email = self.email
user.save
else
User.create(:email => self.email, :member_id => self.id)
end
end
BUT THIS RESULTS IN A CONSTRAINT ERROR - on email - it already exists in the database. From the logs, the else statement here appears to be attempting to create the user twice.
def update_user
if User.find_by_member_id(self.id).present?
user.email = self.email
user.save
else
User.create(:email => self.email)
end
end
Can anyone explain this? I am suprised I had to pass the foreign key to the user in the else block.
I'm not sure what fields are being stored in the User Model but what you should be doing here that in case of Facebook Callback you should create the User if the Member don't have an user associated with it. i.e your update_user should be something like this:
def update_user
if user.present?
user.email = self.email
user.save
else
User.create(:email => self.email, :member_id => self.id)
/*note that a lot of other information is returned in
facebook callback like First Name, Last Name, Image and
lot more, which i guess you should also start saving to
User Model */
end
end
**** EDIT ******** . You should also check if there is user with the same email and associate that with the member.
def update_user
if user.present?
user.email = self.email
user.save
else
user = User.find_by_email(self.email)
if user
user.update_attribute(:member_id, self.id)
else
User.create(:email => self.email, :member_id => self.id)
end
end