So here I am, minding my own business on Labor Day, working on the upcoming teams branch of Tempo. Basically, it introduces a new account model that’s distinct from users and projects, ideally facilitating the setup of teams for small organizations. Accordingly, an account has an owner, and every user belongs to an account. In addition, users can be managers for the whole account (new!), or users can be marked as a manager for a particular project assignment.
This all sounds great on paper, but in the Projects controller there arise a number of concerns:
The account owner or one of the managers should see all the account’s projects on the projects index, and also the /archive resource collection
All other users should only see accounts they are assigned to as managers
index should only show active projects and archive should only show inactive projects
index and archive both support an id parameter for the XML api, enabling the lookup of an aribitrary collection of managed projects
As you can imagine, this could lead to a lot of spaghetti in a controller method, that you’d want to hide away in a model. Typically, you’d want your User model to set up the various project collections as association (has_many) resources so you can provide find options as needed (such as :conditions => { :is_active => true }). That keeps your redundancy down, but your controller (or a model method) would have to conditionally decide which resource to provide. There was the possibility of adding a method to the user model that handled the conditional logic and passed back the right result set, but it would have been a royal pain to correctly pass through find options, especially given all the various circumstances above..
To make things further complicated, we need to also use these methods for the reporting interface and to make sure users only see the time entries they are supposed to see. That means keeping nonsense and code repetition to a minimum is critical.
ActsAsUnion is included in our ActsAsNetwork plugin, and it allows us to UNION together multiple associations and operate across them as we would any other association, by supporting the various find_* options we are used to with has_many.
On a tangential note, safe_find_from_ids is a quick and dirty monkey-patch we throw in an initializer so we can look up a set of ids that returns an empty set instead of raising an exception when no ids are found:
class ActiveRecord::Base def self.safe_find_from_ids(*args) ids = args.kind_of?(Array) ? args.flatten : [args] ids.empty? ? [] : self.find(:all, :conditions => ["#{self.table_name}.id IN (#{ids.collect{|p| '?'}.join(',')})", ids].flatten) end end
It’s not ideal, but it gets the job done. There may be a better way of doing it in recent versions of Rails, need to look into that.
Recently we hired Aaron Aiken’s Trim Trab Creative to put together some videos demonstrating how Tempo works, and the videos are coming out great (check out the main tour vid here). He has a real knack for putting together interesting demos and communicating the concepts at work in a piece of software.
We put a lot of thought and work in Tempo’s interfaces, but sometimes it gets tricky to explain some of the concepts to new users, especially some of the advanced controls that can save you a lot of time. Introducing people to the benefits of using our simple command line for time tracking is one of those tricky things. Aaron hit it out of the park:
Update: Corrected misspelling of Marco Arment’s surname.
Joe Hewitt, developer of Firebug and Facebook’s iPhone application, writes in an article titled, Innocent Until Proven Guilty, that Apple’s review process for third party iPhone apps in the iTunes App Store should be eliminated entirely. I was impressed with this, as it’s really the elephant in the middle of the room, that few commenters seem to address. The driving justification behind Joe’s short article is quite powerful:
Does that sound scary to you, imagining a world in which any developer can just publish an app to your little touch screen computer without Apple’s saintly reviewers scrubbing it of all evil first? Well, it shouldn’t, because there is this thing called the World Wide Web which already works that way, and it has served millions and millions of people quite well for a long time now.
Indeed, somehow we’ve managed all these years. And while the Internet has brought us various catastrophic influenza and garbage, the iPhone OS is already exposed to those threats, and third party applications are sand-boxed to the point of being a much poorer vector for malicious activity than looking for your classic remote buffer overflow. Hewitt continues, emphasis mine:
If you think that all apps should be held prisoner by Apple until proven safe, you should also be able to convince yourself that this is how the web should work.
I think the issue becomes a bit clearer if you change that statement to read, “that this is how your personal computer should work.”
In any event, there are many other developers out there who’ve been beating the drums of discontent, griping at Apple and earning some page views in the process, but the writing is usually done with an oddly sycophantic bent. Marco Arment, who is prone to such outbursts (wherein he righteously chastises Apple but then repeats over and over how much he loves the company and the iPhone itself, perhaps to prove his bona fides), recently pushed back against Hewitt’s sentiment, offering the article, Apple can’t stop reviewing iPhone apps. His main point, that Apple has no choice but to continue to review all apps lest some rogue iFart yield bad press or screw it up for all of us, rings quite hollow and altogether avoids Hewitt’s challenge: should this be how the Internet works? More to the point, should it be how your personal computer works?
Even if Apple stopped reviewing apps completely, they would still be blamed in the press for the effects of problematic apps. This creates a difficult position: Apple must attempt to be the gatekeeper in a market full of gray areas, but any decisions they make fall under intense scrutiny, and many decisions don’t have an indisputably “good” option.
Apple must attempt to be the gatekeeper? Arment is missing the painfully obvious, that Apple has been getting a ton of negative press, the likes of which they don’t normally ever see, thanks to their asinine review system and their poor behavior as a business partner. And that’s for the best, for App Store developers. It’s important to take off the Apple Fanboy bifocals long enough to look at this as a question of business relationships and investment risk.
Rather than fighting to abolish app review, it’s far more productive to guide and influence Apple, through both public and private interactions, to improve the system that we’re all, including Apple, probably stuck with for a long time.
Appeasement, or perhaps I should say bargaining with no chips, doesn’t seem very productive so far (is it ever?) Sure, it works well enough that popular developers like Arment have options for recourse; under the current system, if you are already a very popular developer with a wide audience, you can publicly embarrass Apple before that audience in order to get half-decent treatment as a business partner, adding value to their product and generating continued additional revenue for them.
Zetetic is the creator of the encrypted iPhone data vault and password manager Strip and the open source encryption-enhanced database engine SQLCipher.
We’ve just released the Zetetic.Ldap library to github, which makes it, to the best of our collective knowledge, the first and therefore most kickin’est .NET library for working with RFC 2849 LDIF, LDAP schema, and LDAP entry change tracking.
LDIF: This file format is still the best and most universal way to move data in or out of directory systems like OpenLDAP, Active Directory, Fedora or Red Hat Directory Server, etc. Zetetic.Ldap makes it easy to write and read directory entries with LDIF.
Zetetic.Ldap knows how to format binary data, dates, and long fields in the proper way — you don’t have to mess with it!
The same is true for reading entries out of an LDIF file.
LdifEntryReader ldif = new LdifEntryReader(@"c:\temp\stuff.ldif"); for (Entry entry = ldif.ReadEntry(); entry != null; entry = ldif.ReadEntry()) Console.WriteLine("Found: {0}", entry.DistinguishedName"); }
We’ve also included a command-line pivoter program that transforms LDIF files into tab-delimited files, for easy loading into Excel, RDBMS, or what-have-you. This can be incredibly useful for analyzing the data in your directory.
Change tracking: Normally, if you’re using Microsoft’s System.DirectoryServices.Protocols classes, you’ve got to manage the concept of preparing specific LDAP modification commands yourself. It can get pretty complicated, and sometimes you have to resort to trial-and-error to learn the right way to delete an attribute, for example, or rename or move an entry. Zetetic.Ldap provides a wrapper around this complexity, and will track your changes until you’re ready to send them to the server.
MutableEntry entry = new MutableEntry(mySearchResult); entry.ClearAttr("description"); entry.CommitChanges(ldapConnection);
Schema: This part is a little more abstract. Directory servers contain an area that describes the allowable objectClasses (types) and attributes (fields), and those attributes have syntaxes (like string, number, date, etc.). The classes in Zetetic.Ldap.Schema help you work with these data programmatically, so you can quickly discover what the server supports. Moreover, you can find out what kind of value (syntax) each attribute uses conceptually, without delving into the world of OIDs.
Here’s a snippet from the project’s unit tests, showing how to export the basic objectClass definitions from a Microsoft LDAP server.
ISchemaInfo target = new AdsSchemaInfo(); using (LdapConnection conn = new LdapConnection("localhost:20389")) { conn.Bind(System.Net.CredentialCache.DefaultNetworkCredentials); target.Initialize(conn); }
foreach (ObjectClassSchema o in target.ObjectClasses) { System.Console.WriteLine("oc: {0}", o); foreach (AttributeSchema a in o.MustHave) System.Console.WriteLine(" must: {0} as {1}", a, a.LangType);
foreach (AttributeSchema a in o.MayHave) System.Console.WriteLine(" may : {0} as {1}", a, a.LangType); }
We also included some helpers to manage parsing special attributes like GUIDs, or “pwdLastSet” and “accountExpires,” which are usually treated like DateTimes, but stored as 64-bit integers, sometimes outside the range of what DateTime can handle—I always enjoy telling people, “Oh, it’s simply the number of 100-nanosecond intervals since January 1, 1601 UTC!”
So anyway, when you find yourself working with LDAP or LDIF on .NET, grab the release at github, and drop us a line, we’d love to know how you like it.
There’s an extensive email-based interview of us over on Subvert.ca. There’s juicy bits about certain large companies trying to kill off one of our early projects, column fodder, and the approaches we’ve developed for taking on new clients and starting new development projects. Here’s an excerpt:
Many people we speak to find it unusual that we don’t actively prospect, solicit, or answer blind RFPs, etc
Honestly, we like it this way: when we get a referral or start a new project for a past customer, there’s already a relationship in place. The client already knows that they can trust us, and it cuts out the entire “dance” that we’d otherwise have to do to prove ourselves. There are other benefits too.
People only ask us to prepare a proposal when they are seriously considering a project. Plus, we rarely find ourselves as column fodder behind another incumbent company—we call it column fodder when you have no hope of winning a deal and your estimate is just filling in a cell on a spreadsheet for comparison purposes.
This level of trust also means that we can work more closely with our customers to develop requirements. They take our estimates and advice seriously. In the end it works out better for everyone involved.
Zetetic LLC is a small company specializing in applied data security. As the developers behind the SQLCipher encrypted database library and Codebook Password Manager, hundreds of organizations and millions of users trust Zetetic’s software and frameworks.