Welcome to the lab.

The MatchData object in Ruby gsub blocks

[Tweet : ADN : nvALT]

This post is absolutely only of interest to Ruby programmers. Just to save you some time.

I use regular expressions in Ruby a lot. One of the features I’ve come to use frequently is the block syntax for gsub calls. Whereas the other syntaxes for gsub really only provide back referencing for capture groups in replacements, the block syntax allows much more flexibility.

You have access to the $ variables for capture groups, but you also have the full power of the Regexp class available to the captures within the block. Just in case anybody else doesn’t know, here’s the scoop…

Typically, gsub (the global version of sub) is used as a pattern/replacement method with simple \1, \2 back references to make use of capture groups in the pattern (regular expression).

puts "A grin".gsub(/\b[A-Z] (\w+)/, 'Cheshire \1')
=> Cheshire grin

You can also pass a hash as the second argument, and do literal string replacement based on secondary matching.

puts "A grin".gsub(/(\w+)/, 'grin' => 'cat', 'A' => 'Cheshire')
=> Cheshire cat

These are essential tools for quick string manipulations. As you move on to parsing larger quantities of text, you usually want to do something further with the matches, whether it’s additional logic or just more complex manipulations than simple \1 syntax provides. That’s where the block format is perfect.

A gsub call with a block looks like this:

string = "How puzzling all these changes are!"

string.gsub!(/\b(\w+)/) do |match|
	if match =~ /^(\p{Lu}|t)/
			match.reverse
		else
			match.split('').sort().join('')
		end
	end
end

puts string

=> woH gilnpuzz all eseht aceghns aer!

Within that block, I always expected “match” to carry the full set of MatchData methods with it, but it’s just the full string of the overall match. You do have access to the $ operators, which you can use for referencing capture groups ($1,$2,…) in the match. However, you also have access to Regexp.last_match, which provides a MatchData object for the current gsub iteration with all of the capture group’s methods such as :names, :length and :offset, the original string (:to_s), etc..

You can even get the “pre” and “post” parts of the original string for checking context within a broader search expression. I won’t go into a detailed example, but here’s sample usage;

"I'm late, I'm late".gsub(/(\w+)/) do |match| 
	m = Regexp.last_match
	string = m.to_s
	before_string = m.pre_match
	after_string = m.post_match
	# ...
	string
end

You can actually leave off the block param (|match|) entirely. The “match” variable in this case is the equivalent of Regexp.last_match.to_s.

my_string.gsub!(/[[:punct:]]/) do
	match = Regexp.last_match.to_s
	# ...
end

You could also use Regexp.last_match[0]. The MatchData object provides direct access to capture group strings when addressed as an array (:[]), 0 being the full matched string.

Store the :last_match object for each iteration in a variable at the top of the block. If you call any Regexp methods within the block, last_match will be modified.

For short runs, you can put the block format in a single line with bracket syntax and ternary operators. Here’s an overdrawn example to illustrate a simple one-liner:

class String
	def hatter
		gsub(/[[:alpha:]]/) {|m| Regexp.last_match.offset(0)[0] % 3 == 1 ? m.upcase : m.downcase }
		# that was the one-liner!
	end
end

string = "But I don't want to go among mad people.\nOh, you can't help that. We're all mad here. I'm mad. You're mad.\nHow do you know I'm mad?\nYou must be. Or you wouldn't have come here."

puts string.hatter

=>	bUt I dOn'T wAnt to go amOng maD pEopLe.
	oh, yOu Can't HelP tHat. wE'rE aLl Mad heRe. i'M mAd. yoU'rE mAd.
	hoW dO yOu KnoW i'm Mad?
	yOu MusT bE. Or You woUldN't haVe ComE hEre.

Loads of fun. Of course this is only useful for string manipulation/processing up to a certain limit, at which point you’ll probably want to start studying StringScanner.

“I haven’t the slightest idea,” said the Hatter.

Web Excursions for October 27, 2014

[Tweet : ADN : nvALT]

Marksy
Ostensibly it’s a Chrome plugin that translates between various “humane” markup formats (Markdown, Textile, MediaWiki, etc.). It’s Pandoc on steroids. The cool part, though, is that it has an API that you can incorporate into all kinds of tools (e.g. the Sublime Text package).
Tiny Robot Software
Cool little utility for Yosemite that pipes input from the command line to a visual selection and returns output to STDOUT. Also see AppGrid, a speedy window layout manager from the same developer.
iStats
A command-line tool for displaying the CPU temperature, fan speeds and battery information on your Mac, all at once or separately (great for GeekTool, Übersicht, etc.).
Laplock
I like this idea. Have your Apple laptop set off an alarm and alert you via SMS or Yo when disconnected from a power source while locked.
nvalt-prime
“An nvALT Preview Theme on Steroids.” Really cool usage of nvALT’s custom template capabilities, enabling GFM Markdown, GitHub checkboxes, Font Awesome icons, code highlighting, and “User Story” detection and formatting for agile folks.

Bitlyize 1.5

[Tweet : ADN : nvALT]

I’ve been working on multiple “free time” projects for a while, mostly updates to existing projects. The changelogs have gotten long enough that writing them up has been an imposing task. I have a doing update that turns it into a very capable time tracking/reporting tool, a major update to SearchLink that simplifies syntax and makes it even more flexible, big updates for Marked, and more little things than I can remember without consulting my “what was I doing” log. I figured I’d start the ball rolling with a simple one.

Bitlyize is a System Service that converts all links in the selected text to bit.ly links using an authenticated account. I’ve updated it in a few ways for version 1.5.

  • It generates Amazon affiliate links for any detected Amazon links
  • It does a smarter job of creating iTunes affiliate links
  • It functions better as a command line utility
  • It works as a library that can be included in other Ruby scripts
  • If it receives a single url that’s already shortened (with Bitly), it expands it to the original url

I’ll add a project for it soon and include documentation for the library API and command line options. For now, there are two Services available in the zip file below: Bitlyize, and Bitlyize to Clipboard. The former modifies selected text in place, the latter leaves the original text and puts the results in the clipboard (especially handy for quickly shortening a link in a URL field and pasting it into an email).

Some manual configuration is required. You need a bit.ly API key, and if you want automatic affiliate links, you’ll need to include information for iTunes and/or Amazon affiliate accounts. There’s support for custom Bitly domains as well. To edit, open the Service in Automator and look for the variables at the top of the Run Shell Script action. It’s not the most convenient configuration method, but Services don’t offer a lot of options.

I’m exploring Yosemite extensions right now, and I think this would make a decent one once I start creating them. It would make configuration and usage easier, I hope.

The source code is available here, and you can use it as a CLI or include it as a library (for what it’s worth). Documentation coming soon.

Bitlyize Service v1.5

An OS X Service for quickly creating bit.ly short urls

Updated Sat Oct 25 2014.

More info…

BitTorrent Sync vs. The Cloud

[Tweet : ADN : nvALT]

BitTorrent has released the details of a comprehensive speed test they conducted to compare Sync to cloud-based services (Dropbox, OneDrive, Google Drive). The results are impressive.

Using a 1.36GB video file, they tested the time to sync between two MacBook Pros on the same network. Sync performed up to 16x faster than the cloud, transferring the video file in 41 seconds between the machines.

There are plenty of caveats to note (see the full post from BitTorrent), but the test is replicable and well-planned. If you use the cloud for syncing between machines, Sync is clearly the winner. If you’re using cloud services for sharing files and remote storage, there are more factors to consider. Sync is a peer-to-peer platform, optimized for direct connections between machines without the need to upload to the cloud, then download to the other machine. Even considering Dropbox’s LAN sync, though, Sync trumped it with 16x the speed.

Personally, I use Sync between my two local Mac minis, my MacBook Air, and my colocated Mac mini. The remote mini serves as a “cloud” that I own, and functions as a backup. With Sync’s archive support, it’s a Dropbox replacement for most of my needs. I still use Dropbox, primarily for its broad compatibility with the iOS apps I use, but Sync is definitely the fastest way for me to keep identical data on multiple machines, including an iOS app for access on the go.

I also use Transporter, and have experimented with ownCloud. With Transporter’s recently-added support for versioning, it’s a strong contender, especially if you want cloud functionality without trusting your data to a third party. When it comes to transfer speed, though, Sync is still the winner.

For more information on the test, head to the BitTorrent blog, and if you haven’t tried Sync yet, check it out.

App Review Tuesdays

[Tweet : ADN : nvALT]

Here’s a thought, albeit a slightly cheesy one: let’s institute App Review Tuesdays. It’s like Casual Friday, but it will make the App Stores better places instead of just revealing your poor understanding of Business Casual.

App Store reviews matter to App developers, especially those who have apps priced over $2.99. An honest review is helpful to other potential buyers, and a few (valid) good reviews can make a big difference.

The problem I see most often is that people use reviews as a customer support forum, leaving bad reviews based on bugs that could have been easily addressed by going straight to the developer. By leaving a one-star review on an app you’re otherwise satisfied with, you’re affecting the developers rankings while simultaneously preventing them from contacting you to help you out. It’s a lose-lose.

On the flip side, if you attempt to contact a developer and don’t receive a response, or get a response that only makes things worse, a review stating such can be helpful. I’ve never met a good developer or company that would let that happen, so negative reviews help weed out the truly bad or scammy devs.

The best way to help your favorite developers out in the App Store is to leave positive reviews wherever they’re deserved. A rating is great, and a thoughtful review is even better. When it comes to $20+ apps, interested customers are going to read a few reviews and make a decision pretty quickly. Give them something worthwhile to read.

Also note that apps you might have reviewed before may deserve updates. The App Stores separate review counts by versions, so as updates and fixes come out, you might want to revise your ratings and reviews.

So here’s what I’m thinking. Set a repeating reminder in your favorite task management app or in your calendar to spend 5 minutes on Tuesdays dropping in a review for your latest favorite app (or apps). For me, this should probably happen weekly, given the rate at which I accumulate and test out apps. It might be bi-monthly or monthly for “normal” people.

If App Review day rolls around and you’re not sure what to drop a rating on, just open up a Spotlight search in Finder (⌘⌥-Space), type in “kind:app” and arrange by “Last Opened.” See what you’ve been using for the last week and take your pick. Then go make a developers day!

Marked 2 and Yosemite

[Tweet : ADN : nvALT]

Marked 2 has been fixed up for Yosemite. The working version is available immediately for non-Mac App Store customers through download and automatic update. MAS users will have to wait for the App Store stamp of approval to be granted, and based on recent turnaround times that may be a week or more. It’s coming, though.

There are a few other tweaks, improvements and minor features. The next project is adding full RTF support with element conversion, which will mean being able to open converted files in Word and other applications and apply styles and themes. That’s nearly done!

Don’t forget, Marked 2 is 30% off for the rest of October. Use the coupon GOGONANOWRIMO at checkout, or get it on the Mac App Store. All the info you need is at marked2app.com!

Web Excursions for October 20, 2014

[Tweet : ADN : nvALT]

Drafts 4 Keyboard Scripts
A great overview of the new Drafts 4 keyboard extension capabilities.
Keyboardio
While I want an Ergodox, I don’t think I have the energy (or steady hands) to make it worthwhile. This looks like something I might be able to sink my teeth into. Or at least tap on contentedly.
Extensibility and Automation Changes in OS X Yosemite
Great overview of new possibilities in Yosemite.
sudo gem install geektool_kit — robKitson.net
A new toolkit for GeekTool users who want to script in Ruby.
Evernote - Packages - Package Control
A Sublime Text package for Evernote users, complete with Markdown capabilities.

Drafts 4 Keyboard Extensions

[Tweet : ADN : nvALT]

Drafts 4 is out as a Universal app. I’ll leave it to the experts to offer the full review, and just mention that there’s a very cool new feature in it: JavaScript-based text modification actions.

You can use JavaScript to modify text selections and entire documents, and encapsulate them in modules that you can add to the custom keyboard row. Check out the existing Keyboard Extensions for ideas.

I’m excited about this because it’s going to be relatively easy to port the tricks I built for my old WordPress plugin, Markdown QuickTags. I’m working on a few right now, and will publish them as I finish them.

Check out the Agile Tortoise site for more info, and grab Drafts 4 for iPhone and iPad in the App Store for $4.99 US.

On creation without consumption

[Tweet : ADN : nvALT]

I do two podcasts on 5by5 (Systematic and Overtired), but I’ve admitted many times that I rarely listen to podcasts. I’m often asked why (and how) that is, and I’d like to share the results of some self-examination.

In short, my brain isn’t wired for consumption. I’m happiest creating. It’s difficult for me to concentrate on what others are saying once I begin to have ideas of my own. You could say my mind wanders, but I don’t think that’s an accurate word to use. It’s not a meandering path, but a determined course of exploration.

My entire life I’ve been “bad” at school. I can’t sit through lectures, and I can’t concentrate on reading assignments. I can read, and I can comprehend, but it takes great effort to focus on what is being said and to block out my own thought patterns. I learn by doing, and I only learn things that are of interest or immediately applicable to me.

In my elementary years I was placed in a school that catered to this. It was based on Ralph Waldo Emerson’s ideas, and followed Henry David Thoreau’s methods. I was encouraged to develop the curriculum and answer my own questions. I returned to public schools in 5th grade, and never really made the transition. For the same reasons I’d been “excused” from standardized education for a few years, coming back to it was a horrible experience for me.

Despite this, I maintained a B average all through secondary and higher education. I rarely did homework, and I never did reading assignments. I managed to absorb enough contextual information in any given course to make intelligent deductions on exams.

I scored quite well on my ACT test in my Junior year of high school. Well enough to spend most of that year and my senior year in PSEO (Post Secondary Education Option). That meant that I was attending classes at the local college instead of at the high school. I did much better in an environment that rewarded creative thought, but there were still a lot of concepts my mind was unwilling to learn. I only passed microeconomics because the curve was skewed.

Computers and programming have always been of interest to me, but only on the creation side. I can’t play video games for lack of interest. I read about 10% of what comes through my RSS feeds, and then usually just skim articles. I’d rather be writing than reading. I could never even play D&D with my nerd friends because I got bored with other people’s stories.

I can watch movies, and I can listen to music, but I do it from an analytic point of view. I’ve studied filmmaking, and I’ve been playing music my entire life. When I watch a movie, I find myself studying shots and considering why I appreciate them or how I would have done them differently. It’s the same when I listen to music.

One thing that the collegiate environment provided me with was dialectic conversation. In classes I could debate, and I could inject my own questions and thoughts into the dialogue. I found friends who would stay up late with me and feverishly discuss concepts and ideas. It was brainstorming versus learning. Discovery versus study.

Basically, I think too much to be of any use as a consumer of content. I think so much that I spent eight years of my life doing everything I could to stop it. Out of school, though, I’ve started learning to cope with it. I’ve accepted that I’m incapable of absorbing all but the details most pertinent to my current endeavors. I consider it a disability, and a frustrating one, but I’ve found that I can succeed in spite of it.

I can’t create output without input, though. I can’t start from scratch. All of my best work has been the result of seeing what someone else is doing and letting it spark ideas for me. I find myself scouring GitHub late at night. It’s an ideal learning platform for me because I can find things that interest me and immediately determine how they work. In the process, I find pieces that I can build on to create something new. I compile a toolbox that lets me turn ideas into realities.

Whether I’m evolving existing work or doing something contrary, I’m lost without other people’s ideas. I believe that all of human discovery works this way, not just me. If you live in a bubble, you don’t learn, you don’t change, and you don’t invent. For all of my inability to focus and study, I’m always able to get excited about ideas and discoveries.

None of the technology and knowledge we have would be possible without what came before it. It’s a skyscraper of innovation that we’ve built since the first art on cave walls, the first inventions, the beginning of problem solving. I only survive in this world because I love learning how things came to be. If it weren’t for this, my inabilities would leave me living on the street, wondering how other people manage to be productive members of society.

Despite the many things I’m incapable of doing, I’m always able to be interested in learning the chain of concepts that led to an amazing idea, and to put them to use in solving my own problems and pursuing my own creative endeavors. My intelligence, however you quantify it, gets me in a lot of trouble if I don’t have creative outlets for it. I would destroy myself quickly if it weren’t for the ability to convert ideas into tools.

When Dan Benjamin contacted me a couple of years ago to ask if I wanted to do a podcast, I said “sure.” I started doing what I thought a podcast was supposed to be, and learned as I went. Not knowing what a “normal” podcast was like served me well in the beginning. I did some things slightly outside of the norm, and found that there were people that it appealed to. I’ve gotten to a point, though, where growth is stunted because there’s no influx of new ideas. I need to listen to other people, and I think I can do it because I’m hungry for ideas. This is when my mind latches on and I can focus.

I’m starting to listen to some of the podcasts done by people I respect and admire. Even if it’s in the background while I do other things, eavesdropping is allowing me to generate new ideas. I look forward to seeing where things go with my own podcasts in light of new input.