Rails single table inheritance validation - ruby-on-rails

There is a Request model in my app. On different pages I need different validations, for example on /contacts I need to validate a lot of fields, whereas in a 'call me back later' popup I need to validate only phone number and name.
My problem is: data is saved, but without validations and type is not saved aswell.
Structure:
request.rb
class Request < ApplicationRecord
self.inheritance_column = :_type_disabled
def self.types
%w(ContactRequest CallMeBackRequest)
end
scope :contacts, -> { where(type: 'ContactRequest') }
scope :callmebacks, -> { where(type: 'CallMeBackRequest') }
end
routes.rb:
resources :contact_requests, only: [:new, :create], controller: 'requests', type: 'ContactRequest'
resources :call_me_back_requests, only: [:new, :create], controller: 'requests', type: 'CallMeBackRequest'
contact_request.rb:
class ContactRequest < Request
validates :name, :phone, :email, :company_name, presence: true
def self.sti_name
"ContactRequest"
end
end
call_me_back_request.rb:
class CallMeBackRequest < Request
validates :name, :phone, presence: true
def self.sti_name
"CallMeBack"
end
end
requests_controller.rb:
class Front::RequestsController < FrontController
before_action :set_type
def create
#request = Request.new(request_params)
respond_to do |format|
if #request.save
format.js
else
format.js { render partial: 'fail' }
end
end
end
private
def set_request
#request = type_class.find(params[:id])
end
def set_type
#type = type
end
def type
Request.types.include?(params[:type]) ? params[:type] : "Request"
end
def type_class
type.constantize
end
def request_params
params.require(type.underscore.to_sym).permit(Request.attribute_names.map(&:to_sym))
end
end
My form starts with:
=form_for Request.contacts.new, format: 'js', html: {class: 'g-contact__sidebar-right g-form'}, remote: true do |f|
I tried using ContactRequest.new - result was the same.
What I get when I hit the console:
Request.contacts.create!(name: "something") - does get saved, no validations are applied (why?). No type field is populated - why?
ContactRequest.create!(name: "something") - does not get saved, validations are applied
ContactRequest.create!(name: ..., all other required fields) - does get saved, but field type is empty - why?
Whatever I use for my form - ContactRequest.new or Request.contacts.new - neither validations are applied nor field type is set correctly.
Can anyone point me in the right direction? I'm mainly using this tutorial and other SO question, but without success.

Figured it out - since I'm not using the dedicated pages and paths for those contacts, i.e. contact_requests_path and corresponding new.html.haml, I need to pass the type parameter as a hidden field.
So my form now looks like this:
=form_for ContactRequest.new, format: 'js', html: {class: 'g-contact__sidebar-right g-form'}, remote: true do |f|
=f.hidden_field :type, value: "ContactRequest"
Considering validations - I don't know what I did, but after restarting the server a few times, they work now. The only this I remember really changing was the sti name here:
class CallMeBackRequest < Request
validates :name, :phone, presence: true
def self.sti_name
"CallMeBack" <- changed it from "CallMeBack" to "CallMeBackRequest"
end
end

Related

Rails form rendering multiple times if using `.build`

So I have a user form form_for that accepts nested attributes from account_prices. Whats happening is on my user controller I have this private method.
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.account_prices.where(status: 'default')
#accountable_temporary = #user.account_prices.where(status: 'temporary')
end
end
reason for the condition is, if I don't do a check here it will render 2 forms. an empty form and with data form. So checking is a need here
but my problem is this.Im on edit route, and when I try to submit an invalid form it renders multiple empty forms. heres an image.
if I kept submitting invalid form it will render multiple times. I was thinking if checking through JS if theres a multiple child I will remove it. is that the best approach?
here's my asscociations
Class User
has_many :account_prices, as: :accountable, autosave: true
accepts_nested_attributes_for :account_prices
end
polymorphic
class AccountPrice
enum status: {default: 'default', temporary: 'temporary'}
validates :accountable, presence: true
validates :status, presence: true
validates :exp_start_date, presence: true, if: :is_temporary_status?
validates :exp_end_date, presence: true, if: :is_temporary_status?
belongs_to :accountable, polymorphic: true
belongs_to :variant_price_set, class_name: "Spree::VariantPriceSet"
belongs_to :shipping_method_price_set, class_name: "Spree::ShippingMethodPriceSet"
def is_temporary_status?
status == 'temporary'
end
end
user controller
Class UsersController
before_action :pre_build_user_account_prices, only: :edit
def update
if #user.update_attributes(user_params)
flash.now[:success] = Spree.t(:account_updated)
redirect_to show_admin_user_path(#user)
else
render :edit
end
end
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.account_prices.where(status: 'default')
#accountable_temporary = #user.account_prices.where(status: 'temporary')
end
end
end
I guess when you're trying #user.account_prices.where(... it is reading data which is not persisted to the db too. I mean the account_prices which was just build in the previous step.
Try,
def pre_build_user_account_prices
if #user.account_prices.empty?
#accountable_default = #user.account_prices.build(status: 'default')
#accountable_temporary = #user.account_prices.build(status: 'temporary')
else
#accountable_default = #user.reload.account_prices.where(status: 'default')
#accountable_temporary = #user.reload.account_prices.where(status: 'temporary')
end
end
reload, reloads the object's attributes from the database
Also, when using accept_nested_attributes for to update an existing entry, you need to make sure that id of the nested object for which you're accepting nested attribute for is part of the attributes passed.

Response body not being serialized

I have an ActiveModel Serializer that changes the fields names to lowerCamelCase. But It's not working on my response.body when I try test it on rspec
ActiveModel::Serializer.setup do |config|
config.key_format = :lower_camel
end
class DevelopmentAgentsSerializer < ActiveModel::Serializer
attributes :id, :name, :email, :created_at, :updated_at, :phone
end
class DevelopmentAgentsController < ApplicationController
def index
#development_agents = DevelopmentAgent.all
render json: #development_agents
end
end
it "returns a list of development agents" do
get :index, format: :json
expect(JSON.parse(response.body)).to eq(JSON.parse({development_agents: serialized_development_agent}.to_json))
end
expected: {"development_agents"=>[{"id"=>3, "name"=>"Some name", "email"=>nil, "createdAt"=>"2019-08-06T17:30:47.372-03:00", "updatedAt"=>"2019-08-06T17:30:47.372-03:00", "phone"=>"(21)999999999"}]}
got:
{"development_agents"=>[{"id"=>3, "name"=>"Some name", "email"=>nil, "created_at"=>"2019-08-06T17:30:47.372-03:00", "updated_at"=>"2019-08-06T17:30:47.372-03:00", "phone"=>"(21)999999999"}]}
By convention, the serializer's name is singular, i.e. DevelopmentAgentSerializer instead of DevelopmentAgentsSerializer (Don't forget to also change the file name). If this convention is not followed, the serializer you defined won't be used, and the response will just be #development_agents.as_json

Duplicate two linked models Ruby

I want to duplicate my models 'Formulaire' and 'Question'. They have a has_may/belongs_to relation.
I'm able to duplicate the first model but I have a " NoMethodError in FormulairesController#duplicate
undefined method `save' for # " when I duplicate the second
My models :
Formulaire.rb
class Question < ActiveRecord::Base
belongs_to :formulaire
validates :nom, presence: true
end
Question.rb
class Formulaire < ActiveRecord::Base
has_many :questions, dependent: :destroy
end
formulaire_controller.rb
def duplicate
template = Formulaire.find(params[:id])
#formulaire= template.dup
#formulaire.save
#for question in #formulaire.questions
# question.dup
# question.save
#end
template2 = Question.where(formulaire_id: 47)
#question = template2.dup
#question.save
redirect_to #formulaire, notice: "Formulaire dupliqué"
end
def formulaire_params
params.require(:formulaire).permit(:name, :description,
questions_attributes: [:id, :nom, :typequestion, :image, '_destroy', photos_attributes:[:id],
answers_attributes:[:id, :content,'_destroy']]) if params[:formulaire]
#puts YAML::dump params
end
My view
formulaire/show.html.erb
<li class="Dupliquer"><%= link_to 'Dupliquer', duplicate_formulaire_path(#formulaire) %> </li>
routes.rb
resources :formulaires do
member do
get 'duplicate'
end
end
Thank you
Here in stackoverflow, I found it here:
If you want to copy an activeRecord object you can use its attributes to create new one like
you can have an action in your controller which can be called on link,
def create_from_existing
#existing_post = Post.find(params[:id])
#create new object with attributes of existing record
#post = Post.new(#existing_post.attributes)
render "your_post_form"
end
what's here: Rails clone copy or duplicate
I never used this gem, but it's for this, I'm going to leave it there and you'll see what you want.
https://github.com/amoeba-rb/amoeba
And also on reddit
def clone
#location = Location.find(params[:id]).dup
............
render :new
end
link: https://www.reddit.com/r/rails/comments/6phfy4/form_how_to_implement_a_clone_action/

ActiveRecord::RecordNotFound - in a descendant class' associated_controller#index

I am attempting to locate a parent object in a nested controller, so that I can associate the descendant resource with the parent like so:
# teams_controller.rb <snippet only>
def index
#university = Univeresity.find(params[:university_id])
#teams = #university.teams
end
When I call find(params[:university_id]) per the snippet above & in line 6 of teams_controller.rb, I receive ActiveRecord::RecordNotFound - Couldn't find University without an ID.
I'm not only interested in fixing this issue, but would also enjoy a better understanding of finding objects without having to enter a University.find(1) value, since I grant Admin the privilege of adding universities.
The Rails Guides say the following about the two kinds of parameters in a website:
3 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of
parameters possible in a web application. The first are parameters
that are sent as part of the URL, called query string parameters. The
query string is everything after “?” in the URL. The second type of
parameter is usually referred to as POST data. This information
usually comes from an HTML form which has been filled in by the user.
It’s called POST data because it can only be sent as part of an HTTP
POST request. Rails does not make any distinction between query string
parameters and POST parameters, and both are available in the params
hash in your controller:
It continues a little further down, explaining that the params hash is an instance of HashWithIndifferentAccess, which allows usage of both symbols and strings interchangeably for the keys.
From what I read above, my understanding is that Rails recognizes both parameters (URL & POST) and stores them in the same hash (params).
Can I pass the params hash into a find method in any controller action, or just the create/update actions? I'd also be interested in finding a readable/viewable resource to understand the update_attributes method thats called in a controller's 'update' action.
Please overlook the commented out code, as I am actively searching for answers as well.
Thanks in advance.
Here are the associated files and server log.
Webrick
teams_controller.rb
class TeamsController < ApplicationController
# before_filter :get_university
# before_filter :get_team
def index
#university = University.find(params[:univeristy_id])
#teams = #university.teams
end
def new
#university = University.find(params[:university_id])
#team = #university.teams.build
end
def create
#university = University.find(params[:university_id])
#team = #university.teams.build(params[:team])
if #team.save
redirect_to [#university, #team], success: 'Team created!'
else
render :new, error: 'There was an error processing your team'
end
end
def show
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def edit
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def update
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
if #team.update_attributes(params[:team])
redirect_to([#university, #team], success: 'Team successfully updated')
else
render(:edit, error: 'There was an error updating your team')
end
end
def destroy
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
#team.destroy
redirect_to university_teams_path(#university)
end
private
def get_university
#university = University.find(params[:university_id]) # can't find object without id
end
def get_team
#team = #university.teams.find(params[:id])
end
end
team.rb
class Team < ActiveRecord::Base
attr_accessible :name, :sport_type, :university_id
has_many :home_events, foreign_key: :home_team_id, class_name: 'Event'
has_many :away_events, foreign_key: :away_team_id, class_name: 'Event'
has_many :medias, as: :mediable
belongs_to :university
validates_presence_of :name, :sport_type
# scope :by_university, ->(university_id) { where(team_id: team_id).order(name: name) }
# scope :find_team, -> { Team.find_by id: id }
# scope :by_sport_type, ->(sport_type) { Team.where(sport_type: sport_type) }
# scope :with_university, joins: :teams
# def self.by_university(university_id)
# University.where(id: 1)
# University.joins(:teams).where(teams: { name: name })
# end
def self.by_university
University.where(university_id: university_id).first
end
def self.university_join
University.joins(:teams)
end
def self.by_sport_type(sport_type)
where(sport_type: sport_type)
end
def self.baseball
by_sport_type('Baseball/Softball')
end
end
university.rb
class University < ActiveRecord::Base
attr_accessible :address, :city, :name, :state, :url, :zip
has_many :teams, dependent: :destroy
validates :zip, presence: true, format: { with: /\A\d{5}(-\d+)?\z/ },
length: { minimum: 5 }
validates_presence_of :name, :address, :city, :state, :url
scope :universities, -> { University.order(name: 'ASC') }
# scope :by_teams, ->(university_id) { Team.find_by_university_id(university_id) }
# scope :team_by_university, ->(team_id) { where(team_id: team_id).order(name: name)}
def sport_type
team.sport_type
end
end
views/teams/index.html.erb
Placed in gists for formatting reasons
rake routes output: (in a public gist)
enter link description here
rails console
You're not going to want to have both:
resources :universities #lose this one
resources :universities do
resources :teams
end
As for params... you have to give a param. So, when you go to http://localhost:3000/teams there are no params, by default. If you go to http://localhost:3000/teams/3 then params[:id] = 3 and this will pull up your third team.
Keep in mind the nomenclature of an index. The index action of Teams, is going to list all of the teams. All of them. There is no one University there, so what are you actually trying to find? If anything, you'd have, for your University controller:
def show
#university = University.find(params[:id])
#teams = #university.teams
end
so, the address bar will be showing http://localhost:3000/universities/23, right? params[:id] = 23, then you can find the teams associated with that university.

Getting active scaffold to work without :mark action

I'm currently working on a project using Rails 3.2 and Active Scaffold. I've created a simple controller for one of my models that is coded thusly:
class StudentsController < ApplicationController
before_filter :authenticate_user!
active_scaffold :student do |conf|
conf.label = "Students"
conf.columns = [:last_name, :first_name, :age, :gender, :grade_level, :current_grade]
conf.create.columns = [:last_name, :first_name, :age, :gender, :grade_level]
conf.update.columns = [:last_name, :first_name, :age, :gender, :grade_level]
conf.columns[:current_grade].actions_for_association_links = [:show]
conf.columns[:current_grade].sort_by :sql => "grade_level"
conf.actions = [:list, :search, :create, :update, :show, :delete]
list.columns.exclude :customer_id, :grade_level
list.sorting = {:last_name => 'ASC'}
end
def conditions_for_collection
["customer_id = #{current_user.customer_id}"]
end
def before_create_save(record)
record.customer_id = current_user.customer_id
end
end
My problem is this: When I delete a record, I receive a message that states '$record_name can't be deleted'. Yet I find the record is in fact deleted if I refresh the page. Upon examining my log file I see an error message stating:
undefined method `as_marked=' for #<Student:0x0000000554c1d0>
I tried adding :mark to my list of actions and that does solve the problem. However, I don't want a mark/checkbox column to show up in my list.
Any ideas? This is my first time using active scaffold, and I find this... annoying.
I've discovered that if I add this to my model:
def as_marked= (x)
end
it works, without showing a mark/checkbox column in my list.
For the record, I hate this solution :) If I come up with anything better I'll make sure to come back and update this answer.

Resources