temporarily replace content of has_many relationship - activerecord

when my model is in a particular state, I need to render it with hiding some specific data.
my model knows the data I need to hide through a has_many relationship.
my idea is to retrieve the model, replace the content of the has_many relationship with a dummy, non persisted object, and then render it, without saving the model.
So that when rendering the data shown will be from the dummy object.
here's my code:
the model:
class Car < ActiveRecord::Base
....
has_many :owners
....
end
in the controller:
#car.owners = [ Owner.new(name: "", phone: "") ] if hide_owner?
it actually attempts to do the update on the DB and fails with this error:
*** ActiveRecord::RecordNotSaved Exception: Failed to replace owners because one or more of the new records could not be saved.

It feels like this would be easier if you were accessing a Decorator class instead of the model directly.
Then in the decorator you could define:
def owners
if hide_owner?
[ Owner.new(name: "", phone: "") ]
else
object.owners
end
end
... where object is the instance of Car.
Maybe look at the Draper gem or others.

Related

Is there a way to serialize nested attributes outside of the parent with ActiveModel Serializer?

I upgraded activemodel serializer and now the output is different than what it once was. I'm trying to get the JSON output to match what it previously was. Specifically, I'd like an objects nested attributes to be on the same level as the main object.
For example, let's say my serializer is as follows:
class DishesSerializer < ActiveModel::Serializer
has_many :ingredients
end
This would be the current output:
{
"dish": {
"dish_stuff_1": "dish_stuff_1",
"dish_stuff_2": "dish_stuff_2",
"dish_stuff_3": "dish_stuff_3",
"ingredients": {
"ingredients_stuff_1": "ingredients_stuff_1"
}
}
}
And what I'd like is something like this:
{
"dish": {
"dish_stuff_1": "dish_stuff_1",
"dish_stuff_2": "dish_stuff_2",
"dish_stuff_3": "dish_stuff_3"
}
"ingredients": {
"ingredients_stuff_1": "ingredients_stuff_1"
}
}
I am currently doing this in the controller using multiple serializers, but it takes some additional querying and feels wrong. I feel like there should be some hacky way to do it in AMS.
I tried something like this:
def attributes
hash = super
hash.merge!(:dishes => dishes)
end
but that ends up in the same layer.
Any help would be greatly appreciated.
In this case, can't you just create a new serializer that has all the data you need - say a menu serializer:
class MenuSerializer < ActiveModel::Serializer
has_one :dish
has_many :ingredients
end
Not sure having a serializer that returns 2 root elements is a good idea, as that would make it hard to reuse in the future.

how to call custom serializers in sinatra application

I am working on a backend of an application written in Sinatra.
It has a route "/notifications"
which renders all the notifications in JSON.
I want to change the json structure and wrote some custom serializer and it is failing now.
the error i get is
"{"message":"undefined method `read_attribute_for_serialization' for nil:NilClass"}"
I have a file called webservice/notification.rb
which selects a notification serializer.
the code is something like this
serializer = NotificationSerializer
json serialize(notifications, root: :notifications, each_serializer: serializer)
The NotificationSerializer is something like this.
class NotificationSerializer < Serializer
attributes :id, :tag, :event, :time, :read
has_one :reference, polymorphic: true, include: true
The reference here can be a lot of things.
the notification model defines reference as
def reference
company || contact || deal || invitation || meeting || todo || reference_email || reference_user ||
contact_import_job
end
now all of these models in reference have there Serializer implements in directory Serializer/*
I want to make custom Serializers for all of these which will render limited information.
how can I call my custom Serializer for things inside reference.
I wrote a custom serializer for notifications and called it like this inside my refernce function and it worked.
...|| UserNotificationSerializer.new(reference_user) || ...
but if i do the same for my other models i get the error given above.
what would be the correct way to call my custom serializers.
A good way to do it is to write an instance method on the model:
class Notification < ActiveRecord::Base
def public_attributes # or call it whatever
attributes_hash = attributes
# in activerecord, the attributes method turns a model instance into a hash
# do some modifications to the hash here
return attributes_hash
end
end
then say you're returning json in a controller:
get '/some_route' do
#notifications = Notification.all # or whatever
serialized_notifications = #notifications.map(&:public_attributes)
# For a single record, you could do #notification.public_attributes
json_data = serialized_notifications.to_json # a serialized array of hashes
content_type :json
return json_data
end

Object Not Persisting in CouchDB

I am trying to store my ruby object in couchdb with couchrest. I am extending my model from CouchRest::Model::Base But still i am unable to see the changes in db.
I've defined Server as CouchRest.new also mentioned in model to use_database 'players'
# Controller Method
get '/new/:name' do
DB = SERVER.database!('players')
#new_player = Player.new(params['name'])
#new_player.create
"success: #{#new_player.persisted?}" #shows true
# Model
class Player < CouchRest::Model::Base
use_database 'players'
property :name ,String
timestamps!
def initialize(arg)
#name=arg
end
end
How To Persist the object?
How can i retreive all persisted objects?
Is there any simple applications which i can refer to?

ruby on rails "before_save" in parent model to check attribut of child

I have a model 'place_detail' that has many a child 'emails'
has_many :emails, :dependent => :destroy
and in the email model:
belongs_to :place_detail
Now in the place_detail i want to make sur i added a email to check a attribut 'has_email'
so i added:
before_save :check_if_has_email
...
def check_if_has_email
if emails.count >0
self.has_email = true;
else
self.has_email = false;
end
end
the problem is that the attribute has_email does not check event if i created a email child. what i understand is that the parent is saved before the child
how can i get my has_email checked in place_detail when i create a child email?
EDIT:
I could simply put the has_email boolean in a method like
def has_email?
if self.emails.count >0
return true
..
but i prefer the boolean in the attribute because i use it in many scope and it would be a lot a change in the app
This will ensure that your model has at least one email (place it in your model file place_detail.rb)
has_many :emails, :dependent => :destroy
validates :emails, :length => { :minimum => 1 }
EDIT:
One suggestion would be just to check the trait place_detail.emails count when you need it. If you examine such data multiple times a request you can store it like so
def has_email?
(result ||= self.emails.count) > 0
end
That way it will only check your database once
If you're forced to use the 'has_email' attribute within the place_detail model, you could simply save the place_detail in the Create method of the Email controller.
#email = Email.new(email_params)
#email.place_detail.save

Do not autosave belongs-to association

I have a model Subscription with a belongs-to association to Participant.
The subscription form uses fields_for to build the associated participant fields.
Also in the form is a radio button called called 'other_person'.
What I want is to NOT save the associated participant table (so also not validate) when the other_person field is set to false.
I will assume other_person is a field of the Subscription model on the following example:
class Subscription < ActiveRecord::Base
before_save :remove_empty_participant
belongs_to :participant
private
def remove_empty_participant
self.participant = nil unless self.other_person
end
end
If it's not a field of your Subscription model, you will have to remove the attributes in the controller's action:
class SubscriptionsController < ActionController
def create
params[:subscription].delete(:participant) unless params[:other_person]
# Save the subscription with your current params...
end
end
Hope it helps.

Resources