a work on process

Viewing posts tagged: Tutorial

book sleeve of Ruby on Rails Enterprise Application Development It’s very clear from posts on the rails email list that there’s space for books that take the novice developer beyond what they learn in beginners’ books like Agile Web Development With Ruby on Rails. Those books will help you build your first app and get a sense of the structure of Rails but before long you’ll want to write code that needs to be reusable between models, or across projects, or to interface with services other than databases. The beginners’ books, by definition, don’t deal with intermediate topics. That’s the niche Ruby on Rails Enterprise Application Development sets out to fill, but sadly it doesn’t offer much beyond those beginners’ guides.

Based on the title, I had expected going in that the book would be mostly focussed on cross-systems integration such as the use of LDAP, web services (particularly SOAP), connecting to multiple and/or legacy databases and other topics often lumped together as “enterprisey” which push at the rails conventions or require them to be abandoned. In fact the book would be better titled along the lines of “Up and running on Ruby on Rails in a small business” as the application developed through the course of its chapters is very simple and doesn’t demonstrate much about Rails itself that couldn’t be gleaned from AWDwRoR or one of its competitors.

Where some may find this book most useful is in the attention paid to windows-based development and to deployment (where it focusses on unix-based systems). Most of the books currently on the market do touch on using windows, but the authors of this one clearly use it themselves and actually advocate the use of an IDE for windows development. If your tastes run to Eclipse on Windows, that may draw you to this.

Though the book references Rails 1.2.3, the application built inside could just as easily have been built on Rails 1.1. The app is CRUD-based, but there is no mention of REST or map.resources, despite the fact that they could have simplified it. Authors don’t have to buy into the prevailing wisdom in the rails community, but it’s a shame when strategies that can simplify apps aren’t covered.

Perhaps more worrying given that Rails 2.0 is now out the door and will be the default for anyone starting out with this book now is the use of deprecated techniques such as dynamic scaffolds (the scaffold keyword was removed 10 months ago in changeset 6306) and old-style pagination (which left in changeset 6992 back in June)

There certainly aren’t as many books out there for newcomers to Rails as there are for some other languages/frameworks, so its inevitable that more are to come. It’s a shame that already at least two such books have been pitched at intermediate developers rather than their more correct audience, and hopefully that trend won’t continue. It would also be very helpful if packt would publish an addendum to this volume detailing the aspects that no longer work in Rails 2.0. Missing some great new feature in Edge Rails is one thing, but it’s quite another to rely on features that were scheduled for removal over six months before a book goes to press.

Disclaimer: I was sent a copy of this book for review by the publisher. You can find it at packt, amazon US, amazon UK and all sorts of other places.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

Granting and Checking Permissions with LiveUser

15 February 2006 (10:52 pm)

By James Stewart
Filed under: Snippets
Tagged: , , , , ,

I was reminded by maxi_million in the comments on one of my previous LiveUser tutorial entries that I never completed the promised third entry in that series. After the initial procrastination wore off and I initially turned my mind to writing this piece, my main project using LiveUser ended up being converted (for various reasons) into a drupal site, so my further use of the library has been quite minimal. But I do have a little code sitting around, so will try and draw together a few notes on how I was using Liveuser.

While many powerful authorization systems are purely role-based, something that can be achieved in LiveUser with the groups functionality covered in the previous entry on this subject, LiveUser also supports much more fine grained permissions. Each permission is its own entity, and permissions are grouped into areas. I created my areas directly in the database so won’t be covering that here. Once you have your area’s id in the variable $area_id and an instance of LiveUser_Admin in $admin you can call:

$right = $admin->perm->addRight(
  array('area_id' => $area_id, 
    'right_define_name' => 'a name for your right'));

to create the right and capture its ID in $right.

You can then ‘grant’ that right to a user with:

$admin->perm->grantUserRight(
  array('right_id' => $right, 
    'perm_user_id' => $perm_user_id));

Or grant it to a group with:

$admin->perm->grantGroupRight(
  array('right_id' => $right, 
    'group_id' => $group_id));

Where I ran into problems was when I attempted to check those permissions. From the API documentation, it wasn’t clear how to check for a right given only its name, but after a little exploration I put together the following method (used within my wrapper class) that handles it. $this->user is an instance of LiveUser and $this->admin is an instance of LiveUser_Admin:

function checkRight($area_id, $right_desc) {
  if (empty($this->admin)) {
    $this->getAdminInstance();
  }
  $filter = array(
    "fields" => array("right_id"),
    "filters" => array(
      "area_id" => $area_id,
      "right_define_name" => $right_desc));
  $rights = array_keys($this->admin->getRights($filter));
  $right_id = $rights[0];
  return $this->user->_perm->checkRight($right_id);
}

You’ll note that the final line makes use of the _perm property. In PEAR coding conventions a preceding underscore means a method or property is private and should not be accessed from outside the class, but this was the neatest approach I was able to find in the time. If anyone can tell me how to do this within the approved public API I’ll happily update this entry.

Update: Be sure to check out the comments, where Lukas has been adding some very useful information.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

A Lengthier Introduction to DB_NestedSet

2 March 2005 (9:03 am)

By James Stewart
Filed under: Snippets
Tagged: , ,

A little more work, and a little more discussion with Daniel Khan finally has my tools for interacting with PEAR::DB_NestedSet complete. There’s a mistake in the documentation for the moveTree method that had me thrown for a while. Since it’s taken a while to get to grips with this module, I thought a summary of how I’ve used it might be in order.

Please note that this is a fairly hastily put together piece, which does not delve into all the intricacies of the package but should give an experienced PHP coder with a basic understanding of the theory behind nested sets with the information they need to get up and running. For some backgrounf on nested sets I’d recommend this article.

Configuration and Instantiation

To use the module with a database, you will need a table with (mostly integer) fields for at least:

  • Node ID
  • Node Parent
  • Left
  • Right
  • Order_Number
  • Level
  • Name (I use a varchar for this one)

and another field for category locks so that NestedSet can make changes withour worrying about simultaneous changes confusing matters. Mine has the following fields:

  • lockId (char(32))
  • lockTable (char(32))
  • lockStamp (int(11))

I’m initially using the module to represent a navigation menu so I also added a ‘url’ field to allow me flexibility in where each link points, and a ‘permissions’ field so I can determine which of my users can see a given node. I defined how my field names match the required ones my setting up an array:

$table_match = array(
	'id' => 'id',
	'parent' => 'rootid',
	'lft' => 'l',
	'rght' => 'r',
	'order_num' => 'norder',
	'level' => 'level',
	'name' => 'name'
);

The key for each array entry is the database field name I’ve used and the value assigned to it is the name the module prefers.

The one other value we need is the DSN for accessing our database. eg:

$dsn = 'mysql://user:password@host/database'

With all of those in place we can start up our object:

$nestedSet =& DB_NestedSet::factory('DB', $dsn, $table);

You also need to tell NestedSet which tables in the database it is using. Mine are called ‘categories’ and ‘cat_locks’, so I tell it to use those with:

$nestedSet->setAttr(array(
	'node_table' => 'categories',
	'lock_table' => 'cat_locks'
));

With all that in place, $nestedSet is ready for us to interact with.

Adding Nodes

NestedSet supports storing multiple ‘trees’ within one database table, each of which can have its own root node. To create a root node we use the following method:

$parent = $nestedSet->createRootNode(array('name' => 'Home'), false, true);

This creates a root node with the name ‘home’ (we can set other attributes by including them in the array, and returns it as an object in $parent.

We can add sub-nodes with:

$subnode = $nestedSet->createSubNode($parent, array('name'  => 'Sub Node'));

This returns the sub-node as an object in $subnode. Adding further subnodes is as simple as repeating this method with $parent replaced with whichever object you want this node to be a child of. So to add another element which is a sibling of “Sub Node” we would use:

$siblingnode = $nestedSet->createSubNode($parent, array('name' => 'Sub Node Sibling'));

and to add a child of that node we could use:

$third = $nestedSet->createSubNode($siblingnode, array('name' => 'Level 3'));

NestedSet takes care of all the database access for you, so once you’ve made these calls there’s no need to worry about telling it to store your values.

Changing Node Properties

Once you have your nodes created, you may well want to add or change some of the information associated with them. In order to do that we can make use of the setAttr() method. Say we wanted to change the name of a node with id = 3:

$ournode = $nestedSet->pickNode(3);
	$changes = $nestedSet->setAttr(array('name' => 'A new name'));

Deleting Nodes

To delete a node, we want to make use, unsurprisingly, of the deleteNode() method. All the method requires is the numeric ID of the node that you want to delete. ie.

$delete = $nestedSet->deleteNode(2);

The method returns “true” if the node is successfully deleted, and “false” otherwise. Remember that deleting a node could “orphan” any “children” it might have had, so use with caution.

Moving/Copying Nodes

Rather than employ a moveNode() method to move single nodes, which if used wrongly could leave orphaned nodes all over the place, the developers of NestedSet have implemented the moveTree() method which allows you to copy or move a node and all of its children in one go.

moveTree() takes four arguments:

  • id of the node you want to move/copy
  • name of the “target” node
  • a constant defining how you want the new position to relate to that target node
  • optionally a boolean saying you want to copy rather than move the tree

The method offers three constants to describe how you want to make the move:
NESE_MOVE_BEFORE, NESE_MOVE_AFTER, NESE_MOVE_BELOW

So in other words, if you want to move node number 4 (and any of its children) to be a child of node 2 you would use:

$move = $nestedSet->moveTree(4, 2, NESE_MOVE_BELOW);

If instead of moving it you wanted to copy, and instead of making it a child you wanted to make it a sibling coming next in order, you would use:

$copy = $nestedSet->moveTree(4, 2, NESE_MOVE_AFTER, 1);

Producing Output

Before we can produce output, we need to specify exactly which nodes we want to output. If we want all the nodes from all the trees in the database, we would use:

$data = $nestedSet->getAllNodes(true);

To retrieve all nodes within the tree that $id is a member of we would use:

$data = $nestedSet->getBranch($id, true);

And to get all the descendants of node $id:

$data = $nestedSet->getSubBranch($id,true);

This produces an associative array called $data containing all of the nodes. Just as with any associative array we can iterate through it in a variety of ways and make any changes we want, so say we wanted to add a “url” field based on the name of each node we could use:

foreach ($data as $id => $node) {
		$data[$id]['url'] = "/option/".urlencode($node['name']);
	}

NestedSet supports a number of “output drivers” or mechanisms for connecting with other tools to produce our output. The most commonly used option is the “Menu” driver which can be used with PEAR::HTML_Menu to output HTML representations. To use that we first need to define some parameters eg:

$params = array(
		'structure' => $data,
		'titleField' => 'name',
		'urlField' => 'url'
	);

Here “structure” is the associative array with our data in it, and “titleField” and “urlField” are the keys in that array for the title and URL of each node.

With that array in place we have a few more steps to produce our output. Firstly, we want to get an Output object from NestedSet. This will allow us to access the data in a method that is useful for our different output systems. We use a factory method with the parameters we just defined and the name of the driver we want to use:

$output =& DB_NestedSet_Output::factory($params, 'Menu');
	$structure = $output->returnStructure();

At this point, $structure will contain a “nested” array which looks rather like the structure of our tree. You might like to try:

print_r($structure);

to take a look.

If you want to make use of HTML_Menu, this is the data you pass on to it:

$menu =& new HTML_Menu($structure, 'sitemap');

should do the trick.

Since I tend to use Smarty for all my templating, I have taken to assigning $structure into smarty and then use a custom plugin to iterate over it and produce the menus. You can find that plugin here.

Conclusion

Once you’re up and running with NestedSet it provides a simple way of maintaining tree structures and using them in web applications. While it’s not the appropriate choice in all contexts (I wouldn’t choose it if I were using XML to store my data, for example) it’s well worth a little investigation.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

More DB_NestedSet

23 February 2005 (3:16 pm)

By James Stewart
Filed under: Snippets
Tagged: , ,

Daniel Khan, one of the maintainers of PEAR::DB_NestedSet jumped in and offered help after my last post on the subject of that package. It turned out there was a bug in the file Menu.php within the current version of NestedSet which is now fixed in CVS. Once that change was made it turned out there were still a few bugs in my code to be worked out.

With everything fixed I can now access menus as I would wish.

To retrieve all nodes: $data = $nestedSet->getAllNodes(true)

NestedSet supports multiple trees in one database table, each with their own parent. To retrieve all nodes within the tree that $id is a member of: $data = $nestedSet->getBranch($id, true);

And to get all the descendants of $id: $data = $nestedSet->getSubBranch($id,true);

It’s definitely making my menu handling much easier. I’m still working out how best to integrate functions to move and delete nodes with my admin tools and once I’m done I’ll try and write up a few examples here.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]