Is `orphan_types` the only way to specify possible resolve types for an interface? - graphql-ruby

The ruby-graphql documentation says the following about resolve types for interfaces:
...a schema finds it types by traversing its fields, starting with query, mutation and subscription. If an object is never the return type of a field, but only connected via an interface, then it must be explicitly connected to the schema via orphan_types.
See here
I have the following definition of a module with two classes that implement it:
module Contact
include GraphQL::Schema::Interface
field :id, ID, null: false
end
class Cmpany < Types::Base
implements Contact
graphql_name "Company"
field :name, String, null: false
end
class Person < Types::Base
implements Contact
graphql_name "Person"
field :first_name, String, null: false
field :last_name, String, null: false
end
As it stands, my code matches the description in the documentation, in that the types Company and Person are never the return type of any field in the schema other than the query which has the interface Contact as its return type. Without any other changes, GraphQL does not recognize the contacts query.
Is orphan_types to interfaces what possible_types is to unions? Perhaps it's the naming that puts me off, but orphan_types seems like a workaround that shouldn't be used. Is it the correct solution?

Related

Single Table Inheritance and 'type' value for namespaced classes

While working on Rails 2.3.18 to Rails 3.2.x migration I am facing name issue in type column
Here is the relation that is defined.
app/models/reservation.rb
class Reservation
end
class Reservation::Guest < Reservation
end
class Reservation::Event < Reservation
end
While saving Reservation::Guest Or Reservation::Event instance, the type value being saved is Reservation::Guest and Reservation::Event in Rails 3. But in Rails 2 it saves without namespace i.e., Guest or Event.
It requires lots of efforts to migrate existing data and change all the places which expects type without namespace.
Would it be possible to save type without namespace and rest work without making lots of modification across the application?
Take a look at sti_name and find_sti_class. (The methods responsible for setting and getting the sti_name)
You can customize them as follows:
class Reservation
def self.find_sti_class(type_name)
type_name = self.name
super
end
end
class Reservation::Guest < Reservation
def self.sti_name
"Guest"
end
end
class Reservation::Event < Reservation
def self.sti_name
"Event"
end
end
I know it's an old question, but maybe someone will use it.
Expanding on the answer by mohameddiaa27, the easiest way to get rid of namespace is to declare store_full_sti_class, since it's used by sti_name.
class Reservation
# don't include namespace in type column
def self.store_full_sti_class
false
end
end

Accessing params in validations

I would like to show some extra info in error messages resulting from a failed validation. For example suppose I have a class Book with the following validation
validates :name, presence: true, uniqueness: true
When someone tries to insert a book by the same name the following error message is returned
{"name":["has already been taken"]}
Instead I wanna show
{"name":["Book 'Great Expectaions' has already been taken at id:7"]}
Right now to make this happen I have to remove the uniqueness validation that I mentioned above and do the following
validate do |book|
existing_book = Book.find_by_name(book.name)
if existing_book
book.errors.add(:name, "#{existing_book.name} already exists at id: #{existing_book.id}")
end
end
Is there a way to get custom error messages like above without writing a custom uniqueness validation? I was thinking something along the lines of
validates :name, presence: true, uniqueness: {message: "#{self.name} already exists at id: #{Book.find_by_name(self.name).id}"
But this does not seem to work as self.name returns 'Book'. Is there a way to access the passed parameters in this context?
You'll have to do this as a custom validation. I would do it like so:
validate :name_is_unique
private
def name_is_unique
errors.add(:name, "#{other_book.name} already exists at id: #{other_book.id}") if other_book = Book.find_by_name(name)
end
The issue isn't really that you can't include the current model attributes in your validation, its that there's no 'one-liner' way to include another model. The good news is, that's what the validate method is for.
If it bothers you to have this in your model, just write a custom validator so it can be re-used application-wide.

Automatically get column names in DataMapper

In ActiveRecord I had:
class Patient < ActiveRecord::Base
end
In DataMapper I had:
class Patient
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :orders
# ... a lot of properties and associations more
end
How to automatically get column names in DataMapper?
You don't. One of the main features of Datamapper is that you define your mappings in your models, which is why Datamapper is a great choice when working with legacy schemas so you can use rails column naming conventions without having to change a whole database schema.
property :deleted, Boolean, field: 'isdeleted'
Checkout the docs for more info but in short, this is a feature of datamapper, not a restriction. And I kind of like having the table properties defined explicitly in the model.
http://datamapper.org/why.html
This question seems to be a little old but there is a gem called 'dm-is-reflective'. It essentially does most of your mapping for you. If you are using timestamps then you will need to map your DateTime columns with something like the following. property :created_at, DateTime, :field => 'create_date'
Just install the gem, require it in your model with require 'dm-is-reflective' and then in each class you can do something like the following:
class Comment
include DataMapper::Resource
is :reflective
# manually mapping a timestamps field
property :created_at, DateTime, :field => 'create_date'
reflect /.*/ # This is just one way to do this.
end
There are a couple of other ways to set up your models but this way worked just fine for me.
For more information... https://github.com/godfat/dm-is-reflective

Is it possible to access the parent class (instead of the subclasses) when using single table inheritance?

I have a Post class, with TextPost, ImagePost, and LinkPost subclasses (using STI). These Post types are specified as strings in Post.type (as per STI convention).
I can call TextPost.all, ImagePost.all, and LinkPost.all just fine.
I thought I'd still be able to call Post.all, but I'm getting the following error:
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'text'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Post.inheritance_column to use another column for that information.
For reference, here is the relevant part of my schema.rb:
create_table "posts", :force => true do |t|
t.string "title"
t.string "type"
t.integer "author_id"
t.datetime "publish_datetime"
...
end
And my subclasses (each in their own appropriately-named .rb file):
class TextPost < Post
...
end
class ImagePost < Post
...
end
class LinkPost < Post
...
end
Am I doing something wrong? Or is it just not possible to (simply & succinctly) call the parent class when using STI?
Sounds like you have a row in your database with the type column equal to "text". Rails is trying to STI that to a text class. Looks like what you want is TextPost in the type column, not text.

Trying to get an value from a DataMapper join table and association

I have 3 classes - Mix, MixClip, and Clip.
class Mix
include DataMapper::Resource
property :id, Serial
# <removed other code for brevity>
has n, :mix_clips
has n, :clips, :through => :mix_clips
end
class MixClip
include DataMapper::Resource
property :id, Serial
property :order, Integer
belongs_to :mix
belongs_to :clip
end
class Clip
include DataMapper::Resource
property :id, Serial
property :title, String
property :description, Text
has n, :mix_clips
has n, :mixes, :through => :mix_clips
end
MixClip joins the Mix/Clip tables and includes an extra property to describe the clip (order). I would like to know if it's possible to have a clip object and be able to reference the current clip in the context it was loaded in.
So let's say I load a Mix and a Clip like so:
mix = Mix.first
clip = mix.clips.first
Is there a way to get the MixClip that is associated with that specific Clip?
clip.mix_clip.order
It was loaded through join between the table, so I would think there would be a way to do it.
I know I can just get all the mix->mix->clips-> and and drill down, but was wondering if I would be able to go back up levels... it would be simpler.
For those wondering, I'm trying to use this because dm-serializer doesn't have full support for nested associations when returning json/xml and I'd like to be able to just define a method that returns the data.
Thanks.
Without changing any of your code, you should be able to do:
mix_clip = clip.mix_clips.first(:mix => mix, :clip => clip)
to get at the join record associated with your specific mix and clip resources.
Currently, there's a bug in DM that makes it kinda unreliable to do the following without any additional measures:
mix_clip = clip.mix_clips.get(mix.id, clip.id)
This is because DM forgets the order in which relationships have been defined, and thus cannot currently know reliably in which order the .get method should accept the primary key components.
You can work around this by defining the foreign key properties explicitly in your join model, like so (note that you still have to declare the relationships explicitly, of course):
class MixClip
include DataMapper::Resource
property :id, Serial
property :order, Integer
property :mix_id, Integer, :key => true
property :clip_id, Integer, :key => true
belongs_to :mix
belongs_to :clip
end
This will make sure that DM knows that .get accepts the primary key as (mix_id, clip_id) so you're now able to call
mix_clip = clip.mix_clips.get(mix.id, clip.id)
A reason for wanting to do that is that calls to .get take the identity map into account, which, based on your access characteristics, might be able to yield better performance.