tag:blogger.com,1999:blog-84656557363374010052024-03-23T03:14:26.800-07:00Joe Merante's Blogjoehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comBlogger75125tag:blogger.com,1999:blog-8465655736337401005.post-35431687810595917612024-03-21T12:10:00.000-07:002024-03-21T12:10:56.700-07:00Privacy by Design References<div>(This was written and first published elsewhere in June 2020)</div><div><br /></div><div>Below are some resources that have been useful as I've been exploring the relationship between privacy by design and engineering.</div><ul><a name='more'></a>
<li><a href="https://www.cnil.fr/en/cnil-publishes-gdpr-guide-developers">CNIL GDPR Guide for Developers</a><ul>
<li>GDPR or not, this guide is a great stepwise process to keep an eye on privacy issues throughout the SDLC.</li>
</ul>
</li>
<li><a href="https://www.salingerprivacy.com.au/training/webinars/free-paw/">Putting the D into PbD: Turning Privacy Law into Design Solutions</a><ul>
<li>This fun webinar during Privacy Awareness Week in May 2020 featured a role playing exercise to highlight the disconnect that lawyers and engineers often experience with respect to privacy engineering. Many of its lessons really resonated with experiences I've had. With an engineering hat on, answers seem like a simple binary solution. On the privacy side, however, it's one of the fuzzier areas of law. What to do?</li>
<li>Many of the examples in the webinar focused on clarifying shared language in context. For example, "sensitive data", "encrypted" or "personal information" are likely to have nuanced definitions across jurisdictions. Similarly, consent is not a magic wand to fix all of your potential legal or regulatory issues. (And might have a <a href="https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/consent/what-is-valid-consent/">precise definition</a> that only a lawyer could love.)</li>
<li>Another great idea reviewed was taking the classic (and still very useful) <a href="https://www.csoonline.com/article/3519908/the-cia-triad-definition-components-and-examples.html">CIA security triad</a> and expanding with a few privacy categories to ensure that privacy issues are included in risk assessments and threat models.</li>
<li>There are some great materials on the <a href="https://www.salingerprivacy.com.au/blog/">Salinger Privacy blog</a> as well, check them out!</li>
</ul>
</li>
<li><a href="https://iapp.org/news/a/how-to-operationalize-privacy-by-design/">How to operationalize privacy by design</a><ul>
<li>This IAPP article highlighted important communication considerations in implementing an organization-wide or product-specific privacy program. The importance of C-level support and early communication, getting involved in the right meetings early in projects, and trying to automate processes are among the recommendations here.</li>
<li>The need to constantly reevaluate your approach and integrate continuous learnings from various sources really resonated for me. Not only does guidance change -- hello, CCPA and maybe soon, CPRA -- but project needs rapidly shift. Whether checklists and guidance, code reviews, or advising on the privacy tradeoffs of new features, there's a lot for privacy pros to keep up with.</li>
</ul>
</li>
</ul>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-38705289706334969372024-03-21T12:06:00.000-07:002024-03-21T12:07:24.008-07:00CodeQL jQuery Example(This was written and first published elsewhere in June 2020) <p>The example below comes from a <a href="https://www.youtube.com/watch?v=pYzfGaLTqC0" target="_blank">May 2020 workshop</a> on using CodeQL. To set up CodeQL, you import code into a database to run queries against using the CodeQL language. The results of queries can be viewed inside Visual Studio Code. More resources <a href="https://github.com/githubsatelliteworkshops/codeql/blob/master/javascript.md">here</a>.</p>
<p>The snippet below checks for potentially unsafe input to <code>$</code> in jQuery plugins. In the older version of Bootstrap examined in the workshop, an xss vulnerability existed because the library didn't check whether actual DOM elements were being passed to <code>$</code>, creating an xss sink. For example, when <code>.text()</code> is called in code like <code>$(options.textSrcSelector).text()</code>, an unsafe string passed to <code>$</code> could be executed by jQuery. The <a href="https://github.com/githubsatelliteworkshops/codeql/blob/master/javascript.md">workshop repo</a> suggests one better way to refactor the code.</p>
<p>The from/where/select syntax is a little SQL-y, you <code>import javascript</code> to get the autocomplete goodies in VS Code, use classes and predicates to organize and reuse code. Note that <code>=</code> is equality, not assignment. Other concepts of data flow nodes and data flow analysis (like a DAG) to account for alternate syntax & indirection were explained well. Built in taint tracking is another neat feature.</p>
<pre><code class="language-js">import javascript
import DataFlow::PathGraph
class Config extends TaintTracking::Configuration {
Config() { this = "Config" }
override predicate isSource(DataFlow::Node source) {
exists(DataFlow::FunctionNode plugin |
plugin = jquery().getAPropertyRead("fn").getAPropertySource() and
plugin.getLastParameter() = source
)
}
override predicate isSink(DataFlow::Node sink) {
sink = jquery().getACall().getArgument(0)
}
}
from Config config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink, "Potential XSS vulnerability in plugin."
</code></pre>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-9803362508449231022024-03-21T12:04:00.000-07:002024-03-21T12:07:11.175-07:00D3.js Favorites<p><i>(This was written and first published elsewhere in June 2020)</i></p><p>A few years ago I got really into d3.js while working on some front end-heavy projects, and still love to see the awesome work shared by its community. (Observable notebooks are 😻)</p>
<a name='more'></a>
<p>Getting started</p>
<ul>
<li><a href="https://bost.ocks.org/mike/">https://bost.ocks.org/mike/</a><ul>
<li>To get a feel for the power of the library, browse through these examples from the creator of d3. Prolific, educational, inspiring, there aren't enough words to describe the great output from Mike Bostock. Thanks!</li>
<li>More recent work is on <a href="https://observablehq.com/@mbostock">Observable</a> including <a href="https://observablehq.com/collection/@d3/learn-d3">this collection of Learn D3 notebooks</a>.</li>
</ul>
</li>
<li><a href="https://bl.ocks.org/">Blocks</a><ul>
<li>A simple way of visualizing GitHub gists. I used to search for "[chart type] bl.ocks.org" when looking for a starter.</li>
<li>Now things are moving to <a href="https://observablehq.com/@d3/">Observable</a>, you can find just about anything there.</li>
</ul>
</li>
<li><a href="https://www.amazon.com/gp/product/1617294489/ref=as_li_tl?ie=UTF8&tag=joemerante-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1617294489&linkId=d0a9fbd0f382cc2b0b398f9cbb79c5e9">D3.js in Action: Data visualization with JavaScript 2nd Edition</a><ul>
<li>I read the first edition of this book and would highly recommend it for anyone really getting into data visualization. The author does a great job of breaking down d3 concepts, walks you through plenty of examples, and grounds everything in practice. A little copy pasta might do the trick for a simple bar chart, but when you're dealing with interactivity and complex interactions, hierarchical data, or just want to feel the freedom to be really expressive, you've gotta dig in. This is a great way to get there.</li>
</ul>
</li>
<li><a href="http://vallandingham.me/bubble_chart_v4/#">Gates Bubbles</a><ul>
<li>This visualization and its <a href="https://vallandingham.me/bubble_charts_with_d3v4.html">companion blog post</a> opened a new set of possibilities for me at the time. (And some cool demos on my former team that never saw the light of production 🙁) The author hosts a ton of other great posts and examples, too.</li>
</ul>
</li>
<li><a href="https://observablehq.com/@unkleho/covid-19-bubble-chart-with-d3-render">Covid-19 Bubble Chart</a><ul>
<li>Another neat bubble chart. I recently spotted this example that works through a great way of organizing objects into "components".</li>
</ul>
</li>
<li><a href="https://github.com/mdezube/sms-analysis">SMS Analysis</a> This project was demo'ed at a Meetup a few years (eek!) ago. It takes one's SMS from an iPhone backup and produces various visualizations.</li>
<li><a href="https://observablehq.com/@d3/choropleth">Choropleth</a> No collection would be complete without a choropleth map!</li>
</ul>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-83284514961033978282024-03-21T11:39:00.000-07:002024-03-21T12:03:09.607-07:00Read-once objects<p><i>(This was written and first published elsewhere in June 2020)</i></p><p>This concept comes from a book I really enjoyed reading last year, <a href="https://www.amazon.com/gp/product/1617294357/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1617294357&linkCode=as2&tag=joemerante-20&linkId=fcf392fba69449c44920daf2383d12d2" target="_blank">Secure by Design</a>. The authors suggest using a "read-once object" to represent sensitive values to avoid unintentional use or data leakage.</p>
<a name='more'></a><p>A read-once objects has the following properties -</p><p></p><ol style="text-align: left;"><li>Its main purpose is to facilitate detection of unintentional use</li><li>It represents a sensitive value or concept</li><li>It's often a domain primitive</li><li>Its value can be read once, and once only</li><li>It prevents serialization of sensitive data</li><li>It prevents subclassing and extension</li></ol><p></p><p>In the book, the authors start with the example of a <code>Password</code> domain primitive as a read-once object. If you're one of the brave readers of this note, you've likely seen a few passwords stored as strings on user objects. While there are plenty of benefits to the use of a domain primitive over a string (validations, unit testing, encapsulation), once you've read the password to compare it during authentication, there's no need to keep it around. While there are other ways of removing from memory or scrubbing logs, why bother? There's enough to remember as it is. </p><p>The book provides additional examples and relies heavily on the value of DDD as a foundation for secure design throughout the book. There's a free chapter on the publisher's site on <a href="https://freecontent.manning.com/domain-primitives-what-they-are-and-how-you-can-use-them-to-make-more-secure-software/" target="_blank">domain primitives</a>.</p><p></p><p>I spotted <a href="https://github.com/azu/read-once" target="_blank">this repo</a> putting the concept to practice in TypeScript. Maybe one day will get around to putting together some examples in other languages (or better yet, using the idea on a project). In Ruby, for example, I'd think about overriding <code>to_s</code> and <code>to_json</code>, maybe use object lifecycle callbacks to prevent subclassing, and clone and return a temporary value in the property getter. There's plenty more in <a href="https://www.amazon.com/gp/product/1617294357/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1617294357&linkCode=as2&tag=joemerante-20&linkId=fcf392fba69449c44920daf2383d12d2" target="_blank">the book</a>, I'd highly recommend it.</p><p><br /></p><p><i><br /></i></p>joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-89585925992255822042020-06-19T14:20:00.002-07:002024-03-21T11:58:43.381-07:00A New Serverless Look<div><i>(Update 3/20/2024: If it ain't broke, do the classic developer thing and rebuild it! I've taken the site described below offline, and moved its posts here.)</i></div><div><br /></div>Until recently, I had no reason to build anything elaborate with a serverless architecture. Yet it's <a href="https://stackoverflow.blog/2020/05/18/you-want-efficient-application-scaling-go-serverless/">everywhere</a> now! There's <a href="https://aws.amazon.com/amplify/console/">Amplify</a>, <a href="https://www.netlify.com/">Netflify</a>, <a href="https://www.gatsbyjs.org/docs/glossary/jamstack/">JAM</a>, <a href="https://aws.amazon.com/serverless/sam/">SAM</a>, <a href="https://aws.amazon.com/lambda/">lambdas</a>, <a href="https://www.cloudflare.com/en-ca/products/cloudflare-workers/">workers</a> and more to learn about.
<a name='more'></a>
A few weeks ago on another stuck-inside pandemic Saturday, it felt like a good time to experiment with <a href="https://www.gatsbyjs.org/">Gatsby</a>. The documentation is great, and there are plenty of articles and great community resources available. I wanted to find a theme or examples of pulling in Markdown files which I'd populate the files with notes, code snippets, and other handy references I've collected related to infosec recently. Then over time maybe dig through years of scattered snippets of knowledge that I find myself reaching for, like "that link to a presentation about the history of browser privacy standards that I know I saved somewhere!!!" among other greatest hits.<br />
<br />
The <a href="https://www.gatsbyjs.org/docs/quick-start/">Quick Start</a> in the official docs provided an easy cli installation and familiar directory structure. There were obvious examples of things like images, links, some available globals if needed, and a sense of how data is meant to flow. Also an SEO component making metadata generation easy. Nice.<br />
<br />
I hopped over to GitHub and set up my repo and local remotes. Next up, AWS. I started with a private S3 bucket to host the static site behind a Cloudfront distribution and a new IAM user for GitHub Actions. <a href="https://blog.elantha.com/gatsby-s3-cloudfront/">This series of tutorials</a> was a great simple walkthrough, though I opted to write the GitHub Action to deploy myself rather than use the library suggested by the article. Once things looked good on Cloudfront, I added a <a href="https://gist.github.com/lmakarov/e5984ec16a76548ff2b278c06027f1a4">Lambda@Edge function to restrict access</a> for now. Then it was back to the look and feel. I read a bit about themes and plugins, and started looking around.<br />
<br />
<i>*Fast forward to the following weekend*
</i><br />
<br />
Along came <a href="https://github.com/mrmartineau/gatsby-starter-code-notes">this great code notes theme</a>! Perfect. A <a href="https://get.dev/">.dev TLD</a> seemed appropriate, which led to <a href="https://icannwiki.org/.dev">ICANN</a> and a <a href="https://medium.engineering/use-a-dev-domain-not-anymore-95219778e6fd">great read on Internet history</a> and the origins of the <a href="https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security">HSTS</a> <a href="https://www.chromium.org/hsts">preload list</a>, aka why .dev (and some other sites and TLDs) are https-only.<br />
<br />
Since I didn't purchase the domain name through AWS, I had to create a Hosted Zone and point the domain's nameservers to the values displayed in Route 53. I <a href="https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html">requested a certificate</a> in AWS Certificate Manager and added the values for CNAME verification into Route 53. The final step was attaching it to the Cloudfront distribution along with adding CNAMEs in Cloudfront as Alternate Domain Names. <a href="https://deliciousbrains.com/wp-offload-media/doc/custom-domain-https-cloudfront/">Here's a nice write up</a> on some of these steps.<br />
<br />
I pushed a test commit to ensure my GitHub Actions pipeline still worked. All good. Then a few finishing touches, a few new notes, and off came the authorization lambda! Not bad for a couple of afternoons. Thanks to the Gatsby community, the theme author, and all the other resources noted. I almost forgot, you can see it at <a href="https://joemerante.dev/">https://joemerante.dev</a>!joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-61437678124226787942020-01-18T22:41:00.002-08:002020-01-20T19:48:49.967-08:00Changing PDF Metadata with PythonWhile updating a pdf recently, I noticed some metadata I wanted to change and a few annotations that were hidden from view but still in the file. However, the "Get Info" pane in Preview on OS X doesn't provide a metadata editor, nor does its Export function, so it seemed like a good opportunity to learn a bit more about the PDF standard and Python packages for getting the job done. Adobe Acrobat or other GUI's would've been much faster, but I'll likely need to do this programmatically again at some point like those of you who might've found this post by looking on your favorite privacy-preserving search engine for "change pdf metadata in python". So here we go.<br />
<br />
Before starting, I hopped into a new folder and created a git repository with a first commit of my original pdf in case anything went wrong. Then I ran <code>conda create --name pdf --python=3.8.1</code> and <code>conda activate pdf</code> to set up an <a href="https://docs.conda.io/projects/conda/en/latest/user-guide/concepts/environments.html">Anaconda virtual Environment</a> named <code>pdf</code> to keep my work isolated. I've found Anaconda to be a simple way of managing Python packages and dependencies after initially trying it because it made launching Jupyter notebooks with various preinstalled packages a breeze.<br />
<br />
After browsing in a few places for pdf editing libraries, I first tried <a href="https://pypi.org/project/pdfrw/">pdfrw</a>. While editing metadata based on the code in <a href="https://github.com/pmaupin/pdfrw/blob/master/examples/alter.py">this example</a> in its GitHub repo was easy, the library had no support for dealing with annotations. Next up was <a href="https://pypi.org/project/PyPDF2/">pypdf2</a>, but its <a href="https://pythonhosted.org/PyPDF2/PdfFileWriter.html#PyPDF2.PdfFileWriter.removeLinks">removeLinks()</a> method removed annotations (as expected) along with other metadata like title and author info I wanted to preserve (unexpected).<br />
<br />
<a href="https://pymupdf.readthedocs.io/en/latest">PyMuPDF</a> turned out to have exactly what I needed. I found the <code>deleteAnnot</code> function in the documentation right away and got things working pretty quickly to strip the annotations. In updating the modification date, I noticed a date string I hadn't seen before. Thankfully, there's StackOverflow to the rescue with <a href="https://stackoverflow.com/questions/41661477/what-is-the-correct-format-of-a-date-string">this entry</a> showing how the elements of the date-time string break down with links to the references. Then it was just a matter of formatting the <code>datetime</code> to the string I needed. The basics of my script are below:<br />
<pre>import datetime
import fitz # assumes pymupdf
doc = fitz.open("existing_document.pdf")
for idx, page in enumerate(doc):
try:
while (next(page.annots())):
annot = next(page.annots())
page.deleteAnnot(annot)
except StopIteration:
print(f'annotations from page {idx + 1} removed')
# use more recent date format
# https://stackoverflow.com/questions/41661477/what-is-the-correct-format-of-a-date-string
if doc.metadata['creationDate'][-1] == "'":
doc.metadata['creationDate'] = doc.metadata['creationDate'][:-1]
# funky ISO 32000-1:2008 date format
# see https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/PDF32000_2008.pdf
formatted_no_utc_offset = datetime.datetime.utcnow().strftime("%Y%m%d%H%M%SZ00'00")
doc.metadata['modDate'] = formatted_no_utc_offset
doc.setMetadata(doc.metadata)
# see https://pymupdf.readthedocs.io/en/latest/document/#setmetadata-example
doc._delXmlMetadata()
doc.save("spiffy_new_document.pdf", garbage = 4)
</pre>
Maybe there are better libraries, or more Pythonic ways of doing the above, please leave a comment on <a href="https://gist.github.com/joemerante/6d30ed62edf7ac0eda124a8d262f160e">the GitHub gist</a> with any suggestions.
Note that you might still find surprises if you look at the file in a text editor; I thought the removals above were good enough until my curiosity got the better of me and I opened the file in a text editor. It was mostly garbled and not meant to be viewed as plain text, yet I still found my name in what looked like a popup annotation object, despite thinking they were all gone! Yet no comments or annotations showed in any GUIs. I suspected some corrupt data or hidden object was leftover since the file had been around for years and passed through many editors. Who knows.<br />
<br />
<a href="https://unix.stackexchange.com/a/109177">This answer</a> led me to the <a href="https://github.com/qpdf/qpdf">qpdf</a> library and I was able to decompress the file, change the string I noticed (preserving the byte length as the answer hints, otherwise the file breaks), and recompress it quickly. This final bit of the exercise served as a good reminder of just <em>how much info</em> gets packed into the innards of files or put in places you're unlikely to notice. As documents age, get updated or passed around organizations, it's important to remember to occasionally take a look at what's lurking in the metadata.joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-68840295930254754132019-11-19T06:53:00.003-08:002019-11-19T08:28:23.840-08:00Private Enough<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; min-height: 14.0px}
span.s1 {text-decoration: underline ; color: #dca10d}
span.s2 {color: #dca10d}
span.s3 {text-decoration: underline}
</style>
<br />
<div class="p1">
I recently attended a discussion of <a href="https://mitpress.mit.edu/books/smart-enough-city"><i>The Smart Enough City</i></a>, which got me thinking about what "private enough" online services might mean to people. Privacy is an admittedly slippery concept and your idea of privacy may differ dramatically from mine. Privacy as <a href="https://nyuscholars.nyu.edu/en/publications/privacy-as-contextual-integrity">"contextual integrity"</a> is one concept that helps address the definitional inconsistencies by <a href="https://theory.stanford.edu/people/jcm/papers/barth-datta-mitchell-nissenbaum-2006.pdf">focusing on information transfer</a>. However, the scholarly literature which I have great respect for won't be particularly useful in explaining what I'm working on to my relatives at Thanksgiving dinner. A look at some everyday online activities will demonstrate how the battle to make the Internet private enough is coming from many directions.</div>
<div class="p2">
<br /></div>
<div class="p1">
The first elephant in the room is online advertising, which is something I'm not entirely opposed to. It's just the undisclosed third party data sharing without anything that feels like meaningful opt out that’s too invasive. My views lean towards those <a href="https://twitter.com/dhh/status/1189643706300657664">articulated by David Heinemeier Hansson</a> of Rails fame recently, where he notes the acceptance (or even desirability) of contextual ads and wonders how things went so horribly wrong with behavioral profiling (for individual privacy and the web ecosystem overall).<br />
<br />
I've long been a user of various ad- and content-blocking extensions, or sometimes putting in place network level blockers, primarily because ads destroy my ability to actually read or focus. ("<a href="http://osxdaily.com/2019/03/28/enable-safari-reader-view-automatically-ios/">Reader View</a>" is another great help in this respect.) Preventing data about my browsing activity from going to unknown places is a very close second though. Would you walk into a physical store or news stand knowing dozens of barely disclosed parties were tracking your every move, classifying your emotional state, bidding on you in real time? So there's one thing for me, the web as it currently sits on a bed of undisclosed advertising and nonconsensual data aggregation obviously isn't private enough.<br />
<br />
While quality content is hard to come by and should be supported whether via subscriptions, donations, or even a little bit of advertising, the current user data tradeoff for browsing the web isn't nearly private enough. Despite past attempts like DNT that never materialized, there's great progress being made in browsers to move us toward an opt-in world too. With <a href="https://blog.mozilla.org/blog/2019/10/22/latest-firefox-brings-privacy-protections-front-and-center-letting-you-track-the-trackers/">Firefox's built in protections</a>, WebKit's <a href="https://webkit.org/blog/category/privacy/">Intelligent Tracking Prevention</a> and even <a href="https://www.blog.google/products/chrome/building-a-more-private-web/">moves by Google</a> (discussed in detail <a href="https://www.eff.org/deeplinks/2019/08/dont-play-googles-privacy-sandbox-1">here</a>), plus an ad blocker here and there, you can do a decent job of cutting down the number of stray requests to sites you've never heard of while cleaning up your viewing experience. <a href="http://brave.com/">Brave</a> is also pioneering a new model that aims to be private enough by default and the results are promising, a good non-technical write up is <a href="https://download.cnet.com/news/brave-browser-review-the-chrome-alternative-that-supports-extensions-and-takes-an-unusual-path-to/">here</a>. Whether Brave's model of revenue sharing or WebKit's <a href="https://webkit.org/blog/8943/privacy-preserving-ad-click-attribution-for-the-web/">Ad Click Attribution</a> take off is yet to be seen.</div>
<div class="p2">
<br /></div>
<div class="p1">
As the browsers change, many sites are also doing a better job of disclosing which third parties data is shared with, either as part of cookie preference management options or in privacy policies. In some ways this might be driven by vendors moving aggressively into the space, which isn't necessarily a bad thing. For example, if you visit <a href="http://nature.com/">http://nature.com/</a> and click on Manage Cookies, you'll see a <a href="https://www.onetrust.com/">OneTrust</a> modal to manage cookies which lays things out much more clearly than the banner notices we commonly see. (Don't forget to scroll through the list of Targeting Cookies. Gross.) At the same time, there are still far too many vague, "we may share your data with our third party partners for marketing and/or other purposes" clauses out there. These privacy policies definitely aren't private enough. Fortunately it seems like we're on the edge of laws like GDPR and CCPA forming enough common ground that it's becoming easier to <a href="https://github.blog/2019-11-11-our-tldr-for-californias-new-privacy-law-do-not-sell/">broadly comply</a> than try and parse out regulations per consumer, per transaction, per jurisdiction, etc. That's a good step.</div>
<div class="p1">
<br /></div>
<div class="p1">
Beyond browsewrap disclosures with some preference toggles, some sites that require registration still don’t feel private enough. For example, I subscribe to some news sites' "ad-free" options. While this prevents ads from <i>displaying</i> and cuts down on the number of scripts loaded onto the page, there are still a bunch of trackers and constant pings monitoring my behavior on the site. In the examples I've looked at, there's no separate privacy policy prohibiting that info from being used for advertising purposes, i.e. a third party company compiling it into a behavioral profile that follows me around elsewhere. (Looking at you, Wired.) Sure, I might be one of the few people who <a href="https://www.pewresearch.org/fact-tank/2019/11/15/key-takeaways-on-americans-views-about-privacy-surveillance-and-data-sharing/">actually read the policies</a>, but the connection between the way services are marketed and run still isn't private enough. It'll be interesting to see how companies present the CCPA's <a href="https://www.cyberadviserblog.com/2019/10/analysis-notice-provisions-of-ccpa-proposed-regulations/">notice requirements</a> will be presented to explain things like "ad-free but we still track you a little bit." </div>
<div class="p2">
<br />
Zooming back out to the network, ISP data collection definitely isn't private enough. While Maine has a law prohibiting ISPs from selling consumer data without consent, and California's law will apply to ISPs, other states should step up here. Then again, would this be something tucked into a national privacy law? There have been plenty of those proposed recently. I found <a href="https://www.eff.org/deeplinks/2019/11/equifax-data-breach-update-backsliding">this list</a> of topics from the EFF to be a reasonable wish list, though I'm not sure a lack of federal preemption would bring the certainty businesses are looking for. Then again, considering the difficulty in futureproofing any federal language, it might be necessary to let states keep experimenting on top of a federal baseline. A couple of the recently proposed federal bills are discussed <a href="https://arstechnica.com/tech-policy/2019/11/new-bill-would-create-digital-privacy-agency-to-enforce-privacy-rights/">here</a> and <a href="https://www.theverge.com/2019/10/22/20926742/mark-warner-josh-hawley-facebook-access-zuckerberg-data-portability-open-api-interoperability">here</a>, it seems a new one pops up every few days!</div>
<div class="p2">
<br /></div>
<div class="p1">
So there's some holiday food for thought on privacy. This year, try asking some family members about the privacy tradeoffs made while browsing or conducting other online activities. Or maybe ask if they've noticed anything changing about disclosures. If you’re of my distinguished generation and have some Boomers or even Greatest Generation members at your meal, ask them about how marketing has changed over the years, you might be surprised by what you hear. Some eyes will roll or be too focused on the stuffing to care, others might be shocked to know how much of their info is given up. Many might not have even realized there are other options or choices. Most will have too little time or too much FOMO to opt out across the board. All should agree things still aren’t private enough.</div>
<div class="p1">
<br /></div>
<div class="p1">
<i><span style="font-size: xx-small;">As usual, the views expressed here are strictly personal and don't reflect the views of any past or present employers or clients.</span></i></div>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-87795113596500371282019-11-03T18:16:00.002-08:002020-01-20T19:35:04.970-08:00Thinking About BIPA and Machine Learning<a href="https://www.nytimes.com/interactive/2019/10/11/technology/flickr-facial-recognition.html">One article</a> that really caught my attention recently discussed the use of Creative Commons-licensed images from Flickr as part of the <a href="http://megaface.cs.washington.edu/">MegaFace</a> dataset for training facial recognition algorithms. Despite its aggressive (but not untrue) title, it highlights the many sides of the questions we the people and we the companies building products with these technologies <strike>face</strike> confront.<br />
<br />
Focusing on the licensing, Flickr truly expanded the available commons of openly-licensed images by allowing its community to choose Creative Commons (CC) licenses. Interestingly, the <a href="https://creativecommons.org/licenses/by/4.0/legalcode">latest version of the most permissive CC license</a> expressly does not license "publicity, privacy, and/or other similar personality rights", yet the licensor agrees not to assert such rights to the extent necessary to support the rest of the license. However, previous versions of this or other CC licenses probably apply to many photos in the data set, and not all of the other licenses contain this language. For the Creative Commons licenses specifically, unlocking default copyright restrictions so users can create derivative works is the whole point; yet <a href="https://www.ft.com/content/cf19b956-60a2-11e9-b285-3acd5d43599e">the organization acknowledges the difficulties</a> when other rights overlap. A <a href="https://creativecommons.org/share-your-work/licensing-types-examples/licensing-examples/">CC-BY-ML license</a>, where the "ML" means you have permission to reuse the media for machine learning, would probably be a stretch given the requirements of the <a href="http://www.ilga.gov/legislation/ilcs/ilcs3.asp?ActID=3004">Illinois Biometric Information Privacy Act (BIPA)</a>. It has a nice ring to it. The biometric issues aside, any other ML uses I can imagine seem covered by the license already. But who knows what the future might bring.<br />
<br />
Since the statute clearly contemplates business and corporate transactions in its legislative findings, and it goes on to cover "A private entity in possession of biometric identifiers or biometric information...," I agree with the university official quoted in the article that they'd escape a BIPA claim for including the photos in the data set. Though I'm sure there's been internal discussion about whether this qualifies as <a href="https://www.washington.edu/research/hsd/">research on human subjects</a>. Should the companies or other users of the data set be responsible to the subjects of the photos then? The article mentions but doesn't dig into one definitional problem I find with the statute. BIPA places restrictions on various things a private entity can do with "biometric identifiers" or "biometric information." From the statute,<br />
<blockquote class="tr_bq">
"Biometric identifier" means a retina or iris scan, fingerprint, voiceprint, or scan of hand or face geometry. Biometric identifiers do not include ... photographs ...</blockquote>
There are more examples listed in the statute, and some other exclusions. Notably, MRI's are excluded, although they've <a href="https://www.wsj.com/articles/facial-recognition-software-was-able-to-identify-patients-from-mri-scans-11571864543">recently been used to identify people</a>. The definitions continue,<br />
<blockquote class="tr_bq">
"Biometric information" means any information, regardless of how it is captured, converted, stored, or shared, based on an individual's biometric identifier used to identify an individual. Biometric information does not include information derived from items or procedures excluded under the definition of biometric identifiers.</blockquote>
When I first read these, I immediately wondered why the definitions didn't come up recently in <i>Patel v. Facebook</i> (<a href="http://www.smsm.com/blogs-litigationblog,patel-vs-facebook-illinois-bipa">summary</a>, <a href="http://cdn.ca9.uscourts.gov/datastore/opinions/2019/08/08/18-15982.pdf">case</a>), which introduced a circuit split over whether BIPA confers Article III standing to sue, a separate issue from the definitional one discussed here. In <i>Patel</i>, it's assumed that the face signatures created from analyzing uploaded images are a "scan" of "face geometry." Facebook uses these to match uploaded photos for its image tagging features. Note where the scan is included in the statute's definition. It's a list of methods that take info directly from a live person. Photos are then excluded, and the definition of "biometric information" excludes information "derived" from the excluded items (the photos). Since Facebook's face signatures are derived from photos, rather than something like a one-time scan from a live camera that directly produces the signature, they should be excluded by definition.<br />
<br />
Wrong, according to various courts that have interpreted the same argument in the past. <a href="https://blog.ericgoldman.org/archives/2016/01/shutterfly-cant-shake-face-scanning-privacy-lawsuit.htm">Here's one</a> involving Shutterfly from a few years ago. <a href="https://www.mediapost.com/publications/article/260440/facebook-seeks-dismissal-of-lawsuit-over-faceprin.html">Here lies another</a>. It doesn't seem like the courts are going back to split hairs at this point. As a very privacy-conscious consumer of online services, it's good to see these laws given some teeth given how silently intrusive and ethically questionable many of these technologies' uses have become. At the same time, under the expansive readings of "face geometry" I don't want to see the courts go too far and start putting a chill on all intermediate or stored ML outputs for fear of litigation. Maybe it's ok for now, as we've seen many abuses of biometrics and one can't just reset them.<br />
<br />
Compliance with the law's Section 15 doesn't seem so onerous but is it just one more area where only the larger players will remain aware and have the resources to comply? The NYT article notes some features in image-related apps that were disabled for Illinois users, presumably because of BIPA. MegaFace highlighted images but it's easy to imagine the same happening with voice. Under the interpretations above, a data set of <a href="https://medium.com/@ageitgey/machine-learning-is-fun-part-6-how-to-do-speech-recognition-with-deep-learning-28293c162f7a">spectrograms</a> produced from audio clips would likely be considered "voiceprints." The same logic could extend to any data set, and I wonder about secondary liability for using a pretrained model that you knew was trained on such a data set.<br />
<br />
We still haven't figured out what to do about the Flickr images in the MegaFace data set. Are the privacy <a href="https://www.youtube.com/watch?v=7AEMiz6rcxc">nihilists</a> mentioned in the article right? Maybe one of the entities involved can set up a site where you upload an image of yourself, and if a model trained on the MegaFace data set recognizes you to a very high degree of accuracy, you can request removal (of both images). Is the Web ready for a DMCA regime for privacy? A similar idea was actually tried with the ImageNet Roulette project. Now offline, it allowed you to upload your picture and see how it was categorized according to labels generated from the ImageNet data set's "person" categories. Read all about it <a href="https://www.excavating.ai/">here</a>. MegaFace is still available for download, and I think that's the right outcome. The despicable uses of AI for surveillance or oppression seem an unfortunate consequence of the potential for evil use of any technology. One can imagine plenty of other uses falling squarely within the terms of the CC licenses that a licensor might not like but would be powerless to stop. I'd like to see clearer legislation and communication with users, at least BIPA gets part of the way there.<br />
<br />
<i>Update: Looks like The Glass Room San Francisco recently featured "a custom built facial recognition system to search for your face amongst the millions of images used for training facial recognition algorithms." Check it out <a href="https://theglassroom.org/object/adam_harvey-mega_pixels">here</a>.</i>joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-58868709904789868902019-01-31T20:58:00.000-08:002019-01-31T21:14:51.198-08:00Looking AheadJanuary almost passed without an inaugural post for the year, and clearly the months quickly turn into years! Here's to sticking with New Year's resolutions and polishing up many drafts waiting to see the light of screens 🥂 To start gaining momentum again, below are a few topics and technologies I'd like to spend more time with in the coming year.<br />
<br />
<a name='more'></a><br />
<h3>
JavaScript: Back to Basics</h3>
Last year, I slowed up on the relentless pursuit of All Thing JS. After years of doing something full time at work, it's often hard to maintain momentum and curiosity beyond work hours or work-related studying. Most important, there are other fun things to do besides coding! While my knowledge of React, build tools, Vue and (Chrome's) Developer Tools increased, I didn't spend nearly as much time with the language and browser internals as <a href="http://joemerante.blogspot.com/2016/05/recent-presentation-understanding.html">past years</a>. This year I'd like to shift some attention back to the fundamentals of the language and <a href="https://github.com/tc39/proposals">its future direction</a>. Also, one of my favorite topics, looking at trends in design/abstraction patterns in a given language and the problems they solve. Some of my favorites in this category include <i><a href="http://shop.oreilly.com/product/9780596806767.do">JavaScript Patterns</a> </i>(2010, still lots of good stuff) and <a href="https://addyosmani.com/resources/essentialjsdesignpatterns/book/"><i>Learning JavaScript Design Patterns</i></a> (2017).<br />
<h3>
IoT</h3>
Time to dust off the Raspberry Pi and build some cool stuff this year. Nothing more to say on that. In terms of the FAANGs in the room, there are a few, though I've gravitated toward HomeKit ecosystem. Perhaps because I've been in the Apple ecosystem forever. (Wassup, <a href="https://en.wikipedia.org/wiki/Macintosh_LC_500_series#LC_575">Performa 575</a>?) Some accessories haven't been as seamless to set up like this <a href="https://www.belkin.com/us/p/P-F7C063/">Wemo Mini Smart Plug</a>. While this had to do with my home Internet router, for a company like that builds various wifi and router devices, I expected setup would be much more seamless. Others devices like the <a href="https://www2.meethue.com/en-us">Phillips Hue bridge and lights</a> have been a breeze and tons of fun.<br />
<br />
The addition of Apple's Home app to OS X Mojave was a very welcome addition, especially since it brought OS X Siri HomeKit integration. Somewhere on my infinite side project list is doing something with the <a href="https://developer.apple.com/videos/play/wwdc2018/231/">HomeKit APIs</a>, we'll see what happens. Here's a free product idea for non-morning persons everywhere: <a href="https://www.apple.com/ios/home/accessories/">HomeKit-compatible</a> Smart Shades that open when your alarm goes off. Please let me know if you've seen them!
<br />
<h3>
Python and Friends</h3>
Last year I decided to begin getting up to speed on all the amazing things going on in the Python community, particularly in machine learning and scientific computing. I'd written some Python over the years, used it on a hackathon here, a toy project there, reading others' code everywhere, but never really took the time to learn the language. As has been covered at length in many places coming from a long time in Ruby wasn't much of a jump. (Don't worry Ruby, you're still my go-to!) Last year, I made it through a great 3-part course, <a href="https://www.edx.org/professional-certificate/berkeleyx-foundations-of-data-science">Foundations of Data Science</a>, that provided a great foundation. <br />
It's fun to feel like a n00b again. <br />
<br />
AI policy and governance is something that also really burst through into popular discussion last year, and is something I've long enjoyed thinking about. From the common themes and lessons learned in (the attempted) regulation of any technology, to more recent literature like <a href="https://en.wikipedia.org/wiki/Weapons_of_Math_Destruction">this</a> and <a href="http://www.hup.harvard.edu/catalog.php?isbn=9780674368279&content=reviews">this</a> on (the potential for) algorithmic inequality and abuse, to the discussions finally starting to percolate amongst legislatures everywhere, get your tech policy geek popcorn ready! There's a wealth of materials and food for thought in <a href="https://www.media.mit.edu/courses/the-ethics-and-governance-of-artificial-intelligence">this free MIT course</a> too. It's been on my "started strong then got distracted with another topic, and would love to get back to it" MOOC list.<br />
<br />
And with that, see ya later 2018 In Tech, looking forward to what's next.
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-18270772731072563622017-04-05T20:31:00.001-07:002017-04-05T20:38:55.018-07:00Verifying Doubles with ActionMailer in Rails 4<div class="p1">
Verifying doubles came out in RSpec 3, and it’s something that always sounded like such a brilliant yet obvious idea - make sure methods you stub actually exist. One friend in particular really stressed how many times this could have saved his tests from false positives. Sure, I thought, but usually I’d just modify something in the real code or modify the expectation to make sure it broke/worked in predictable ways, and move on. Then along came the perfect scenario while working on a little side project.<br />
<br /></div>
<div class="p1">
Below is the original spec and the corresponding controller method it's testing. (Things aren't especially DRY and a bunch of expectations are crammed into a single test to make things more explicit for this post.)</div>
<code>
</code><br />
<div class="p1">
<code># spec</code></div>
<div class="p1">
<code>it 'sends the admin a message about a new order' do</code></div>
<div class="p1">
<code> controller.stub(:params).and_return(fake_full_params)</code></div>
<code>
</code>
<div class="p1">
<code> expect(AdminMailer).to receive(:order_confirmation).with(fake_email_params).and_return(Mail::Message.new)</code><br />
<code> allow_any_instance_of(Mail::Message).to receive(:deliver)</code></div>
<code>
<div class="p1">
fake_stripe_success_path</div>
<div class="p1">
expect(subject.status).to eq(200)</div>
<div class="p1">
end</div>
<div class="p2">
<br /></div>
<div class="p1">
# controller</div>
<div class="p1">
def create<br />
# create a customer, charge, send confirmation, check for errors, etc</div>
<div class="p1">
AdminMailer.order_confirmation(email_params).deliver<br />
end</div>
</code>
<div class="p1">
Since the AdminMailer already existed with other methods in it, I ran this spec and it passed - after all, AdminMailer received #order_confirmation with my fake params, and a mock stubs the method too, so nothing happened when it got called (and there's no #and_return chained to the expectation).</div>
<div class="p2">
<br /></div>
<div class="p1">
Now, I <i>knew</i> that #order_confirmation wasn’t implemented on AdminMailer yet - that was what I set out to do. Fast forward to getting smacked in the face with why I should be using verifying doubles and stop trying to keep even more state in my mind - it’s bad to cram too much state into objects and code, why force it on your poor brain?<br />
<br /></div>
<h3>
Verifying Doubles to the Rescue.. Almost</h3>
<div class="p1">
<br />
So that I could use a double in my expectation, I changed things to the following:</div>
<code>
</code><br />
<div class="p1">
<code># spec </code></div>
<div class="p1">
<code>it 'fires two mailers' do</code></div>
<div class="p1">
<code> admin_mailer_double = class_double(AdminMailer)</code></div>
<code>
</code>
<div class="p1">
<code> controller.stub(:admin_mailer).and_return(admin_mailer_double)</code></div>
<code>
<div class="p1">
controller.stub(:params).and_return(fake_full_params)</div>
<div class="p1">
allow_any_instance_of(Mail::Message).to receive(:deliver) </div>
<div class="p1">
expect(AdminMailer).to receive(:order_confirmation).with(fake_email_params).and_return(Mail::Message.new)<br />
# stubbed success from Stripe API and other checks</div>
<div class="p1">
fake_success</div>
<div class="p1">
expect(subject.status).to eq(200)</div>
<div class="p1">
end</div>
<div class="p2">
<br /></div>
<div class="p1">
# controller</div>
<div class="p1">
def create
# do other stuff</div>
<div class="p1">
admin_mailer.order_confirmation(email_params).deliver
end</div>
<div class="p2">
<br /></div>
<div class="p1">
private</div>
<div class="p1">
def admin_mailer</div>
<div class="p1">
AdminMailer</div>
<div class="p1">
end<br />
<br /></div>
</code>
<div class="p1">
But wait! I looked back at my ActionMailer class and realized that methods added to ActionMailer classes aren’t actually class methods, despite being called that way! Some more background in <a href="http://smsohan.com/blog/2011/01/28/actionmailer-3-why-do-you-call-instance/">this post</a>, and how ActionMailer in Rails 4 handles it <a href="https://github.com/rails/rails/blob/v4.2.2/actionmailer/lib/action_mailer/base.rb#L565-L571">here</a>.<br />
<br /></div>
<div class="p2">
<h3>
The Fix</h3>
<div>
<br /></div>
</div>
<div class="p1">
Fortunately, the brilliant folks behind RSpec have considered this, and there are instance, class, and object doubles. There’s more too, see <span class="s1"><a href="https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/verifying-doubles">https://relishapp.com/rspec/rspec-mocks/v/3-5/docs/verifying-doubles</a></span>. The final, passing spec that actually verifies the double is below. The key change to use an object_double is bolded -</div>
<code>
</code><br />
<div class="p1">
<code>context 'success path' do</code></div>
<div class="p1">
<code> let(:admin_mailer_double) { <b>object_double</b>(AdminMailer, :order_confirmation => Mail::Message.new) }</code></div>
<div class="p1">
<code> before(:each) do</code></div>
<code>
<div class="p1">
controller.stub(:params).and_return(fake_full_params)<br />
controller.stub(:admin_mailer).and_return(admin_mailer_double)<br />
allow_any_instance_of(Mail::Message).to receive(:deliver)</div>
<div class="p1">
end</div>
<div class="p2">
<br /></div>
<div class="p1">
it ‘sends an admin confirmation email’ do</div>
<div class="p1">
expect(admin_mailer_double).to receive(:order_confirmation).with(fake_email_params).and_return(Mail::Message.new)</div>
<div class="p1">
fake_stripe_success</div>
<div class="p1">
expect(subject.status).to eq(200)</div>
<div class="p1">
end</div>
<div class="p1">
end</div>
</code>
<br />
<div class="p1">
After making that change, I got the failure I expected and was back in the merry TDD path I enjoy so much. I implemented the method and was all set.</div>
<br />
<div class="p1">
<code>Failures:</code></div>
<div class="p1">
<code> 1) CartsController#create success path sends a an admin confirmation email</code></div>
<code>
<div class="p1">
Failure/Error: expect(admin_mailer_double).to receive(:order_confirmation) #.with(fake_email_params).and_return(Mail::Message.new)</div>
<div class="p1">
<b> AdminMailer does not implement: order_confirmation</b></div>
<div class="p1">
# ./spec/controllers/carts_controller_spec.rb:44:in `block (4 levels) in <top required="">'</top></div>
</code>
<br />
<div class="p1">
Kudos to the RSpec team for making life so much easier, and code so much stronger, for Rails developers everywhere. Hope this helps someone out there!</div>
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #454545}
p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px 'Helvetica Neue'; color: #454545; min-height: 14.0px}
span.s1 {color: #e4af0a}
</style>joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-70771071538465380372016-05-22T14:45:00.001-07:002016-05-22T17:05:43.671-07:00Recent Presentation: Understanding JavaScript in the BrowserI recently gave a short presentation to mostly non-Web developers, on what goes into the soup of the browser platform. I began by covering the many parties involved in standards - the W3C, WhatWG, TC39 committee, and others. Then, I introduced some of the other characters, like each browser vendor's rendering engine and JavaScript engine. It's a lot to keep track of, even just to keep a loose eye on things, not to mention the day-to-day effort (and fun discovery) of the many libraries and frameworks available to solve a problem. tldr - there are a lot of cooks in the Web kitchen.<br />
<br />
I tried to highlight the great community tools like caniuse.com or csstriggers.com, and later demonstrated a few features of the developer tools, and the idea of the browser as the app platform and the IDE. (the JavaScript profiler, logging and analyzing xhr requests, the timeline, ol' trusty console) I also showed the final example from a great talk, <a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ">What the heck is the event loop anyway?</a><br />
<br />
Finally, we looked at some of the idiosyncrasies of the language. After talking about some of the basic structures in the language (objects, functions, less-than-intuitive rules around 'this'), I posted <a href="https://twitter.com/weitzelb/status/718623065480019968">this example</a>, to a room of very experienced (but mostly non-JavaScript) developers, and there were some pretty confused looks on people's faces. However, when we went through line by line, everyone immediately understood. And shook their heads in disbelief.<br />
<br />
The slides can be viewed <a href="https://drive.google.com/file/d/0B3Qfr2MYxP52dEYzSmRVX0lpVGM/view">here</a>.<br />
<br />
<br />joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-39439836957630439482015-09-25T08:36:00.000-07:002015-09-30T08:28:35.225-07:00September in (and out of) iOS<div class="p1">
<span class="s1">This month my intention was to continue the Udacity Nanodegree program. I think the learn-by-doing approach it takes is great, and usually works well for me. However, in a fit of frustration -- wrestling with NSRegularExpression for something that would be trivial in many other languages and forgetting about the .rangeOfString method -- I began to reflect on the overall experience and my approach to learning at this point. What were really my goals in becoming a better iOS programmer? I'm familiar with enough of the APIs and resources for learning, can wade through the docs if necessary, have built some toy apps, etc. So why the urgency to add more to the never-ending list of things to keep up with (in code, business and law)? What other stuff would get put on the backburner for a while? </span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">Well, part of the problem is that the other stuff is never really on the backburner. I spend my days in Ruby and the browser, and absolutely love it, and my subway rides reading articles and keeping up with developments in the Rails world. People have their reasons for not using or liking Ruby or Rails, but the joy of coding in Ruby coupled with the productivity boost of Rails still makes it my go-to. If/when I need to deal with millions of concurrent connections and the other performance problems at crazy scale that many see as a drawback to using Rails, or the speed of the language is more important than my productivity with it, I'll reconsider. I've checked out many of those other tools - just enough to know where to start looking when a problem presents itself, and to improve my existing approaches (hopefully).</span></div>
<div class="p2">
<span class="s1"></span><br /></div>
<div class="p1">
<span class="s1">And so back to iOS, and really any new language, framework or library. Yesterday I quit the Nanodegree program since I didn't feel like it was materially pushing me forward as a programmer generally, or an iOS programmer specifically. Sure, the exercises are good and you need a guide beyond just the docs, Stack Overflow and Google. At the same time, I'd already done a lot of that stuff and know the areas I need to learn or improve. Plus, at $200/month for something they give away for free, it just didn't feel worthwhile to me. (N</span>o offense to those doing it and making the most of it, or the company for expecting to get paid for their work.) For those shifting careers or just starting out, go for it. But having an iOS Nanodegree isn't going to make or break my resume at this point.</div>
<div class="p1">
<br /></div>
<div class="p1">
While much folk wisdom (or PR spin) about online learning suggests that an extra credential and paying a bit gives you some skin in the game, and maybe it works for some people, it really just made it feel like work to me. I've got enough of that (thankfully). This was supposed to be a fun hobby. With Ruby and Javascript, even outside of work, it's still *fun*. Maybe I'm just too hooked on the instant feedback cycle (and not waiting for the iOS Simulator or XCode to catch up) or productive enough to look past the frustrations that occur, or just impatient in getting a fast enough workflow going with iOS. That's not to say I'm completely giving it up. Just not spreading my free time too thin for no good reason or clear personal or professional benefit. I could spend that time on core CS concepts, reading (gasp!) non-tech books, or writing more to avoid awkward sentence constructions like these last two. </div>
<br />
<div class="p1">
<span class="s1">So I'll end with this. Before diving into something new - clearly state your goals, and resist any OCD/stereotypical coder urges to overwhelm yourself and burn out on it. The mental energy of frustration just isn't worth your time. Is it for a job? Is it for fun? Is it for a new perspective? Just to stretch your brain? Learn enough to be dangerous, if the opportunity ever presents itself. For now, I need to refocus on relaxing, reviewing years of Ruby and Rails code and notes, and working with a few new Javascript libraries I'll need at my new job. After a great 2 years of growth at Simon & Schuster, I'll be starting at Bloomberg next month. Onward... </span></div>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-42740793098837962462015-08-31T20:26:00.003-07:002015-09-05T11:27:25.948-07:00August in iOSLast year I began working with iOS right around the time Swift came out. While I'm glad to have some familiarity with Objective C, Swift is much more comfortable (coming from working mostly in Ruby and Javascript). I soon dove in! While this blog's been silent in 2015 so far, I've got tons of draft posts sitting around that need to see the light of day.<br />
<br />
One new commitment I've made is to start posting a monthly rundown of tips, tricks and adventures as I get back into iOS development, similar to the <a href="http://joemerante.blogspot.com/search/label/tmil">TMIL (This Month I Learned)</a> series I posted in 2014. Zooming <a href="http://joemerante.blogspot.com/2014/12/august-tmil-getting-started-with-ios.html">back to last year</a> when I wanted to start learning another programming ecosystem beyond the web world I know and love, here are a few of the resources that helped me get started (and very much continue to help) with the world of iOS:<br />
<br />
<ul>
<li><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/">The Swift Programming Language</a> (<a href="https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11">iBooks</a>) </li>
<li><a href="http://www.raywenderlich.com/">http://www.raywenderlich.com/</a></li>
<li><a href="https://www.objc.io/">https://www.objc.io/</a> </li>
<li>WWDC videos (<a href="https://developer.apple.com/videos/wwdc/2015/">2015</a>, <a href="https://developer.apple.com/videos/wwdc/2014/">2014</a>)</li>
<li><a href="http://shop.oreilly.com/product/0636920034247.do">Introducing iOS 8</a></li>
<li><a href="http://www.stanford.edu/class/cs193p/cgi-bin/drupal/">CS 193P</a></li>
</ul>
<br />
Zooming forward to a few months ago, I decided that this summer I'd get back into it. The <a href="http://www.meetup.com/Brooklyn-Swift-Developers/#past">Brooklyn Swift meetups</a> have been an awesome source of keeping up, and it's exciting to follow the language as it develops. Among other technical tidbits, one exciting note from these meetups is the emerging open source ecosystem around iOS development. As was widely reported after the announcement at WWDC 2015, Swift itself will <a href="https://developer.apple.com/swift/blog/?id=29">soon be open-sourced</a>. While the language hasn't moved into the <a href="https://github.com/blog/2047-language-trends-on-github">top GitHub languages</a> just yet, there are <a href="https://github.com/trending?l=swift">plenty of repo's</a> gaining popularity and it's on its way up in the <a href="http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html">TIOBE index</a> (as of August 2015). Notably, more companies are following the lead of groups like Artsy and open sourcing their <a href="https://github.com/artsy/eigen">entire production apps</a>. (e.g. <a href="https://github.com/gilt/Cleanroom">Gilt</a>) As noted at an earlier meetup, hopefully the emerging Swift community <a href="https://twitter.com/joemerante/status/613856270232223744">can build on the successful lessons of others</a>.<br />
<br />
<h3>
</h3>
<h3>
Udacity Nanodegree</h3>
With the above in mind, and a million ideas, books and tutorials but never enough time, I thought I should add more to my learning load! So, I've enrolled in Udacity's <a href="https://www.udacity.com/course/ios-developer-nanodegree--nd003">iOS Developer Nanodegree program</a> to help solidify my understanding and work on more significant apps to showcase. (My first project in the program, a work in progress called Pitch Perfect, can be found on my GitHub <a href="https://github.com/joemerante/pitch_perfect">here</a>.)<br />
<br />
While there's the essential work of design, writing good algorithms and such, in web and backend development I've found that a prerequisite to getting anything done (and often most of the day-to-day work of being efficient) is knowing your environment and tools well. For me, that means keyboard shortcuts. Fortunately, they're laid out in a pretty consistent way in XCode.<br />
<ul>
<li>Navigator - use cmd-1, cmd-2, etc to jump between different the File, Debug, Test and other panels </li>
<li>Utilities - use cmd-option-1, cmd-option-2, etc to jump between the Attributes, Size and other utility inspectors </li>
<li>Jumping to a file - cmd-shift-O (the letter O, i.e. open) to open a file or jump to a symbol </li>
<li>Getting around the menus - this is a must-use item found in most (all?) OS X apps, cmd-shift-? to open an application's Help search index, and using the arrow keys to select the item. In XCode, I've found this particularly helpful when adding constraints, such as 'pin' (rather than the tedium of using the mouse to select items from the Editor->Pin menu)</li>
</ul>
You can find a much more detailed guide at <a href="http://www.raywenderlich.com/72021/supercharging-xcode-efficiency">http://www.raywenderlich.com/72021/supercharging-xcode-efficiency</a>.<br />
<br />
So far, the Pitch Perfect app has been a good reminder of the value of adding constraints to UI elements as you go (at least, that works better for me). Also, it's been fun playing around with the audio APIs. The first project fetches a local file and <a href="https://github.com/joemerante/pitch_perfect/blob/master/Pitch%20Perfect/PlaySoundsViewController.swift">plays it back slow or fast</a>. Though not part of the course, I'd like to next fetch a file from the web and play it back... But that's enough for tonight! See you next month!joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-23109554092903594522014-12-31T17:07:00.002-08:002014-12-31T17:07:35.300-08:00December #TMILThis month I learned that this year I learned to keep <a href="http://joemerante.blogspot.com/2014/02/january-tmil.html">a New Year's resolution</a> alive, that documenting everything is usually a good idea, and most important, to relax and have fun during the holidays.<br />
<br />
Happy 2015!joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-49837118852967781712014-12-31T17:01:00.004-08:002014-12-31T17:15:42.148-08:00NovEmber #TMILOver the summer, I decided it was time to learn a client-side Javascript framework. I chose to start with <a href="http://emberjs.com/">Ember</a>, mostly because it felt more like Rails and I kept hearing about the combination of an Ember app with a Rails backend. While I usually jump right in and try to build something, I had enough <a href="http://joemerante.blogspot.com/2014/10/june-tmil-ruby-under-microscope.html">other</a> <a href="http://joemerante.blogspot.com/2014/12/august-tmil-getting-started-with-ios.html">coding</a> <a href="http://buildyourbestbump.com/">side projects</a> going on at the time (and it was summer!), so I joined the <a href="http://emberweekly.com/">Ember Weekly</a> mailing list, attended a few of the <a href="http://www.meetup.com/EmberJS-NYC/">NYC meetups</a> and kept a loose eye on things... which have <a href="https://github.com/emberjs/rfcs/pull/15">been</a> <span id="goog_1961860818"></span><a href="http://www.ember-cli.com/#overview">moving</a><span id="goog_1961860819"></span> <a href="http://emberjs.com/guides/deprecations/">along</a> <a href="http://emberjs.com/blog/2014/12/22/inside-fastboot-the-road-to-server-side-rendering.html">quickly</a>! By November, it was time for another look.<br />
<br />
<a name='more'></a><br />
I started <a href="https://www.codeschool.com/courses/warming-up-with-ember-js">here</a> then began going through the official <a href="http://emberjs.com/guides/">Ember Guides</a> and browsing some <a href="http://www.manning.com/skeie/">other</a> <a href="https://www.youtube.com/playlist?list=PLE7tQUdRKcyaOyfBnAndJxQ9PNVmKva0d">sources</a>. Up next? I'd like to use Ember for <a href="https://github.com/joemerante/crmed">this Rails project's</a> admin page to have a single view for all <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> actions. Also, it would be fun to play with the <a href="http://www.rdio.com/developers/">Rdio API</a> as demonstrated by core team member Erik Bryn in <a href="https://www.youtube.com/watch?v=Hm8XsgKT0Qw">this talk</a>, and add some other features (like streaming via <a href="https://github.com/benvanik/node-airplay">AirPlay</a>, searching and displaying <a href="http://developer.echonest.com/docs/v4/artist.html#biographies">artist bios</a> or Wikipedia entries, etc.). We'll see what 2015 brings...joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-61025751019833939782014-12-31T15:38:00.003-08:002014-12-31T17:15:57.816-08:00October #TMIL - Browser Extensions<i>From the Unfinished Business department:</i><br />
<br />
At the <a href="http://booksinbrowsers.org/">Books in Browsers Conference</a> hackathon in October, I worked with a few others to port the <a href="https://chrome.google.com/webstore/detail/readium/fepbnnnkkadjhjahcafoaglimekefifl?hl=en-US">Readium Chrome extension</a>, an in-browser epub reader, to Firefox. The core team had already been working on <a href="https://github.com/readium/readium-js-viewer/tree/feature/firefox-addon/firefox-addon">a branch</a> which we didn't notice before diving in. Oops. However, it seems like they <a href="https://github.com/readium/readium-js-viewer/commit/5c88997a582332723e91abd448fec11bf0c3202c">ran into the same issue</a> that prevented us from going all the way: working with the user's filesystem from the extension.<br />
<br />
<a name='more'></a>While our Firefox port works, the only way to add a new epub is by moving the file into the project, adding an entry in a json file, then recompiling and re-adding the extension to Firefox. Rather than using local storage, our goal was to store the json file in the user's Firefox profile folder, and copy (or add a symlink to) epubs there as well. This way, if the user had to reinstall Firefox or cleared local storage, their settings and library would be preserved.<br />
<br />
In the latest attempt, gaining 'chrome' privileges has been the blocker. We couldn't figure out how to successfully <code>require('chrome')</code> as the documentation shows, since the Firefox API's 'require' is a namespace collision with <a href="http://requirejs.org/">RequireJS</a>, used by the core Readium Javascript! <a href="http://requirejs.org/docs/faq-advanced.html#rename">Later attempts</a> with a colleague also left us scratching our heads. More details can be found in the project's <a href="https://github.com/joemerante/readium-js-viewer-firefox">Read Me</a>.joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-15702024663649319622014-12-30T15:54:00.005-08:002014-12-31T17:16:19.793-08:00September #TMIL - Back to DrupalLast fall, I finally got around to moving joemerante.com, a Drupal site, to a new host. It was getting a little ridiculous that I had no way to ssh into the hosted space and couldn't simply <code>git push origin master</code> or similar to deploy (no need for anything fancy given the traffic and simplicity of the site - see my previous workaround <a href="http://joemerante.blogspot.com/2013/02/drupal-workflow-with-drush-git-and-git.html">here</a>). Why didn't I just quickly redo the site in Sinatra or Rails? Mostly because it works fine as-is, I like Drupal (the nostalgia factor of being the first web framework I learned well, also it's a great CMS and community) and jumping around between different environments keeps you sharp. This post rolls through a few things I came across while moving the site, maybe it'll help someone out there.<br />
<br />
<a name='more'></a>First, a quick to do list:<br />
<ul>
<li>get latest database and code</li>
<li>import database to new host, add new database user and privileges to the database, change Drupal admin user password</li>
<li>update production database connection info in settings.php</li>
<li>get ssh access and set up drush on new host </li>
<li>add git remote of new production code location, push (move git-ignored settings.php separately)</li>
<li>point domain nameserver to new host</li>
<li>profit</li>
</ul>
OK, well maybe not so much the last part. And there might've been a few other steps along the way but those are the basics. I just like making lists. Along the way, I wanted to make a change to the way a legacy project (a Drupal site living in my Drupal site) displayed a field, since ... no one ever looks at it. The principle of trees falling in the forest, I guess.<br />
<br />
To make the change, I needed to brush up on the Drupal 7 database API and drush. I thought I'd check my work locally before deploying, which means MAMP. While it's usually trouble-free, sometimes things go wrong...<br />
<br />
Can't get Apache to start?<br />
<ul>
<li>try changing /Applications/MAMP/Library/bin/envars to _envvars</li>
</ul>
Great, now the MySQL and Apache servers are running. (Assuming you're all set up on localhost otherwise - <a href="http://foundationphp.com/tutorials/vhosts_mamp.php">this post</a> helps) Now from the command line I was hoping to simply <code>drush sql-cli</code> when things went wrong again.<br />
<br />
Can't connect to local MySQL server through socket '/tmp/mysql.sock'?<br />
<ul>
<li>Create a symlink in your application's root directory then restart MAMP (or just MySQL), something like <code>public_html git:(master) ✗ ln -s /Applications/MAMP/tmp/mysql/mysql.sock /tmp/mysql.sock</code></li>
<li>ht @ https://coderwall.com/p/w5kwzw (which redirects to the right url but shows a 500 error as of this writing)</li>
</ul>
OK, enough is enough. Turns out what I was really looking for is <code>drush php-eval</code> to run code and get instant feedback (wish I knew this when I built my first Drupal site in 2009-10!), though I needed to use double quotes inside my query and pass everything as a single-quoted argument, like<br />
<br />
<code>drush php-eval '$doctype = "document"; $result = db_query("SELECT n.title, n.changed FROM {node} n WHERE n.type = :doctype ORDER BY n.changed DESC LIMIT 1", array(":doctype" => $doctype));<br />
foreach ($result as $record) { $datestamp = $record->changed;<br />
$unixtime_to_date = date("F jS, Y", $datestamp);<br />
echo $unixtime_to_date;<br />
}'</code>
<br />
More complicated examples can be found in the documentation for <a href="https://www.drupal.org/node/1251174">database API Result Sets</a>. Going through this reminded me of something a wise friend once told me as I was moving from PHP/Drupal to Ruby/Rails a few years ago, "It's all the same issues and concepts, just different syntax."joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-40053615455537277482014-12-28T20:59:00.002-08:002014-12-31T17:16:31.099-08:00August #TMIL - Getting Started with iOS By August I wanted to get out of the browser and learn something entirely different! Although my journey began in anticipation of iOS 8's September release and the introduction of Swift in June, after reading tons of comments and discussions about whether to learn Swift, I decided to stick with iOS 7 and Objective-C at first to gain a better understanding of the entire iOS ecosystem.<br />
<br />
<a name='more'></a>I started with the <a href="https://itunes.apple.com/us/course/developing-ios-7-apps-for/id733644550">Stanford iOS 7 class</a>, but after the first few assignments couldn't adjust to the course's style and pace, so began again with <a href="https://www.codeschool.com/paths/ios">Code School</a>. One intellectual crutch I used while learning Objective-C was to compare it to Ruby. After getting used to the syntax, which isn't so terrible after all, it's all just objects sending messages to one another, class and instance methods, and MVC setup in iOS (which the Stanford course stressed; I'm also curious about <a href="http://www.objc.io/issue-13/mvvm.html">MVVM</a>). By the end of <a href="http://www.manning.com/lim2/">iOS 7 in Action</a>, things started to click. I'm hoping for a similar iOS 8 version; each chapter does an excellent job of walking through building a single-feature app, e.g. retrieving remote data for a Chuck Norris quotes app, managing photos and a tasks app using Core Data.<br />
<br />
In October, my curiosity got the better of me and I began reading <a href="https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11">the Swift book</a> and using <a href="https://developer.apple.com/videos/wwdc/2014/?id=408">playgrounds</a>. Although I hadn't released anything in Objective-C, I'd seen enough to feel comfortable understanding it. The realtime feedback and feeling of a more modern language (the syntax, <a href="http://2014.funswiftconf.com/">functional features</a>, use of closures) made it an easy switch. Along with <a href="https://itunes.apple.com/us/book/swift-programming-language/id881256329?mt=11">the reference book</a>, I've found the tutorials on the excellent <a href="http://www.raywenderlich.com/">www.raywenderlich.com</a> to be supremely helpful. And don't miss the <a href="https://developer.apple.com/videos/wwdc/2014">WWDC 2014 videos</a> (they make excellent subway time killers!).<br />
<br />
I plan to continue my adventures in Swift and hopefully get something into the app store next year. Now that I'm semi-proficient in the language and know my way around Xcode and building basic apps, it's time to learn about testing, get over the hurdle of really understanding <a href="https://developer.apple.com/design/adaptivity/">AutoLayout</a>, and continue the relentless march of tutorials while having fun learning a new language.joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-56433133572207076212014-12-21T14:46:00.001-08:002014-12-31T17:21:01.143-08:00July #TMIL - Some command line favoritesGetting close to the end of the year.. will I make all twelve <a href="http://joemerante.blogspot.com/search/label/tmil">TMIL</a> posts? Let's see how far we can get as things might start to take on a "best of 2014" flavor. Here are some command line favorites I've noted and usually forget, mostly around searching:<br />
<br />
List the first ten items in a directory<br />
<code>ls | head -10</code><br />
<br />
Show only the human-readable sizes of all xml files in a directory<br />
<code>ls -lSah *.xml | awk '{print$5}'</code><br />
<br />
Sum the sizes of all zip files in a directory (notice there's no -h flag to ls, this way you sum only bytes, then later convert to kilobytes then to megabytes)<br />
<code>ls -lSa *.zip | awk '{ total += $5}; END {print total/1024/1024 "Megabytes"}'</code><br />
<br />
<a name='more'></a>Find all files recursively, count them<br />
<code>find . -type f | wc -l</code><br />
<br />
Find files in app/views with case-insensitive 'important' somewhere in the name<br />
<code>find app/views -iname "*important*" -type f</code><br />
<code><br /></code>
Find visible or hidden files in the current directory with case-insensitive 'important' anywhere in the title and move them to an existing directory named 'temp'<br />
<code>find . -iname '*important*' -type f -maxdepth 1 | while read FILE; do mv $FILE ./temp; done</code><br />
<br />
Find xml files that don't contain 'beer' in the current directory<br />
<code>grep -v 'beer' *.xml</code><br />
<br />
Find files in config/locales that have been modified in the last day<br />
<code>find config/locales -mtime -1</code><br />
<br />
Find the word 'beer' in files within the app/models directory and show the match in color<br />
<code>grep --color -Rin beer app/models</code><br />
<br />
Find directories starting with the path app/views/external_*<br />
<code>find app/views/external_* -type d</code><br />
<br />
Show only the matching parts of a line (-o) from a regular expression search for ten consecutive numbers in xml files in the currently directory (add -h to hide the matching filenames)<br />
<code>egrep -o '[0-9]{10}' *.xml</code>joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-8492942420719874312014-10-06T20:25:00.003-07:002014-12-31T17:16:55.362-08:00June #TMIL - Ruby Under a MicroscopeThis post marks the halfway point of fulfilling my New Year's resolution to post about coding once a month this year. As the published date suggests, summer got in the way of timeliness! The curious reader can calculate the stats on how many days after the first day of the month it's taken to post. (Internal lawyer says: The resolution was to create a post for each month, not publish them on time.) Alright, let's get down to business.<br />
<br />
In June, I completed <a href="http://patshaughnessy.net/">Pat Shaughnessy's</a> book, <a href="http://www.nostarch.com/rum">Ruby Under a Microscope</a>. <br />
<br />
<a name='more'></a>The writing is clear and concise, and the layout of each chapter extremely helpful in reinforcing the material. The chapters are interspersed with illustrations, "Experiments" (usually benchmarking code) and definitions (that start simple and grow more complete as nuances are explained). The book is also a great way to gain comfort in fundamental programming concepts, like how a compiler works. Below are a few highlights from my notes.<br />
<br />
The book starts with an explanation of Ruby's tokenization and parsing process (take a peek with the <a href="http://ruby-doc.org/stdlib-2.0/libdoc/ripper/rdoc/Ripper.html">Ripper class</a>), and how code is compiled into instructions that <a href="http://en.wikipedia.org/wiki/YARV">YARV</a> can execute. While the first few chapters go pretty deep into topics, such as how Ruby puts values on stack frame and how the environment and stack pointers deal with scope, there are still things to play with. If you run the following snippet in irb and would like to know what the output means, this book is your guide.<br />
<br />
<code>
stuff = <<STR<str code=""></str></code><br />
<code><end p=""> 5.times do<br />
puts "foo!!!"<br />
end<br />
STR<br />
puts RubyVM::InstructionSequence.compile(stuff).disasm</end></code><br />
<br />
After the deep dive down to the metal, the author walks through the Ruby object model and its repesentation in C structures, then method and constant lookup, until we come to "The Hash Table: The Workhorse of Ruby Internals." Like the early chapters, it presents a fundamental computer science concept through the lens of Ruby, enhancing understanding of both. For example, when you create<br />
<br />
<code>
hash = {}<br />
hash[:some_key] = "foo"</code><br />
<br />
the basic idea is that Ruby takes the key, runs it through a hash function, and puts it in an available "bin", determined by the hashed key modulus the number of bins. Then, when retrieving values, Ruby recalculates the hashed value and looks in the corresponding bin for the item you're retrieving.<br />
<br />
Eventually, two keys will have the same hash, resulting in a hash collision. Ruby has built in constants that determine when to allocate more bins to avoid this, and Pat's experiments and corresponding graphs show the spike in milliseconds when this happens. (And also the spike after inserting the 7th item into a hash since Ruby 2 stores up to six items in an array instead of a pointer to the hash table. It then just compares the keys when looking things up, instead of using the hashing function to find the right bin to compare keys in.) Pat generously posted a draft of this chapter while writing it that you can find <a href="http://patshaughnessy.net/Ruby-Under-a-Microscope-Rough-Draft-May.pdf">here</a>. There's also a great explanation (using Ruby code to rebuild the basic functionality) of how hashing works posted <a href="https://blog.engineyard.com/2013/hash-lookup-in-ruby-why-is-it-so-fast">here</a>.<br />
<br />
And finally, a few other fun snippets that get unpacked and explained in the book:<br />
<ul>
<li>Each level on the stack can have a different self</li>
<li>instance_eval uses self and an environment pointer to access variables in different scopes - when you call instance_eval, a closure and new lexical scope is created, and self becomes the receiver</li>
<li>disabling garbage collection when running benchmarks can help avoid skewed results</li>
<li>There are lots of goodies in Ruby core and standard library that don't get much attention, like <a href="http://www.ruby-doc.org/core-2.1.3/ObjectSpace.html">ObjectSpace</a></li>
<li>Object structures like RString, RArray and friends are defined in <a href="https://github.com/ruby/ruby/blob/trunk/include/ruby/ruby.h">include/ruby/ruby.h</a> </li>
</ul>
This book provides much more than this post can do justice to. If you've been wondering what happens behind-the-scenes when you create a lambda and how it's later called, or have been confused about class variables (shared by subclasses) and class instance variables (not shared; each instance of a class or subclass has its own), wondered how JRuby and Rubinius fit into the picture, or the similarites between what RObjects, RStrings and the C struct that comprise Ruby objects look like, then this is the book for you. And if you haven't wondered about those things, there's no better way to get started!joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-11965057015172282602014-07-22T10:53:00.000-07:002014-12-31T17:17:04.389-08:00May #TMIL - A Touch of eval()In a Rails app, I've got a Plan that belongs to a Subscription. The Plan has a duration, and the Subscription has an expiration. The expiration gets set in an after_create callback using the current time (DateTime.now) plus the Plan's duration. Then, there's an expired? method on the Subscription which checks if <code>self.expiration < DateTime.now</code>. Pretty straightforward, right? (code snippet below)<br />
<br />
<a name='more'></a>When updating seeds for the Plan, I was hoping to get away with using ActiveSupport conveniences, like <code>2.weeks</code>, for the Plan's duration. However, <code>2.weeks</code> evaluates, i.e. it is not stored as "2.weeks" in the database. Storing the value of <code>2.weeks.to_s</code> (1209600, or the number of seconds in two weeks) also wouldn't work, since <code>DateTime.now + 1209600</code> gives an unexpected result - it adds that many <i>days</i>.<br />
<br />
The above makes sense, but this is Ruby, there's gotta be another way. How about serializing <code>2.weeks</code> and storing it as a lambda in the database? The seemed to work in a console:<br />
<br />
[5] pry(main)> now = DateTime.now<br />
=> Tue, 22 Jul 2014 17:48:52 -0400<br />
[6] pry(main)> duration = -> {2.weeks}<br />
=> #<Proc:0x007fc18b3a14a8@(pry):2 (lambda)><br />
pry(main)> now + duration.call<br />
=> Tue, 05 Aug 2014 17:48:52 -0400<br />
<br />
Next, I added serialize :duration into my model, and did the <code>RAILS_ENV=test rake db:drop db:create db:migrate db:seed</code> rigamarole (since the helper tasks for this never seem to work as thoroughly for me) ... BOOM! I pry'ed in where the Plan's were getting created and tried to manually create one, and it appears that ActiveRecord won't let you serialize a proc (or lambda, a flavor of proc). While tempting to go down the rabbit hole of why, I knew there had to be an easier way than converting all my DateTime's into seconds, and done some other conversions, etc.<br />
<br />
The solution? Store the Plan duration as a string, then use eval(). Since the Plan's will rarely change or get newly created, and tests are in place, this seemed like an appropriate use of what many consider to be an evil method. Ahh, I love writing in Ruby. Snippet below:<br />
<script src="https://gist.github.com/joemerante/ab83d7bde85a682b9608.js"></script>joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-67022837455287353342014-06-29T10:11:00.000-07:002014-12-31T17:21:34.215-08:00April #TMIL - RSpecAs expected, this series of posts has drifted off schedule. At least the drafts have been on time! Below is a list of RSpec tips, preferences and best practices I've been accumulating over the last year, finally realizing that's it much more art than science.<br />
<br />
<a name='more'></a><br />
<ul>
<li>Start by writing an outline of your specs so that when you run them with the <a href="https://www.relishapp.com/rspec/rspec-core/v/3-0/docs/command-line/format-option">--format documentation</a>, or -fd, option, you get a readable outline that clearly explains what the code should (or shouldn't) be doing. This can be more high level, or have a line for the happy/sad/edge paths of each method, or whatever works for you. I've found that using documentation format as a guide, along with organizing your tests using context/describe blocks, helps keep the code <a href="http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)">SOLID</a> and intentions clear. You can make this the default, and enhance readability with --color, by adding a line for each one to a <a href="https://www.relishapp.com/rspec/rspec-core/v/2-0/docs/configuration/read-command-line-configuration-options-from-files">.rspec file</a>.</li>
<li>Along with the above, driving from the outside in has been extremely useful in many situations for me. This <a href="http://www.rubytapas.com/episodes/120-Outside-In">Ruby Tapas episode</a> provides a great example of this approach. I've found that starting with BDD leads to more reliable code and helps me to see relationships between objects and scenarios, though I try to avoid mocking (or get rid of them once the design is driven out). I like to start by describing what should happen as a series of Given-When-Then statements, e.g. Given I'm on the home page, When I sign in with valid credentials, Then I should see something awesome, then converting those into grouped specs, extracting helper methods, etc. While Cucumber more clearly enforces that syntax, the additional overhead of setting it up gets in the way, and I stick with Capybara in <a href="https://www.relishapp.com/rspec/rspec-rails/docs/feature-specs/feature-spec">RSpec feature specs</a>.</li>
<li>Add complete coverage, but don't over do it. If it feels like you're rewriting your code in the specs or you're <a href="http://fredwu.me/post/59395419899/writing-sensible-tests-for-happiness">testing parts of your framework</a>, you're doing it wrong. <a href="http://youtube.com/watch?v=z9quxZsLcfo">This video</a> has a great discussion of the idea that testing should be "<a href="http://stackoverflow.com/questions/153234/how-deep-are-your-unit-tests/153565#153565">as little as possible to achieve a given level of confidence</a>." </li>
<li><a href="http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails/">Don't forget about Mini Test</a> as an alternative to RSpec. After all, it's <a href="http://ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest.html">in the standard library</a>, complete with examples involving cheeseburgers! <a href="http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails/">Brandon Hilkert's post</a> also reminds us of other benefits, like using fixtures (especially if your factories' <a href="https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations">associations</a> and <a href="http://robots.thoughtbot.com/remove-duplication-with-factorygirls-traits">traits</a> are getting out of control).</li>
<li>Get to know <a href="https://github.com/vcr/vcr">VCR</a> for recording HTTP interactions and <a href="https://github.com/oesmith/puffing-billy">Puffing Billy</a> to record browser interactions. This way your specs never make real web requests, resulting in more deterministic tests, and so you don't have to deal with "stubbing the internet" and can run your test suite without an internet connection.</li>
<li>Be careful with mocks. I find them helpful when it's absolutely essential to know a message on another object gets called as a result of what you're testing. More succintly, "<a href="http://stubs%20are%20for%20queries%20and%20mocks%20are%20for%20commands/">stubs are for queries and mocks are for commands</a>." Or sometimes using them to drive out the design of collaborator objects (and to encourage decoupled code via dependency injection as suggested <a href="http://jamesgolick.com/2010/3/10/on-mocks-and-mockist-testing.html">here</a>). For example, mocking out a Song class when writing tests for your Playlist class then converting the mocks to real objects (and non-stubbed methods) when you've built your Song class. More often, I've found that when there's too much mocked or stubbed behavior necessary to set up a test, it's usually a sign the test is focused on the wrong thing, in the wrong place, or the code is too coupled. Opinions on the use and misuse of mocks vary greatly; I've found more success avoiding them when possible. More discussion on the use/misuse of mocks <a href="http://youtube.com/watch?v=z9quxZsLcfo">here</a>, <a href="http://myronmars.to/n/dev-blog/2012/06/thoughts-on-mocking">here</a> and <a href="http://martinfowler.com/articles/mocksArentStubs.html#CouplingTestsToImplementations">here</a>. And <a href="http://martinfowler.com/articles/mocksArentStubs.html">here</a>. This <a href="http://jamesgolick.com/2010/3/10/on-mocks-and-mockist-testing.html">post by James Golick</a> demonstrates some benefits of the mockist style and reminds one of the importance of having intergration tests (which you should regardless of your testing style) to avoid the common problems with mocking (i.e. mocked behavior allows false positives when the underlying interface has changed). </li>
<li>Redo <a href="http://testfirst.org/learn_ruby">these exercises</a> every now and then and see how much better and faster you're getting.</li>
<li><a href="https://github.com/guard/guard">Guard</a>, <a href="https://github.com/guard/guard-livereload">Guard::LiveReload</a>, and <a href="https://github.com/guard/guard-rspec">Guard::RSpec</a> are essential for front end development. The basic setup of your Guardfile found in the <a href="http://railscasts.com/episodes/264-guard">Railscast</a> will get you going. Just add the gems and bundle, then install the LiveReload browser extension (for Chrome, Safari or Firefox). You can add more plugins, like <a href="https://github.com/guard/guard-coffeescript">Guard::CoffeeScript</a>, by running `guard init pluginname`. A list of plugins can be found on the <a href="https://github.com/guard/guard/wiki/List-of-available-Guards">Guard wiki</a>. After enabling the extension in your browser while your development server is running, when you change a file, Guard will refresh your browser window and kick off your specs. Nice.</li>
<li>There are lots of other must-have's in your development group, a good overview can be found <a href="http://blog.waymondo.com/2013-06-18-pimp-your-rails-gemfile-development-group/">here</a>. I also like to liberally use Pry and so-called "REPL Driven Development" as described by Pry core contributor Conrad Irwin. (<a href="https://www.youtube.com/watch?v=D9j_Mf91M0I">Video</a> | <a href="https://speakerdeck.com/conradirwin/repl-driven-development-with-pry">Slides</a>) </li>
</ul>
That's all for now, happy spec'ing!joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-88100501372003421932014-05-17T18:39:00.000-07:002015-01-10T17:26:19.145-08:00March #TMIL - Ruby class macrosMy how the time flies. Still, I'd rather be late than abandon my New Year's resolution early! Here are some notes that have been sitting in this draft for the last two months.<br />
<br />
<h3>
<b>Two quick tips</b></h3>
<ul>
<li><a href="https://pqrs.org/macosx/keyremap4macbook/pckeyboardhack.html">PC Keyboard Hack</a> is a must-have extension for me. I find it's much nicer on your hands to avoid using the Return key on the right side of the keyboard. (You can make some remappings in the Keyboard->Modifier Keys panel in the OS X system preferences; however, Return is not an available choice for remapping Caps Lock.) </li>
<li>Enable control-R backsearch in irb by adding this code to your ~/.editrc file :</li>
<ul>
<li>bind "^R" em-inc-search-prev</li>
<li><a href="http://blog.tech-angels.com/post/963080350/improve-irb-and-fix-it-on-mac-os-x">Source</a></li>
</ul>
</ul>
<h3>
<b>Class macros in Ruby<a name='more'></a></b></h3>
The explanation of class macros in <a href="http://pragprog.com/book/ppmetr/metaprogramming-ruby">Metaprogramming in Ruby</a> by Paolo Perrotta really helped me understand the concept (and review and dive deep on many others - its explanation of the Ruby object model and coding idioms is great.). Let's say you manage the code for a death metal record label's applications, and the company decides to add folk rock artists to its roster. You'll need to make your code more generic, and warn developers that the method they called is now deprecated, then tell them what the new one is.<br />
<br />
Perotta reminds the reader that although classes are typically thought of as containers for methods, when you open a class by creating a new instance (here, a Guitarist), other code will be executed. In this case, there is a direct call to the class method ::deprecate which then uses <a href="http://www.ruby-doc.org/core-2.1.1/Module.html#method-i-define_method">Module#define_method</a> to dynamically add the deprecated methods to the class definition; the deprecated method then warns the user and calls the correct method. Another familiar class macro is <a href="http://api.rubyonrails.org/v3.2.13/classes/ActiveModel/MassAssignmentSecurity/ClassMethods.html#method-i-attr_accessible">attr_accessible</a> in Rails 3, or Ruby's attr_accessor.<br />
<br />
To begin making changes, the Guitarist class will now need a guitar, not an axe. The Guitarist will also play instead of shred. (Maybe you'll want more specific sub-classes for each genre, or add functionality with modules like Shreddable or Strummable, but that's a further step.)<br />
<br />
<code>
class Guitarist<br />
def guitar</code><br />
<code> # ...<br />
end<br />
<br />
def play(song)<br />
puts "Playing #{song}."<br />
# ...<br />
end<br />
<br />
def self.deprecate(old_method, new_method)<br />
define_method(old_method) do |*args, &block|<br />
warn "Warning: #{old_method}() is now deprecated. Use</code><br />
<code> #{new_method}()."<br />
send(new_method, *args, &block)<br />
end<br />
end<br />
<br />
deprecate :axe, :guitar<br />
deprecate :shred, :play<br />
end<br />
<br />
g = Guitarist.new<br />
g.shred(Song.new("Like a Rolling Stone")<br />
<br />
# outputs the following:<br />
# => "Warning: shred() is deprecated. Use play()."<br />
# => "Playing Like a Rolling Stone."<br />
</code>
<br />
<br />
That's all for this month, more to come.joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-7438964289068092442014-03-29T14:39:00.002-07:002014-03-29T15:01:02.489-07:00Add a Keyboard Shortcut to Toggle WiFi On and OffClicking the WiFi icon in OS X on and off when you're trying to troubleshoot Internet connectivity or AirPort issues can be annoying and disruptive to your workflow. Here's how to add a global keyboard shortcut to do it:<br />
<br />
<ul>
<li>Copy the script from <a href="https://discussions.apple.com/message/16275366#16275366">here</a> and paste it into a new document in the AppleScript Editor. Save it as ToggleWifi (or whatever you want). Save it with Application as the file format.</li>
<li>Since there's no built in way to create a keyboard shortcut to launch an application in the Keyboard system preferences panel, first create a Service in Automator.</li>
<li><a href="http://computers.tutsplus.com/tutorials/how-to-launch-any-app-with-a-keyboard-shortcut--mac-31463">This post</a> shows screenshots of the steps to create the Service. Open Automator, go to File->New and choose Service from the options that appear. Set "Service receives selected" to 'no input' and leave "in" set to 'any application'. Then search for the Launch Application action in the Actions menu on the left, and drag it to the open area that says "Drag actions or files here to build your workflow". Choose your AppleScript application from the Launch Application dropdown. Click compile then save the Service with a descriptive title, like "Toggle WiFi".</li>
<li>Open the Keyboard in System Preferences. Go to the Shorcuts tab, then go to Services and scroll down until you see the one you added in the last step. Make sure it's selected, then to the right of its name, click the grayscale "none" and a button to 'add shorcut' will appear.</li>
<li>Enter a unique keyboard shortcut. I chose control-option-command-9 so it wouldn't conflict with any other global or application shortcuts.</li>
</ul>
<br />
<div>
The above instructions should take effect immediately, so if your chosen keyboard shortcut doesn't work, try a different one. You might also need to modify a line of the AppleScript as <a href="https://discussions.apple.com/message/21122973#21122973">this comment</a> suggests. </div>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.comtag:blogger.com,1999:blog-8465655736337401005.post-87487844791686942712014-03-16T15:58:00.001-07:002015-01-10T17:25:25.680-08:00February #TMIL - vimAlthough it's already halfway through March, I'm not quite ready to give up on my <a href="http://joemerante.blogspot.com/2014/02/january-tmil.html">resolution</a> to collect a few of the things I've been learning each month as an opportunity to review the material. Don't worry, it's all a healthy exercise in non-competitive professional development. I haven't gone crazy but the risks of losing balance described <a href="http://www.businessinsider.com/syndromes-drive-coders-crazy-2014-3">here</a> are very real, especially when you enjoy what you're doing. OK, on to the good stuff.
<br />
<br />
<h3>
<b>Vim</b></h3>
I've been meaning to learn more than the basics of Vim for years and finally dug in last month. <br />
<br />
<a name='more'></a>Like so many things, getting your environment set up properly in the first place will make things much easier. And repeatable. I began with Vundle, a plug-in manager for Vim. Although the project's founder <a href="http://gmarik.info/blog/2014/02/04/why-i-stopped-contributing-to-vundle">recently announced</a> he'd be stepping down, it looks like <a href="https://github.com/gmarik/Vundle.vim/issues/383">a few people</a> have stepped up to work on it, so it seems solid for now. Grab it <a href="https://github.com/gmarik/Vundle.vim">here</a>.<br />
<br />
<div>
To further ease the transition, I downloaded and expanded <a href="https://code.google.com/p/macvim/">MacVim</a> then moved the <i>mvim</i> executable into /bin so I could trigger it from the command line. To get around, I knew I needed <a href="https://github.com/scrooloose/nerdtree">NERDTree</a>. After adding the bundle to my <a href="https://github.com/joemerante/dotfiles/blob/master/.vimrc">~/.vimrc file</a>, (based on the sample one <a href="https://github.com/gmarik/Vundle.vim/blob/master/README.md">here</a>) then running :BundleInstall in MacVim. Next up was basic navigation. The :NERDTree command opened a sidebar file explorer, and :NERDTreeToggle to show/hide it. While you can launch a project from the terminal with <code>mvim .</code> you can also get there from within MacVim with :NERDTree path/to/myawesomeapp. <a href="http://robots.thoughtbot.com/intro-rails-vim">This post</a> from Thoughbot shows the basics of the vim-rails plugin, such as commands like :Rmodel mymodel to jump right to app/models/mymodel in edit mode. The plugin also adds syntax highlighting (set `syntax on` in vimrc), which is always helpful. There are bundles for Coffeescript and Javascript, too.</div>
<div>
<br />
To make these more easily available to me at work or wherever, I created a <a href="https://github.com/joemerante/dotfiles">dotfiles repo</a>. Now on whatever machine I'm using, I can install vundle, download my repository and create a symlink to the .vimrc from the user home path, then :BundleInstall.</div>
<div>
<br />
Here are a couple of other essential vim shortcuts in normal mode:<br />
<ul>
<li>gg=G to tidy up the indentation of a file</li>
<li>zt to put the line the cursor is on at the top of the screen</li>
<li>:%s/searchterm/replacementterm/g to <a href="http://vim.wikia.com/wiki/Search_and_replace">search and replace</a> </li>
<li>$ or 0 to get to end/beg of line, hjkl for left, down, up, right</li>
<li>dd to delete a line</li>
<li>gg or 1G jumps to top of file, can use 2G to jump to start of line 2, G to jump to the end of the file</li>
<li>:set number to add line</li>
</ul>
And a few for visual block mode (control-v to get into it):<br />
<ul>
<li>5j$ will select five lines and select to end of each line</li>
<ul>
<li>replace by hitting r after a selection, then the next key will replace </li>
<li>or delete with d</li>
</ul>
<li>when you have an area of text selected, the letter o jumps to the opposite corner to more quickly resize the selection</li>
<li><a href="http://vim.wikia.com/wiki/Search_and_replace_in_a_visual_selection">search and replace in a selection</a> by pressing : to enter a command, then s/searchterm/replaceterm/g and hit enter</li>
<li>ctrl-V then G to select column down to end of the file</li>
</ul>
<h3>
<b><br /></b></h3>
<h3>
<b>Ctags</b></h3>
One feature I'd really like to master is jumping around with ctags to explore source code and documentation. Here are some common commands:<br />
<table style="background-color: rgba(255, 255, 255, 0.247059); border-collapse: collapse; border-spacing: 0px; color: #333333; font-family: Georgia, serif; font-size: 13.63636302947998px; line-height: 19.09090805053711px; margin: 1em 0px; width: 587.272705078125px; word-wrap: break-word;"><tbody style="word-wrap: break-word;">
<tr class="odd" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">:tag [identifier]</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">Jump to the identifier.</td></tr>
<tr class="even" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">:tags</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">List the tag stack.</td></tr>
<tr class="odd" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">Control-]</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">Jump to the tag under the cursor.</td></tr>
<tr class="even" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">:tselect</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">Select which tag location to go to for the current tag.</td></tr>
<tr class="odd" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">Control-t</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">Jump back from the current tag.</td></tr>
<tr class="even" style="word-wrap: break-word;"><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;"><code style="font-family: monospace, serif; font-size: 1em; word-wrap: break-word;">:stjump [identifier]</code></td><td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: dotted; border-bottom-width: 1px; padding: 1em 0.5em; word-wrap: break-word;">Jump to the identifier in a new split window.</td></tr>
</tbody></table>
(<a href="http://www.scholarslab.org/research-and-development/code-spelunking-with-ctags-and-vim/">chart source</a> and how to get setup)<br />
<br />
<a href="https://coderwall.com/p/du_sgq">This link</a> shows how to set them up in a Rails project (<code>ctags -R . $(bundle list --paths)</code>), then usage is simple as, for example, :tag link_to to show show the source of the link_to helper. The commands in the chart above work as well. control-G will show current file's location and you can use use :!echo % | pbcopy to copy the path to your system clipboard. Pry has some great features for jumping around documentation as well, and I've been using Pry-rescue much more after watching <a href="http://www.youtube.com/watch?v=D9j_Mf91M0I">this great video</a> (<a href="https://speakerdeck.com/conradirwin/repl-driven-development-with-pry">slides</a>).<br />
<br />
<br />
<h3>
Misc.</h3>
-Check out <a href="https://mizage.com/divvy/">Divvy</a>, which has become an essential part of my workflow. It allows you to auto-resize and move windows around your screen using only keyboard shortcuts.<br />
-I completed <a href="http://pragprog.com/book/ppmetr/metaprogramming-ruby">Metaprogramming in Ruby</a>, a book I'd highly recommend. I'll look at some examples from the book in next month's post after digesting it a bit more.</div>
<div>
<br /></div>
joehttp://www.blogger.com/profile/12450046024309367298noreply@blogger.com