Problems exposing has_and_belongs_to_many relation using Grape::Entity - ruby-on-rails-4

i have Trip model which has destinations defined like that:
class Trip < ActiveRecord::Base
...
has_and_belongs_to_many :destinations, join_table: :trips_destinations
...
end
What I want to do is to expose the trip information included the associated destinations. I defined this response entity for the destinations:
module Services
module Trips
class DestinationResponseEntity < Grape::Entity
expose :id
expose :name
end
end
end
And the trip destination entity is this:
module Services
module Trips
class TripResponseEntity < Grape::Entity
expose :id
expose :title
expose :duration
expose :total_price
expose :description
expose :destinations, using: Trips::DestinationResponseEntity
expose :photo
end
end
end
I'm presenting the result in that way:
present trip, :with => Trips::TripResponseEntity
But the response of the service returns always an empty destination array.
[{"id":3,"title":"Islandhopping in Thailand","duration":14,"total_price":3450,"description":"Relax under swaying palm trees then jump into crystal-clear waters",**"destinations":[]**,"photo":"http://s3.amazonaws.com/ntradadevelopment/images/trips/3/original/thailand.jpeg"]
In the console I can see all the destinations associated with the trip properly.
Any clue of what could be causing the issue is really appreciated.

What you're doing with Grape::Entity looks correct to me.
I encountered a similar issue that turned out to be a problem with my has_and_belongs_to_many relationship not being defined correctly so you might need to check that again.
In particular, you seem to be overriding the default naming convention of the join table. Could it be tripping up Grape?

Related

NoMethodError - new since upgrading to Rails 4

I'm at my wit's end. I upgraded to Rails 4.2.10, and everything is terrible.
Here is the relevant part of /models/product.rb
class Product < ActiveRecord::Base
delegate_attributes :price, :is_master, :to => :master
And here is /models/variant.rb:
class Variant < ActiveRecord::Base
belongs_to :product
The variants table has fields for "price" and "is_master". Products table does not.
It used to be the case that one could access Product.price and it would get/set the price for the master variant (there's really only one variant per product, the way things are currently set up).
Now it complains that:
NoMethodError: undefined method `price=' for #<Product:0x0000000d63b980>
It's true. There's no method called price=. But why wasn't this an issue before, and what on earth should I put in that method if I create it?
Here's the code to generate a product in db/seeds.rb:
product = Product.create!({
name: "Product_#{i}",
description: Faker::Lorem.sentence,
store_id: u.store.id,
master_attributes: {
listing_folder_id: uuids[i],
version_folder_id: uuids[i]
}
})
product.price = 10
product.save!
end
delegate_attributes isn't a Rails method and looks like it comes from a gem (or gems) that aren't actively maintained?
If there's a new version of whatever gem you're using that might help, because the short answer is that part of the "delegating" of an attribute would involve getting and setting the attribute, so it would generate #price= for you.
If you want to define it yourself, this should do it (within your Product class):
def price=(*args)
master.price=(*args)
end
or if you want to be more explicit:
def price=(amount)
master.price = amount
end

Rails 4 polymorphic has_many ignores table_name

Short version: I'm building a new Rails 4 application that uses (read-only) some tables from a database used by a legacy Rails 2 application, which is still in use. The old application models/tables were very confusingly named, however (especially in the context of the new application), so I want to use different names for the models/tables using self.table_name. This all works perfectly until I tried to add in a polymorphic relationship. Rails ignores my defined table_name and does a query on the type using the new model name, which of course is different so it doesn't work. Is there any way to change this?
Long version: There are three models in this equation, and here they are:
class Exporter < MysqlBase
has_many :lic_exporter_addresses, :as => :place
self.table_name = 'excons'
self.primary_key = 'id'
end
class LicBusiness < MysqlBase
has_one :physical_address, -> { where(category: 'Physical') }, :class_name => 'LicExporterAddress', :as => :place
has_one :mailing_address, -> { where(category: 'Mailing') }, :class_name => 'LicExporterAddress', :as => :place
has_many :lic_exporter_addresses, :as => :place
self.table_name = 'businesses'
self.primary_key = 'id'
end
class LicExporterAddress < MysqlBase
belongs_to :place, polymorphic: true
self.table_name = 'addresses'
self.primary_key = 'id'
end
We have a ton of different kinds of businesses, so the Business model is the most problematic. I really don't want to have that in the new app because it would be very confusing as to what a "business" actually is. With the current code if I go into the rails console and try to get lic_exporter_addresses for a LicBusiness or Exporter, it does:
SELECT `addresses`.* FROM `addresses` WHERE `addresses`.`place_id` = '00044c693f6848f9b0978f873cf9999a' AND `addresses`.`place_type` = 'LicBusiness'
when what I need is place_type = 'Business'.
Is there any way to tell Rails what place_type to look for? I did see this question and the second answer looked promising, except that I'm already sort of doing that with Physical and Mailing addresses so I can't figure out how that'd work with both options at the same time... Thanks for any info or ideas.
In Rails 4.2, it looks like the exact string used for the query is defined as owner.class.base_class.name, where owner is the model declaring the association. So I don't think it's directly supported. But there are a few ways I can think of to hack around this. I think the most promising might be, in LicBusiness:
has_many :lic_exporter_addresses, ->{where place_type: "Business"}, foreign_key: "place_id"
That is, don't define the association as polymorphic, but define the type scope yourself. This will NOT correctly define place_type in the lic_exporter_addresses table if you ever use lic_exporter_address.place = some_lic_business_instance. However you said this table was read-only, so this may in fact not be an issue for you. If it is, there may be ways to override the behavior to get what you need.
Two other ideas both make me very nervous and I think they are probably quite dangerous for unintended side-effects. They are to override LicBusiness.base_class (this might actually be ok if you do not now and never will have STI set up on LicBusiness, but I'm still nervous), or to override LicBusiness.name (I'm pretty sure this would have unintended side-effects).

Where should I place bestseller? method - in model or somewhere else?

I've got some simple model:
class Product < ActiveRecord::Base
has_many :categories
end
Now I would like to check in some service, if product is a bestseller and do other action for it:
class ProductService
def remind
Product.all.each do |product|
puts product unless bestseller?
end
end
end
So now what is the best place to put the bestseller? method - inside model or in the service as private method?
In future it may be used in some other services or actions.
Do you think the model is right place to put this method there?
Example of bestsellers method (bestsellers are picked manualy by adding to category 'bestsellers':
def bestseller?(product)
product.categories.include?(BESTSELLER_CATEGORY_ID)
end
or
def bestseller?(product_id)
Category.find(BESTSELLER_CATEGORY_ID).products.include?(product_id)
end
I still haven't decided which one is better (both do the same)

Can I make a belongs_to association use eager loading by default?

I am connecting to one of my company's SQL Server databases, and trying to set up ActiveRecord so I can treat them just the same as Rails objects.
I have these two models:
class Change < ActiveRecord::Base
belongs_to :affected_contact, class_name: "Contact"
end
class Contact
# Contact's primary key is a binary UUID; I can't change this
end
I am trying to get the affected contact of one particular change. Normally, this would be a simple case, but:
Change.first.affected_contact
Change Load (52.6ms) EXEC sp_executesql N'SELECT TOP (1) [chg].* FROM [chg] ORDER BY [chg].[id] ASC'
Contact Load (28.0ms) EXEC sp_executesql N'SELECT TOP (1) [ca_contact].* FROM [ca_contact] WHERE [ca_contact].[contact_uuid] = #0', N'#0 binary', #0 = 0xfcf9a8ac6381aa4386c9b10ee382e10b [["contact_uuid", "<16 bytes of binary data>"]]
=> nil
... that's not what I want! And yet, if I eager-load the join first, it works:
Change.eager_load(:affected_contact).first.affected_contact
SQL (34.4ms) EXEC sp_executesql N'SELECT TOP (1) holy_crap_theres_a_lot_of_columns FROM [chg] LEFT OUTER JOIN [ca_contact] ON [ca_contact].[contact_uuid] = [chg].[affected_contact] ORDER BY [chg].[id] ASC'
=> #<Contact contact_uuid: "\xFC\xF9\xA8\xACc\x81\xAAC\x86\xC9\xB1\x0E\xE3\x82\xE1\v", ... >
In fact, if I force the matching to happen in the JOIN clause in any way, it will work, but belongs_to seems to use the WHERE clause instead, and nil is the best response I can get (a lot of the time, there are conversion errors between the string and its binary type).
Is there a way to ensure eager-loading through the JOIN clause happens by default on the belongs_to association?
I found that #find_by_contact_uuid (contact_uuid being the primary key) worked, where #find didn't, for some reason. That led to this being implemented.
I have ended up essentially rewriting the association methods that Active Record supplies:
module AssociationMethods
def self.included(base)
base.reflect_on_all_associations(:belong_to).each do |a|
define_method a.name do
# #find_by_<uuid_pk> seems to work where #find doesn't
a.klass.send "find_by_#{a.association_primary_key}", self[a.foreign_key]
end
end
base.reflect_on_all_associations(:has_many).each do |a|
define_method a.name do
a.klass.where(a.foreign_key => self.send(a.association_primary_key))
end
end
end
end
class Contact
has_many :changes, foreign_key: :affected_contact_id
include AssociationMethods # include *after* all associations are defined
end
class Change
belongs_to :affected_contact, class_name: 'Contact'
include AssociationMethods
end
It doesn't cover everything that Active Record supplies when setting up the associations, but it seems to do the trick.
Using includes should resolve your problem. This is because includes will preload or eager_load depending on your other conditions.
read more here

Rails4: Access child object in parent while creating parent from child form

I have associations as
Delivery Model
class Delivery < ActiveRecord::Base
belongs_to :schedule, inverse_of: :deliveries
accepts_nested_attributes_for :schedule
end
Schedule Model
class Schedule < ActiveRecord::Base
has_many :deliveries, inverse_of: :schedule
include PushUpdates
def offer
deliveries.last.try(:offer)
end
end
PushUpdates: app/model/concerns/push_updates.rb
module PushUpdates
extend ActiveSupport::Concern
included do
after_create {update_client_store :create unless Rails.env.test? }
after_update {update_client_store :update unless Rails.env.test? }
end
def update_client_store(operation)
...
self.offer
end
end
Now, while saving delivery, am also accepting the one set of schedule details.
and when it saves delivery and schedule, there is after_create callback called for schedule model, and when it tries to find out the deliveries.last it gives nil value.
Since am accepting it as a part of nested attributes, then delivery object should be available for it but still it gives nil value.
Am I missing anything here? Thanks.
It looks to me that Rails is trying to make a database call before the associated records have been committed.
There is an additional callback that you could look at after_commit (in place of after_create that is fired once the records have been committed to whatever database you are using.
That should mean that your deliveries.last should come back non-empty. You could also force a database lookup on the model with self.reload, although that could cause unexpected behaviour.