header syntax in action definitions

29 views
Skip to first unread message

Stephen Crosby

unread,
Jul 7, 2014, 3:10:02 PM7/7/14
to praxis-de...@googlegroups.com
Currently we have a special DSLCompiler to provide special syntax for praxis action definitions. This allows you to declare headers like this:
headers do
  header :X_REQUESTED_WITH
end

rather than:
headers do
  attribute :X_REQUESTED_WITH, String, :required => true
end

This makes declaring headers simpler than using the attributor interface directly. But my thought is that Praxis users should already be used to dealing with the attributor interface and I think the simplification could actually add to the confusion. Here's an example where you can pass 'blog_id' as a query string parameter or the request body. But you must pass token as a header:
action :create do
  routing { post '/' }
  params  { attribute :blog_id, String }
  payload { attribute :blog_id, String }
  headers { header :blog_id }
end

To me it actually seems simpler and more consistent to spell it out:
action :create do
  routing { post '/' }
  params  { attribute :blog_id, String }
  payload { attribute :blog_id, String }
  headers { attribute :header,  String, required: true }
end

I like the consistency in the way we treat all three action inputs (params, payload and headers). It would also simplify the implementation and add flexibility to the way developers can set up headers in praxis.

Josep Blanquer

unread,
Jul 8, 2014, 12:43:39 PM7/8/14
to praxis-de...@googlegroups.com
Stephen,

 First some quick history about this. The special DSL for headers was initially built for a couple of reasons:
  1. To accommodate the common case where header values need to be handled as pure, unmodifyed strings 
  2. To simplify their definition and avoid all the complexity of defining :required, possible values, regexps through the hash set of options.
Even beyond that, the fact that we reuse an Attributor::Attribute to store load and dump each header is actually hidden from the users.
What this means is that underneath, the current DSL really does the following:
  • header :Foo   (i.e., a header named Foo must exist in the request)
    • maps to: attribute String, required: true   
  • header :Baz , "some_value"    (i.e., if a header named Baz exists, it must match "some_value" string)
    • maps to: attribute String, values: ["some_value"]
  • header :Bar, /pattern/   (i.e., if a header named Bar exists, it must match the /pattern/ regexp)
    • maps to: attribute String, regexp: /pattern/
More concise and less verbose. Now, this headers syntax, also allows passing any extra options, and those will be applied to the underlying attribute. For example, you could express that the Bar header should not only match the /pattern/ but that also should exist by:  header :Bar, /pattern/ , required: true
So suddenly, the simplicity starts becoming a little less clear...

So, while this case made sense for simplifying 99% of the header definitions, the current pervasiveness of the Attributor syntax in the ResourceDefinitions might make that simplification less "interesting", like you're pointing out. In fact, after thinking about it, I see no reason not to create a URI attributor type, and using wholesale in headers like Location, so that by the time it makes it to the app we have a nicely loaded and validated type ready to go...no need to make the app convert the string on its own. 

For example:
  •  attribute :Location, Attributor::URI, required: true

Also, I'm  not keen on having different ways to do the same thing, unless there is a massive gain, so I wouldn't advocate to support both ways, at least from the get go. The way we can inject DSL definitions for attributor blocks leaves the door open to change and add new methods in the future. So, I think I'm buying the approach of ditching the special DSL "header" in lieu of a normal, full fledged attribute syntax. More verbose, but already a familiar syntax, and also potentially more powerful for the intrepid devs :)

I'm interested in hearing what are the other people thoughts.

Josep M.

Raphael Simon

unread,
Jul 8, 2014, 5:32:58 PM7/8/14
to praxis-de...@googlegroups.com
This all makes sense but just playing Devil's advocate for a minute: one major difference between headers and payloads or params is that headers can only be strings. My first thought when I see that I can use a "standard attribute syntax" to define a header is "what does that mean if I use a integer, a collection, a hash or attributor model as type for a header definition?". From that point of view using the full syntax does add more confusion.

Josep Blanquer

unread,
Jul 8, 2014, 5:58:12 PM7/8/14
to Raphael Simon, praxis-de...@googlegroups.com
Raphael, 

  that was my initial impression too, and the reason behind the header DSL being different.

But like I was describing above, there is really no reason for a header value not to be coerced to the appropriate type. The example of the URL above I think is a good one. Yes, obviously anything that the web gives to us is going to be a string of sorts, but that doesn't mean it doesn't encode some specific structure or brings with it a specific semantic with it.

I'm just repeating this cause that was how I saw it, but after thinking more about it due to this thread, I don't see coercing header values any weirder than coercing path parameter or query strings...and I think we all agree and see the value there...so why not?

Josep M.


On Tue, Jul 8, 2014 at 2:32 PM, Raphael Simon <rap...@rightscale.com> wrote:
This all makes sense but just playing Devil's advocate for a minute: one major difference between headers and payloads or params is that headers can only be strings. My first thought when I see that I can use a "standard attribute syntax" to define a header is "what does that mean if I use a integer, a collection, a hash or attributor model as type for a header definition?". From that point of view using the full syntax does add more confusion.

--
You received this message because you are subscribed to the Google Groups "praxis-development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to praxis-developm...@googlegroups.com.
To post to this group, send email to praxis-de...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/praxis-development/67efb0ef-69c8-43d8-b572-b7b031c85277%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Raphael Simon

unread,
Jul 8, 2014, 6:12:24 PM7/8/14
to Josep Blanquer, praxis-de...@googlegroups.com
Makes sense, let's just make sure to document the fact that a type used in a header definition must be be compatible with (coercible(?) into) a string.

--
Raphael. 

Raphael Simon

unread,
Jul 8, 2014, 6:17:05 PM7/8/14
to Josep Blanquer, praxis-de...@googlegroups.com
Josep just pointed out that this discussion is about specifying headers for incoming requests and not about specifying headers in response definitions. So my previous comment doesn't apply...

--
Raphael.
Reply all
Reply to author
Forward
0 new messages