CrudVision - Lisa Seelye

June 30, 2007

wants.csv?

Filed under: evedb.info, rails, snippet — Lisa Seelye @ 10:40 am

So you're looking to spit back CSV? Here's some Agent stuff for the RESTification of evedb.info! the

RUBY:
  1. @agents.collect{ |a| a}.each do |agent|

is for paginating_find to return all the Agents.

RUBY:
  1. format.csv do
  2.         cols = Agent.columns.map(&:name).sort
  3.         csv_str = FasterCSV.generate do |csv|
  4.           csv.push cols
  5.           @agents.collect{ |a| a}.each do |agent|
  6.             row = []
  7.             cols.each do |k|
  8.               row.push agent.attributes[k]
  9.             end
  10.             csv.push row
  11.           end
  12.         end
  13.         render :text => csv_str

June 28, 2007

Dynamic Proc Objects

Filed under: rails, ruby — Lisa Seelye @ 6:04 pm

There's a problem. Proc objects can't be Marshalled which means they can't easily be stored.

I wrote a small method to build such a Dynamic proc first:

RUBY:
  1. def procbuilder(cmd,*r)
  2. argary = []
  3. r.each_index do |i|
  4. argary.push "arg_#{i}"
  5. end
  6. argstr = "|#{argary.join(',')}|"
  7. p = %q(Proc.new {) +  argstr  + ' ' + cmd + '}'
  8. eval p
  9. end

To use it:

RUBY:
  1. class Foo
  2. attr_accessor :foo
  3. def initalize
  4. end
  5. end
  6.  
  7. m = Foo.new
  8. m.foo = 10
  9.  
  10. # First parameter is the command to perform. arg_? is predictable
  11. # Following parameters are enumerated as arg_0, arg_1, arg_2
  12. p = procbuilder('arg_0.foo = arg_0.foo * arg_1',m,1.05)

And for output:

RUBY:
  1. puts p.call(m,1.05)
  2. puts m.foo

Result of the call is 10.5 and the instance on the object is changed too.

This is kind of cool but not much use unless there's a way to store it. Here's some Rails schema.rb:

RUBY:
  1. create_table "dyna_procs", :force => true do |t|
  2. t.column "name",       :string,   :null => false
  3. t.column "procsrc",    :text
  4. end

In the DynaProc model:

RUBY:
  1. attr_reader :dproc
  2. def after_initialize
  3. procbuilder(self.procsrc) unless self.procsrc.nil?
  4. end
  5.  
  6. private
  7. def procbuilder(cmd)
  8. @dproc = eval cmd
  9. end

Then after the DynaProc is loaded the dproc is a Proc object. Whee.

How the DynaProc object is created (and stored) is irrelevant. It could be from an custom form where people drag and drop predefined elements or it could be a scaffolded web form. White list everything and validate everything. Next trick is to get it to automatically sandbox itself...

June 27, 2007

Why CRUD? - Part 4

Filed under: crud, rails, work — Lisa Seelye @ 1:59 pm

This is probably going to be the final part.

Last time I stopped just getting ready to talk about designing a JSON based RESTful API.

My initial design had no consideration for an administration component. Every action had a block that looks, at the bare minimum, like this:

RUBY:
  1. respond_to do |format|
  2. format.js
  3. end

I cared about JSON more than HTML or XML (I included XML where it was easy enough on the first pass simply because .to_xml is just as easy as .to_json). Another developer's concern was an HTML-based administration interface for the end users to use. My JSON part was going to be used by "meat" of the project to interact with the database.

At the start of the project I had about two dozen or so models to represent the application. This is all going to sound the same:

ruby script/generate resource -c Person
ruby script/generate resource -c Avatar

And so forth.

Add the basic four (really five) CRUD operations to each controller:

RUBY:
  1. def index
  2. end
  3. def show
  4. end
  5. def create
  6. end
  7. def update
  8. end
  9. def destroy
  10. end

(Don't forget helpers!

RUBY:
  1. before_filter :get_thing, :only => [ :show, :update, :destroy ]
  2. protected
  3. def get_thing
  4. @thing = Thing.find params[:id]
  5. end

)

And then fill in the blanks. Edit config/routes.rb to do resource nesting so editing the first Person's Avatar(s) is baked in to the URL and the caller doesn't have to worry about polluting the URL with parameters that aren't needed.

The tests become simple, too. There isn't much complexity to most resources, thankfully, it's always testing five basic actions.

My coworker that was doing the HTML only had to add a format.html in the respond_to block as well as two new actions (new and edit) to handle peripheral HTML forms that my JSON-using clients would never use. Handy, that.

By using CRUD it meant that REST could be used in Rails automatically. The actions just worked. Resource nesting just worked. I got to spend time thinking about cool stuff, like the application, and not how to make "it" work, kind of like why I liked Rails in the first place! No glue code.

CRUD/REST makes me a happy coder. Using CRUD means, for example, that buying a Product in a E-Commerce store it isn't an operation on a Product per se, but rather it is the creation of a CartEntry in a Person's ShoppingCart (POST /people/1/shopping_cart/cart_entries) and to check out it's just a creation of an action on the ShoppingCartController called, for example, checkout attached to the DELETE HTTP verb. It's debatable whether or not DELETE should semantically mean "Okay, now let me purchase everything" but that's another post. ;)

In the checkout method there's then a concern of what do I do with all these CartEntries? What about a model called a ShippingQueue that the people in the shipping department at the store can use (with RESTful interfaces!) that has some state attached to it (ShippingStatus < AR::Base and then Shipped < ShippingStatus; Received < ShippingStatus, etc). Models are cool.

That's the end. CRUD helps me and that's all that matters in the end.

June 26, 2007

What Is CRUD?

Filed under: crud, rails — Lisa Seelye @ 9:47 pm

CRUD is an acronym for the four basic types of SQL commands: Create, Read, Update, Delete.

Sticking to those four basic operations when dealing with models (of your data structures) and relationships between them can make handling the project easier as abstract things like join tables now have a real name and can hold real data (instead of two foreign keys).

It makes more sense in a Web application environment to match HTTP verbs with CRUD operations:

  • GET - Read
  • POST - Create
  • POST - Update
  • DELETE - Destroy/Delete

Each model in the project, for example in a store application one might have a collection of Products for sale, Categories of Products, and Customers that buy browse Categories and (buy) Products. In a RESTful application (one which does the above verb mapping) each instance of each model gets it's own URL, such as:

  • /products - refers to all of the Products (POST a new Product to this URL to add it to the collection)
  • /products/1 - refers to the Product whose ID is 1 (RUD operations are done on this URL)
  • /categories/1/products - In a properly set up application this refers to the Products that belong to the Category whose id is 1
  • /customers/1/products - This could refer to the products the Customer whose id is 1 has purchased.

This contrived example is incomplete (there is no shopping cart!) and there models are simple. However our store just had a holiday and we gave special codes to the top 100 customers to redeem for a free hat. There's now a model called Coupons (and they would live at /coupons with a cool GUI for the store manager to tinker with) but there's nowhere to redeem the Coupon. It doesn't make sense to do it with the Customer model or the Product model because there's nothing in those models that stores that the Customer has redeemed a Coupon. Looks like we need a join table with columns coupon_id and customer_id. But we don't know when they redeemed it and there's no way for them to store cool feedback saying congrats on the holiday.

At this point I would like to remind that this is very contrived.

There's got to be a better way to do the redemption of Coupons and get a free Product. And there is, I think (otherwise I wouldn't have this blog), CRUD to the rescue.

Call the new thing a UsedCoupon. It still has the attributes from the old join table and perhaps a timestamp and text field. Creating a UsedCoupon is equivelant to the act of redeeming (and getting the cool Product). Redeeming it is POSTing a new UsedCoupon object to /used_coupons; the associated product is given to the Customer and I have a reason to blog.

I happen to like the limitations because it's illogical to add unnecessary methods onto controllers (in MVC terms) when there's no need. Why not just make another URL, they're cheap. CRUDdy controllers will be lightweight since there aren't many methods (a typical max of 5!). Rails happens to afford the developer helpful methods to generate the right URLs from models.

To answer the question What is CRUD? I answer: CRUD is helpful.

June 25, 2007

Why CRUD? - Part 3

Filed under: crud, rails, work — Lisa Seelye @ 9:45 pm

In the last part of what is probably going to obnoxiously long I realised that not using CRUD was making the application codebase ugly and making me not want to work with it. I rediscovered resources and discovered REST.

One of the projects at my new job was to migrate two Rails SVN repos into one (combining both Rails projects). I must admit that the codebase wasn't the best I had seen. It was clearly designed without REST in mind (especially considering there was no simply_restful and no rails 1.2) or CRUDdiness in mind. A lot of actions in controllers and a lot of scoping (addComment to some model in the model's controller, for example).

Tracing through the code was difficult: views had partials referencing partials that referenced other partials. If the project was started from scratch right now (the client would probably freak out) and it would definiately have a better structure.

After I had merged the two SVN repos into one work began on adding more features and bugfixing to the same application. The usual "Getting to know the codebase" time was applicable, but I can't help wonder what may have been if it had used REST concepts in the beginning adding to it and maintaining it would be much easier (but I may be biased).

Eventually I've become familiar with the codebase and understand how it works and its quirks. I'd love to talk the powers that be into letting us rewrite it RESTfully... but I doubt that will happen any time soon. ;-) Maybe Version 2.0.

The next project was one from scratch to expose an API to a database. In the beginning SOAP through ActionWebService was considered but ultimately discarded in favour of a RESTful design, much to my relief! ActionWebService::Struct derrived classes would have numbered in the hundreds in an effort to map the data combinations. Yick.

And so I got to design a RESTful design, this is where I really got to see the goodness of REST; prior to this project everything was just theory. Now I had to chose between scoping methods (PeopleController#addAvatar, which was actually creating an Avatar) or REST. I researched more into REST, played, and was thankful to see that it wasn't as hard as I had imagined. Rather, it was easy. Very easy, almost too easy.

The project in question is a large scale and involves a dozen or so people. There is a Java part, a Flash part, a Rails part and those crazy art-type people (who I believe practice dark arts to make such images appear on paper and computer screens!). The Java and Flash guys had the harder part. All I had to do was architect a bunch of models and controllers to do CRUD operations with an agreeable data envelope (we chose JSON). Everyone else had to knit everything together and make it all work. CRUD is easy, Working with Java and Flash is painful.

Part four will talk about the way I went about architecting the API. It's going to be very boring - CRUD is predictable (which is good!)

June 24, 2007

Technorati thingy

Filed under: blog — Lisa Seelye @ 10:05 am

Technorati Profile

Think they'll make me famous? (Probably not).

Why CRUD? - Part 2

Filed under: crud, rails — Lisa Seelye @ 10:01 am

This is Part 2, following Why CRUD? - Part 1

I realised some time later that I was not working on the site; the same thing was happening that had happened with PHP! I wasn't developing because it was a pain. The code was horrible. I had developed a code base that I didn't want to work with it!

Something needed to change. I liked Ruby and I liked Rails - something needed to change...

The thing that changed was me re-realising that URL don't mean "The address you put into the browser and hope you get something back" but rather it meant: Uniform RESOURCE Locator! A URL is a (fully qualified) way to point to a resource on the internet. I wrote about this in 2003 on my personal site and touted the glories of "Extensionless URLs" but had seemingly forgotten my own example. /images/mycity should give the requestor what they want based on the HTTP_ACCEPT headers sent to the server, so if they want a BMP more than a PNG more than a JPEG they should get it, in that order, if possible. /images/mycity is a resource, the extension simply specified the format that the resource was in.

And it's still damn cool.

Reading about REST and RESTful URL helpers in Rails made the somewhat dimmed lightbulb bright up again. I got it; I understood. The address in the bar is simply an address to a resource and we should give the user what they want (within reason).

Further limiting the thinking to basic CRUD (Create, Read, Update, Destroy) operations means that instead of getting /view/agents/:id I'd have /agents/:id linking to the show method in the AgentsController. Not to parrot too much but DHH was right: These constraints are liberating. No guessing that to talk about an Agent I have to look into the ViewController. How is that intuitive? Why should I have to worry about that crap? Let a convention deal with it.

So I was all about Rails again, which was good because about the time my interest in Rails was really really growing (again!) I was hired to do Rails coding.

Part 3 will deal with having to use an existing non CRUD/RESTful codebase and then being able to write an API to use CRUD/REST exclusively.

June 23, 2007

Why CRUD? - Part 1

Filed under: crud, rails — Lisa Seelye @ 4:30 pm

To preface: I'm going to talk a lot about my pet project, evedb.info, which is a fansite for Eve Online which seems to be stuck in a perpetual development phase. The site was written in rails as a learning project.

Like many other web developers that use a MVC model of coding I thought the pinacle of design (and by "design" I mean code design and URL presentation) was to have a ViewController that would display Agents, the Corporations to which an Agent belongs, the Stations that a Corporation owns, the Systems in which the Stations are and the Items available in the game and so forth. This meant my ViewController had a bunch of methods: agents, corporations, stations, systems, items and so forth. The URL looks like /view/agents/[name of agent]. Station names are very long so I used their id instead of their name.

When I migrated to Rails 1.2 I had a problem. Since my URLs included the names of the thing being viewed (/view/agents/Hoken+Isikesu and /view/items/Dragon+F.O.F.+Cruise+Missile+I) sometimes included a period (.). When Rails 1.2 came about it introduced the respond_to method that can be used to handle requests for different types of representations for the same data:

RUBY:
  1. respond_to do |format|
  2. format.html
  3. format.js
  4. end

So /views/agents/1 will default to the HTML format and 1.xml will want the XML format.

So now the URL that looks like: /view/items/Dragon+F.O.F.+Cruise+Missile+I will want the /view/items/Dragon+F resource with the O.F.+Cruise+Missile+I format. Ooops. Bug!

And so it begins

Filed under: blog, england, life, php, rails, uk — Lisa Seelye @ 2:17 am

My name is Lisa Seelye and I'm a Ruby on Rails developer. That is to say I use Rails in my hobbies as well as professionally. I've been using it since May 2006 and have loved it since.

Prior to using Ruby and Rails I came from a PHP background (the astute observer will note that my site uses PHP) and before that Perl.

I live in the United Kingdom (by way of the United States) and work in Leeds, England. I love my job and the chance to use Rails is really nifty!

So yeah... I'm hoping to use this space to write about Ruby, Rails and other things.

The name of this domain name is shamelessly stolen from DHH's keynote from Railsconf 2006. I happen to appreciate the CRUD way and REST so I appreciate David's "crudvision".

Hello, World.

June 22, 2007

Hello World

Filed under: Uncategorized — Lisa Seelye @ 5:27 pm

So, like, does this thing work?

Powered by WordPress