Rails 4 API - Accepts nested attribute for namespaced model - ruby-on-rails

I have something like this:
module Api
module V1
class Order < ActiveRecord::Base
has_many :order_lines
accepts_nested_attributes_for :order_lines
end
end
module Api
module V1
class OrderLine < ActiveRecord::Base
belongs_to :order
end
end
In my orders controller, I permit the order_lines_attributes param:
params.permit(:name, :order_lines_attributes => [
:quantity, :price, :notes, :priority, :product_id, :option_id
])
I am then making a post call to the appropriate route which will create an order and all nested order_lines. That method creates an order successfully, but some rails magic is trying to create the nested order_lines as well. I get this error:
Uninitialized Constant OrderLine.
I need my accepts_nested_attributes_for call to realize that OrderLine is namespaced to Api::V1::OrderLine. Instead, rails behind the scenes is looking for just OrderLine without the namespace. How can I resolve this issue?

I am pretty sure that the solution here is just to let Rails know the complete nested/namespaced class name.
From docs:
:class_name
Specify the class name of the association. Use it only
if that name can't be inferred from the association name. So
belongs_to :author will by default be linked to the Author class, but
if the real class name is Person, you'll have to specify it with this
option.
I usually see, that class_name option takes the string (class name) as a argument, but I prefer to use constant, not string:
module Api
module V1
class Order < ActiveRecord::Base
has_many :order_lines,
class_name: Api::V1::OrderLine
end
end
end
module Api
module V1
class OrderLine < ActiveRecord::Base
belongs_to :order,
class_name: Api::V1::Order
end
end
end

Related

Specifying custom serializer for a relationship has_one json-api-rb

I have serializers:
class SerializableContact < JSONAPI::Serializable::Resource
type :contacts
has_one :email_event, class: 'SerializableEmailEvent' # class is not working
has_many :geofence_notifications
attributes :email,
:order_id,
:frequent_order_id
end
class SerializableEmailEvent < JSONAPI::Serializable::Resource
...
end
Models:
class Contact < ApplicationRecord
has_one :email_event, -> { email }, class_name: 'Event'
end
class Event < ApplicationRecord
end
I am trying to make SerializableContact to use SerializableEmailEvent as the serializer for email_event relationship. But I cant figure out how to do that. It always error out with:
JsonapiCompliable::Errors::MissingSerializer - Could not find serializer for class 'Event'!
Looked for 'SerializableEvent' but doesn't appear to exist.
Use a custom Inferrer if you'd like different lookup logic.
I am not sure how to use a custom inferrer for the different lookup logic
Maybe I am a bit late responding to your issue, but here you have how to do it:
has_one :email_event do
linkage(always: true) do
{ type: "email_event", id: #object.email_event_id } if #object.email_event_id
end
end
They type has to match with the type on your serilizable resource class. So I was expecting it to be:
class SerializableEmailEvent < JSONAPI::Serializable::Resource
type "email_event"
...
end

How can I create a model object with deeply nested associations in Rails using only my params whitelist method?

To replicate this question, my base model is project:
class Project < ApplicationRecord
has_many :tones, as: :analyzable
has_many :sentences
accepts_nested_attributes_for :tones, :sentences
end
And I have two other models:
class Sentence < ApplicationRecord
belongs_to :project
has_many :tones, as: :analyzable
accepts_nested_attributes_for :tones
end
class Tone < ApplicationRecord
belongs_to :analyzable, polymorphic: true
end
In my ProjectsController, I want to have a single create action that just takes in the whitelisted params and creates a project with all the appropriate associations. My project_params method looks like this:
private
def project_params
params.permit(:text, :title, :img, sentences_attributes: [:id, :text, tones_attributes: [:score, :tone_name]], tones_attributes: [:id, :score, :tone_name])
end
But if I try something like:
project = Project.create(project_params)
I will get an error like the one below:
| ActiveRecord::AssociationTypeMismatch (Sentence(#70119358770360) expected, got {"text"=>"extremely disappointed with the level of service with Virgin Media.", "tones"=>[{"score"=>0.64031, "tone_name"=>"Sadness"}, {"score"=>0.618451, "tone_name"=>"Confident
"}]} which is an instance of ActiveSupport::HashWithIndifferentAccess(#70119356172580)):
I ended up writing this for my create action, which gets the job done but feels quite clumsy:
project = Project.create({text: params[:text], title: params[:title],
img: params[:img]})
params[:sentences].each do |sent|
sentence = project.sentences.create({text: sent["text"]})
sent["tones"].each do |tone|
sentence.tones.create({score: tone["score"], tone_name: tone["tone_name"]})
end
end
params[:tones].each do |tone|
project.tones.create({score: tone["score"], tone_name: tone["tone_name"]})
end
Is there a better way to handle this kind of situation than what I have immediately above, where one is trying to create an object from nested params that has several levels of association?

Accessing (Model) ApplicationRecord's name inside the class definition itself

I have this model definition for Products inside a Rails application. I need to get the name products inside the custom after_initialize method. In the code below, I have written it explicitly. My question is there a way to get the name products programmatically inside the model definition class?
class Product < ApplicationRecord
has_one :photo, dependent: :destroy
accepts_nested_attributes_for :photo
after_initialize :set_photos_parent
def set_photos_parent
self.photo.parent = "products" unless self.photo.nil ?
end
end
try this
self.class_name.pluralize.downcase

Rails 5 "no implicit conversion of Symbol into Integer" for double nested has_many attributes

I keep getting the error no implicit conversion of Symbol into Integer when trying to create/update a Form model via a form. I've narrowed it down to something to do with actions_attributes in the form_params. trigger_attributes works fine if actions_attributes is removed. I suspect it has something to do with the enum field, double nested attributes, and/or the has_many relationship, but not sure.
Any ideas on what could be causing this error?
Running Rails 5.0.x and Ruby 2.3.x, with the relevant models and controller below.
class Form < ApplicationRecord
has_one :rule
accepts_nested_attributes_for :rule
end
class Rule < ApplicationRecord
has_one :trigger
has_many :actions
accepts_nested_attributes_for :trigger
accepts_nested_attributes_for :actions
end
class Trigger < ApplicationRecord
belongs_to :rule
enum name: [:example]
end
class Actions < ApplicationRecord
belongs_to :rule
enum name: [:example]
end
class FormsController < ApplicationController
...
private
def form_params
params.require(:form).permit(
:title,
:description,
rule_attributes: [
trigger_attributes: [:name],
actions_attributes: [:name]
]
)
end
end
I got this to work by changing actions_attributes: to actions: under def form_params, as well as making the relevant change to the form, changing fields_for :actions_attributes to fields_for :actions.
I'm often confused when to use _attributes and when not to. If anyone has information about when to use which, I would appreciate it if you could provide a link to the information in the comments. Thanks.

How to "accept" nested class object instances by running a custom method before to store those?

I am using Ruby on Rails 3.1.0 and I would like to run a :reject_if method (as described in the official documentation related to the accepts_nested_attributes_for method in the "One-to-many" association section) on class object instances "at all" (that is, to run the :reject_if method not on attributes - as made in the linked documentation - but on records that should be "subsequently" stored in the database). In few words, I would like to reject\filter some nested model\class object instances that should be stored in the database by running a custom method on those instances.
For example, if I have the following:
class User < ActiveRecord::Base
has_one :article,
accepts_nested_attributes_for :article
end
I would like to make something like this (the following code does not work):
class User < ActiveRecord::Base
has_one :article,
# Note: by using 'article' in the block I (would like to) refer to class
# object instances
accepts_nested_attributes_for :article, :reject_if => { |article| article.can_be_created? }
end
class Article < ActiveRecord::Base
def can_be_created?
true if user.has_authorization?
end
end
Is it possible? If so, how can I make that?
Make a method that returns a boolean, and give the method name as a symbol to :reject_if, eg:
accepts_nested_attributes_for :article, :reject_if => :article_uncreateable?
def article_uncreateable?
!Article.new(article_attributes).can_be_created?
end

Resources