Episode #426 - December 13th, 2013

Posted 4 months back at Ruby5

thoughtbot open source issues now on Stack Overflow, verifying doubles in RSpec 3, interactive mockups with Stagehand, bundler is stayin' alive (for the time being), and updates to the roar gem all in this episode of the Ruby5!

Listen to this episode on Ruby5

This episode is sponsored by New Relic
New Relic is _the_ all-in-one web performance analytics product. It lets you manage and monitor web application performance, from the browser down to the line of code. With Real User Monitoring, New Relic users can see browser response times by geographical location of the user, or by browser type.

thoughtbot on Stack Overflow
thoughtbot is moving away from mailing lists and towards Stack Overflow posts instead. Check out their blog post explaining why.

New in RSpec 3: Verifying Doubles
Add a little dose of reality to your RSpec doubles.

Stagehand
Easily make interactive mockups with the Stagehand library.

Bundler Not Dead Yet
The rumors of Bundler's death have been greatly exaggerated.

roar
The roar just has received some love lately, including a brand-new and very well-written README.

Phusion Passenger 4.0.29 released

Posted 4 months back at Phusion Corporate Blog

Phusion Passenger is a fast and robust web server and application server for Ruby, Python, Node.js and Meteor. Passenger takes a lot of complexity out of deploying web apps, and adds powerful enterprise-grade features that are useful in production. High-profile companies such as Apple, New York Times, AirBnB, Juniper, American Express, etc are already using it, as well as over 350.000 websites.

Phusion Passenger is under constant maintenance and development. Version 4.0.29 is a bugfix release.

Phusion Passenger also has an Enterprise version which comes with a wide array of additional features. By buying Phusion Passenger Enterprise you will directly sponsor the development of the open source version.

Recent changes

4.0.28 has been skipped because a compilation problem on OS X Mavericks as discovered shortly after uploading the tarball. The changes in 4.0.28 and 4.0.29 combined are as follows:

  • Introduced a workaround for a GCC 4.6 bug. This bug could cause Phusion Passsenger to crash during startup. Affected operating systems include Ubuntu 12.04 and Amazon Linux 2013.09.01, though not every machine with this OS installed exhibits the problem. See issue #902.
  • Improved Node.js support: the Sails framework is now supported.
  • Improved Node.js support: the streams2 API is now supported.
  • Introduced support for hooks, allowing users to easily extend Phusion Passenger’s behavior.
  • Fixed a bug in the `passenger start -R` option. It was broken because of a change introduced in 4.0.25.
  • Fixed a bug in PassengerMaxInstancesPerApp. Fixes issue #1016.
  • Fixed compilation problems on Solaris.
  • Fixed compilation problems on OS X Mavericks.
  • Fixed an encoding problem in the Apache autodetection code. Fixes issue #1026.
  • The Debian packages no longer depend on libruby.
  • Application stdout and stderr are now printed without normal Phusion Passenger debugging information, making them easier to read.

Installing or upgrading to 4.0.29

OS X OS X Debian Debian Ubuntu Ubuntu
Heroku Heroku Ruby gem Ruby gem Tarball Tarball

Final

Fork us on Github!

Phusion Passenger’s core is open source. Please fork or watch us on Github. :)

<iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;repo=passenger&amp;type=watch&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="170" height="30"></iframe><iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;repo=passenger&amp;type=fork&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="170" height="30"></iframe><iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;type=follow&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="190" height="30"></iframe>

If you would like to stay up to date with Phusion news, please fill in your name and email address below and sign up for our newsletter. We won’t spam you, we promise.



Capybara Webkit 1.1.0: Screenshots and Better ClickFailed Debugging

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Capybara Webkit 1.1.0 has been released and it contains some great new features like screenshots and better ClickFailed debugging.

ClickFailed

You're writing a nice little Capybara spec, and you innocently try to click on something using Capybara that you can see plainly in your browser:

click_on 'Sign Up'

You're SURE this should work, but Capybara gets sassy:

Failed to click element /html/body/div[1]/button[2] (Capybara::Webkit::ClickFailed)

PC Load Letter

Introducing Capybara Webkit 1.1.0: ClickSuccess edition

These ClickFailed errors are pretty frustrating, and unfortunately there's no easy fix. These errors occur when your Capybara driver tries to click on an element, but it can't find the element or finds a different element where it expects the target element to be.

Although we haven't come up with a definitive fix for these issues, Capybara Webkit includes several changes that will help you to work through them. Let's run through the above scenario again, this time using the latest release of Capybara Webkit.

First, you attempt to click on the element:

click_on 'Sign Up'

You'll still get a ClickFailed error, but this time the error message is much more helpful:

Failed to click element /html/body/div[1]/button[2] because of overlapping
element /html/body/div[@id='myModal']/div/div/div[2] at position 191, 121;
(Capybara::Webkit::ClickFailed)

Hey now, that's interesting! There's some div tag with an ID of myModal where you expect your button to be? That's something you can work with.

But wait! There's more!

Screenshots

You'll also see a line like this:

A screenshot of the page at the time of the failure has been written to
/var/folders/5c/h1zzj9wx70n4y1qcvnzy2h8h0000gn/T/click_failed_C10849.png

If you pop open this file, you'll get to clearly see what Capybara Webkit did when you were trying to click on that button:

ClickFailed

There's even a beautiful mouse cursor illustrating the exact location you attempted to click.

Updating

If you're using Capybara Webkit 1.0.0, you can update easily.

Edit your Gemfile:

gem 'capybara-webkit', '~> 1.1.0'

Update your bundle:

% bundle update capybara-webkit

And you're good to go!

We also included a handful of other bugfixes and improvements in this release.

Props go out to Austen Ito, Jon Rowe, Kazuhiro Nishiyama, Kirill Nikitin, Kohei Suzuki, and Matthew Horan for helping out with this release.

Capybara Webkit 1.1.0: Screenshots and Better ClickFailed Debugging

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

Capybara Webkit 1.1.0 has been released and it contains some great new features like screenshots and better ClickFailed debugging.

ClickFailed

You're writing a nice little Capybara spec, and you innocently try to click on something using Capybara that you can see plainly in your browser:

click_on 'Sign Up'

You're SURE this should work, but Capybara gets sassy:

Failed to click element /html/body/div[1]/button[2] (Capybara::Webkit::ClickFailed)

PC Load Letter

Introducing Capybara Webkit 1.1.0: ClickSuccess edition

These ClickFailed errors are pretty frustrating, and unfortunately there's no easy fix. These errors occur when your Capybara driver tries to click on an element, but it can't find the element or finds a different element where it expects the target element to be.

Although we haven't come up with a definitive fix for these issues, Capybara Webkit includes several changes that will help you to work through them. Let's run through the above scenario again, this time using the latest release of Capybara Webkit.

First, you attempt to click on the element:

click_on 'Sign Up'

You'll still get a ClickFailed error, but this time the error message is much more helpful:

Failed to click element /html/body/div[1]/button[2] because of overlapping
element /html/body/div[@id='myModal']/div/div/div[2] at position 191, 121;
(Capybara::Webkit::ClickFailed)

Hey now, that's interesting! There's some div tag with an ID of myModal where you expect your button to be? That's something you can work with.

But wait! There's more!

Screenshots

You'll also see a line like this:

A screenshot of the page at the time of the failure has been written to
/var/folders/5c/h1zzj9wx70n4y1qcvnzy2h8h0000gn/T/click_failed_C10849.png

If you pop open this file, you'll get to clearly see what Capybara Webkit did when you were trying to click on that button:

ClickFailed

There's even a beautiful mouse cursor illustrating the exact location you attempted to click.

Updating

If you're using Capybara Webkit 1.0.0, you can update easily.

Edit your Gemfile:

gem 'capybara-webkit', '~> 1.1.0'

Update your bundle:

% bundle update capybara-webkit

And you're good to go!

We also included a handful of other bugfixes and improvements in this release.

Props go out to Austen Ito, Jon Rowe, Kazuhiro Nishiyama, Kirill Nikitin, Kohei Suzuki, and Matthew Horan for helping out with this release.

Using JavaScript Promises to Reason About User Interaction

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

In functional programming, we rarely worry about concurrency primitives like threads. Instead, we use abstractions to make our code easier to reason about. In JavaScript, a Promise is the most common abstraction we encounter. It is usually associated with an Ajax request. We often use abstractions to reason about our concurrent code, but those abstractions can apply in other situations, as well. High level user interactions are essentially large asynchronous events, and can be represented as such in code.

Example: A video storefront

Let's take a look at an example. We're working on a video store front. The user can buy videos, and play videos that they own. If they aren't signed in, they need to sign in to buy a video, and we'd like to use modal dialogs for the sign in and purchasing process.

In order to determine if a user already owns the content, we need them to be signed in. It's also possible that upon signing in, they could already own the content they tried to purchase. This can quickly lead to an extremely complex set of conditionals, but we can simplify it by thinking about the problem like we would think about concurrency.

Making promises about user interactions

Any time we're dealing with user interaction, it is inherently asynchronous. In most code, this begins and ends with event handlers on individual DOM elements. This can be better abstracted if we think about the larger interactions a user can take, and represent that as a single asynchronous operation in the code.

A user may or may not be signed in. We could expose that, and let that conditional bubble throughout our code. However, most of the time, if a user isn't signed in, we're just going to prompt them to sign in. That means that rather than having a user who may or may not be signed in, we've always got a user that will be signed in at some point in the future. Let's look at how we might represent this in code:

class UserSession
  constructor: (@Q, @AuthModals, @Session) ->
    @_session = @Session.get()

  signIn: ->
    @_authDeferred = @Q.defer()
    @_resolveOrPrompt()
    @_authDeferred.promise

  completeSignIn: (data) ->
    @Session.save(data).then(@_completeAuth)

  _resolveOrPrompt: ->
    if @_session?.signedIn
      @_completeAuth(@_session)
    else
      @AuthModals.openSignIn()

  _completeAuth: (session) ->
    @_session = session
    @_authDeferred.resolve(session.user)

There's a lot going on here. Q is Kris Kowal's Q library for working with promises. We'll gloss over the implementation of AuthModals, and Session. We'll just assume that AuthModals opens the modal dialog, and something will call completeSignIn when the form is submitted. Session does two things. It asks the server if we're already signed in, and will submit the Ajax request to the server, and return a promise with the session object.

When code calls the signIn method, we're returning a promise that will eventually be resolved with the current user. If they're already signed in, we resolve that promise immediately. Otherwise, we open the modal dialog for the user to sign in, and resolve the promise once the form is submitted.

Good patterns are made to be built upon

This is incredibly powerful. We've completely removed the need for code to worry about whether or not the user is signed in for almost all cases. We can build upon this further by applying the same concept to purchases.

class UserPurchases
  constructor: (@Q, @UserSession, @PurchaseModals) ->

  purchase: (@video) =>
    @UserSession.signIn().then(@_promisePurchase(video))

  completePurchase: (license)
    @user.addLicense(@video, license)
    @_purchaseDeferred.resolve(license)

  _promisePurchase: (video) => (@user) =>
    @_purchaseDeferred = @Q.defer()
    @_resolveOrPrompt(video)
    @_purchaseDeferred.promise

  _resolveOrPrompt: (video) =>
    if @user.owns(video)
      @completePurchase(@user.licenseFor(video))
    else
      @PurchaseModals.openSaleForm(video)

We apply the same pattern here. If a user already owns the video, we resolve the promise with the license immediately. If not, we prompt the user to buy it, and resolve it when the license is given to us later. A user can only buy one thing at a time. If the user closes the modal and tries to buy something else, the old promise is effectively thrown away.

Finally, we can hide all of this away inside of an object responsible for playing the video.

class VideoPlayer
  constructor: (@UserPurchases, @PlayerModal, @Video) ->

  play: (video) =>
    @UserPurchases.purchase(video).then(@_loadAndPlay(video))

  _loadAndPlay: (video) => (license) =>
    @Video.load(video, license).then(@PlayerModal.openPlayer)

With this in place, code that wants to play a video needs only to call VideoPlayer.play. It doesn't need to concern itself with whether or not the user is signed in, or whether they already own the content. We would still need to have a conditional inside of the template to decide whether the button should say 'Buy' or 'Play'. However, if we use the Null Object pattern, and give the template a Guest when the user isn't signed in, this still remains relatively simple.

By thinking about higher level user interactions the same way we would think about a larger asynchronous computation, we've avoided littering our code with ugly conditionals, and are able to isolate concerns like login and purchasing content to a single place. Testing each of these objects individually also becomes much easier than having to deal with the four cases in one place.

These kinds of situations are everywhere if you look for them. You just have to remember that asynchronicity isn't just for concurrency, and promises aren't just for XHR.

What's next?

If you found this useful, you might also enjoy:

Using JavaScript Promises to Reason About User Interaction

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

In functional programming, we rarely worry about concurrency primitives like threads. Instead, we use abstractions to make our code easier to reason about. In JavaScript, a Promise is the most common abstraction we encounter. It is usually associated with an Ajax request. We often use abstractions to reason about our concurrent code, but those abstractions can apply in other situations, as well. High level user interactions are essentially large asynchronous events, and can be represented as such in code.

Example: A video storefront

Let's take a look at an example. We're working on a video store front. The user can buy videos, and play videos that they own. If they aren't signed in, they need to sign in to buy a video, and we'd like to use modal dialogs for the sign in and purchasing process.

In order to determine if a user already owns the content, we need them to be signed in. It's also possible that upon signing in, they could already own the content they tried to purchase. This can quickly lead to an extremely complex set of conditionals, but we can simplify it by thinking about the problem like we would think about concurrency.

Making promises about user interactions

Any time we're dealing with user interaction, it is inherently asynchronous. In most code, this begins and ends with event handlers on individual DOM elements. This can be better abstracted if we think about the larger interactions a user can take, and represent that as a single asynchronous operation in the code.

A user may or may not be signed in. We could expose that, and let that conditional bubble throughout our code. However, most of the time, if a user isn't signed in, we're just going to prompt them to sign in. That means that rather than having a user who may or may not be signed in, we've always got a user that will be signed in at some point in the future. Let's look at how we might represent this in code:

class UserSession
  constructor: (@Q, @AuthModals, @Session) ->
    @_session = @Session.get()

  signIn: ->
    @_authDeferred = @Q.defer()
    @_resolveOrPrompt()
    @_authDeferred.promise

  completeSignIn: (data) ->
    @Session.save(data).then(@_completeAuth)

  _resolveOrPrompt: ->
    if @_session?.signedIn
      @_completeAuth(@_session)
    else
      @AuthModals.openSignIn()

  _completeAuth: (session) ->
    @_session = session
    @_authDeferred.resolve(session.user)

There's a lot going on here. Q is Kris Kowal's Q library for working with promises. We'll gloss over the implementation of AuthModals, and Session. We'll just assume that AuthModals opens the modal dialog, and something will call completeSignIn when the form is submitted. Session does two things. It asks the server if we're already signed in, and will submit the Ajax request to the server, and return a promise with the session object.

When code calls the signIn method, we're returning a promise that will eventually be resolved with the current user. If they're already signed in, we resolve that promise immediately. Otherwise, we open the modal dialog for the user to sign in, and resolve the promise once the form is submitted.

Good patterns are made to be built upon

This is incredibly powerful. We've completely removed the need for code to worry about whether or not the user is signed in for almost all cases. We can build upon this further by applying the same concept to purchases.

class UserPurchases
  constructor: (@Q, @UserSession, @PurchaseModals) ->

  purchase: (@video) =>
    @UserSession.signIn().then(@_promisePurchase(video))

  completePurchase: (license)
    @user.addLicense(@video, license)
    @_purchaseDeferred.resolve(license)

  _promisePurchase: (video) => (@user) =>
    @_purchaseDeferred = @Q.defer()
    @_resolveOrPrompt(video)
    @_purchaseDeferred.promise

  _resolveOrPrompt: (video) =>
    if @user.owns(video)
      @completePurchase(@user.licenseFor(video))
    else
      @PurchaseModals.openSaleForm(video)

We apply the same pattern here. If a user already owns the video, we resolve the promise with the license immediately. If not, we prompt the user to buy it, and resolve it when the license is given to us later. A user can only buy one thing at a time. If the user closes the modal and tries to buy something else, the old promise is effectively thrown away.

Finally, we can hide all of this away inside of an object responsible for playing the video.

class VideoPlayer
  constructor: (@UserPurchases, @PlayerModal, @Video) ->

  play: (video) =>
    @UserPurchases.purchase(video).then(@_loadAndPlay(video))

  _loadAndPlay: (video) => (license) =>
    @Video.load(video, license).then(@PlayerModal.openPlayer)

With this in place, code that wants to play a video needs only to call VideoPlayer.play. It doesn't need to concern itself with whether or not the user is signed in, or whether they already own the content. We would still need to have a conditional inside of the template to decide whether the button should say 'Buy' or 'Play'. However, if we use the Null Object pattern, and give the template a Guest when the user isn't signed in, this still remains relatively simple.

By thinking about higher level user interactions the same way we would think about a larger asynchronous computation, we've avoided littering our code with ugly conditionals, and are able to isolate concerns like login and purchasing content to a single place. Testing each of these objects individually also becomes much easier than having to deal with the four cases in one place.

These kinds of situations are everywhere if you look for them. You just have to remember that asynchronicity isn't just for concurrency, and promises aren't just for XHR.

What's next?

If you found this useful, you might also enjoy:

How to Use Notification for Best Results

Posted 4 months back at entp hoth blog - Home

When creating a new profile in Tender, the email preferences default to notifying the user of changes in the issues/discussions they have participated in (either by creating or commenting on), but you can curate your notifications for any given category or (if support staff) queue that you’d like.

Just go to Edit Your Profile and scroll mid-page where you’ll find Email Preferences. Select as you see fit and Update Permissions.

image

*Tender Tip: Choose your notifications wisely, young Jedi. If you get click happy, you may find your inbox quickly becomes inundated with things you don’t really want to know about.

Here’s more on Notifications in our Knowledge Base article.

Episode #425 - December 10th, 2013

Posted 4 months back at Ruby5

ActionParameter, Purgatory, Promise.rb, Sharing Rails sessions, JRuby 1.7.9

Listen to this episode on Ruby5

This episode is sponsored by Top Ruby Jobs
If you're looking for a top Ruby job or for top Ruby talent, then you should check out Top Ruby Jobs. Top Ruby Jobs is a website dedicated to the best jobs available in the Ruby community.

ActionParameter v0.0.2
Ezequiel Delpero released a new version of ActionParameter. The gem helps you move all your parameter's logic from controllers into it's own class. This way you'll keep your controllers dry and they'll be easier to test.

Purgatory
Purgatory is a Rails gem that allows you to save changes to an ActiveRecord model so that they can be applied at a later time.

Promise.rb
Promise.rb is a Ruby implementation of the Promises/A+ spec.

Sharing Rails Sessions With Non-Ruby Apps
Matt Aimonetti wrote a blog post describing the problem he ran into with sharing Rails sessions with another application written in Go, and how he managed to get around it.

JRuby 1.7.9
JRuby 1.7.9 was released. This release includes a total of 36 issues fixed since the last release, including some Windows specific bugs and some encoding issues. There's lots of improvements to the Enumerable module as well.

Speeding up Travis CI builds

Posted 4 months back at No Strings Attached

Travis CI added a feature to cache dependencies between builds for their paying customers. In projects where resolving and fetching dependencies is a slow operation–e.g. in any Ruby project–this can shave a significant amount off total build time, resulting in faster feedback from CI for the developer. This post explores a DIY method of providing such a cache to open source projects as well.

This is not a new idea. Michał Czyż famously posted a tip how to Speed up Travis-CI build preparation on Coderwall. Two projects on GitHub, bundle_cache and travis_bundle_cache, implement the pattern of caching the results of bundle install to Amazon S3. However, both projects depend on the aws-sdk library, which in turn depends on Nokogiri and JSON libraries that have native extensions to be compiled. As a result, installing the library that is supposed to speed up your build time is still slow, and this is unnacceptable.

WAD by Manfred Stienstra is another Ruby solution, but it doesn’t depend on aws-sdk and is a standalone script that you can vendor in your project. This is great because it frees you from having to gem install anything.

However, I wanted to go a step further and explore whether we need Ruby at all, or can the whole process be handled by a simple shell script and utilities available on a stock Unix system.

The result is the cached-bundle script whose entire core logic can be seen below. It delegates the Amazon S3 upload logic to a separate s3-put script:

cache_name="${TRAVIS_RUBY_VERSION}-${gemfile_hash}.tgz"
fetch_url="http://${AMAZON_S3_BUCKET}.s3.amazonaws.com/${TRAVIS_REPO_SLUG}/${cache_name}"

if download "$fetch_url" "$cache_name"; then
  tar xzf "$cache_name"
fi

bundle "$@"

if [ ! -f "$cache_name" ]; then
  tar czf "$cache_name" vendor/bundle
  script/s3-put "$cache_name" "${AMAZON_S3_BUCKET}:${TRAVIS_REPO_SLUG}/${cache_name}"
fi

The cache key is constructed from the Ruby version and MD5 sum of Gemfile.lock. If any of these change, it’s considered a cache miss and gem dependencies will be fetched and installed normally.

You can fetch the cached-bundle and s3-put scripts, which combined weigh less than 70 lines of code.

The dependencies of these scripts are:

  • An Amazon S3 bucket in the default region
  • Amazon access credentials via environment variables (see below)
  • Gemfile.lock checked into version control
  • openssl
  • curl

To enable caching of Bundler dependencies, add the scripts to the script/ directory of your project and add this to .travis.yml:

install: script/cached-bundle install --deployment
env:
  global:
  - AMAZON_S3_BUCKET=my-bucket
  - AMAZON_ACCESS_KEY_ID=MYACCESSKEY
  - secure: "..."

…where secure: value is obtained by means of the offical travis CLI tool:

$ travis encrypt AMAZON_SECRET_ACCESS_KEY="..."

That’s it! The caching of gem dependencies this way resulted in a >1 minute speedup per build in a project with a relatively small gem bundle.

s3-put is useful for more than just caching dependencies. ruby-build, for instance, uses Travis CI and this script to keep its Ruby download mirror up to date whenever someone adds a new version of Ruby to the project.

<script> if (document.querySelectorAll) { var i, el, els = document.querySelectorAll('.highlight .l-Scalar-Plain') for (i = 0; i < els.length; i++) { el = els[i] if (el.nextSibling.textContent == ':') el.className = el.nextSibling.className = 'nv' else el.className = 's2' } } </script>

Introducing Metis Bootcamp

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

I am excited to announce that Kaplan Test Prep, the global leader in test prep, and thoughtbot have partnered to launch a new web development bootcamp for individuals interested in becoming professional Ruby on Rails web developers, called Metis.

The inaugural bootcamp will be held in our Boston office, and runs from February 24 to May 16, 2014 — a total of 12 weeks. Applications are open now, and we will accept students on a rolling basis, with the final round of acceptance targeted for January 10th.

We’ve developed the bootcamp curriculum based on our experiences running apprentice.io and Learn (both of which will continue), and informed by Kaplan’s proprietary learning science and assessment insights.

Kaplan brings a global brand and resources we haven’t yet seen in the developer bootcamp space. As an education industry heavyweight, Kaplan represents a significant new presence in what is still a young market.

Other bootcamps expect their students to spend 70-100 hours/week on their programs. We practice sustainable pace in our jobs and will ask the same of our students. We’re big believers in a full night’s sleep, weekends with friends and families, and time to recharge our brains so that each working hour is used effectively. We don’t want our students to work around the clock after they get placed. So, we don’t want a program that promotes that behavior.

Our students will also actively participate in the open-source community. This will give them additional experience, build their resume, and help introduce themselves in the community within which they are building their new career.

Every day will include in-person instruction by thoughtbot developers and hands-on, individual and group project work. Students will build real applications, and will contribute to open-source. They will work hard, and we will work hard to help them succeed.

For more information and to apply to the program, please visit the Metis website.

Introducing Metis Bootcamp

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

I am excited to announce that Kaplan Test Prep, the global leader in test prep, and thoughtbot have partnered to launch a new web development bootcamp for individuals interested in becoming professional Ruby on Rails web developers, called Metis.

The inaugural bootcamp will be held in our Boston office, and runs from February 24 to May 16, 2014 — a total of 12 weeks. Applications are open now, and we will accept students on a rolling basis, with the final round of acceptance targeted for January 10th.

We’ve developed the bootcamp curriculum based on our experiences running apprentice.io and Learn (both of which will continue), and informed by Kaplan’s proprietary learning science and assessment insights.

Kaplan brings a global brand and resources we haven’t yet seen in the developer bootcamp space. As an education industry heavyweight, Kaplan represents a significant new presence in what is still a young market.

Other bootcamps expect their students to spend 70-100 hours/week on their programs. We practice sustainable pace in our jobs and will ask the same of our students. We’re big believers in a full night’s sleep, weekends with friends and families, and time to recharge our brains so that each working hour is used effectively. We don’t want our students to work around the clock after they get placed. So, we don’t want a program that promotes that behavior.

Our students will also actively participate in the open-source community. This will give them additional experience, build their resume, and help introduce themselves in the community within which they are building their new career.

Every day will include in-person instruction by thoughtbot developers and hands-on, individual and group project work. Students will build real applications, and will contribute to open-source. They will work hard, and we will work hard to help them succeed.

For more information and to apply to the program, please visit the Metis website.

Moving Open Source Project Mailing Lists to Stack Overflow

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

For several years, there have been two ways of getting in touch with us for feedback regarding our open source projects:

  • GitHub, for reporting bugs or submitting patches in the form of pull requests.
  • Mailing lists, for general discussion and questions, including ideas for new features.

However, over time, we've grown to feel as though the mailing lists are not pulling their weight.

For one, the mailing lists are very rarely used for actual discussion. They're hardly ever used for feature requests, either. People mostly post to the mailing lists to ask for help with specific issues.

For another, very few people answer questions on the mailing lists. Few people think to subscribe to them until they have a problem themselves, so the number of people available to help out is limited.

As if that weren't enough, we also have to moderate the mailing lists to prevent spam. Because they're open to anybody to sign up and post to, they're a tempting target to spammers.

Lastly, a giant in the field of online help has entered since we started supporting open source projects: Stack Overflow.

Since Stack Overflow became popular, more and more people have been submitting their questions there instead of to the mailing lists. Having GitHub issues, the mailing lists, and Stack Overflow means that we have three places we need to look when helping out. However, instead of seeing Stack Overflow as a problem, we see it as the solution: the community is larger, the infrastructure is better, and the questions and answers are there.

Therefore, we're shutting down all our support mailing lists and pointing users to Stack Overflow instead.

We're still there to help you

Stack Overflow is a great community, and we're hoping to leverage their larger numbers to answer questions faster. However, that doesn't mean we've abandoned you.

Just make sure to tag your question appropriately when posting it, and we'll be notified. For example, if you tag your questions with "factory-girl," you can bet that Josh Clayton or I will come running. We're using Zapier to pipe new questions into our company Campfire chat, as well as individually subscribing to Stack Exchange filters for projects we maintain.

Which tags?

Here's a list of tags for our projects that you might want to subscribe to:

Where do I post this?

With the mailing lists out of the picture and Stack Overflow part of our official plan, here's what you should do if you need help or would like to contribute:

  • If you need help, use Stack Overflow.
  • If you found a bug, use GitHub issues.
  • If you have an idea, use GitHub issues.
  • If you'd like to ask a general question, use GitHub issues.
  • If you want to contribute, submit a pull request.

We're looking forward to your questions on Stack Overflow. We'll see you there!

Moving Open Source Project Mailing Lists to Stack Overflow

Posted 4 months back at GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS - Home

For several years, there have been two ways of getting in touch with us for feedback regarding our open source projects:

  • GitHub, for reporting bugs or submitting patches in the form of pull requests.
  • Mailing lists, for general discussion and questions, including ideas for new features.

However, over time, we've grown to feel as though the mailing lists are not pulling their weight.

For one, the mailing lists are very rarely used for actual discussion. They're hardly ever used for feature requests, either. People mostly post to the mailing lists to ask for help with specific issues.

For another, very few people answer questions on the mailing lists. Few people think to subscribe to them until they have a problem themselves, so the number of people available to help out is limited.

As if that weren't enough, we also have to moderate the mailing lists to prevent spam. Because they're open to anybody to sign up and post to, they're a tempting target to spammers.

Lastly, a giant in the field of online help has entered since we started supporting open source projects: Stack Overflow.

Since Stack Overflow became popular, more and more people have been submitting their questions there instead of to the mailing lists. Having GitHub issues, the mailing lists, and Stack Overflow means that we have three places we need to look when helping out. However, instead of seeing Stack Overflow as a problem, we see it as the solution: the community is larger, the infrastructure is better, and the questions and answers are there.

Therefore, we're shutting down all our support mailing lists and pointing users to Stack Overflow instead.

We're still there to help you

Stack Overflow is a great community, and we're hoping to leverage their larger numbers to answer questions faster. However, that doesn't mean we've abandoned you.

Just make sure to tag your question appropriately when posting it, and we'll be notified. For example, if you tag your questions with "factory-girl," you can bet that Josh Clayton or I will come running. We're using Zapier to pipe new questions into our company Campfire chat, as well as individually subscribing to Stack Exchange filters for projects we maintain.

Which tags?

Here's a list of tags for our projects that you might want to subscribe to:

Where do I post this?

With the mailing lists out of the picture and Stack Overflow part of our official plan, here's what you should do if you need help or would like to contribute:

  • If you need help, use Stack Overflow.
  • If you found a bug, use GitHub issues.
  • If you have an idea, use GitHub issues.
  • If you'd like to ask a general question, use GitHub issues.
  • If you want to contribute, submit a pull request.

We're looking forward to your questions on Stack Overflow. We'll see you there!

Inbound

Posted 4 months back at Mike Clark

Inbound

Don't mistake those cute stocking feet as a sign of weakness.

Happy High-Resolution Holidays!

Posted 4 months back at mir.aculo.us - Home

This holiday season, delight your users with pixel-perfect deliciously retinafied web sites and apps—my book Retinafy your web sites and apps is ON SALE, but just this weekend (December 6 through 8).

offer

Use coupon code highresholidays on checkout for $15 off the regular price! :)

Get it for yourself or a beloved designer or developer!