Giter Club home page Giter Club logo

liberator's Introduction

Liberator Build Status Clojars Project

Liberator is a Clojure library for building RESTful applications.

Quick Links

You can find documentation at http://clojure-liberator.github.io/liberator

If you have any questions, visit our fine google group at https://groups.google.com/forum/#!forum/clojure-liberator

Similar projects

Liberator used to be known as compojure-rest. It got renamed in July 2012.

Liberator is loosely modeled after webmachine and shares the same aims as Bishop.

Warming up

Dependencies

The examples in this document rely on you installing leiningen 2.

We'll also use curl for testing. If you don't have curl installed (ie. you're using Windows), there's some Clojure tests you can use instead.

Running the examples

A set of examples is included.

If you want to see the examples in a browser, run

lein examples

This will start a web server on port 8000 (but you can specify a alternative port with an argument, eg. lein examples 8001). Alternatively you can run the web server with lein ring server).

Ensuring the tests pass

Liberator uses Midje for testing. You can run all the tests like this :-

lein midje

Documentation

Documentation and a tutorial can be found on http://clojure-liberator.github.io.

License

Liberator is licensed under EPL 1.0 (see file epl-v10.html).

liberator's People

Contributors

andrewmcveigh avatar ansman avatar bagl avatar belucid avatar bostonaholic avatar brogowski avatar cch1 avatar daviddpark avatar dergutemoritz avatar graue avatar jbarber avatar jeluard avatar jeremyheiler avatar jonase avatar malcolmsparks avatar melvinzhang avatar michaelsbradleyjr avatar ordnungswidrig avatar paulsamways avatar pelle avatar plucury avatar r0man avatar samroberton avatar scgilardi avatar sethkrasnianski avatar timreinke avatar tobiasbayer avatar trevor avatar wasamasa avatar wunki avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liberator's Issues

Bugs in Getting Started page

  1. Due to markup, the map to run-jetty is not visible.
  2. The first resource, (ANY "/" [] (resource)), does not yield OK, but rather a 406. Digging into the code, I believe the absence of a :available-media-types causes a problem resulting in the string "text/html" being used as a function. That said, I didn't see any exception, which is odd.

Implement PATCH requests

PATCH requests are used when updating a resource that already exists in a partial way (much like how most people use PUT requests)

http://tools.ietf.org/html/rfc5789

PATCH Method for HTTP

Abstract

Several applications extending the Hypertext Transfer Protocol (HTTP)
require a feature to do partial resource modification. The existing
HTTP PUT method only allows a complete replacement of a document.
This proposal adds a new HTTP method, PATCH, to modify an existing
HTTP resource.

Necessity of handling decisions in separate way by using map

In my development, I often write decisions for different request-method as following code

decision? (fn [ctx]
                   (case (get-in ctx [:request :request-method ])
                      :get do-get-decision
                      :put do-put-decision))

So I want to make this easier like this:

decision? {:get do-get-decision :put do-put decision}

I don't know if there is anyone wants this too.

Broken examples

I was working through examples.clj yesterday and found that several of the examples didn't work without modification. The problem seems to be that they're lacking :available-media-types declarations.

For example, the hello-george example give on line 19.

I wasn't sure if that points to a problem with those examples (i.e. they're simply incorrect), or whether liberator's API and defaults have changed since the examples were written and the (originally correct) examples haven't been updated.

Also, the drag-drop example isn't working, but the issue seems to be in the ClojureScript code as I'm getting errors related to malformed JSON in my browser's console. I can't say more than that as I didn't dig into the ClojureScript parts to figure out what's going on. My focus so far has been on understanding liberator's API for decisions, handlers and declarations.

error in representation.clj

(defmethod render-map-generic "application/json" [data context]
  (with-out-str (json/print-json data)))

when running a subset of the example code (as it doesn't work for unrelated reasons) i get this when launching my ring server

Exception in thread "main" java.lang.RuntimeException: No such var: json/print-json, compiling:(liberator/representation.clj:79)
...

looking at the data.json API (http://clojure.github.com/data.json/#clojure.data.json/print-json) i see that the offending method is deprecated, and looking at recent code it appears that the function is gone.

the API states this 'DEPRECATED; replaced by 'write' to out.' about print-json. maybe line 79 of representation.clj should change to reflect the new api of data.json.

what wrong with this piece of code from your examples?

(ANY "/babel" []
(resource
:available-media-types
["text/plain" "text/html"
"application/json" "application/clojure;q=0.9"]
:handle-ok
#(let [media-type (get-in % [:representation :media-type])]
(condp = media-type
"text/plain" "You requested plain a very text"
"text/html" "

You requested HTML

"
{:message "You requested a media type"
:media-type media-type}))
:handle-not-found "Oh no, I cannot speak those language"))

It refuses to return the clojure code both from the CLI and the browser. It simply returns nothing. but the other text/plain examples worked fine. I have been forced to look at the source because of this but can't really determine the main cause of the failure.

:known-content-type?

i have implemented my known-content-type? like so

:known-content-type? (fn [{{content-type :content-type} :request}]
                         (#{"application/json"} content-type)) 

is there a better/liberator-idomatic way to do this?

Support parsing request body depending on content-type

The counterpart to representation generation is the parsing of the entity in the request body for POST, PUT, PATCH. We need a extensible mechanism like for the representation to enable parsing of the body input stream into a data structure.

Enable keywords as functions as handler

(defresource ticket-resource
  :method-allowed? (request-method-in :get)
  :available-media-types ["text/plain"]
  :exists? (fn [{{id :id} :params} :request}]
              (if-let [ticket (get-ticket-by-id id)]
                  {::ticket ticket})) ;; associate in context
  :handle-ok ::ticket ;; use the keyword as a function
  :handle-not-found "ticket not found")

Currently does not work because liberator tries to coerce ::ticket to a Representation. It would be nice if it would work exactly like :handle-ok #(::ticket %)

Understanding decision merge

The merge performed by a decision function is not a Clojure map merge, e.g. merging these two maps

{:request {:params {:a "1"}}}
{:request {:params {:b "2"}}}

will leave :params with both :a and :b keys. The current behavior is exactly what I wanted, but I had to test it to understand. I think docs need a different word than merge -- having now checked the impl I see you call it merge-map-element. Maybe there is a standard name from some other FP language...

Documentation is too low-contrast and the sidebar does not scroll

Hi! ... I love the updated docs, but:

1.- The color scheme is too low contrast ... I have trouble reading the boxes under "Include in project.clj" on the home page (the "highlight" and "alert warning" styles in the CSS.

2.- The position of the sidebar is fixed. I'm using Chrome on a 1336x768 screen and I cannot read the links below DOCUMENTATION > Integration unless I zoom out the screen which is troublesome.

3.- Finally, this may sound like an aesthetic choice but, please try to use a heavier font which enhances readability. Lato is fine, but Lato-light is too narrow!

Other than the above I applaud the updated docs, thanks!

should handle-created be dependent on the action completion?

For example, if I have a resource such as

(def users (atom ["foo" "bar"]))

(defresource add-user
  :method-allowed? (request-method-in :post)  
  :post!  
  (fn [context]             
    (let [params (get-in context [:request :form-params])] 
      (swap! users conj (get params "user"))
      (println "in post!" @users)))
  :handle-created (do
                    (println "in handle ok" @users)
                    (generate-string @users))
  :available-media-types ["application/json"])

(defroutes home-routes
  (ANY "/add-user" request add-user))

handle-created gets called before post!, so the old state is returned to the client. Is there something I'm missing here?

Make a complete, simple follow along example

I read the README.md and the example starts with some excitement but fizzles out.

I will personally explore further and see if I can get the basics going by reversing engineering your samples. I know plenty of devs who will not go to those lengths.

Bug with how [:representation :charset] slot is getting populated in the context at the 'charset-available?' decision

The liberator.conneg/best-allowed-charset function contains an "or" expression in which the 3rd clause explicitly tests for "iso-8859-1" (and returning a quality value of 1), and this is causing some trouble.

For example, if I have a request with an "Accept-Charset" value of: "ISO-2022-CN", best-allowed-charset returns "ISO-8859-1". But if I have request with value: "UTF-8", best-allowed-charset returns "UTF-8". This is because "UTF-8" appears after "ISO-8859-1" from a lexicographic sort order, and since the 'select-best' function does a sort, reverse, first, UTF-8 is returned. But for ISO-2022-CN, ISO-8859-1 is returned.

FYI, (best-allowed-charset "ISO-2022-CN;q=1,ISO-8859-1;q=0.8") correctly returns ISO-2022-CN because 'select-best' does the sort based on the parsed quality number first.

I feel the solution is simply to remove the explicit 3rd clause of the aforementioned 'best-allowed-charset' function; I just don't see why an explicit clause should be there for the "ISO-8859-1" charset.

Thank you,

-Paul

POST to existing should return 409 error

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
409 conflict
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

10.4.10 409 Conflict

The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough

information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.

Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

Support custom decision graphs

Extensions of the http spec, like webdav, introduce additional status codes and need support for custom decision graphs. Today, the decision graph is defined as a hardcoded set of functions calling each other in a well defined way. Injection of custom paths is very hard and error prone.

I suppose to change the decision graph execution model to support multiple graphs not defined as chain of functions in a namespace but as data. This can be a map:

{:service-available? (decision :service-available? :known-method? :handle-service-not-available?)}

where decisionexpands to a function that looks up the callback from the resource implementation and branches to known-method? or handle-service-not-available?

Liberator will provide default decision graphs for RFC2616 (what we have in 1.0) and RFC4918 for Webdav, e.g.

We should look into https://github.com/Prismatic/plumbing

Support new interceptor model that Ring now supports

Ring refactored the standard handlers to support the more complicated, but async-io friendly, interceptor model that separates request handling from response generation. Is liberator friendly to a similar refactoring? If so, is this something you are interested or I might take on in a few months when I need the interceptor model?

POST request issue

I am using liberator to expose my services as REST service,I use Angular.js for all the UI work, So I need response like this {status:200,event_id:11234} and if fails I should get {status:400,error:"Could not create event"}.I have a POST request, Below is the code, I could do the process on calling the service as POST, but I want to send back the event id as response of the POST,

(defresource send-event-resource
:method-allowed? (request-method-in :post)
:available-media-types ["text/plain"]
:post! (fn [context](workers/send-event context)))

Preserve media type params in conneg

Currently in 0.6.0, media types from the accept header and :available-media-types are stripped down to pairs like ["text" "html"]. That pairs are used to compare acceptable types and so on.

I need to be able to specify media types with params like application/json;profile="urn:org.restfulobjects:homepage" in :available-media-types because that is my content type of the representation I like to return. You can find such media types in the Restful Objects Spec.

I already looked into the liberator.conneg namespace. But this wouldn't be a small patch. I think the namespace would benefit from a MediaType protocol and record. But I like to discuss this first.

Merging the return value of a decision causes duplicate values in vectors

:allowed-methods [:put :delete :get]
  :available-media-types ["application/json"]
  :allowed? (fn [_]
                     {::instance {:children ["one" "two"]})
  :exists? ::instance
  :can-put-to-missing? false
  :new? false
  :respond-with-entity? (fn [{{method :request-method} :request}] (= :put method))
  :put! (fn [ctx]
          (let [old (get ctx ::instance)
                new {::instance {:children ["one"]}]))
...
  :handle-ok (fn [ctx]
               (get ctx ::instance)))

The above example yields ["one" "one" "two"], but I think it's reasonable to expect the same behavior as merge?

user=> (merge {:foo ["one" "two"]} {:foo ["one"]})
{:foo ["one"]}

Caused by the use of concat on lists and vectors:
https://github.com/clojure-liberator/liberator/blob/master/src/liberator/core.clj#L77

Mail group thread:

http://groups.google.com/group/clojure-liberator/t/4c58338847eb585e

Requesting OPTIONS when allowed in resource gives NullPointerException

java.lang.NullPointerException
    at liberator.core$generate_options_header.invoke(core.clj:465)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$is_options_QMARK_.invoke(core.clj:467)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$valid_entity_length_QMARK_.invoke(core.clj:470)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$known_content_type_QMARK_.invoke(core.clj:473)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$valid_content_header_QMARK_.invoke(core.clj:475)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$allowed_QMARK_.invoke(core.clj:478)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$authorized_QMARK_.invoke(core.clj:481)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$malformed_QMARK_.invoke(core.clj:484)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$method_allowed_QMARK_.invoke(core.clj:487)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$uri_too_long_QMARK_.invoke(core.clj:490)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$known_method_QMARK_.invoke(core.clj:493)
    at liberator.core$decide.invoke(core.clj:110)
    at liberator.core$service_available_QMARK_.invoke(core.clj:496)
    at liberator.core$run_resource.invoke(core.clj:554)
    at clinicico.server.handler$test_resource.invoke(handler.clj:41)
    at compojure.response$eval1165$fn__1166.invoke(response.clj:27)
    at compojure.response$eval1126$fn__1127$G__1117__1134.invoke(response.clj:10)
    at compojure.core$make_route$fn__1301.invoke(core.clj:93)
    at compojure.core$if_route$fn__1285.invoke(core.clj:39)
    at compojure.core$if_method$fn__1278.invoke(core.clj:24)
    at compojure.core$routing$fn__1307.invoke(core.clj:106)
    at clojure.core$some.invoke(core.clj:2443)
    at compojure.core$routing.doInvoke(core.clj:106)
    at clojure.lang.Rest2013-05-13 13:12:32.409:WARN:oejs.AbstractHttpConnection:/test
java.lang.Exception: Unrecognized body: java.lang.NullPointerException
    at ring.util.servlet$set_body.invoke(servlet.clj:103)
    at ring.util.servlet$update_servlet_response.invoke(servlet.clj:112)
    at ring.adapter.jetty$proxy_handler$fn__94.invoke(jetty.clj:20)
    at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$0.handle(Unknown Source)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
    at org.eclipse.jetty.server.Server.handle(Server.java:349)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:452)
    at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:884)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:938)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:634)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:2Fn.applyTo(RestFn.java:139)
    at clojure.core$apply.invoke(core.clj:619)
    at compojure.core$routes$fn__1311.invoke(core.clj:111)
    at ring.middleware.keyword_params$wrap_keyword_params$fn__1734.invoke(keyword_params.clj:27)
    at ring.middleware.nested_params$wrap_nested_params$fn__1780.invoke(nested_params.clj:65)
    at ring.middleware.params$wrap_params$fn__1571.invoke(params.clj:55)
    at clinicico.server.middleware$wrap_request_logger$fn__4288.invoke(middleware.clj:45)
    at clinicico.server.middleware$wrap_exception_handler$fn__4285.invoke(middleware.clj:29)
    at clinicico.server.middleware$wrap_response_logger$fn__4293.invoke(middleware.clj:51)
    at clojure.lang.Var.invoke(Var.java:415)
    at ring.middleware.reload$wrap_reload$fn__968.invoke(reload.clj:18)
    at ring.middleware.stacktrace$wrap_stacktrace_log$fn__755.invoke(stacktrace.clj:15)
    at ring.middleware.stacktrace$wrap_stacktrace_web$fn__786.invoke(stacktrace.clj:79)
    at ring.adapter.jetty$proxy_handler$fn__94.invoke(jetty.clj:18)
    at ring.adapter.jetty.proxy$o30)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:76)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:609)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:45)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:599)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:534)
    at java.lang.Thread.run(Thread.java:722)
rg.eclipse.jetty.server.handler.AbstractHandler$0.handle(Unknown Source)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
    at org.eclipse.jetty.server.Server.handle(Server.java:349)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:452)
    at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:884)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:938)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:634)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:76)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:609)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:45)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:599)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:534)
    at java.lang.Thread.run(Thread.java:722)

For the following resource

(defresource test-resource
  :available-media-types ["text/plain"]
  :method-allowed? (request-method-in :post :get :options)
  :handle-ok "Foo"
  )

(defn assemble-routes []
  (->
    (routes
      (ANY "/test" [] test-resource)

I tried with both 0.8 and the 0.9-SNAPSHOT

Any idea what is causing this?

Accept-charset not present

I was having an issue today that Firefox was interpreting a response as iso-8859-1 when no charset header was set. Liberator does not set the charset in the content-type header when no Accept-charset header is set. And Firefox was not sending an Accept-charset header.

My available-charsets is set to UTF-8.

:available-media-types ["text/html" "application/json"]
:available-charsets ["UTF-8"]

I wonder if the default behavior for no Accept-charset header should be to treat it as * instead of setting no charset. If the charset of my content is known, is there any reason not to set the charset?

Thanks!

Last-modified returns false on exact matches

When checking to see if a resource has been modified since a certain time, if the time sent by the request exactly matches the resource, 304 Not Modified is not triggered, and the resource is sent back. I would expect it to be not modified, especially as the time the request sends will likely be the exact time on the resource, since it got it from an original request to the resource.

The line of code that I think should be changed is here: https://github.com/clojure-liberator/liberator/blob/master/src/liberator/core.clj#L310. Instead of (.after ...), I think it should be (not (.before ...)). I can of course submit a pull request, but I want to ascertain first if I am missing something important about this behavior.

Sessions

Hi,

I'm using ring.middleware.session, trying to set the session like this:

(GET "/wall" [] (resource :available-media-types ["text/html"]
                            :handle-ok (fn [ctx]
                                         (log! :trace (get-in ctx [:request :session]))
                                         (let [counter (if-let [counter (get-in ctx [:request :session :counter])]
                                                                (+ counter 1)
                                                                1)]
                                               (ring-response {:session {:counter counter} :body (str "You accessed this page " counter " times.")}))))

But the session is not set. I must be missing something fundamental. Can someone point me to an explanation on how to work with sessions in liberator?

Thank you in advance.

JSON of class java.sql.Timestamp

When I return an entity from database (using sqlkorma) in :handle-ok I get:

Exception: java.lang.Exception: Don't know how to write JSON of class java.sql.Timestamp

With ring-response and cheshire library everything is okay.

Liberator version: 0.9.0

Available keywords should be discoverable and validatable

The available resource declaration keywords are only discoverable from the documentation or the source. They should be easily available from a namespace.

Simple typos of a declaration keyword are not detected and lead to hard to find bugs. A validation mechanism should be available, at least optional.

customizable representation generation

Currently the representation generation in liberator cannot be easily replace for a certain media type. While custom rendering is possible, it is arkward becuase it needs to be implemented for each handler and each resource.

(resource foo
  :handle-ok
  (fn [_] {:this :is :a [:json :map]}
  :render-representation
  (fn [{{mt :media-type} :representation}]
    (when (= mt "application/json") my-custom-render-multi-method)))

I suggest to introduce a new resource key :render-representation which is a function that renders a representation (as generated by a handler) to a ring response. For backward compatibility this function will be set to as-response be default.

This idea was discussed here: https://groups.google.com/d/msg/clojure-liberator/LKFrpv_OEUY/7d2gjyaHk0EJ

Setting response headers

Hello,

I was wondering what is the recommended way to set a response header. For example right now the only way I found out was to do this inside a resource:

(defresource my-resource
   :handle-ok {:headers my-headers :status my-status :body my-body})

Is there a better way to do it?

Thank you for your time

Basic Liberator questions

Hi,

First I would like to thank your for this great project.

I'm having some difficulties understanding how Liberator works.

For a resource such:

/tickets

It should handle listing all the tickets when HTTP method is GET and create a new ticket when HTTP method is POST.

post! will handle creating new ticket but how to handle listing all tickets?

Also, how send to the client the URI of the new created ticket?

Thanks for help and time.
(sorry to use "Issues" section but I'm unable to get help).

Media-types should support parameters

As an example, I have this media-type:

application/json;schema=vnd.fcms.item;version=1

I'd like to be able to take advantage of the schema and version media-type parameters.

A numeric "level" parameter should be supported as well.

Maybe we should sort
on numeric values (highest) first and then all other parameter values in order of declaration in :available-media-types.

Maybe we can take some ideas from

http://search.cpan.org/~gaas/libwww-perl-5.834/lib/HTTP/Negotiate.pm
http://httpd.apache.org/docs/current/content-negotiation.html

RESTful best practices through a particular scenario

Please indulge me and other "frameworks conditioned" escapees new to the wonderful world of REST.

Scenario: a resource requires authorization.

If I leverage the :authorized? key available in resources, handle-unauthorized only allow me to send a message wrapped in a pre tag. First question: why this limitation?

Left pondering, I dare to assume that RESTful practices propose that the client should handle the situation, recognizing that it needs to go to an authorization page.

I believe that the spec specifies in this case: "The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource." But Liberator doesn't follow this, right? Second question: why?
Liberator is all about reifying the HTTP spec, no?

Most apps written in web frameworks will not return a 401, but instead do a redirect to the authorization page. This is possible in Liberator too, because nothing stops you from handling the authorization in the resource itself (handle-ok + logic).
Is this considered a breach of practice? And more importantly, what is the downside to that? Do we lose benefits otherwise retained by good style?

Finally, if you let the server generate html dynamically, you don't need to write client code at all (or very little). If RESTful forces the client to handle errors, does it mean that it challenges the whole model of server-side generated web apps?

These are basic questions. I'm not wondering alone, though. Someone posted recently a similar question on stack overflow

Looking forward to hearing enlightening explanations.

Thank you very much indeed.

redundant :method-allowed

i notice that we have

:post!
:delete!
:put!

so, if i use one of these then the :method-allowed is implied.
this isn't the case for :get :head :options :patch or whatever else we want

wouldn't it make sense to have the default :method-allowed to be derived from the resource map itself? adding the other method keys to the map would allow for this.

or am i missing something? i am new to liberator.

Trace ui

I noticed a behavior which I'm not sure how to interpret.

I turned on trace in the ui on two machines where the same liberator application is running.

On one machine, the flowchart has coloring, so you can see how the decision tree was traversed for any request.
On the other instance, the flowchart is missing the highlighting/coloring.

One machine runs Mac OS X, the other Debian.
Here is a link to a request trace on the linux box where I don't see highlighting/coloring.

http://gad.tuppu.net:8000/x-liberator/requests/midqg

Am I missing something?

Thanks.

HEAD by default shouldn't return a body

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

9.4 HEAD

The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. The metainformation contained in the HTTP headers in response to a HEAD request SHOULD be identical to the information sent in response to a GET request. This method can be used for obtaining metainformation about the entity implied by the request without transferring the entity-body itself. This method is often used for testing hypertext links for validity, accessibility, and recent modification.

The response to a HEAD request MAY be cacheable in the sense that the information contained in the response MAY be used to update a previously cached entity from that resource. If the new field values indicate that the cached entity differs from the current entity (as would be indicated by a change in Content-Length, Content-MD5, ETag or Last-Modified), then the cache MUST treat the cache entry as stale.

currently a head request will reach :handle-ok which by default returns "OK"

Adding liberator to existing project cause cljs/analyzer__init.class exception

Hello Liberators!
Adding [liberator "0.3.2"] to my project.clj cause the complication fail with the following:
"Exception in thread "main" java.io.FileNotFoundException: Could not locate cljs/analyzer__init.class or cljs/analyzer.clj on classpath"
Might be related to the ring/ring-jetty-adapter "1.1.0"; compojure "1.1.3" I'm using.
Would appreciate your help.

Full project.clj and stack trace below

(defproject textflow "0.1.0-SNAPSHOT"
  :description "Online generation of RFC like call flows"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.4.0"]
                 [compojure "1.1.3" :exclusions [ring/ring-core]]
                 [hiccup "1.0.2"]
                 [com.novemberain/monger "1.4.2"]
                 [ring/ring-devel "1.1.6"]
                 [ring/ring-jetty-adapter "1.1.0"]
                 [jayq "2.0.0"]
                 [ring/ring-json "0.1.2"]
                 [liberator "0.3.2"]]
  :min-lein-version "2.0.0"
  :plugins [[lein-cljsbuild "0.2.10"]]
  :hooks [leiningen.cljsbuild]
  :cljsbuild { 
              :builds {
                       :main {
                              :source-path "src-cljs"
                              :compiler
                              {
                               :output-to "resources/public/js/cljs.js"
                               :optimizations :whitespace
                               :pretty-print true
                               }
                              }
                       }
              }
  :production {:misc "configuration"
               :offline true
               :mirrors {#"central|clojars"
                         "http://s3pository.herokuapp.com/clojure"}}
  :main textflow.server)

Stack trace

lein compile
Compiling ClojureScript.
Exception in thread "main" java.io.FileNotFoundException: Could not locate cljs/analyzer__init.class or cljs/analyzer.clj on classpath: 
    at clojure.lang.RT.load(RT.java:432)
    at clojure.lang.RT.load(RT.java:400)
    at clojure.core$load$fn__4890.invoke(core.clj:5415)
    at clojure.core$load.doInvoke(core.clj:5414)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5227)
    at clojure.core$load_lib.doInvoke(core.clj:5264)
    at clojure.lang.RestFn.applyTo(RestFn.java:142)
    at clojure.core$apply.invoke(core.clj:603)
    at clojure.core$load_libs.doInvoke(core.clj:5298)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.core$apply.invoke(core.clj:603)
    at clojure.core$require.doInvoke(core.clj:5381)
    at clojure.lang.RestFn.invoke(RestFn.java:457)
    at cljsbuild.compiler$eval5$loading__4784__auto____6.invoke(compiler.clj:1)
    at cljsbuild.compiler$eval5.invoke(compiler.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6511)
    at clojure.lang.Compiler.eval(Compiler.java:6501)
    at clojure.lang.Compiler.load(Compiler.java:6952)
    at clojure.lang.RT.loadResourceScript(RT.java:359)
    at clojure.lang.RT.loadResourceScript(RT.java:350)
    at clojure.lang.RT.load(RT.java:429)
    at clojure.lang.RT.load(RT.java:400)
    at clojure.core$load$fn__4890.invoke(core.clj:5415)
    at clojure.core$load.doInvoke(core.clj:5414)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5227)
    at clojure.core$load_lib.doInvoke(core.clj:5264)
    at clojure.lang.RestFn.applyTo(RestFn.java:142)
    at clojure.core$apply.invoke(core.clj:603)
    at clojure.core$load_libs.doInvoke(core.clj:5298)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.core$apply.invoke(core.clj:603)
    at clojure.core$require.doInvoke(core.clj:5381)
    at clojure.lang.RestFn.invoke(RestFn.java:436)
    at user$eval1.invoke(NO_SOURCE_FILE:1)
    at clojure.lang.Compiler.eval(Compiler.java:6511)
    at clojure.lang.Compiler.eval(Compiler.java:6500)
    at clojure.lang.Compiler.eval(Compiler.java:6477)
    at clojure.core$eval.invoke(core.clj:2797)
    at clojure.main$eval_opt.invoke(main.clj:297)
    at clojure.main$initialize.invoke(main.clj:316)
    at clojure.main$null_opt.invoke(main.clj:349)
    at clojure.main$main.doInvoke(main.clj:427)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:419)
    at clojure.lang.AFn.applyToHelper(AFn.java:163)
    at clojure.lang.Var.applyTo(Var.java:532)
    at clojure.main.main(main.java:37)
Subprocess failed

duplicate 501 response

(defhandler handle-unknown-method 501 "Unknown method.")
(defhandler handle-not-implemented 501 "Not implemented.")

http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

405 Method Not Allowed
A request was made of a resource using a request method not supported by that resource;[2] for example, using GET on a form which requires data to be presented via POST, or using PUT on a read-only resource.

maybe the below should be switched to

(defhandler handle-unknown-method 405 "Method Not Allowed.")

OPTIONS should return 200 OK and "Allow" header.

Currently curl -X OPTIONS http://localhost:8000 returns a 201 Created. This should be a 200 OK and a Allow contained within the header. Right?

If so, let me know and I will send a pull request your way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.