I took up the challenge posted over at Binstock regarding Small Classes and Short Methods and have to say it was an interesting exercise. Overall implementing these suggestions made for much more readable code, and it was certainly more OO after I finished than before.
And indeed it was. It was also one of the more important constraints. From this constraint arose insights into the relationships between elements in the underlying structure. It forced thinking *this much* harder about what is going on, and in the process finding ways to do things that are a bit more OO.7. Don’t use any classes with more than two instance variables. This is perhaps the hardest constraint.
This "50 lines" constraint is the only one that I really felt to be almost impossible to strictly adhere to. If your class overrides Object methods (toString(), equals(), hashCode(), etc.) then you are already eating up some portion of this. I ignored these for purposes of the "lines per class" metric; I don't think the spirit of the exercise suffered.6. Keep entities small. This means no more than 50 lines per class and no more than 10 classes per package.
Current revisions of Eclipse prevent the use of SoyLatte, the OpenJDK port of Java 1.6 for OS X 10.4/10.5. There is an open bug about this, and even better a patch for StandardVMType attached to the bug. I've applied the patch and it does indeed work. There is actually only a single jar used by Eclipse that needs to be built, and most of this can be done within Eclipse itself. The steps for doing this are outlined below.
- Download the Eclipse source. Unzip it.
- From within Eclipse do File | Import... | Existing Projects into Workspace and hit Next. Set your root directory to ${eclipse_src}/plugins/org.eclipse.jdt.launching. Copy the project into the workspace, just to save potential trouble.
- Check out the patch. Copy it to your clipboard.
- To apply the patch go to StandardVMType.java in the Package Explorer, right click, Team | Apply Patch...
- Now to export the plugin, i.e. build the jar needed by Eclipse. Again from within Eclipse: File | Export... | Plug-In Development | Deployable plugs-ins and fragments. Check off the plugin and perhaps change the output directory. Hit finish. You should have the jar with the applied patch in your output directory now.
- Exit Eclipse.
- From your Eclipse home directory (not the source installation, your binary install directory), go to plugins/ and find a file that is named something like org.eclipse.jdt.launching_3.3.1.v20070808_r331.jar. Move this file to a backup directory of your choosing.
- Copy the file generated in step (5) to here.
- Restart Eclipse.
- Go to Eclipse preferences | Java | Installed JREs and try and add SoyLatte as a JRE. If everything worked you should be able to do so without error.
Here is what I've come to learn from the mainstream IT media: Every "reporter" who uses the word "fanboy" -- except to decry the stupidity of the word -- should be forced to wear a scarlet letter "F" around their neck. The "F" is for "FAIL".
I tend to avoid evangelism of any sort these days, but if you are a unix weenie of any flavor and do not use screen then you are, quite frankly, not cool.
hardstatus onhardstatus alwayslastlinehardstatus string "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "FYI: JCAPTCHA uses the robust set of ImageFilters from JH Labs. These include a WarpFilter, which is what I was looking for but was unable to find. For a while. A long while. Far longer than it should have.
Friend of mine asked for something to merge two stylesheets together. I've been dinking with Python here and there when I have the time, and this was the result. It could definitely be improved, namely to work with X number of stylesheets, but it will do for now.
I like Python. Better than Ruby, actually, mainly because Ruby is too Perl-ish for my tastes. Nothing against either Ruby or Perl from a technical standpoint, just a matter of personal preference.
[--Begin--]
from __future__ import with_statement
import re
class Stylesheet:
def __init__(self):
self.cssClasses = []
def addClass(self, cssClass):
self.cssClasses.append(cssClass)
def updateClass(self, changedCssClass):
for cssClass in self.cssClasses:
if cssClass.className == changedCssClass.className:
cssClass = changedCssClass
def getClass(self, className):
for cssClass in self.cssClasses:
if cssClass.className == className:
return cssClass
def getClassNames(self):
names = []
[names.append(cssClass.className) for cssClass in self.cssClasses]
return names
def __len__(self):
return len(self.cssClasses)
def __str__(self):
cssStr = str()
self.cssClasses.sort()
for cssClass in self.cssClasses:
cssStr = cssStr + str(cssClass)
return cssStr
@staticmethod
def parseStylesheet(filename):
"""Given a physical stylesheet filename, parses it and returns a Stylesheet object."""
cssRegex = re.compile(r'\s*(.*?)\s?\{(.*?)\}', re.DOTALL)
cssDefRegex = re.compile(r'\s*?(\w.*?):\s*(.*)')
css = Stylesheet()
with open(filename) as cssFile:
classes = re.findall(cssRegex, cssFile.read())
for cssClassTuple in classes:
classStyles = []
if cssClassTuple[0].find(',') != -1:
commaClasses = cssClassTuple[0].split(',')
else:
commaClasses = [cssClassTuple[0]]
for cssClassName in commaClasses:
cssClass = CssClass(cssClassName, [])
# Now get the CssStyle objects
tmpList = cssClassTuple[1].split(';')
for cssDef in tmpList:
cssSplit = cssDefRegex.split(cssDef)
if len(cssSplit) >= 3:
style = CssStyle(cssSplit[1], cssSplit[2])
cssClass.addStyle(style)
css.addClass(cssClass)
return css
@staticmethod
def merge(css1, css2):
"""Takes two Stylesheet objects and merges them, returning the merged results"""
classes1Names = set(css1.getClassNames())
classes2Names = set(css2.getClassNames())
# This gets the classes that are in the 2nd sheet but not in the first
nameIntersection = classes1Names.intersection(classes2Names)
for name in nameIntersection:
cssClass = css1.getClass(name)
cssClass.addStyles(css2.getClass(name).cssStyles)
css1.updateClass(cssClass)
# Now get the ones that are in the 2nd one but not the first, and add them to the first
nameDiff = classes2Names.difference(classes1Names)
for name in nameDiff:
css1.addClass(css2.getClass(name))
return css1
class CssClass:
def __init__(self, className, cssStyles):
"""className: String representing the CSS class name"""
"""cssStyles: List of CssStyle objects"""
self.className = className.strip()
self.cssStyles = cssStyles
def addStyle(self, cssStyle):
"""Takes a CssStyle object and adds it to this class. If the style definition already exists it is ignored."""
if self.cssStyles.count(cssStyle) == 0:
self.cssStyles.append(cssStyle)
def addStyles(self, cssStyles):
"""Takes a List of CssStyle objects and adds them to this class. If the style definition already exists it is ignored."""
[self.addStyle(cssStyle) for cssStyle in cssStyles]
def __str__(self):
if len(self.cssStyles) > 0:
classStr = "\n" + self.className + ": {\n"
self.cssStyles.sort()
for cssStyle in self.cssStyles:
classStr = classStr + str(cssStyle)
classStr = classStr + "}\n"
return classStr
else:
return
def __cmp__(self, other):
return cmp(self.className, other.className)
def __hash__(self):
val = hash(self.className)
for cssStyle in self.cssStyles:
val+=hash(cssStyle)
return val
class CssStyle:
def __init__(self, prop, val):
self.property = prop.strip()
self.val = val.strip()
def __str__(self):
return "\t" + self.property + ": " + self.val + ";\n"
def __eq__(self, other):
return (self.property == other.property) & (self.val == other.val)
def __cmp__(self, other):
return cmp(self.property, other.property)
def __hash__(self):
return self.property.__hash__() + self.val.__hash__()
# Main logic begins here
css1 = Stylesheet.parseStylesheet('./ms1.css')
css2 = Stylesheet.parseStylesheet('./ms2.css')
merged = Stylesheet.merge(css1, css2)
with open('./merged.css', 'w') as newCssFile:
newCssFile.write(str(merged))
print "Wrote %d classes to merged.css" % len(merged)
There are three applications that come preinstalled with most *nix distros that if you spend any time whatsoever at a bash prompt make life vastly easier.
- vi/vim - IMHO everyone should at least have basic familiarity with vi. It's damn near always available, works like a charm, and once you are familiar with it allows you to do amazing things, quickly and without reaching for a mouse.
- ssh - Not much to say about this one. Very basic on the totem poll, right below "cp" or "ls" on the "yeah you should know these" scale.
- GNU screen - This one not as many people seem to be familiar with, but also makes life much more tolerable. Think of it as a way to introduce tabbed browsing into your terminal, all without needing a mouse. To create a new "tab" just hit Ctrl-A, c. To go to the next "tab", hit Ctrl-A, n. Now, there are plenty of terminal programs out there that have implemented tabs, but screen is higher on my list for several reasons. First, I don't have to use the mouse. Second, you can copy/paste text between tabs using only your keyboard. Third -- and probably the coolest -- you can detach from a screen session and reattach from a different machine, and maintain everything.
Let me explain.
Let's say you have a screen session running on your machine at work, and have 3 sessions open: one has vim open with some code you're working on, another is tailing the logs of your webserver, and another is you're general purpose bash session. So you go home, and in a flash of inspiration realize what you need to do to fix that code. So you ssh in to your work machine and type in "screen -d -r". This detaches the already running screen process and gives all those sessions to you. Voila. You have your editor, your webserver logs, and your bash shell exactly as they were when you went home.
If you decide to give screen a try, drop this in your ~/.screenrc. It puts a nice, shiny toolbar at the bottom of your window that lists the title of each session.
hardstatus on
hardstatus alwayslastline
hardstatus string "%{.bW}%-w%{.rW}%n %t%{-}%+w %=%{..G} %H %{..Y} %m/%d %C%a "
Occasionally when working in Terminal.app I've experienced strange behavior at seemingly random times where my control characters would no longer be interpreted by the shell. For example, take the following:
[1204][macjavadev@Ivan:~] grep -ri asdfasdf
Hitting control-c after running this would not actually send a break; a ^C would instead be echoed to the console. Quitting Terminal.app and reloading it didn't help.
Not sure about the cause, but I at least found a fix: tack. This tool helps you solve problems around terminfo, and playing around with it for a few minutes seems to have fixed my problem.
About a week ago iCal started giving me an error whenever I would try and accept a meeting invite. The error was sporadic, only happening about half the time:
Someone invited you to an event using an email address that isn’t on your “me” card in Address Book. Find your email address in the following list and add it to your card in Address Book.
Unfortunately there were no email addresses "following"; that was the end of the error dialog.
I checked the "to" fields in the .ics file and sure enough my email address was listed. I looked at another .ics file that did work and could find no significant differences between the two. Eventually, in true monkey-banging-on-a-keyboard style, I stumbled upon the fact that I had two different cards in Address Book for myself, both with the same email addresses. I deleted the redundant card and haven't seen the problem since.
I tried SpotlightFS for a while yesterday and was unfortunately not that impressed. It's a nice concept, but still needs work. You are limited to multi-word searches (no booleans) so it's difficult to refine the results such that they are useful. Example:
[1133][jchilders@Ivan:/Volumes/SpotlightFS/cocoa]$ ls
:usr:include:wx-2.5:wx:brush.h
:usr:include:wx-2.5:wx:button.h
:usr:include:wx-2.5:wx:checkbox.h
:usr:include:wx-2.5:wx:checklst.h
:usr:include:wx-2.5:wx:choice.h
:usr:include:wx-2.5:wx:clipbrd.h
....
[snip]
So.. interesting, but not there yet. At least the regular Spotlight window groups your search results by type. As far as I can tell with SpotlightFS there is no way to refine your results beyond simple keywords. Also, you cannot open files directly from within the SpotlightFS directory, which almost defeats the purpose of having it.
This is an interesting experiment, and I'll keep an eye on it, but as it exists right now it's not particularly useful on a day-to-day basis.

on Eclipse, OS X, and SoyLatte