Specifying custom serializer for a relationship has_one json-api-rb - ruby-on-rails

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

Related

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.

Iterate over STI list in rails 4

I have two sub models, called: Service and Product that inherits from ProductBase. And I have another model to consume it. Acquire that have many AcquireBasket. Check out my code:
product_base.rb:
class ProductBase < ActiveRecord::Base
extend ::EnumerateIt
include Searchable
self.table_name = 'products'
end
product.rb:
class Product < ProductBase
default_scope { where(kind: ProductKind::PRODUCT) }
def initialize(attributes = {})
super(attributes)
self.kind = ProductKind::PRODUCT
self.status = ProductStatus::DRAFT
end
end
service.rb:
class Service < ProductBase
default_scope { where(kind: ProductKind::SERVICE) }
def initialize(attributes = {})
super(attributes)
self.kind = ProductKind::SERVICE
self.status = ProductStatus::DRAFT
end
end
acquire_basket.rb:
class AcquireBasket < ActiveRecord::Base
extend ::EnumerateIt
belongs_to :acquire
belongs_to :product
end
In some part of my project, I get a list (acquire baskets) of both models, Service and Product. And I need to check if I have services inside of it.
My code to check was:
def services_in?(acquire)
acquire.baskets.map(&:product).detect(&:service?)
end
The code works, ONLY if I pass services first, and products after!! Or if I have only one of them.
You should be able to utilize the descendents method to iterate over all of the subclasses
I can't find the answer in blog post around the world, so I will share with you:
class AcquireBasket < ActiveRecord::Base
extend ::EnumerateIt
belongs_to :acquire
belongs_to :product, class_name: 'ProductBase'
end
The problem was, when I try to find (lazily) in a ActiveRecord::Relation, Rails lookup (I think) to just Product model. And It can't find other type models inside of it. So using this typo I put it to work.

Rails 4 scope has_many

I have two models
class Portfolio < ActiveRecord::Base
has_many :project_types, dependent: :destroy
end
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
end
ProjectType model has field ptype. It can be 'web' or 'mobile', etc.
How can I get all 'web' or 'mobile' portfolios using scopes?
Add an appropriate scope to your model:
# in models/project_type.rb
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
scope :web, -> { where(ptype: 'web') }
end
To load portfolios with type web only use that scope with a join in the controller:
# in the controller
#web_portfolios = Portfolio.joins(:project_types).merge(ProjectType.web)
You can have explicit scopes for both, or you may have one scope that selects for you based on ptype, or you may metaprogram a scope for each unique ptype in the database. If you are adding a variety of different "ptypes" you can do any of the following which will give you a scope for handling any "ptypes":
scope :ptype, -> (ptype) { where(ptype: ptype) }
called as:
ProjectType.ptype('web')
or
class ProjectType < ActiveRecord::Base
self.pluck(:ptype).each do |ptype|
scope ptype.gsub(/\s+/,"_").downcase.to_sym, -> { where(ptype: ptype) }
end
end
I don't recommend this and I also don't recommend individual scopes for each "string" (e.g. ProjectType.web or ProjectType.mobile) in there. The best balance is to pass a string value into a scope which retrieves what you are looking for. Just my opinion, I'm sure others feel differently about it.
To be honest, I think the ptype field is ripe for an enumerator -- this will give some clarity in your code and define what that field actually expects as opposed to allowing any string to be placed in there at random. So something like:
class ProjectType < ActiveRecord::Base
enum ptype: [:web, :mobile]
scope :ptype, -> (ptype) { where(ptype: self.ptypes[ptype] }
end
and called like so:
ProjectType.ptype(:web)
I don't think any one of these solutions presented here or above is any more "right", except the meta-programming solution that's generating a scope for each string in the ptype field stored in the database.
Finally, here is a scope for your Portfolio:
class Portfolio < ActiveRecord::Base
scope :by_ptype, -> (ptype) { joins(:project_type).merge(ProjectType.ptype(ptype) }
end
And called like so:
Portfolio.by_ptype(:web)
You can do it this way
class Portfolio < ActiveRecord::Base
has_many :project_types, dependent: :destroy
scope :ptype, -> (p_type) { includes(:project_types).where(project_types: {ptype: p_type}) }
end
And you can call
Portfolio.ptype('web')
Following a Rails 4.1 approach I would consider my ptype field an enum and write this:
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
enum ptype: { web: 'web', mobile: 'mobile' }
end
This will give you:
ProjectType.web
ProjectType.mobile
scopes to fetch objects based on ptype, but will give you also other useful methods like:
#projectType.web?
#projectType.web!
Please refer to docs: http://api.rubyonrails.org/v4.1/classes/ActiveRecord/Enum.html
In here you ask how to retrieve "web" Portfolio and I will suppose you mean "All Portfolio with at least a ProjectType of ptype 'Web'.
Then I would write something like:
Portfolio.joins(:project_types).merge(ProjectType.web)
see docs: http://apidock.com/rails/ActiveRecord/SpawnMethods/merge
In a final refinement you can then create a scope out of it:
class Portfolio
has_many :project_types
scope :web, -> { joins(:project_types).merge(ProjectType.web) }
end
and call just
Portfolio.web

Rails 4 API - Accepts nested attribute for namespaced model

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

Two models depends on each other – catch 22

Here is my scenario:
A model called Course has many CourseCodes. A CourseCode belongs to a Course.
A CourseCode can't be created without Course and a Course can't be created without at least one CourseCode.
class Course < ActiveRecord::Base
has_many :course_codes
validate :existence_of_code
private
def existence_of_code
unless course_codes.any?
errors[:course_codes] << "missing course code"
end
end
end
class CourseCode < ActiveRecord::Base
belongs_to :course
validates_presence_of :course
end
The whole scenario feels a bit like catch 22.
Is there a way to create both on the same time?
I'm using Rails 3.2
Solved the problem by using accepts_nested_attributes_for.
Nested attributes allow you to save attributes on associated records through the parent. By default nested.
class Course < ActiveRecord::Base
has_many :course_codes, inverse_of: :course
validate :existence_of_code
accepts_nested_attributes_for :course_codes
private
def existence_of_code
unless course_codes.any?
errors[:course_codes] << "missing course code"
end
end
end
class CourseCode < ActiveRecord::Base
belongs_to :course, inverse_of: :course_codes
validates_presence_of :course
end
Used like this.
Course.create!({
course_codes_attributes: [{ code: "TDA123" }],
# ...
})
Looks good to me. Removing the validates_presence_of :course might make things easier on you, too, as it will tend to get in the way an not add much.
When you create a course, do it like this:
Course.create course_codes: [CourseCode.new(...), CourseCode.new(...)]
ActiveRecord will figure things out.
You could add an unless to whichever model you would plan to create first. For instance:
class CourseCode < ActiveRecord::Base
belongs_to :course
validates_presence_of :course, :unless => lambda { Course.all.empty? }
end

Resources