In general, the code is still impressively verbose. OTOH, it's incredibly short for Java code. I suspect that line/feature added count will increase more quickly than it would with Python or Ruby and it will quickly become a nightmare scenario indistinguishable from a more traditional Java web approach.
It may be slightly more verbose but you have to consider that the Java variant contains much more information in a single location. For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window constantly Ctrl-Fing through it to sort out whats going on.
> For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window
URL mapping is a framework detail, not a language detail — although the framework can be limited by the language. In Sinatra you'd write:
each style (these and half a dozen others) has its advantages and inconvenients. For instance, separate urls mapping (Rails, Django) give a starting point laying out the structure of the site and make views/controllers easier to reuse (as they're not coupled to URLs)[0], whereas annotating the handler directly gives a better view of where they're involved and how.
[0] They also make it simpler to "graft" sub-sites in
@GET
public Collection<Integer> listStudentIds() {
return STUDENTS.keySet();
}
or
@DELETE
@Path("{uid: [0-9]+}")
public boolean deleteStudent(@PathParam("uid") int uid) {
final Student deletedStudent = STUDENTS.remove(uid);
if (deletedStudent == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
} else {
return true;
}
}
I'll grant that Java programs often become more verbose than their Python/Ruby counterparts - but I would argue that trait can be held to a minimum with the proper libraries and coding style.
the last line generally wouldn't be this complex, in Flask you'd just call `flask.abort(404)` (which can be used inline with an `or`, as with waffle_ss's ruby example, but that's not usual/good Python style), in Django it'd be `raise Http404()`.
Although in Django you'd really use the `get_object_or_404` shortcut for ORM objects:
And you wouldn't bother with the return since a 200 result would mean the deletion was correctly executed.
[0] Used Python's MutableMapping API here, it works slightly differently than Java's equivalent Map interface: `.keys()` returns a sequence of the mapping's keys and `.pop()` asks for a default value to return in case nothing was found there, otherwise it raises `KeyError` if the key was not found)
In Ruby, everything evaluates to true except false and nil. Also, the last statement executed is your return value. Therefore your first function could be written as
def delete_student(uid)
STUDENTS.remove(uid) || raise WebApplicationException.new(Response.Status.NOT_FOUND)
end
I have no idea what the annotations mean, but if we use exceptions for transmitting error conditions, then there is no point having a true | exception function.
public void deleteStudent(int uid) {
if (STUDENTS.remove(uid) == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
In that case it is preferable to use "or" over "||" since then you do not have to worry so much about operator precedence because "or" has about the weakest of all operators. This means in this case that you can skip the explicit ".new" for the exception.
def delete_student(uid)
STUDENTS.remove(uid) or raise WebApplicationException, Response.Status.NOT_FOUND
end
You could make it shorter by using something like sinatra[1]. Here is my pseudo-sinatra code.
get "/list_student_ids" do
STUDENTS.key_set
end
delete "/delete_student/:uid" do
STUDENTS.remove(params[:uid]) or raise WebApplicationException, Response::Status::NOT_FOUND
true
end
1. Thing's most of those types provide no value in being spelled out like that. The ones on the methods have documentary advantages, but that's about it, the rest is magical invocations to make a stupid compiler happy.
2. There's also a gain from the framework being able to map things to each other without being told explicitly. `@PathParam("uid")` for instance, it's only there because the framework has no way to match the parameter name `uid` to the URL-extracted parameter `uid`. Likewise with HTTP methods being part of the routing instead of a separate annotation, in part.
3. Java's APIs and type system, such as implicit nulls and not being able to use arbitrary types in boolean contexts
I would say they are mainly from removing the need for decorators due to there being a nice lambda function/block syntax. So we can define methods like "get" and "delete" which does the job of the decorators, creating something kind of like a mini-DSL.
Some savings through the removal of types, sure, but that is not what I personally see as most import here.
With Ruby folks can always try to remove redundancy. :-)
I find it amazing that "DRY" has become such a positive pattern when it was touted as one of the driving points of the Ruby on Rails framework. Don't repeat yourself.
For instance, Java folks are always ready to throw exceptions instead of letting stuff resolve itself if it doesn't quite work.
In that example I only throw an exception when you try to delete a student who doesn't exist. The reason I do so is that it contains semantic information which is useful for the client.
If I returned a fake value - the client would get a 200 response on his attempt to delete a non-existent resource, which is clearly the incorrect behavior. I could return nothing - but then the client would see a 204. So, I choose to throw an exception with a response 'NOT_FOUND'
throw new WebApplicationException(Response.Status.NOT_FOUND);
There are a few things I'd like to point out to add to this example:
1) JAX-RS is still new. In the future, they might improve the exception handling or whatnot (so if you return null, they might decided to return 404 ... who knows)
2) We can always wrap all JAX-RS with a Filter. So you can throw JPA level exception (not found) and let the filter catch it and convert it to WebApplicationException(404). Thus in theory, your JAX-RS implementation won't have any RuntimeException explicitly in the code.
I did try and build your code to run the test (I've no idea where the CsvParam and DateParam classes come from) but I think that the following will give you what you want without throwing the exception.
@DELETE
@Path("{uid: [0-9]+}")
public Response deleteStudent(@PathParam("uid") int uid) {
final Student deletedStudent = STUDENTS.remove(uid);
if (deletedStudent == null) {
return Response.status(Response.Status.NOT_FOUND).build();
} else {
return Response.ok().entity(true).build();
}
}
I've written large Rails and Sinatra apps, as well as a couple Spring apps and straight Java apps. I"m evaluating Play now and even though I usually prefer Ruby to Java, I'm finding Play (with Eclipse configured to be Textmate like) as productive, if not more so, than Rails.
Comments
In general, the code is still impressively verbose. OTOH, it's incredibly short for Java code. I suspect that line/feature added count will increase more quickly than it would with Python or Ruby and it will quickly become a nightmare scenario indistinguishable from a more traditional Java web approach.
It may be slightly more verbose but you have to consider that the Java variant contains much more information in a single location. For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window constantly Ctrl-Fing through it to sort out whats going on.
> For example having a single file with methods annotated with @GET or @POST is so much cleaner to me than having routes.rb open in another editor window
URL mapping is a framework detail, not a language detail — although the framework can be limited by the language. In Sinatra you'd write:
http://www.sinatrarb.com/intro.html#Routeseach style (these and half a dozen others) has its advantages and inconvenients. For instance, separate urls mapping (Rails, Django) give a starting point laying out the structure of the site and make views/controllers easier to reuse (as they're not coupled to URLs)[0], whereas annotating the handler directly gives a better view of where they're involved and how.
[0] They also make it simpler to "graft" sub-sites in
I think that's a matter of preference.
I don't mind both styles (after working with Spring MVC 3, Servlet 3, JAX-RS, and Rails)
How would these be any shorter in Python or Ruby?
or I'll grant that Java programs often become more verbose than their Python/Ruby counterparts - but I would argue that trait can be held to a minimum with the proper libraries and coding style.First example, Python (keeping a similar API):
second example, Python: the last line generally wouldn't be this complex, in Flask you'd just call `flask.abort(404)` (which can be used inline with an `or`, as with waffle_ss's ruby example, but that's not usual/good Python style), in Django it'd be `raise Http404()`.Although in Django you'd really use the `get_object_or_404` shortcut for ORM objects:
And you wouldn't bother with the return since a 200 result would mean the deletion was correctly executed.[0] Used Python's MutableMapping API here, it works slightly differently than Java's equivalent Map interface: `.keys()` returns a sequence of the mapping's keys and `.pop()` asks for a default value to return in case nothing was found there, otherwise it raises `KeyError` if the key was not found)
An other note: the @path decorator would probably use named groups instead of some sort of DSL, so
(the `r` prefix is for `r`awstring, so the developer does not have to string-escape each backslash used in the regex among other things)In Ruby, everything evaluates to true except false and nil. Also, the last statement executed is your return value. Therefore your first function could be written as
I have no idea what the annotations mean, but if we use exceptions for transmitting error conditions, then there is no point having a true | exception function.
I love it!
And that is the reason I still write in Python or Lisp when I don't need something (Hadoop, Lucene, etc) from the JVM ;-)
If you need something from the JVM and want to write Lisp, use Clojure. Checkout Hadoop extensions in Clojure at https://github.com/jblomo/oddjob .
In that case it is preferable to use "or" over "||" since then you do not have to worry so much about operator precedence because "or" has about the weakest of all operators. This means in this case that you can skip the explicit ".new" for the exception.
I seem to remember this same feature causing lots of headache in C in the form of subtle bugs mostly the result of this being used in awkward ways.
Personally I'd rather have a distinct boolean value to test for, the java version is much more explicit as to exactly what its doing.
Hmm can't say I've ever encountered something like that in Ruby so far. However, if you want a strict boolean it's pretty easy to get:
In Ruby, you don't write code, code writes you! ;-)
You could make it shorter by using something like sinatra[1]. Here is my pseudo-sinatra code.
1. http://www.sinatrarb.com/Your savings here are mainly from removing types. That's another matter entirely.
1. Thing's most of those types provide no value in being spelled out like that. The ones on the methods have documentary advantages, but that's about it, the rest is magical invocations to make a stupid compiler happy.
2. There's also a gain from the framework being able to map things to each other without being told explicitly. `@PathParam("uid")` for instance, it's only there because the framework has no way to match the parameter name `uid` to the URL-extracted parameter `uid`. Likewise with HTTP methods being part of the routing instead of a separate annotation, in part.
3. Java's APIs and type system, such as implicit nulls and not being able to use arbitrary types in boolean contexts
I would say they are mainly from removing the need for decorators due to there being a nice lambda function/block syntax. So we can define methods like "get" and "delete" which does the job of the decorators, creating something kind of like a mini-DSL.
Some savings through the removal of types, sure, but that is not what I personally see as most import here.
With Ruby folks can always try to remove redundancy. :-)
I find it amazing that "DRY" has become such a positive pattern when it was touted as one of the driving points of the Ruby on Rails framework. Don't repeat yourself.
For instance, Java folks are always ready to throw exceptions instead of letting stuff resolve itself if it doesn't quite work.
In that example I only throw an exception when you try to delete a student who doesn't exist. The reason I do so is that it contains semantic information which is useful for the client.
If I returned a fake value - the client would get a 200 response on his attempt to delete a non-existent resource, which is clearly the incorrect behavior. I could return nothing - but then the client would see a 204. So, I choose to throw an exception with a response 'NOT_FOUND'
so that the client gets a 404 response.Yes, this is only an example.
There are a few things I'd like to point out to add to this example:
1) JAX-RS is still new. In the future, they might improve the exception handling or whatnot (so if you return null, they might decided to return 404 ... who knows)
2) We can always wrap all JAX-RS with a Filter. So you can throw JPA level exception (not found) and let the filter catch it and convert it to WebApplicationException(404). Thus in theory, your JAX-RS implementation won't have any RuntimeException explicitly in the code.
I did try and build your code to run the test (I've no idea where the CsvParam and DateParam classes come from) but I think that the following will give you what you want without throwing the exception.
Ah, I see what you mean - thanks!
The full (buildable) source code - including the CsvParam and DateParam classes - are here: https://github.com/smanek/students
I've written large Rails and Sinatra apps, as well as a couple Spring apps and straight Java apps. I"m evaluating Play now and even though I usually prefer Ruby to Java, I'm finding Play (with Eclipse configured to be Textmate like) as productive, if not more so, than Rails.