Rails ActiveRecord Ignoring Params and Saving Nil Data - ruby

This is stumping me; For some reason the db is saving the record with nil fields instead of my params. Can anyone help me understand why ActiveRecord isn't using my params?
db migration:
class CreateRoutes < ActiveRecord::Migration
def change
create_table :routes do |t|
t.integer :user_id
t.string :start_address
t.string :end_address
t.text :waypoints
t.text :schedule
t.integer :duration
t.timestamps
end
add_index :routes, :user_id
end
end
route.rb:
class Route < ActiveRecord::Base
attr_accessor :start_address, :end_address, :waypoints, :schedule, :duration
belongs_to :user
#serialize :waypoints, :schedule
validates :user_id, presence: true
validates :start_address, presence: true
validates :end_address, presence: true
validates :schedule, presence: true
validates :duration, presence: true, numericality: { only_integer: true, greater_than: 0 }
end
routes_controller.rb:
class RoutesController < ApplicationController
.
.
.
def create
logger.debug "\n\n*** #{params[:route]} ***"
#route = current_user.routes.build(params[:route])
logger.debug "*** The route is #{#route.inspect} ***\n\n"
if #route.save
flash[:success] = "Route saved!"
redirect_to user_path(current_user)
else
render 'new'
end
end
.
.
.
end
logger output:
*** {"start_address"=>"123 Sample St.", "end_address"=>"321 Elpmas St.", "waypoints"=>"None", "schedule"=>"Mondays", "duration"=>"15"} ***
*** The route is #<Route id: nil, user_id: 1, start_address: nil, end_address: nil, waypoints: nil, schedule: nil, duration: nil, created_at: nil, updated_at: nil> ***

The attr_accessors will overwrite the accessors generated by ActiveRecord, causing them to not be persisted in the DB--they'll be like plain old Ruby properties/members instead of the meta-programmed magic of ActiveRecord.
DB properties (persistent properties) can have things like attr_accessible, though.

Related

Sinatra and Ruby error: ActiveRecord::RecordNotUnique at /shoes/1

Good morning, I'm using Sinatra and Ruby to create a running app that logs your runs. I keep getting this error anytime I try to save a run:
ActiveRecord::RecordNotUnique at /shoes/1
Here is my controller for this route:
post '/shoes/:id' do
#shoe = Shoe.find(params[:id])
if logged_in?
#runs = #shoe.runs.build(params)
#newest_run = (#shoe.current_mileage.to_i + #runs.latest_run.to_i)
#shoe.current_mileage = #newest_run
#runs.save
flash[:message] = "A new run has been added!"
redirect to("/shoes/#{#shoe.id}")
else
redirect to("/login")
end
end
I've been trying to debug this all morning and can't find the reason as to why the run can't happen. I have 3 models as well: A user has_many shoes; a shoe belongs_to a user and has_many runs; a run belongs_to a shoe. Could there be an issue with the associations I have set up?
Edited: Here is my schema:
ActiveRecord::Schema.define(version: 20200708142633) do
create_table "runs", force: :cascade do |t|
t.integer "latest_run"
t.date "date"
t.string "location"
t.integer "shoe_id"
end
create_table "shoes", force: :cascade do |t|
t.string "name"
t.date "date"
t.integer "new_mileage"
t.integer "current_mileage"
t.integer "latest_run"
t.decimal "price", precision: 10, scale: 3
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.string "password_digest"
end
end
Edited #2:
Here are my params:
{"latest_run"=>"150", "location"=>"Kansas", "date"=>"2020-07-07", "id"=>"1"}
Here is are my Shoe and Run models:
class Shoe < ActiveRecord::Base
belongs_to :user
has_many :runs
validates :name, :date, :new_mileage, :current_mileage, :price, presence: true
end
class Run < ActiveRecord::Base
belongs_to :shoe
validates_presence_of :latest_run, :date, :location, :shoe_id
end
Thanks everyone!

before_action and nested attributes for models

The question is really simple.
Please, take a look at my schema:
ActiveRecord::Schema.define(version: 20170812094528) do
create_table "items", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "parent_id"
end
create_table "parents", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "item_id"
end
end
This is how models looks like:
class Parent < ApplicationRecord
has_one :item
accepts_nested_attributes_for :item
before_create :set_something
def set_something
self.item.build
end
end
and
class Item < ApplicationRecord
belongs_to :parent
end
The question: When creating a Parent, why does it raise the following error?
undefined method `build' for nil:NilClass
How should I set this up so that I can add and item record at the same time as creating a parent?
Try using
def set_something
build_item
end
The model Parent has_one :item. In Rails, the has_one association adds a helper method build_association(attributes = {}) to the owner class, and not association.build(attributes = {}). This is the reason that you are getting the error undefined method 'build' for nil:NilClass.
Please see the has_one documentation for more details on has_one association.

How can I combine two Rails 4 where queries that query either side of a boolean condition?

Question:
Is there a way to combine the following two queries (including the assignments) into one query? I'm not sure how much time I'd really save. In other words, I'm not sure if it is worth it, but I'd like to be as efficient as possible.
#contacts = #something.user.contacts.where.not(other_user_id: 0)
#user_contacts = #something.user.contacts.where(other_user_id: 0)
More context:
Here is my contacts table from schema.rb:
create_table "contacts", force: true do |t|
t.string "name"
t.string "email"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "profile_picture"
t.string "phone_number"
t.integer "other_user_id"
end
And here is the important stuff from the users table:
create_table "users", force: true do |t|
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
...
t.string "first_name"
t.string "second_name"
end
And here is the pertinent information of the models:
class Contact < ActiveRecord::Base
belongs_to :user
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :name, presence: true
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }
validates :user_id, presence: true
def get_email_from_name
self.email
end
end
[...]
class User < ActiveRecord::Base
has_many :contacts
has_many :relationships,
foreign_key: 'follower_id',
dependent: :destroy
has_many :reverse_relationships,
foreign_key: 'followed_id',
class_name: 'Relationship',
dependent: :destroy
has_many :commitments,
class_name: 'Commitment',
dependent: :destroy
has_many :followers,
through: :reverse_relationships
has_many :followed_users,
through: :relationships,
source: :followed
[...]
before_save { email.downcase! || email }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
validates :email,
presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
[...]
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
if create_contact?(self, id, other_user.id)
create_contact(other_user.name,
other_user.email,
self.id,
other_user.id,
other_user.gravatar_url)
elsif create_contact?(other_user, other_user.id, id)
create_contact(name, email, other_user.id, id, gravatar_url)
end
end
def create_contact?(user, followed_id, follower_id)
user.admin? && ! Relationship.where(followed_id: followed_id, follower_id: follower_id).empty?
end
def create_contact(name, email, user_id, other_user_id, profile_picture)
Contact.create!(name: name,
email: email,
user_id: user_id,
other_user_id: other_user_id,
profile_picture: profile_picture)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy
Contact.destroy_all(user_id: self.id, other_user_id: other_user.id)
end
[...]
end
The other contacts that may not have an account with the website (yet), and I'd like to distinguish that in the view. So I keep track of which contacts I import through Google contacts using the omniauth gem. For the other contacts, I gather the other users that are friends with current_user.
Goal:
I'd like to save these two record collections to use in the view, but I'd like to avoid looking through all the user's contacts twice, checking the same column in each pass-through.
Any ideas? I'm sure there are lots of ways this can be done, and I'd like to learn as much as I can from this! Thanks in advance!
You can use Array#partition to split up the array in memory, after the query was performed.
#user_contacts, #contacts = #something.user.contacts.partition{|u| other.id == 0 }
However checking for this magic 0 id is smelly. You should try to get rid of such special cases whenever possible.
It is not the best solution, but if you think partition hard to understand, it can be an optional.
#user_contacts, #users = [], []
#something.user.contacts.each do |record|
if record.other_user_id == 0
#user_contacts << record
else
#users << record
end
end

FactoryGirl ActiveRecord::RecordInvalid: Validation failed: Name has already been taken

I have three models, Course, Category and partner, a course can have many categories and a course belongs to one partner. When i create my course factory i get the following error:
Partner has a valid factory for course
Failure/Error: expect(FactoryGirl.create(:course)).to be_valid
ActiveRecord::RecordInvalid:
Validation failed: Name has already been taken
Here are my models:
class Category < ActiveRecord::Base
has_many :categorisations
has_many :courses, :through=> :categorisations
belongs_to :user
#validation
validates :name, presence: true , uniqueness: { scope: :name }
end
class Partner < ActiveRecord::Base
has_many :courses
belongs_to :user
validates :name, presence: true, uniqueness: { scope: :name }
validates :short_name, presence: true
VALID_HEX_COLOR= /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
validates :primary_color, presence: true, format: { with: VALID_HEX_COLOR}
validates :secondary_color, presence: true, format: { with: VALID_HEX_COLOR}
end
class Course < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
has_many :categorisations, :dependent => :destroy
has_many :categories, :through=> :categorisations
belongs_to :partner
belongs_to :user
# validates_uniqueness_of :title
validates :title, presence: true
# validates :start_date, presence: true
# validates :duration, presence:true
# validates :state, presence:true
validates :categories, length: { minimum: 1 , message:"please select"}
validates :partner_id, presence: true, allow_nil: false
end
Here are my factories:
factory :partner do |f|
f.name Faker::Name.name
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categorisation
end
factory :categorisation do |categorisation|
categorisation.category {|category| category.association(:category)}
categorisation.course {|course| course.association(:course)}
end
I am not to sure what i am doing wrong, if anyone could advise me on what the problem may be or the process i can go about fixing this problem may be that would be a great help
try this out:
factory :partner do |f|
f.sequence(:name) { |n| "#{Faker::Name.name} #{n}" }
f.short_name "UCT"
f.primary_color "#009bda"
f.secondary_color "#002060"
end
factory :category do |f|
f.sequence(:name) { |n| "Category #{n}" }
end
All that i had to do was to add the following line to my course factory:
categories {[FactoryGirl.create(:category)]}
couse factory:
factory :course do |f|
f.title "Introduction to Accounting short course"
f.start_date "2014-02-27 00:00:00"
f.duration "10 WEEKS ONLINE"
partner
categories {[FactoryGirl.create(:category)]}
end

Rails -- before_save not working?

I am following Michael Hartl's RoR tutorial, and it is covering the basics of password encryption. This is the User model as it currently stands:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email,: password, :password_confirmation
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
private
def encrypt_password
#encrypted_password = encrypt(password)
end
def encrypt(string)
string
end
end
(Obviously this isn't doing any encrypting because the encrypt method isn't really implemented but that's not my question)
I then wrote the following Spec (according to the tutorial):
require 'spec_helper'
describe User do
before(:each) do
#attr = { :name => "Example User", :email => "user#example.com",
:password => "abc123", :password_confirmation => "abc123"}
end
describe "password encryption" do
before(:each) do
#user = User.create!(#attr) # we are going to need a valid user in order
# for these tests to run.
end
it "should have an encrypted password attribute" do
#user.should respond_to(:encrypted_password)
end
it "should set the encrypted password upon user creation" do
#user.encrypted_password.should_not be_blank
end
end
end
The first of these tests passes, but since #user.encrypted_password is nil, the second test fails. But I don't understand why it's nil since the encrypt_password method should be being called by before_save. I know I must be missing something -- can someone please explain?
The encrypt_password method is incorrect, it should read:
def encrypt_password
self.encrypted_password = encrypt(password)
end
Note the use of self, which will properly set the attribute for the user object rather than creating an instance variable which is forgotten.
This is an old question and this is more of a comment but I don't have enough reputation to comment yet. Just wanted to link this question too as it goes into some solid detail about self.
Why isn't self always needed in ruby / rails / activerecord?

Resources