Media Type/ Resource Definition Confusion

21 views
Skip to first unread message

ribas...@gmail.com

unread,
Oct 14, 2014, 10:00:29 AM10/14/14
to praxis-...@googlegroups.com
I'm sure that I'm not understanding the concept of media types in your implementation, but my impression was that you would define an object as a media type that has ALL of the possible attributes that are available for the request AND the response for this resource.

For example, we have a referral object that has the following structure defined in the media type.

# in the media type definition

attributes do
attribute :id, Integer, description: "Referral identifier"
attribute :prospect do
attribute :email, String, description: "Prospect's email"
attribute :name, String, description: "First and Last Names"
attribute :phone, String, description: "Phone Number"
end
attribute :advocate do
attribute :id, Integer, description: "ID of contact"
attribute :email, String, description: "Advocate's email"
end
end


In the Resource Definition, it would make sense to me that when I define an action referencing the media type, I can use that media type definition. So the payload would contain the attributes that I want to use from that media type. However, the code below causes the error

Attributor::AttributorException: can not construct from already-constructed Struct

because the prospect is already defined in the media type.

My impression was the the definition in the media type is available for use here. But it's not. Also having to set the type ie. String also seems weird because you've already defined, say, email as a String in the media type. Ideally I would only define it here if it was different from the media type definition.

# in the resource definition

media_type MediaTypes::Referral
routing { prefix '/referrals' }

action :create do
routing { post '' }

payload do
attribute :prospect do
attribute :email, String, required: true
attribute :phone, String
attribute :name, String
end
attribute :advocate do
attribute :id, Integer
attribute :email, String, required: true
end
end

response :created
end


So what would be the proper way to define this?
Thanks for your help, I'm confused.

Enric Ribas

Josep Blanquer

unread,
Oct 14, 2014, 5:54:46 PM10/14/14
to ribas...@gmail.com, praxis-...@googlegroups.com
Enric, 

Your understanding is correct. At least from the "output" point of view. A MediaType is a definition that must contain ALL of the possible attributes that can ever appear in the response. It does not have any strict implications about the attributes that can appear in any request.
That being said, however, Praxis gives you the ability to have a given MediaType as a reference for the incoming payload (and params) definition in any of your actions, as a way to easily achieve exactly what you're looking for:
  1. Be able to simply list which attributes that already exist in the MediaTypes to include
  2. Be able to avoid repeating their type, their options, description etc...as they're all already in the MediaType too.
  3. Be able to "add" other attributes that might not be part of the MediaType too.
This behavior allows you to mimic an incoming request payload to be exactly (or very similar to) a given MediaType definition. 

So, the error that you're getting is tricky to comprehend because of the implicit subtleties of defining Attributor::Structs. In a nutshell, instead of your original definition:

payload do
  attribute :prospect do
    attribute :email, String, required: true
    attribute :phone, String
    attribute :name,  String
  end
...

what you really want to do is define it like this:

payload do
  attribute :prospect, Attributor::Struct do
    attribute :email, required: true
    attribute :phone
    attribute :name
  end
...


In particular, note 2 things:
  1. You need to tell the system you're defining a Struct, instead of implying that the exact type you want is the fully defined struct with all the attributes of the MediaType. You don't want that exact type, as you want to "decorate" some of it with the "required" and possibly other options. If you did not need to decorate anything at all, it would be enough to do: "attribute :prospect" (and no block). An easy way to think about this is that you don't want the "pointer" to the original type, but a copy of some of its attributes, which you can then expand and decorate.
    1. The reason for the confusing error is that the system tries to "add" your attributes in the block on top of the original MediaType Struct. And that cannot be done as the MediaType attributes are already been defined and sealed.
  2. Also note that when you're defining your struct, you're still inheriting any inner definitions from the MediaType's struct, so you don't need to specify their types (the system knows them). You also implicitly inherit any options, but you can add or override them at your leisure. For example, you can use the :required option, or override a description...etc.
I hope this makes sense. We'll look at some ways to provide more information for that type of error...because, as correct as it is, it is extremely confusing from a user point of view.

Thank you for bringing this up!

Josep M.

PS: Just for some juice and extra credit, what Praxis does under the scenes for the reference object (when defining params and payload) is equivalent to adding the right class to the "reference" option of an attribute:

payload reference: YourMediaType do
  attribute :prospect, Attributor::Struct do
    attribute :email, required: true
    attribute :phone
    attribute :name
  end

where YourMediaType is the default MediaType of the resource definition where the action belongs to. This option in the payload/params attaches a reference object to use for the inheriting attributes...so any attributes that exist in YourMediaType can directly be inherited by using just the name.



--
You received this message because you are subscribed to the Google Groups "praxis-support" group.
To unsubscribe from this group and stop receiving emails from it, send an email to praxis-suppor...@googlegroups.com.
To post to this group, send email to praxis-...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/praxis-support/c8d050b8-fc4e-4fac-b687-27a80082f61e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

ribas...@gmail.com

unread,
Oct 15, 2014, 9:48:02 AM10/15/14
to praxis-...@googlegroups.com, ribas...@gmail.com
Again, thank you for your very in-depth explanation.

I completely understand now, I think. :) After doing more thinking as well, using the media-type for the request exclusively has some limitations and your approach is very good.

However, I do agree that it is very confusing. At least for me. :)

Adding the implicit type of Attributor::Struct in the docs made me think that I was creating a new struct, while leaving it out (as in my explanation) made me think that it would reference an existing definition and allow overwriting. In fact, from my understanding, it is the opposite.

Perhaps, an idea is to define attributes, like

attribute :prospect, inherit: true do ... # this would look for a prospect object and allow overwriting

or

attribute :prospect, inherit: false do ... # this would create a new object with no reference to existing.

Just an idea. Personally, I think leaving it out should default to inherit: true but that wouldn't be backwards compatible.

In any case, I understand how it works now. Thanks again.

We at Influitive are looking forward to using Praxis to define our API for our Rails project. It seems like a great project. We especially like the ability to define what the API looks like before we write any code and then use that in the controllers. I'm sure we'll have more questions when we start adding it to our Rails project. :)

Thanks for your work on this.


On Tuesday, 14 October 2014 17:54:46 UTC-4, Josep Blanquer wrote:
> Enric, 
>
>
>
> Your understanding is correct. At least from the "output" point of view. A MediaType is a definition that must contain ALL of the possible attributes that can ever appear in the response. It does not have any strict implications about the attributes that can appear in any request.
> That being said, however, Praxis gives you the ability to have a given MediaType as a reference for the incoming payload (and params) definition in any of your actions, as a way to easily achieve exactly what you're looking for:
> Be able to simply list which attributes that already exist in the MediaTypes to includeBe able to avoid repeating their type, their options, description etc...as they're all already in the MediaType too.Be able to "add" other attributes that might not be part of the MediaType too.
> This behavior allows you to mimic an incoming request payload to be exactly (or very similar to) a given MediaType definition. 
>
>
> So, the error that you're getting is tricky to comprehend because of the implicit subtleties of defining Attributor::Structs. In a nutshell, instead of your original definition:
>
>
>
> payload do
>   attribute :prospect do
>     attribute :email, String, required: true
>     attribute :phone, String
>     attribute :name,  String
>   end
> ...
>
>
> what you really want to do is define it like this:
>
>
>
>
> payload do
>   attribute :prospect, Attributor::Struct do
>     attribute :email, required: true
>     attribute :phone
>     attribute :name
>   end
> ...
>
>
>
>
> In particular, note 2 things:
> You need to tell the system you're defining a Struct, instead of implying that the exact type you want is the fully defined struct with all the attributes of the MediaType. You don't want that exact type, as you want to "decorate" some of it with the "required" and possibly other options. If you did not need to decorate anything at all, it would be enough to do: "attribute :prospect" (and no block). An easy way to think about this is that you don't want the "pointer" to the original type, but a copy of some of its attributes, which you can then expand and decorate.
> The reason for the confusing error is that the system tries to "add" your attributes in the block on top of the original MediaType Struct. And that cannot be done as the MediaType attributes are already been defined and sealed.Also note that when you're defining your struct, you're still inheriting any inner definitions from the MediaType's struct, so you don't need to specify their types (the system knows them). You also implicitly inherit any options, but you can add or override them at your leisure. For example, you can use the :required option, or override a description...etc.

andrea.s...@gmail.com

unread,
May 19, 2016, 8:27:28 AM5/19/16
to praxis-support, ribas...@gmail.com
Hi Joseph,

the fact that you can reference MediaType in resource's payload (and - I guess - even in the params) seems not to be documented on Praxis reference, under the paragraph "Resource Definitions and Actions" (where it should be, IMHO).

I suggest you to add this very very useful piece of information, even in the praxis example app.

Thank you!

Il giorno martedì 14 ottobre 2014 23:54:46 UTC+2, Josep Blanquer ha scritto:
> Enric, 
>
>
>
> Your understanding is correct. At least from the "output" point of view. A MediaType is a definition that must contain ALL of the possible attributes that can ever appear in the response. It does not have any strict implications about the attributes that can appear in any request.
> That being said, however, Praxis gives you the ability to have a given MediaType as a reference for the incoming payload (and params) definition in any of your actions, as a way to easily achieve exactly what you're looking for:
> Be able to simply list which attributes that already exist in the MediaTypes to includeBe able to avoid repeating their type, their options, description etc...as they're all already in the MediaType too.Be able to "add" other attributes that might not be part of the MediaType too.
> This behavior allows you to mimic an incoming request payload to be exactly (or very similar to) a given MediaType definition. 
>
>
> So, the error that you're getting is tricky to comprehend because of the implicit subtleties of defining Attributor::Structs. In a nutshell, instead of your original definition:
>
>
>
> payload do
>   attribute :prospect do
>     attribute :email, String, required: true
>     attribute :phone, String
>     attribute :name,  String
>   end
> ...
>
>
> what you really want to do is define it like this:
>
>
>
>
> payload do
>   attribute :prospect, Attributor::Struct do
>     attribute :email, required: true
>     attribute :phone
>     attribute :name
>   end
> ...
>
>
>
>
> In particular, note 2 things:
> You need to tell the system you're defining a Struct, instead of implying that the exact type you want is the fully defined struct with all the attributes of the MediaType. You don't want that exact type, as you want to "decorate" some of it with the "required" and possibly other options. If you did not need to decorate anything at all, it would be enough to do: "attribute :prospect" (and no block). An easy way to think about this is that you don't want the "pointer" to the original type, but a copy of some of its attributes, which you can then expand and decorate.
> The reason for the confusing error is that the system tries to "add" your attributes in the block on top of the original MediaType Struct. And that cannot be done as the MediaType attributes are already been defined and sealed.Also note that when you're defining your struct, you're still inheriting any inner definitions from the MediaType's struct, so you don't need to specify their types (the system knows them). You also implicitly inherit any options, but you can add or override them at your leisure. For example, you can use the :required option, or override a description...etc.

Josep Blanquer

unread,
May 20, 2016, 12:44:06 PM5/20/16
to Salicetti Andrea, praxis-support, enric ribas
Absolutely!

 I checked and we have mentions in the Getting Started guide but nothing whatsoever in the reference. Good catch!

I've craeted a PR with some docs for it:

Would you mind looking at it and see if you think that's enough? I can merge the PR later/tomorrow unless you have some suggestions.

Thanks for the feedback! These things are great to capture from people that go through the docs initially.

Josep M.

Reply all
Reply to author
Forward
0 new messages