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.
When doing J2EE development against Java5 a frequently encountered warning message is "The cast from X to Y is actually checking against the erased type Y." This is most frequently seen when getting a request or session attribute and putting it into a genericized collection:
List<MyObj> = (List<MyObj>) request.getAttribute(SOME_OBJ);
This constantly annoys me, so I came up with the following and put it in a helper class:
public static Collection<?> getAttributeAsTypedCollection(HttpServletRequest request, String attribute) {
Collection<Object> c = null;
Object obj = request.getAttribute(attribute);
if (null != obj && obj instanceof Collection) {
c = new ArrayList<Object>();
Collection<?> tmpList = (Collection<?>) obj;
c.addAll(tmpList);
}
return c;
}
Calling it with the following produces no warnings:
List<Integer> newList = (ArrayList<Integer>) getAttributeAsTypedCollection(request, "myAttr");