Mar 30 2007

RSpec Autotest now a Rails Plugin

admin @ 12:30 am

RSpec Autotest now a Rails Plugin:

“Posted by Nick Sieger Wed, 15 Nov 2006 15:46:00 GMT

Inspired by a posting on the RSpec list and recent comments stating that my Auto RSpec hack wasn’t working, I’ve bitten the bullet and upgraded to RSpec 0.7.2, and made rspec_autotest a plugin in the process. So, here are the necessary incantations to auto-rspec your project.”

OK, so I’m a bit behind on RSpec developments. But this plugin makes a world of difference when using rspec.

While Nick mentions rspec 0.7.2 in the post, I’m using it with the latest (pre-trunk) 0.9.0 without any issues.

If you use rspec (with rails or not.. see rspec-autotest-for-standalone-projects) you should be using this plugin!

Tags: 

Oct 29 2006

RSpec On Rails

admin @ 4:58 pm

Nice post by Defiler on RSpec:

RSpec On Rails:

RSpec is mere days away from a new release with greatly improved Rails support.

Since people are currently paying me to write Rails code, rather than plain old standalone Ruby (hint hint), I’ve been waiting for these features before making serious use of RSpec.

As an exercise, I ‘ported’ the acts_as_authenticated controller tests to RSpec. The results were fairly interesting. Subjectively, I find it more readable than the test/unit version. Objectively, one of the test/unit test cases doesn’t get run because there are two methods with the same name.

So far, so good. Anyone who thinks that RSpec is ‘only’ about a different set of terminology should give it a serious try first.

(Via ~:caboose.)

Tags: 

Oct 29 2006

Using TinyMCE with Rails/AJAX

admin @ 11:56 am

Our current project is basically a very structured (i.e. niche/focused/custom) content management system. There are a few places where we want to give the client the ability to edit some HTML page content. The client is tech-savvy, but not in the “I enjoy slinging HTML” way. Ergo, we needed WYSIWYG HTML editing capability. After some research we decided that TinyMCE was the way to go. We just had to make it work the way we wanted it to.

Here are my notes on getting TinyMCE working nicely in a Rails/AJAX environment.

Start by grabbing the rails plugin (and read the material) from here and install it as per the instructions.

Allowable options to uses_tiny_mce are documented here… there are lots of them

I figured that having followed the directions in the above, my work was over. In fact it was just beginning. The above will work fine if you have a page, with a textarea that you want to be WYSIWYG. Our requirements were a bit more involved. The textarea in question was in a partial that was rendered via a remote updater call (via a link_to_remote in a list on the page). The main issue here is that the textarea didn’t exist when the page was rendered… so TinyMCE had to be hooked up to it later… when it was injected into the DOM tree. Some digging through support forums and I found what I needed. This required a bit of java script in the partial… after the textarea:

<%= form_remote_tag :url => {:action => 'edit_page', :id => @page},
                    :before => "tinyMCE.triggerSave(true,true)" %>
  <b>Page Content:</b><br />
  <%= text_area :page, :content, :rows => 15, :cols => 150 %>
  <br /><br />
  <%= submit_tag "Update" %>
  <script type="text/javascript">
  //<![CDATA[
    tinyMCE.execCommand('mceAddControl', true, 'page_content');
  //]]>
  </script>
<%= end_form_tag %>

The next issue was getting the contents out of TinyMCE and accessible to the parameter construction for the remote call. After a bit of research I ended up with the following:

<%= form_remote_tag :url => {:action => 'edit_page', :id => @page},
                    :before => "tinyMCE.triggerSave(true,true)" %>

Normally the save is trigger by a page unload, I needed to force it to happen before the remote call happened. Putting the call to force (aka trigger) the save in the :before script of the form submission remote call worked great. So now I had a bigger problem. TinyMCE was getting hooked up to the textarea. If the user picked another page item from the list, the div would be refilled with a different rendering of the partial… with a different textarea node. The old textarea would be gone… out from under TinyMCE. I needed a way to reconnect to the new textarea. More digging turned up this example. It gave me the final bit of the puzzle. My final solution includes the following in application.js:

bTextareaWasTinyfied = false; //this should be global, could be stored in a cookie...

function setTextareaToTinyMCE(sEditorID) {
	var oEditor = document.getElementById(sEditorID);
	if(oEditor && !bTextareaWasTinyfied) {
		tinyMCE.execCommand('mceAddControl', true, sEditorID);
		bTextareaWasTinyfied = true;
	}
	return;
}

function unsetTextareaToTinyMCE(sEditorID) {
	var oEditor = document.getElementById(sEditorID);
	if(oEditor && bTextareaWasTinyfied) {
		tinyMCE.execCommand('mceRemoveControl', true, sEditorID);
		bTextareaWasTinyfied = false;
	}
	return;
}

These two functions are used to disconnect from an existing textarea and reconnect to the newly rendered one. In the list item that causes the rendering:

<%= link_to_remote "<span class=\"listTitle\">#{page.title}</span>",
                   {:update => "editPage",
                    :url => {:action => :get_page, :id => page},
                    :before => "Effect.Fade('editPage',
                                            {duration: 0.25,
                                             queue: 'end',
                                             afterFinish: function(effect) {
                                               unsetTextareaToTinyMCE('page_content')}})",
                    :complete => "Effect.Appear('editPage', {duration: 0.5, queue: 'end'})"},
                    :title => "Edit #{page.title}"  %>

To avoid visual weirdness the disconnect is delayed until the fade has completed. Once the new version of the partial has been loaded, it’s faded back in. The relavant bit of the partial is here:

<%= form_remote_tag :url => {:action => 'edit_page', :id => @page},
                    :before => "tinyMCE.triggerSave(true,true)" %>
  <b>Page Content:</b><br />
  <%= text_area :page, :content, :rows => 15, :cols => 150 %>
  <br /><br />
  <%= submit_tag "Update" %>
  <script type="text/javascript">
  //<![CDATA[
    setTextareaToTinyMCE('page_content');
  //]]>
  </script>
<%= end_form_tag %>

The last thing was to add a Done/Cancel button to the form:

<input type="button"
       value="Done"
       onclick="Effect.Fade('editPage',
                            {duration: 0.25,
                             queue: 'end',
                             afterFinish: function(effect) {
                               unsetTextareaToTinyMCE('page_content')
                             }})" />

Now I have a WYSIWYG textarea in a partial that’s rendered via a remote call.. and it all works smoothly and exactly as required.

Tags: 

Oct 24 2006

Encapsulation in Action

admin @ 5:35 pm

I’m an OO bigot, plain & simple. One of my soapboxes/sacred-cows/hot-buttons/whatever is encapsulation… or more to the point, the lack of it.

I was writing some code recently and thought it might make a nice example.

I was just starting with a rough idea: I have a Form object that has FormPages. Pages can be moved around, added, deleted, etc. But they need to be ordered… pages of a form get filled out in some logical sequence.

So here’s my initial spec:

context "A form" do

  setup do
    @form = Form.new
    @form.form_pages << FormPage.new(:name => ‘one’, :number => 3)
    @form.form_pages << FormPage.new(:name => ‘three’, :number => 2)
    @form.form_pages << FormPage.new(:name => ‘two’, :number => 1)
  end

  specify “should be able to order its pages” do
    @form.ordered_pages.collect {|page| page.number}.should_eql [1, 2, 3]
  end
end

Some code to make this work:

class Form < ActiveRecord::Base
  has_many :form_pages

  def ordered_pages
    form_pages.sort
  end
end

class FormPage < ActiveRecord::Base
  belongs_to :form
  has_many :form_fields

  def <=>(other_page)
    number <=> other_page.number
  end
end

And that works just fine. BUT I’m totally violating any semblance of encapsulation. Look at FormPage for starters. We’re reaching into other_page and pulling out its number. Not so bad maybe.. it’s an instance of FormPage too. But it’s still breaking encapsulation. It can be done cleaner with a bit of double dispatch fun. Try this:

class FormPage < ActiveRecord::Base
  belongs_to :form
  has_many :form_fields

  def <=>(other_page)
    other_page.compare_number_to(number)
  end

  def compare_number_to(other_page_number)
    other_page_number <=> number
  end
end

That still works, and things are kept nicely encapsulated.

Now let’s look at the spec:

specify "should be able to order its pages" do
  @form.ordered_pages.collect {|page| page.number}.should_eql [1, 2, 3]
end

That collect is leaving a bloody trail in its wake as it hacks its way through the pages, ripping out numbers like the organ collectors in Monty Python’s “The Meaning of Life”. What are the alternatives? Well.. to support sorting we just wrote FormPage#compare_number_to(other_page_number). Let’s see if we can’t use that:

specify "should be able to order its pages" do
  @form.ordered_pages.each_with_index do |page, index|
    page.compare_number_to(index + 1).should_eql 0
  end
end

That does it. Notice that this works because we had that method to use, and the FormPages in the fixture were number sequentially.. we could clean it up even more by numbering the pages starting with 0 (which is much more ruby-esque anyway):

context "A form" do

  setup do
    @form = Form.new
    @form.form_pages << FormPage.new(:name => ‘one’, :number => 2)
    @form.form_pages << FormPage.new(:name => ‘three’, :number => 1)
    @form.form_pages << FormPage.new(:name => ‘two’, :number => 0)
  end

  specify “should be able to order its pages” do
    @form.ordered_pages.each_with_index do |page, index|
      page.compare_number_to(index).should_eql 0
    end
  end
end

Nice. We can go further… I don’t really care for the exposure of the array of pages. I’d feel better with an addPage method:

class Form < ActiveRecord::Base
  has_many :form_pages

  def add_page(aPage)
    form_pages << aPage
  end

  def ordered_pages
    form_pages.sort
  end
end

That lets us have our context setup be like this:

setup do
  @form = Form.new
  @form.add_page(FormPage.new(:name => 'one', :number => 2))
  @form.add_page(FormPage.new(:name => 'three', :number => 1))
  @form.add_page(FormPage.new(:name => 'two', :number => 0))
end

That’s much nicer from a pure OO perspective. “But,” you say, “that’s not overly Rails-like!” So… it IS very OO. It’s up to you where you draw the line. But remember… just because we can ignore encapsulation doesn’t mean we should. Doing so couples our code to the schema, and the goal of OO is to manage and minimize the coupling in our code. For a one-off, throw-away, 15 minute hack.. who cares.. but a mission critical application needs more thought & care. And it’s amazing how many one-off, throw-away, 15 minute hacks turn into mission critical apps.

David Chelimsky suggested making one more step and removing the reliance on the comparison method. After all, that is an implementation detail and relying on it from the spec makes it hard to change… just like any dependancy. So:

context "A form" do

  setup do
    @form = Form.new
    @pages_in_order = [
      FormPage.new(:id => 10, :name => 'two',  :number => 0),
      FormPage.new(:id => 5,  :name => 'one',  :number => 1),
      FormPage.new(:id => 13, :name => 'zero', :number => 2)
    ]
    @form.add_page(@pages_in_order[2])
    @form.add_page(@pages_in_order[1])
    @form.add_page(@pages_in_order[0])
  end

  specify "should be able to order its pages" do
    @form.ordered_pages.each_with_index do |page, index|
     page.should_equal @pages_in_order[index]
   end
  end
end

This final version is much better. I’ll blame my not seeing it on being sick at the time.

Tags: 

Sep 06 2006

ReadingList is online

admin @ 1:27 pm

Finally.. my ReadingList app is live.

For quite a while people have been asking me for book recommendations. I put together a static reading list on my site with the intention of replacing it with something more dynamic at some time.

As Dr. Suess said “The time has come. The time is now.”

Nancy and I (ok.. mostly Nancy) deployed it to one of the servers (using capistrano & migrations).

I have a few tweaks to make in the maintenance part of the app , and I have a bunch of features to add, but it’s alive.

And it has a Tag Cloud :)

For now it’s here.

Tags: 

Sep 01 2006

Open for Business

admin @ 2:25 am

I am pleased to announce that we are opening our doors as a boutique web application/site development and hosting company.

Dave, who heads up development, has over 2 decades of experience developing software.
He is a thought leader in the areas of Object Oriented Techniques and Agile Software Development Practices.
Nancy, who is in charge of hosting, has extensive experience with web application hosting and server management.
We leverage various state of the art techniques and technologies including OS X, Debian Linux, Ruby, Rails, and AJAX, to name a few.

What’s this mean to you as a client? Solid, high quality applications delivered as efficiently as possible.
Because we can host your application as well as develop it, it’s easy for you to have changes and enhancements made.
There’s only one company to deal with, and our development and hosting are tightly integrated, providing you with the quickest turnaround time possible.

Not only do we use the lastest, most productive technologies available, but we’ve been around long enough to
have a deep understanding of the issues involved in developing and maintaining high quality, robust applications.

Legend has it that Pablo Picasso was sketching in the park when a woman approached him and asked him to sketch her.

After studying her for a moment, he used a single pencil stroke to create her portrait.

When she asked the price, he replied “Five thousand dollars.”

The woman was flabergasted: “How could you want so much money for this picture? It only took you a second to draw it!”

To which Picasso responded, “Madame, it took me my entire life.”

Experience counts. Who do you want developing your applications?

For more information, contact us at info ‘at’ daveastels.com

Tags: 

Aug 29 2006

Role Based Authentication from Rails Recipes. Part 2

admin @ 11:19 pm

Once I had the user roles code in place, working, and cleaned up, I decided to extend it by allowing regular expressions in the rights instead of literal strings (for controller & action names). Whether I stick with this going forward, who knows… but it’s convenient for development. Instead of separate rights for each action on a controller I can specify .* as the action to have the right apply to all actions for a controller.

Recall that I ended up with the core of the rights checking code actually in the Right class:

class Right < ActiveRecord::Base
  has_and_belongs_to_many :roles

  def has_right_for?(action_name, controller_name)
    action == action_name && controller == controller_name
  end
end

My first step was to wrap Rights attributes in a Regexp and do a match with the requested controller/action names:

class Right < ActiveRecord::Base
  has_and_belongs_to_many :roles

  def has_right_for?(action_name, controller_name)
    get_action_regex.match(action_name) && get_controller_regex.match(controller_name)
  end

  def get_action_regex
    Regexp.new(action)
  end

  def get_controller_regex
    Regexp.new(controller)
  end
end

It might be a case of premature optimization, but the Regexps can easily be cached since they are nicely encapsulated:

class Right < ActiveRecord::Base
  has_and_belongs_to_many :roles

  def has_right_for?(action_name, controller_name)
    get_action_regex.match(action_name) && get_controller_regex.match(controller_name)
  end

  def get_action_regex
    @action_regex || (@action_regex = Regexp.new(action))
  end

  def get_controller_regex
    @controller_regex || (@controller_regex = Regexp.new(controller))
  end

end

Part of the reason I blogged this was as an example of how much easier and obvious an enhancement can be when the code is cleanly (and extremely) refactored. More importantly, the details of rights can be changed without anything outside of the Right class being aware of it. If this ability is not one of the core benefits of OO, what is?

Tags: 

Aug 29 2006

Role Based Authentication from Rails Recipes. Part 1

admin @ 10:58 pm

First of all.. this is a great book! I’ve already used a variety of Chad’s recipes, either as is or (more often) as the basis of something customized.

That said, I was a bit disappointed when I read through Recipe 32 “Authorizing Users With Roles”. Mind you the solution was fine and worked nicely. My issue was with the code itself. Specifically, the unless condition in:

def check_authorization
  user = User.find(session[:user])
  unless user.roles.detect{|role|
    role.rights.detect{|right|
      right.action == action_name && right.controller == self.class.controller_path
      }
    }
    flash[:notice] = "You are not authorized..."
    request.env["HTTP_REFERER"] ? (redirect_to :back) : (redirect_to home_url)
    return false
  end
end

What’s wrong? Well, if we look at this code we start with a user, get the users roles, get the roles rights, get the rights action & controller. All from code in a controller. That’s a classic example of function objects & data objects. “But,” you say, “this is Rails.” So what. It’s still Object Oriented Programming. Thankfully, this is easy to clean up.

First, we can extract the condition out into a separate method:

def check_authorization
  user = User.find(session[:user])
  unless has_right?(user)
    flash[:notice] = "You are not authorized..."
    request.env["HTTP_REFERER"] ? (redirect_to :back) : (redirect_to home_url)
    return false
  end
end

def has_right?(user)
  user.roles.detect{|role|
    role.rights.detect{|right|
      right.action == action_name && right.controller == self.class.controller_path
    }
  }
end

Now we can push the code that deals with the user into the User class:

class User
  #...
  def has_right_for?(action_name, controller_name)
    roles.detect{|role|
      role.rights.detect{|right|
        right.action == action_name && right.controller == controller_name
      }
    }
  end
end

Leaving this behind:

def has_right?(user)
  user.has_right_for?(action_name, self.class.controller_path)
end

Most of User#has_right_for? can be pushed farther, into Role:

class User
  #...
  def has_right_for?(action_name, controller_name)
    roles.detect{ |role| role.has_right_for?(action_name, controller_name) }
  end
end

class Role
  #...
  def has_right_for?(action_name, controller_name)
    rights.detect{ |right| right.action == action_name && right.controller == controller_name }
  end
end

We can go one step farther:

class Role
  #...
  def has_right_for?(action_name, controller_name)
    rights.detect{ |right| right.has_right_for?(action_name, controller_name) }
  end
end

class Right
  #...
  def has_right_for?(action_name, controller_name)
    action == action_name && controller == controller_name
  end
end

Now.. the controller has a user in hand. It asks that user if it has the right to access the requested page. That user in turn, has some roles.. it asks each role if it has the right for the requested page, each role in turn asks it’s rights. Everyone in the chain talks only to what it has directly in hand. This is the way it should be. Now the controller doesn’t know or care what it means to have the right to access an action… only that the user knows, and how to ask that user.

Tags: 

Aug 19 2006

has_and_belongs_to_many

admin @ 11:19 pm

For anyone learning rails in my wake, here the “Doh!” moments I had as I tried to get many-to-many relationships working. Either it took some digging to find the required information or it wasn’t immediately obvious.

Here’s my list of things I found by trial/error/reading/ and lots of googling (and, yes, it is a verb now.. I’ve been using it as a verb for at least 3 years).

  1. Put has_and_belongs_to_many in each class, naming the other class (it’s table name really… a plural)
  2. Add instances of one class to the collection in the other, e.g. order.items << album. Do this in only one direction.
  3. Before adding to an object’s collection, save the object. E.g
    
    if order.save
      order.items << an_item
    

    It seems that this is only required in the case where order is a freshly created object. This makes sense as it will not have an id (for the join table) until it is saved. Furthermore, both objects will have to have been previously saved (i.e. have ids).

  4. The join table is made up of the names of the two related tables.. in alphabetic order. E.g. items_orders, not orders_items
  5. Don’t put an id column in the join tables. Mine had such a column because I sketched out my schema in a migration, not realizing this.
  6. In your migrations, use :id => false as a second argument to create_table, e.g.:
    
    create_table :items_orders, :id => false do |t|
      t.column :item_id, :integer
      t.column :order_id, :integer
    end
    

If I’ve missed anything on this list, please let me know and I’ll add it.

Tags: 

Aug 19 2006

Delving ActiveRecords secrets

admin @ 10:50 pm

My ramp up on Rails continues. Today I’ve been getting familiar with ActiveRecords table relationship facilities. Pretty nice. One-to-one and one-to-many were trivial… although I’m currently stuck on getting many-to-many relationship working. I keep making progress in little steps, though.

Tags: 

Next Page »