This wasn't a problem in Java...
September 29, 2006 @ 02:07 AM... which makes this my strongest argument to date for using Java instead of Ruby. :)
This is the blog of Brasten Sager, a freelance software developer, Mariners fan, guitarist, haphazard philosopher.
... which makes this my strongest argument to date for using Java instead of Ruby. :)
Not sure how long ago this happened, but it appears some fairly recently satellite photos of Portland have been taken and updated on Google Maps.
It’s probably been a week or two since I last visited Portland on Google Maps, but their photos were horribly outdated, especially in the Pearl District area.
In the previous photos, the Pearl was mostly ripped apart (particularly the northern sections), but you’re now able to see the completed Tanner Springs Park, the Pinnacle building, and many others that did not exist on the older photos.
Good things happening with myself and Nagilum LLC.
Firstly, Nagilum LLC and Digital Possibilities Inc. have jointly reserved office space in the Fremont area of Seattle. Since Nagilum LLC only needed enough space for myself (at the moment), I was thrilled to hear DigiPoss needed a second office location and was looking in the same area I was. If all goes as planned, we will be moving in upon completion of the building (the Fremont Space Building on N 36th St.) in November.
On a personal level, I’m very excited to get Nagilum LLC out of my home office and into a real one. It’ll be good to get out and be around very smart, highly creative people on a consistent basis.
Secondly, I’m getting all my ducks in a row to increase the visibility and capabilities of Nagilum LLC once we’re in the new office. I’ve got a new website design nearly completed for www.nagilum.com which will likely go live in November. I’m also preparing for the possibility of bringing on more Ruby developers and/or graphic designers.
So, fun times indeed.
Topfunky Red Rover Ruby Speakers Series
Anyone else going tomorrow night? I’ll be there!
If there’s one tool that I cannot live without these days, it’s TextMate. In particular, the “mate” command has been a lifesaver. A simple “mate .” command in my Rails directory and I’m on my way!
The downside to that approach is that often I do not need to focus on the entire application. Sometimes having the entire Rails project visible/available in TextMate can create mental clutter (upper screenshot).
So I tried creating several aliases for “mate” that open just the files I’d most likely need for a particular case. For example, most of my development now is done using this alias:
alias mt="mate app/controllers/ \
app/models/ app/views/ test/ \
db/migrate/ config/routes.rb"
Now, “mt” in my project directory gives me a TextMate project with only the most relevant directories and files (lower screenshot).
I’ve been using this in my recent projects fairly successfully, so I’m putting it out there for anyone else to use.
Rest Controller (unoriginal name, I know) provides basic defaults for your 7 RESTful actions (index, show, edit, new, create, update, destroy) for both HTML and XML. There are some basic customization capabilities, but if you need to customize more than a couple things, you shouldn’t be using this plugin.
script/plugin discover # Yes to http://svn.superruby.com/svn/plugins/Finally:
script/plugin install rest_controller
Rest Controller provides basic default actions for a RESTful controller which can be overridden easily.
There is basic customization capabilities of the default responses. If you need more customization, you should just implement the method in question to bypass this plugin. If you find yourself overriding more than one or two of the default methods, then you probably should not be using this plugin to begin with.
Usage
1 2 3 4 |
# Controller with default actions. class ProductsController < ApplicationController rest_controller :product end |
Default Actions/Responses
These examples use @product / @products, but your actual variable name will depend on the model. “rest_controller :customer” will result in @customer / @customers, for example.
Index - find(:all) on your model.
HTML --> renders index.rhtml
XML --> renders @products.to_xml
RNG --> renders @products.to_rng (if simply_relaxed plugin is installed.)
Show - find(params[:id])
HTML --> renders show.rhtml
XML --> renders @product.to_xml if found / Status Code 404 if not.
Edit - find(params[:id])
HTML --> renders edit.rhtml
New - Model.new
HTML --> renders new.rhtml
Update - update(params[:id], params[:product])
HTML --> sets flash[:notice] appropriately
redirects to show(id) if successful / renders edit.rhtml if not
XML --> Status Code 204 if successful / Status Code 400 if not
Create - create(params[:product])
HTML --> sets flash[:notice] appropriately
redirects to show(id) if successful / renders new.rhtml if not
XML --> Status Code 201 if successful / Status Code 400 if not
Destroy - destroy(params[:id])
HTML --> sets flash[:notice] appropriately
redirects to index
XML --> Status Code 204 / Status Code 404
Overriding Responses You can override the default responses by either providing a custom respond_to block, or provide a block for a specific action/mime-type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# Overriding some responses. class ProductsController < ApplicationController rest_controller :product # Override a DELETE/HTML response. destroy_responds_to(:html) { render :text => "GONE!" } # ... could also be formatted this way ... destroy_responds_to :html do render :text => "GONE!" end # Override all default responses for an action with a custom # respond_to block. show_responds_to do |wants| wants.html wants.xml { render :xml => @product.to_xml(... some includes ...) } wants.graph { ... } end end |
If you need to customize the logic for a particular action and cannot accomplish your desired result with before/meantime filters, you can simply implement the action in your class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# Implement your own actions. class ProductsController < ApplicationController rest_controller :product # Overrides the 'update' action. def update # Do something special for updating. end # You can trigger the default responses, if they still # apply. def index # Some special code. index_responder end end |
Responders expect an instance variable by the name of the model. (Product == @product, etc). index expects the plural name (@products).
Well, I swear I don’t sit around all day writing plugins. I just happened to finalize a couple of these things today. :)
What is Simply Relaxed?? Simply Relaxed converts your models into a RELAX NG schema. Not much more to say.
script/plugin discover
# Say yes to http://svn.superruby.com/svn/plugins
script/plugin install simply_relaxed
1 2 3 4 5 6 7 8 9 10 |
class OrdersController < ApplicationController def index @orders = Order.find(:all) respond_to do |wants| wants.xml { render :xml => @orders.to_xml } wants.rng { render :xml => Order::to_rng } end end end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="UTF-8"?> <orders> <order> <id type="integer">1</id> <placed-at type="datetime">2006-09-13T09:35:40-07:00</placed-at> <status>ACTIVE</status> <total-amount type="decimal">150.25</total-amount> <total-double type="float">150.25</total-double> <total-float type="float">150.25</total-float> <user-id type="integer"></user-id> </order> <order> <id type="integer">2</id> <placed-at type="datetime"></placed-at> <status>PENDING</status> <total-amount type="decimal">0.0</total-amount> <total-double type="float"></total-double> <total-float type="float"></total-float> <user-id type="integer"></user-id> </order> </orders> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?xml version="1.0" encoding="UTF-8"?> <grammar datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" xmlns="http://relaxng.org/ns/structure/1.0"> <start> <choice> <element name="orders"> <zeroOrMore> <ref name="order"/> </zeroOrMore> </element> <ref name="order"/> </choice> </start> <define name="order"> <element name="order"> <zeroOrMore> <choice> <element name="placed-at"> <attribute name="type"/> <optional> <data type="dateTime"/> </optional> </element> <element name="status"> <optional> <text/> </optional> </element> <element name="total-amount"> <attribute name="type"/> <optional> <data type="decimal"/> </optional> </element> <element name="total-double"> <attribute name="type"/> <optional> <data type="float"/> </optional> </element> <element name="total-float"> <attribute name="type"/> <optional> <data type="float"/> </optional> </element> <element name="user-id"> <attribute name="type"/> <optional> <data type="integer"/> </optional> </element> </choice> </zeroOrMore> </element> </define> </grammar> |
I plan on giving more detail about this later, but I put together a little library for using ActiveResource objects in Java. Actually, I guess ActiveResource is the client API, so it’s more like a Java port of ActiveResource compatible with the Rails REST services.
For the examples below, I’ve hacked out things like the imports and getters/setters. So these obviously won’t compile as-is.
Order.java (Model):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import com.nagilum.jactive.*;
import com.nagilum.annotation.*;
@Resource(resourceName="orders")
public class Order implements ActiveResource {
private @ResourceConnector static Connector connector;
private @Attribute int id;
private @Attribute String status;
private @Attribute double totalAmount;
public Order save() throws Exception {
return (Order) connector.save(this);
}
public Order delete() throws Exception {
return (Order) connector.delete(this);
}
public static Order find(int id) throws Exception {
return (Order) connector.find(Order.class, id);
}
(accessor methods for attributes)
} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Connector connector = new Connector("http://localhost:3000/");
connector.registerResource(Order.class);
// Finding/Updating works
Order order = Order.find(1);
order.setStatus("PENDING");
order.save();
// Creating new resources works
Order newOrder = new Order();
newOrder.setStatus("NEW");
newOrder.setTotalAmount(150.00);
newOrder = newOrder.save();
// Update new order
newOrder.setStatus("APPROVED");
newOrder.save();
// We're done here.
newOrder.delete();
order.delete(); |
To answer a few anticipated questions:
This is such a stupidly simple plugin, it’s barely worth a post. BUT…
NilHash prevents chained element references from raising NoMethodErrors by provided NilClass with a limited \[\]\(key\) method.
Before:1 2 3 4 5 6 |
# There are many ways to do this, here's just one. def some_action return if (params[:order].nil? || params[:order][:address].nil? || params[:order][:address][:zip_code].nil?) end |
1 2 3 |
def some_action return unless params[:order][:address][:zip_code] end |
Originally I sent this as a patch to the core team, but apparently this constitutes a “major change in behavior.” I would be interested in knowing if anyone has any problems after installing this. I don’t yet see how it’s a major behavioral change.
# Make sure to say Y to http://svn.superruby.com/svn/plugins/
$ script/plugin discover
$ script/plugin install nil_hash
The new business cards came just in time for the Sun TechDays conference yesterday (which, by the way, was a horribly dull experience).
I was very pleased with the design we came up with, and the printers did an amazing job. Here’s a look.
It’s been a little while since I’ve said anything about Scruffy or rolled out a release. Like all of you, I have bills to pay, so I’ve been squeezing in Scruffy time around client’s projects. I was hoping to release the next version of Scruffy a few days ago, but it looks like it will be next week sometime before that happens.
In the meantime, let me tell you about some of the things you can expect in Scruffy 0.3.0.
Pie Charts! I’ve been promising these for a while, and they will be here. A.J. has done some fantastic work on these, and I’m really excited to get this functionality in your hands and see what you people do with it. The extra time has allowed A.J. to add in some really amazing capabilities.
For a demonstration, check out Ms. Scruffy. Ms. Scruffy was made entirely with pie charts, using some very simple options (offsets and pie-slice exploding, etc). Ms. Scruffy does not actually convey any useful information.
Customization API! While A.J. has been working on pie charts, my entire focus has been on building a really nice user-facing API—something Scruffy is weak at right now. Everything from legend location and orientation, x-axis and y-axis labels and locations, and many other options will be easily setable.
Additionally, graph layouts will shift to best accommodate your options. If you hide the legend, the graph will expand to fill that space. This stuff should be really cool!
Some other cool stuff! A lot of this is changing from day to day, so I’m just going to show some actual code I’m working on right now.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# Rails View Helpers <%= scruffy :performance_graph, @agent %> # A graph is just a representation of data. def user @user = User.find params[:id] respond_to do |wants| wants.html wants.xml wants.graph # renders user.rgraph end end |
Soon….