HellOnline

Icon

Eran's blog

  • Frontpage
    Return home
  • Browse
    By topic
  • Subscribe
    RSS feed
  • Events
  • Fiction
  • gaming
  • General
  • Java
  • Projects
  • Ruby on Rails
  • Social Software
  • The Net
    • Aggregation
    • MicroFormats
    • Mobile
    • Search
    • Tagging

February 14, 2007 • 3:58 pm 4

Sessions on Rails without Cookies

Developing for the mobile web feels like being back in 1998. Standardization is lacking although with XHTML-MP we’re doing much better than back in the bad ole days. Still, browsers are many and varied and have different levels of support for different features. Even something so basic like cookies, which we’ve all been taking for granted for so long is no longer guaranteed.

Some phone browsers support cookies. Some get cookie support handled by the gateway. Some have persistent cookies, some do not. How do we get around that? Just like we did before – URL rewriting.
http://example.com/?_session_id=2b34560e1cfd62a265b243accc493c68 much?

Rails already has some support for sessions without cookies. Check out the CGI library (where Rails’ sessions come from) and you’ll see that it knows how to get the session_id from the request parameters. All that means is we have to get them in there. I found a basic URL rewriting idea here. which works by overriding #url_for. I plugged the new method into ApplicationHelper and it almost worked. The only thing missing was support for named route URLs (e.g. foo_url). On my quest to get those working I stumbled on default_url_options and this, much simpler, solution:


def default_url_options(options)
{‘_session_id’ => session.session_id}
end

Really, that’s all you need. Almost. I was having some problems with forms that looked like:

For some reason, the combination of a POST and passing _session_id as a query parameter did not go over well. Time to override #form_for:


def form_for(object_name, *args, &proc)
raise ArgumentError, "Missing block" unless block_given?
key_name = get_session_key_name

# see if a cookie with this key has been set.
if request.cookies.has_key?(key_name) then
return super
end

options = args.last.is_a?(Hash) ? args.pop : {}
concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
# This is what you’re looking for right here:
concat(hidden_field_tag(key_name, session.session_id), proc.binding)
fields_for(object_name, *(args << options), &proc)
concat('', proc.binding)
end

That hidden field will get your session_id across. It feels like an ugly hack and I bet there’s a better way to do this but for now, it’s what works.

Filed under: Ruby on Rails

February 8, 2007 • 1:21 pm Comments Off

Rails, meet Java

Most of the backend at work is done using a behemoth of a Java server so it’s pretty hard to avoid cooperating with that no matter what language or what application I’m writing. Right now, it so happens, I’m working on a Rails application that acts as a frontend to an app managed by said java server. My first few weeks on this project consisted mostly of getting java and ruby to play well together.

I started out by adding an XML-RPC layer to our existing services. This wasn’t too hard, there’s a selection of XML-RPC libs for Java with pretty solid implementations. Creating a Rails XML-RPC client was even easier but when my API class started to grow over to 20 methods I decided that a different solution is in order.

Not too long ago, Assaf mentioned ActiveRevver in one of his posts and it seemed like that might be a good place to start. ActiveRevver creates an ActiveRecord-like layer on top of the revver’s web services and that’s exactly what I wanted for myself. A clean interface, similar to ActiveRecord’s that plays well with Rails.

Following ActiveRevver’s example, my models are based on OpenStruct which makes it easy to define objects on the fly (unless, of course, you want to have a method name type). method_missing takes care of delegating member access to the underlying @attributes Ostruct object.


class ActiveFoo::Base
def initialize(attrs = {}, opts = {})
@new_record = opts[:record_from_api] ? false : true
initialize_attributes!(attrs)
end

def initialize_attributes!(attrs)
@attributes = OpenStruct.new(attrs.stringify_keys!)
end
end


class ActiveFoo::Account '',
'password' => '',
'email' => '',
'validated' => false
})
super(schema.merge(attrs), opts)
end
end

A very cool feature in ActiveRevver is their implementation of Associations. Using has_many you can add association just like you would with ActiveRecord. This uses the CollectionProxy class which wraps an array of hashes and presents it as a collection of items of the related class. A sprinkling of find and find_by_foo methods later and this feels just like using ActiveRecord.

One thing I needed to do that I’m not sure ActiveRevver supports was store my objects in a session. To do that, classes must be marshallable. This is easily achieved by adding the marshal_load and marshal_dump methods.


def marshal_dump
[@attributes, new_record?]
end

def marshal_load(obj)
@attributes = obj[0]
@new_record = obj[1]
end

I can now almost completely ignore my Java backend and pretend my application is Ruby and Rails all the way.

Filed under: Ruby on Rails

August 18, 2006 • 5:25 pm 3

HowTo: Using Acts as Wiki to Create a Smarter Blog

This post is a tutorial on how to use the Acts as Wiki rails plugin. It assumes you’re already somewhat familiar with ruby and ruby on rails. If you’re not, check out the rails website, it has some good starting points. You can see the full source code for this project on trac or download it as a zip file.

It’s common practice among bloggers to mark updates to existing posts with <INS> and <DEL> tags or similar methods. Wouldn’t it be easier if the content of each post was a wiki? Anyone would be able to see the full revision history of every one of your posts and full disclosure would be served. Using the Acts as Wiki plugin we can achieve just that.

First let’s create the database we’ll be using, call it a bliki (blog + wiki). Once that’s done go ahead and create the rails application itself

rails bliki

Edit database.yml to include the correct details and then go on to create the Posts table, we’ll use a migration for that:

class CreatePosts true
t.column :subject, :string
t.column :content, :text
t.column :created_at, :datetime, :null => false
t.column :updated_at, :datetime, :null => false
end
end

def self.down
drop_table 'Posts'
end
end

Run the migration

rake db:migrate

Now create your Post scaffold.

ruby script/generate scaffold Post

It’s now time to install the Acts as Wiki plugin, just like it says in the Readme:

script/plugin install http://www.hellonline.com/svn/hellonline/plugins/acts_as_wiki/
script/generate acts_as_wiki
rake db:migrate

Now lets use acts_as_wiki on the content field of the Post class and throw in a markdown fitler as well, just to make things pretty

class Post < ActiveRecord::Base
acts_as_wiki :content, Filters.method(:markdown)
end

Let’s display the all the posts in a list, you can see the full view code here but the interesting lines follow:

And later

'revisions', :action => 'history', :type => post.class, :o bj_id => post.id, :title => :content %>

The first one displays the post’s content after passing it through the markdown filter (if you want the raw content, just use post.content as you would otherwise). The second one links to the revision history of this field. The link requires the containing object type and id and the field’s name (supplied under :title). We need to make similar changes to show.rhtml too.

We can also make use of the new update_attirbutes(attributes, author_name) method. Go into post_controller.rb and change the update method to the following:

def update
@post = Post.find(params[:id])
if @post.update_attributes(params[:post], params[:post][:author_name]) # 'show', :id => @post
else
render :action => 'edit'
end
end

This will record the author name along with the new revision. If you want to record that detail for revert actions as well, you’ll need to change revisions_controller.rb. In the revert method change the call to obj.revert() to something like:

obj.revert(@revision.field_name, @revision.id, @session[:username])

This assumes, of course, that you’re using the session variable username to store the current user’s name.

To make things a bit clearer change the post’s _form.rhtml partial, we don’t need to provide anything beyond an author’s name, a subject and the post’s content:

Author name

Subject

Content

That’s it. Start your server and go to http://localhost:3000/posts/. Create a new post (you can use some markdown if you want to), edit it, check out the history, compare revisions, revert to a previous revision. Isn’t this fun?

Filed under: Projects, Ruby on Rails

August 16, 2006 • 5:48 pm 1

Acts as Wiki

A wiki is a very useful thing. In fact many times I’ve wanted to have just one field in a class be a wiki. This behavior is even more desirable with all the Web 2.0 user-generated content type sites coming out lately. The way I see it, Wikis have two useful features:

  1. Revision history – so you don’t have to worry too much about vandalism, opinionated authors, etc.
  2. Markdown or similar filter to make rich content authoring simple and safe.

One of my first additions to WhereAreYouCamping.com (WAYC) was the versioning code. Anybody can edit camp content and so the site is left wide open to vandalism. The code for that is pretty simple


class Camp 'Revision',
:foreign_key => 'camp_id',
:conditions => "field_name = 'description'",
:o rder => "id DESC"
before_save :revise(‘description’)

The revise function is in charge of creating a new revision if the current value for the description field is different from the previous value. I’m omitting it for brevity.

This is very simple and works pretty well as a simple backup method. You can probably imagine that with some pretty simple code in the Revisions_Controller it easy to show the revision history of a field, diff revisions and revert to a previous one. On the other hand, this solution is lacking author details and a few other niceties but the biggest disadvantage is, of course, lack of DRYness. This method is not at all reusable; in fact additional wikified fields requires much replicated code. Enter acts_as_wiki.

Acts_as_wiki is a mixin (soon to be a plugin, I hope) that lets you do all that with one line of code:

acts_as_wiki [:description, :events], Filters.method(‘markdown’)

Adds revisions (with history, diff and revert) and markdown filtering (or whichever filter you decide to use if at all) to the description field. Here’s a simplified version of the method:


def acts_as_wiki(field, filter = nil)
# remember the filter for this field.
Filter_hash[field.to_s] = filter
# add reader to filtered field content
module_eval "def #{field}_filtered self.get_filtered('#{field}'); end"
# add the revisions relation
has_many("#{field}_revisions".to_sym,
:class_name => 'Revision',
:foreign_key => 'obj_id',
:conditions => "field_name = '#{field}' and obj_type = '#{self.class}'",
:o rder => "id DESC",
:as => 'obj')
}
before_save Proc.new{|obj| obj.revise(field)}
end

To record the author who created a revision I’ve updated the update_attributes method:


attr :current_author, true

def update_attributes(attributes, author_name = 'unknown')
self.current_author = author_name
# let super handle the actual update
super(attributes)
end

Later in revise() I refer to current_author to get the author’s details. This can be expanded to support a more complex user model. For content filtering I’ve added the get_filtered method which is used by the FIELD_NAME_fieltered method (mentioned above):


def get_filtered(field_name)
content = self.send(field_name)
filter = Filter_hash[field_name.to_s]
if filter
return filter.call(content)
else
return content
end
end

This allows the programmer to continue accessing the raw field value (as stored in the database) using the field name and so requires very little change throughout the application.

Revisions are simple creatures:


create_table "revisions" do |t|
t.column "created_at", :datetime, :null => false
t.column "revised_at", :datetime, :null => false
t.column "content", :text, :default => "", :null => false
t.column "author", :string, :limit => 60
t.column "obj_type", :string
t.column "obj_id", :integer, :null => false
t.column "field_name", :string, :limit => 20
end

Similarly, the Revision model needs very little besides the relationship to its owner:


class Revision 'obj_id', :polymorphic => true
# some helper functions for browsing through revisions
def prev_rev
Revision.find(:first,
:conditions => ['id 'id DESC') rescue nil
end
def next_rev
Revision.find(:first,
:conditions => ['id > ? AND field_name = ? AND obj_type = ? AND obj_id = ?', self.id, self.field_name, self.obj_type, self.obj_id]) rescue nil
end
def last_rev
Revision.find(:first,
:conditions => ['id >= ? AND field_name = ? AND obj_type = ? AND obj_id = ?', self.id, self.field_name, self.obj_type, self.obj_id],
:o rder => 'id DESC') rescue nil
end
end

The revisions controller is also pretty simple. The basic actions: history, show and revert pretty much write themselves. The diff action is just as easy once you use the diff method from instiki.

And there you have it. The field[s] of your choice can now be a full blown wiki with revision history, markdown, the works. You can see the rest of the code on trac.

Update: I’ve put up a plugin, you can install it using

script/plugin install http://www.hellonline.com/svn/hellonline/plugins/acts_as_wiki

generate the code and migrate your db using


script/generate acts_as_wiki
rake db:migrate

Update 2: Just posted a tutorial on how to use the Acts as Wiki plugin.

Filed under: Projects, Ruby on Rails

Pages

  • About Me

Archives

  • April 2009
  • March 2009
  • February 2009
  • December 2008
  • November 2008
  • September 2008
  • August 2008
  • March 2008
  • February 2008
  • January 2008
  • March 2007
  • February 2007
  • January 2007
  • December 2006
  • November 2006
  • October 2006
  • September 2006
  • August 2006
  • July 2006
  • June 2006
  • April 2006
  • March 2006
  • February 2006
  • January 2006
  • December 2005
  • November 2005
  • October 2005
  • September 2005
  • August 2005
  • July 2005
  • June 2005
  • May 2005

Meta

  • Register
  • Log in
  • Valid XHTML
  • XFN
  • WordPress

Blogroll

  • Ashton
  • Assaf
  • AvaPeeps BlogNation
  • Captured
  • Crimson Canary BioTech Search Services
  • del.icio.us
  • flickr
  • Gamasutra Feature Articles
  • Google Blog
  • Google Weblog
  • I Blame Beer
  • Laughing Squid
  • Marc’s Voice
  • Mark Pincus Blog
  • My brother’s surfboard repair shop in Waikiki
  • Ongoing
  • Prof. David Wolber
  • Riding Rails
  • Ryan King
  • supr.c.ilio.us: The Blog
  • Tantek Çelik
  • Tara Hunt
  • Technorati Profile
  • The Search Guy
  • tribe.net
  • zengestrom.com
  • Frontpage
    Return home
  • Browse
    By topic
  • Subscribe
    RSS feed
  • Events
  • Fiction
  • gaming
  • General
  • Java
  • Projects
  • Ruby on Rails
  • Social Software
  • The Net
    • Aggregation
    • MicroFormats
    • Mobile
    • Search
    • Tagging

Blog at WordPress.com.

Theme: Grid Focus by Derek Punsalan.