[
  {
    "path": "das-0010-fast-tests-with-and-without-rails/test",
    "content": "#!/bin/bash\n#\n# From Destroy All Software screencast #10, at:\n# http://destroyallsoftware.com/screencasts/catalog/fast-tests-with-and-without-rails\n#\n# Released under the MIT license: http://opensource.org/licenses/MIT\n#\n# Put this in the script/ directory of your Rails app, then run it with a spec\n# filename. If the spec uses spec_helper, it'll be run inside Bundler.\n# Otherwise, it'll be run directly with whatever `rspec` executable is on the\n# path.\n\nset -e\n\nneed_rails=1\n\nif [ $# -gt 0 ]; then # we have args\n    filename=$1\n    # Remove trailing line numbers from filename, e.g. spec/my_spec.rb:33\n    grep_filename=`echo $1 | sed 's/:.*$//g'`\n\n    # if we can't match \"spec_helper\" in our file we have a stand-alone spec\n    grep -r '\\bspec_helper\\b' $grep_filename > /dev/null || need_rails=''\nelse # we have no args\n    filename='spec'\nfi\n\ncommand='rspec'\n\nif [ $need_rails ]; then\n    command=\"ruby -S bundle exec $command\"\nfi\n\nRAILS_ENV=test $command $filename\n\n"
  },
  {
    "path": "das-0026-controller-refactoring-demo-part-2/account_controller.rb",
    "content": "#-- copyright\n# ChiliProject is a project management system.\n#\n# Copyright (C) 2010-2011 the ChiliProject Team\n#\n# This program is free software; you can redistribute it and/or\n# modify it under the terms of the GNU General Public License\n# as published by the Free Software Foundation; either version 2\n# of the License, or (at your option) any later version.\n#\n# See doc/COPYRIGHT.rdoc for more details.\n#++\n\nclass AccountController < ApplicationController\n  include CustomFieldsHelper\n\n  # prevents login action to be filtered by check_if_login_required application scope filter\n  skip_before_filter :check_if_login_required\n\n  # Login request and validation\n  def login\n    if request.get?\n      logout_user\n    else\n      authenticate_user\n    end\n  end\n\n  # Log out current user and redirect to welcome page\n  def logout\n    logout_user\n    redirect_to home_url\n  end\n\n  # Enable user to choose a new password\n  def lost_password\n    redirect_to(home_url) && return unless Setting.lost_password?\n    if params[:token]\n      handle_existing_token\n    else\n      generate_token\n    end\n  end\n\n  def handle_existing_token\n    @token = Token.find_by_action_and_value(\"recovery\", params[:token])\n    redirect_to(home_url) && return unless have_valid_token?\n    @user = @token.user\n    if request.post?\n      reset_password\n    else\n      render :template => \"account/password_recovery\"\n    end\n  end\n\n  def have_valid_token?\n    @token and !@token.expired?\n  end\n\n  def reset_password\n    @user.password = params[:new_password]\n    @user.password_confirmation = params[:new_password_confirmation]\n    if @user.save\n      @token.destroy\n      flash[:notice] = l(:notice_account_password_updated)\n      redirect_to :action => 'login'\n    else\n      render :template => \"account/password_recovery\"\n    end\n  end\n\n  def generate_token\n    return unless request.post?\n\n    user = User.find_by_mail(params[:mail])\n\n    if ensure_user_exists(user) && ensure_no_external_auth(user)\n      create_new_token(user)\n    end\n  end\n\n  def ensure_user_exists(user)\n    if user\n      true\n    else\n      flash.now[:error] = l(:notice_account_unknown_email)\n      false\n    end\n  end\n\n  def ensure_no_external_auth(user)\n    if user.auth_source_id\n      flash.now[:error] = l(:notice_can_t_change_password)\n      false\n    else\n      true\n    end\n  end\n\n  def create_new_token(user)\n    token = Token.new(:user => user, :action => \"recovery\")\n    if token.save\n      Mailer.deliver_lost_password(token)\n      flash[:notice] = l(:notice_account_lost_email_sent)\n      redirect_to :action => 'login', :back_url => home_url\n      return\n    end\n  end\n\n  # User self-registration\n  def register\n    redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]\n    if request.get?\n      session[:auth_source_registration] = nil\n      @user = User.new(:language => Setting.default_language)\n    else\n      @user = User.new(params[:user])\n      @user.admin = false\n      @user.register\n      if session[:auth_source_registration]\n        @user.activate\n        @user.login = session[:auth_source_registration][:login]\n        @user.auth_source_id = session[:auth_source_registration][:auth_source_id]\n        if @user.save\n          session[:auth_source_registration] = nil\n          self.logged_user = @user\n          flash[:notice] = l(:notice_account_activated)\n          redirect_to :controller => 'my', :action => 'account'\n        end\n      else\n        @user.login = params[:user][:login]\n        @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]\n\n        case Setting.self_registration\n        when '1'\n          register_by_email_activation(@user)\n        when '3'\n          register_automatically(@user)\n        else\n          register_manually_by_administrator(@user)\n        end\n      end\n    end\n  end\n\n  # Token based account activation\n  def activate\n    redirect_to(home_url) && return unless Setting.self_registration? && params[:token]\n    token = Token.find_by_action_and_value('register', params[:token])\n    redirect_to(home_url) && return unless token and !token.expired?\n    user = token.user\n    redirect_to(home_url) && return unless user.registered?\n    user.activate\n    if user.save\n      token.destroy\n      flash[:notice] = l(:notice_account_activated)\n    end\n    redirect_to :action => 'login'\n  end\n\n  private\n\n  def logout_user\n    if User.current.logged?\n      cookies.delete Redmine::Configuration['autologin_cookie_name']\n      Token.delete_all([\"user_id = ? AND action = ?\", User.current.id, 'autologin'])\n      self.logged_user = nil\n    end\n  end\n\n  def authenticate_user\n    if Setting.openid? && using_open_id?\n      open_id_authenticate(params[:openid_url])\n    else\n      password_authentication\n    end\n  end\n\n  def password_authentication\n    user = User.try_to_login(params[:username], params[:password])\n\n    if user.nil?\n      invalid_credentials\n    elsif user.new_record?\n      onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id })\n    else\n      # Valid user\n      successful_authentication(user)\n    end\n  end\n\n\n  def open_id_authenticate(openid_url)\n    authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|\n      if result.successful?\n        user = User.find_or_initialize_by_identity_url(identity_url)\n        if user.new_record?\n          # Self-registration off\n          redirect_to(home_url) && return unless Setting.self_registration?\n\n          # Create on the fly\n          user.login = registration['nickname'] unless registration['nickname'].nil?\n          user.mail = registration['email'] unless registration['email'].nil?\n          user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?\n          user.random_password\n          user.register\n\n          case Setting.self_registration\n          when '1'\n            register_by_email_activation(user) do\n              onthefly_creation_failed(user)\n            end\n          when '3'\n            register_automatically(user) do\n              onthefly_creation_failed(user)\n            end\n          else\n            register_manually_by_administrator(user) do\n              onthefly_creation_failed(user)\n            end\n          end\n        else\n          # Existing record\n          if user.active?\n            successful_authentication(user)\n          else\n            account_pending\n          end\n        end\n      end\n    end\n  end\n\n  def successful_authentication(user)\n    # Valid user\n    self.logged_user = user\n    # generate a key and set cookie if autologin\n    if params[:autologin] && Setting.autologin?\n      set_autologin_cookie(user)\n    end\n    call_hook(:controller_account_success_authentication_after, {:user => user })\n    redirect_back_or_default :controller => 'my', :action => 'page'\n  end\n\n  def set_autologin_cookie(user)\n    token = Token.create(:user => user, :action => 'autologin')\n    cookie_options = {\n      :value => token.value,\n      :expires => 1.year.from_now,\n      :path => Redmine::Configuration['autologin_cookie_path'],\n      :secure => Redmine::Configuration['autologin_cookie_secure'],\n      :httponly => true\n    }\n    cookies[Redmine::Configuration['autologin_cookie_name']] = cookie_options\n  end\n\n  # Onthefly creation failed, display the registration form to fill/fix attributes\n  def onthefly_creation_failed(user, auth_source_options = { })\n    @user = user\n    session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?\n    render :action => 'register'\n  end\n\n  def invalid_credentials\n    logger.warn \"Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}\"\n    flash.now[:error] = l(:notice_account_invalid_creditentials)\n  end\n\n  # Register a user for email activation.\n  #\n  # Pass a block for behavior when a user fails to save\n  def register_by_email_activation(user, &block)\n    token = Token.new(:user => user, :action => \"register\")\n    if user.save and token.save\n      Mailer.deliver_register(token)\n      flash[:notice] = l(:notice_account_register_done)\n      redirect_to :action => 'login'\n    else\n      yield if block_given?\n    end\n  end\n\n  # Automatically register a user\n  #\n  # Pass a block for behavior when a user fails to save\n  def register_automatically(user, &block)\n    # Automatic activation\n    user.activate\n    user.last_login_on = Time.now\n    if user.save\n      self.logged_user = user\n      flash[:notice] = l(:notice_account_activated)\n      redirect_to :controller => 'my', :action => 'account'\n    else\n      yield if block_given?\n    end\n  end\n\n  # Manual activation by the administrator\n  #\n  # Pass a block for behavior when a user fails to save\n  def register_manually_by_administrator(user, &block)\n    if user.save\n      # Sends an email to the administrators\n      Mailer.deliver_account_activation_request(user)\n      account_pending\n    else\n      yield if block_given?\n    end\n  end\n\n  def account_pending\n    flash[:notice] = l(:notice_account_pending)\n    redirect_to :action => 'login'\n  end\nend\n"
  },
  {
    "path": "das-0030-some-vim-tips/grb256.vim",
    "content": "\" Based on\nruntime colors/ir_black.vim\n\nlet g:colors_name = \"grb256\"\n\nhi pythonSpaceError ctermbg=red guibg=red\n\nhi Comment ctermfg=darkgray\n\nhi StatusLine ctermbg=darkgrey ctermfg=white\nhi StatusLineNC ctermbg=black ctermfg=lightgrey\nhi VertSplit ctermbg=black ctermfg=lightgrey\nhi LineNr ctermfg=darkgray\nhi CursorLine     guifg=NONE        guibg=#121212     gui=NONE      ctermfg=NONE        ctermbg=234\nhi Function         guifg=#FFD2A7     guibg=NONE        gui=NONE      ctermfg=yellow       ctermbg=NONE        cterm=NONE\nhi Visual           guifg=NONE        guibg=#262D51     gui=NONE      ctermfg=NONE        ctermbg=236    cterm=NONE\n\nhi Error            guifg=NONE        guibg=NONE        gui=undercurl ctermfg=16       ctermbg=red         cterm=NONE     guisp=#FF6C60 \" undercurl color\nhi ErrorMsg         guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=16       ctermbg=red         cterm=NONE\nhi WarningMsg       guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=16       ctermbg=red         cterm=NONE\nhi SpellBad       guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=16       ctermbg=160         cterm=NONE\n\n\" ir_black doesn't highlight operators for some reason\nhi Operator        guifg=#6699CC     guibg=NONE        gui=NONE      ctermfg=lightblue   ctermbg=NONE        cterm=NONE\n\nhighlight DiffAdd term=reverse cterm=bold ctermbg=lightgreen ctermfg=16\nhighlight DiffChange term=reverse cterm=bold ctermbg=lightblue ctermfg=16\nhighlight DiffText term=reverse cterm=bold ctermbg=lightgray ctermfg=16\nhighlight DiffDelete term=reverse cterm=bold ctermbg=lightred ctermfg=16\n\nhighlight PmenuSel ctermfg=16 ctermbg=156\n\n"
  },
  {
    "path": "das-0030-some-vim-tips/ir_black.vim",
    "content": "\" ir_black color scheme\n\" More at: http://blog.infinitered.com/entries/show/8\n\n\n\" ********************************************************************************\n\" Standard colors used in all ir_black themes:\n\" Note, x:x:x are RGB values\n\"\n\"  normal: #f6f3e8\n\" \n\"  string: #A8FF60  168:255:96                   \n\"    string inner (punc, code, etc): #00A0A0  0:160:160\n\"  number: #FF73FD  255:115:253                 \n\"  comments: #7C7C7C  124:124:124\n\"  keywords: #96CBFE  150:203:254             \n\"  operators: white\n\"  class: #FFFFB6  255:255:182\n\"  method declaration name: #FFD2A7  255:210:167\n\"  regular expression: #E9C062  233:192:98\n\"    regexp alternate: #FF8000  255:128:0\n\"    regexp alternate 2: #B18A3D  177:138:61\n\"  variable: #C6C5FE  198:197:254\n\"  \n\" Misc colors:\n\"  red color (used for whatever): #FF6C60   255:108:96 \n\"     light red: #FFB6B0   255:182:176\n\"\n\"  brown: #E18964  good for special\n\"\n\"  lightpurpleish: #FFCCFF\n\" \n\" Interface colors:\n\"  background color: black\n\"  cursor (where underscore is used): #FFA560  255:165:96\n\"  cursor (where block is used): white\n\"  visual selection: #1D1E2C  \n\"  current line: #151515  21:21:21\n\"  search selection: #07281C  7:40:28\n\"  line number: #3D3D3D  61:61:61\n\n\n\" ********************************************************************************\n\" The following are the preferred 16 colors for your terminal\n\"           Colors      Bright Colors\n\" Black     #4E4E4E     #7C7C7C\n\" Red       #FF6C60     #FFB6B0\n\" Green     #A8FF60     #CEFFAB\n\" Yellow    #FFFFB6     #FFFFCB\n\" Blue      #96CBFE     #FFFFCB\n\" Magenta   #FF73FD     #FF9CFE\n\" Cyan      #C6C5FE     #DFDFFE\n\" White     #EEEEEE     #FFFFFF\n\n\n\" ********************************************************************************\nset background=dark\nhi clear\n\nif exists(\"syntax_on\")\n  syntax reset\nendif\n\nlet colors_name = \"ir_black\"\n\n\n\"hi Example         guifg=NONE        guibg=NONE        gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\n\n\" General colors\nhi Normal           guifg=#f6f3e8     guibg=black       gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\nhi NonText          guifg=#070707     guibg=black       gui=NONE      ctermfg=black       ctermbg=NONE        cterm=NONE\n\nhi Cursor           guifg=black       guibg=white       gui=NONE      ctermfg=black       ctermbg=white       cterm=reverse\nhi LineNr           guifg=#3D3D3D     guibg=black       gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE\n\nhi VertSplit        guifg=#202020     guibg=#202020     gui=NONE      ctermfg=darkgray    ctermbg=darkgray    cterm=NONE\nhi StatusLine       guifg=#CCCCCC     guibg=#202020     gui=italic    ctermfg=white       ctermbg=darkgray    cterm=NONE\nhi StatusLineNC     guifg=black       guibg=#202020     gui=NONE      ctermfg=blue        ctermbg=darkgray    cterm=NONE  \n\nhi Folded           guifg=#a0a8b0     guibg=#384048     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\nhi Title            guifg=#f6f3e8     guibg=NONE        gui=bold      ctermfg=NONE        ctermbg=NONE        cterm=NONE\nhi Visual           guifg=NONE        guibg=#262D51     gui=NONE      ctermfg=NONE        ctermbg=darkgray    cterm=NONE\n\nhi SpecialKey       guifg=#808080     guibg=#343434     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\n\nhi WildMenu         guifg=green       guibg=yellow      gui=NONE      ctermfg=black       ctermbg=yellow      cterm=NONE\nhi PmenuSbar        guifg=black       guibg=white       gui=NONE      ctermfg=black       ctermbg=white       cterm=NONE\n\"hi Ignore           guifg=gray        guibg=black       gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\n\nhi Error            guifg=NONE        guibg=NONE        gui=undercurl ctermfg=white       ctermbg=red         cterm=NONE     guisp=#FF6C60 \" undercurl color\nhi ErrorMsg         guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=white       ctermbg=red         cterm=NONE\nhi WarningMsg       guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=white       ctermbg=red         cterm=NONE\n\n\" Message displayed in lower left, such as --INSERT--\nhi ModeMsg          guifg=black       guibg=#C6C5FE     gui=BOLD      ctermfg=black       ctermbg=cyan        cterm=BOLD\n\nif version >= 700 \" Vim 7.x specific colors\n  hi CursorLine     guifg=NONE        guibg=#121212     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=BOLD\n  hi CursorColumn   guifg=NONE        guibg=#121212     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=BOLD\n  hi MatchParen     guifg=#f6f3e8     guibg=#857b6f     gui=BOLD      ctermfg=white       ctermbg=darkgray    cterm=NONE\n  hi Pmenu          guifg=#f6f3e8     guibg=#444444     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\n  hi PmenuSel       guifg=#000000     guibg=#cae682     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE\n  hi Search         guifg=NONE        guibg=NONE        gui=underline ctermfg=NONE        ctermbg=NONE        cterm=underline\nendif\n\n\" Syntax highlighting\nhi Comment          guifg=#7C7C7C     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE\nhi String           guifg=#A8FF60     guibg=NONE        gui=NONE      ctermfg=green       ctermbg=NONE        cterm=NONE\nhi Number           guifg=#FF73FD     guibg=NONE        gui=NONE      ctermfg=magenta     ctermbg=NONE        cterm=NONE\n\nhi Keyword          guifg=#96CBFE     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE\nhi PreProc          guifg=#96CBFE     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE\nhi Conditional      guifg=#6699CC     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE  \" if else end\n\nhi Todo             guifg=#8f8f8f     guibg=NONE        gui=NONE      ctermfg=red         ctermbg=NONE        cterm=NONE\nhi Constant         guifg=#99CC99     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE\n\nhi Identifier       guifg=#C6C5FE     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE\nhi Function         guifg=#FFD2A7     guibg=NONE        gui=NONE      ctermfg=brown       ctermbg=NONE        cterm=NONE\nhi Type             guifg=#FFFFB6     guibg=NONE        gui=NONE      ctermfg=yellow      ctermbg=NONE        cterm=NONE\nhi Statement        guifg=#6699CC     guibg=NONE        gui=NONE      ctermfg=lightblue   ctermbg=NONE        cterm=NONE\n\nhi Special          guifg=#E18964     guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE\nhi Delimiter        guifg=#00A0A0     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE\nhi Operator         guifg=white       guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE\n\nhi link Character       Constant\nhi link Boolean         Constant\nhi link Float           Number\nhi link Repeat          Statement\nhi link Label           Statement\nhi link Exception       Statement\nhi link Include         PreProc\nhi link Define          PreProc\nhi link Macro           PreProc\nhi link PreCondit       PreProc\nhi link StorageClass    Type\nhi link Structure       Type\nhi link Typedef         Type\nhi link Tag             Special\nhi link SpecialChar     Special\nhi link SpecialComment  Special\nhi link Debug           Special\n\n\n\" Special for Ruby\nhi rubyRegexp                  guifg=#B18A3D      guibg=NONE      gui=NONE      ctermfg=brown          ctermbg=NONE      cterm=NONE\nhi rubyRegexpDelimiter         guifg=#FF8000      guibg=NONE      gui=NONE      ctermfg=brown          ctermbg=NONE      cterm=NONE\nhi rubyEscape                  guifg=white        guibg=NONE      gui=NONE      ctermfg=cyan           ctermbg=NONE      cterm=NONE\nhi rubyInterpolationDelimiter  guifg=#00A0A0      guibg=NONE      gui=NONE      ctermfg=blue           ctermbg=NONE      cterm=NONE\nhi rubyControl                 guifg=#6699CC      guibg=NONE      gui=NONE      ctermfg=blue           ctermbg=NONE      cterm=NONE  \"and break, etc\n\"hi rubyGlobalVariable          guifg=#FFCCFF      guibg=NONE      gui=NONE      ctermfg=lightblue      ctermbg=NONE      cterm=NONE  \"yield\nhi rubyStringDelimiter         guifg=#336633      guibg=NONE      gui=NONE      ctermfg=lightgreen     ctermbg=NONE      cterm=NONE\n\"rubyInclude\n\"rubySharpBang\n\"rubyAccess\n\"rubyPredefinedVariable\n\"rubyBoolean\n\"rubyClassVariable\n\"rubyBeginEnd\n\"rubyRepeatModifier\n\"hi link rubyArrayDelimiter    Special  \" [ , , ]\n\"rubyCurlyBlock  { , , }\n\nhi link rubyClass             Keyword \nhi link rubyModule            Keyword \nhi link rubyKeyword           Keyword \nhi link rubyOperator          Operator\nhi link rubyIdentifier        Identifier\nhi link rubyInstanceVariable  Identifier\nhi link rubyGlobalVariable    Identifier\nhi link rubyClassVariable     Identifier\nhi link rubyConstant          Type  \n\n\n\" Special for Java\n\" hi link javaClassDecl    Type\nhi link javaScopeDecl         Identifier \nhi link javaCommentTitle      javaDocSeeTag \nhi link javaDocTags           javaDocSeeTag \nhi link javaDocParam          javaDocSeeTag \nhi link javaDocSeeTagParam    javaDocSeeTag \n\nhi javaDocSeeTag              guifg=#CCCCCC     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE\nhi javaDocSeeTag              guifg=#CCCCCC     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE\n\"hi javaClassDecl              guifg=#CCFFCC     guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE\n\n\n\" Special for XML\nhi link xmlTag          Keyword \nhi link xmlTagName      Conditional \nhi link xmlEndTag       Identifier \n\n\n\" Special for HTML\nhi link htmlTag         Keyword \nhi link htmlTagName     Conditional \nhi link htmlEndTag      Identifier \n\n\n\" Special for Javascript\nhi link javaScriptNumber      Number \n\n\n\" Special for Python\n\"hi  link pythonEscape         Keyword      \n\n\n\" Special for CSharp\nhi  link csXmlTag             Keyword      \n\n\n\" Special for PHP\n"
  },
  {
    "path": "das-0070-time-to-first-request/time_to_first_request.sh",
    "content": "#!/bin/bash\n\nmain() {\n    print_runtime > /dev/null\n    print_runtime\n}\n\nprint_runtime() {\n    pid=$(start_server)\n    runtime=$(time_command wait_for_server)\n    baseline=$(time_command wait_for_server)\n    echo $runtime - $baseline | bc\n    kill -9 $pid\n}\n\nstart_server() {\n    cd example\n    rackup >/dev/null 2>&1 &\n    echo $!\n}\n\ntime_command() {\n    local cmd=$*\n    TIMEFORMAT=\"%3R\"\n    (time $cmd) 2>&1\n}\n\nwait_for_server() {\n    while true; do\n        lsof -i :9292 > /dev/null\n        if [[ $? == 0 ]]; then\n            break\n        fi\n    done\n}\n\nmain\n\n"
  },
  {
    "path": "das-0094-computing-by-constructing/everything.py",
    "content": "#!/usr/bin/env python3\n\nNULL = (\n    lambda x: x\n)\n\nTRUE = (\n    lambda t: lambda f: t(NULL)\n)\nFALSE = (\n    lambda t: lambda f: f(NULL)\n)\nIF = (\n    lambda cond: lambda t: lambda f: cond(t)(f)\n)\n\nZERO = (\n    lambda f: lambda x: x\n)\n\nADD1 = (\n    lambda n: lambda f: lambda x: f( n(f)(x) )\n)\nADD = (\n    lambda n: lambda m: n(ADD1)(m)\n)\n\n# Is this number equal to zero?\nIS_ZERO = (\n    lambda n: n(lambda x: FALSE)(TRUE)\n)\n\n# Subtract 1: opposite of S, so SUB1(ONE) == ZERO.\n# Like Y, this is more complex and subtle than most\n# of our functions have been. It's OK to skip over it\n# during this introduction.\nSUB1 = (\n    (lambda n:\n     lambda f:\n     lambda x: n (lambda g: lambda h: h(g(f)))\n                 (lambda u: x)\n                 (lambda u: u))\n)\n\n# Multiplication: \"add m to zero n times\".\nMULT = (\n    lambda n: lambda m: n(lambda x:\n                          ADD(x)(m))(ZERO)\n)\n\nONE = (\n    ADD1(ZERO)\n)\nSIX = (\n    ADD1(ADD1(ADD1(ADD1(ADD1(ADD1(ZERO))))))\n)\n"
  },
  {
    "path": "das-0094-computing-by-constructing/lambda.py",
    "content": "#!/usr/bin/env python3\n\nONE = 1\nIS_ZERO = lambda x: x == 0\nSUB1 = lambda x: x - 1\nMULT = lambda x: lambda y: x * y\nIF = lambda cond: lambda t_func: lambda f_func: t_func(None) if cond else f_func(None)\n\nprint(\n    (\n        lambda myself: (\n            lambda n: (\n                IF(\n                    IS_ZERO(n)\n                )(\n                    lambda _: ONE\n                )(\n                    lambda _: MULT(n)( myself(myself)(SUB1(n)) )\n                )\n            )\n        )\n    )(\n        lambda myself: (\n            lambda n: (\n                IF(\n                    IS_ZERO(n)\n                )(\n                    lambda _: ONE\n                )(\n                    lambda _: MULT(n)( myself(myself)(SUB1(n)) )\n                )\n            )\n        )\n    )\n    (6)\n)\n"
  },
  {
    "path": "das-0095-power-of-lambda-calculus/everything.py",
    "content": "#!/usr/bin/env python3\n\nNULL = (\n    lambda x: x\n)\n\nTRUE = (\n    lambda t: lambda f: t(NULL)\n)\nFALSE = (\n    lambda t: lambda f: f(NULL)\n)\nIF = (\n    lambda cond: lambda t: lambda f: cond(t)(f)\n)\n\nZERO = (\n    lambda f: lambda x: x\n)\n\nADD1 = (\n    lambda n: lambda f: lambda x: f( n(f)(x) )\n)\nADD = (\n    lambda n: lambda m: n(ADD1)(m)\n)\n\n# Is this number equal to zero?\nIS_ZERO = (\n    lambda n: n(lambda x: FALSE)(TRUE)\n)\n\n# Subtract 1: opposite of S, so SUB1(ONE) == ZERO.\n# Like Y, this is more complex and subtle than most\n# of our functions have been. It's OK to skip over it\n# during this introduction.\nSUB1 = (\n    (lambda n:\n     lambda f:\n     lambda x: n (lambda g: lambda h: h(g(f)))\n                 (lambda u: x)\n                 (lambda u: u))\n)\n\n# Multiplication: \"add m to zero n times\".\nMULT = (\n    lambda n: lambda m: n(lambda x:\n                          ADD(x)(m))(ZERO)\n)\n\nONE = (\n    ADD1(ZERO)\n)\nSIX = (\n    ADD1(ADD1(ADD1(ADD1(ADD1(ADD1(ZERO))))))\n)\n"
  },
  {
    "path": "das-0095-power-of-lambda-calculus/lambda.py",
    "content": "#!/usr/bin/env python3\n\nprint(\n    ( lambda myself: ( lambda n: ( ( lambda cond: lambda t: lambda f: cond(t)(f))( ( lambda n: n(lambda x: ( lambda t: lambda f: f(( lambda x: x))))(( lambda t: lambda f: t(( lambda x: x)))))(n))( lambda _:( ( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda f: lambda x: x))) )( lambda _: ( lambda n: lambda m: n(lambda x: ( lambda n: lambda m: n(( lambda n: lambda f: lambda x: f( n(f)(x) )))(m))(x)(m))(( lambda f: lambda x: x)))(n)( myself(myself)(( (lambda n: lambda f: lambda x: n (lambda g: lambda h: h(g(f))) (lambda u: x) (lambda u: u)))(n)) )))))( lambda myself: ( lambda n: ( ( lambda cond: lambda t: lambda f: cond(t)(f))( ( lambda n: n(lambda x: ( lambda t: lambda f: f(( lambda x: x))))(( lambda t: lambda f: t(( lambda x: x)))))(n))( lambda _:( ( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda f: lambda x: x))) )( lambda _: ( lambda n: lambda m: n(lambda x: ( lambda n: lambda m: n(( lambda n: lambda f: lambda x: f( n(f)(x) )))(m))(x)(m))(( lambda f: lambda x: x)))(n)( myself(myself)(( (lambda n: lambda f: lambda x: n (lambda g: lambda h: h(g(f))) (lambda u: x) (lambda u: u)))(n)) )))))\n\n    (( ( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda n: lambda f: lambda x: f( n(f)(x) ))(( lambda f: lambda x: x)))))))))\n\n    (lambda x: x + 1)(0)\n)\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/LICENSE",
    "content": "Copyright 2017 Gary Bernhardt\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/README",
    "content": "These are the files used in Destroy All Software production 0102, \"Data\nCompressor From Scratch\", available at:\n\n    https://www.destroyallsoftware.com/screencasts/catalog/data-compressor-from-scratch\n\nThe files are:\n\n- RUNME.sh, which wasn't used in the screencast, but exists to give you an\n  easy way to run this code and see it work. You can execute it via\n  `./RUNME.sh` (without the backquotes).\n\n- garyzip.rb, which is the file written in the screencast.\n\n- bin.rb, which defines BinPacker and BinUnpacker, responsible for packing\n  sequences of int8s, int16s (unused in the screencast), int32s, and arbitrary\n  bits into bytes.\n\n- rendertree.rb, which provides the `render_tree` function used to show the\n  Huffman coding trees visually.\n\n- tree.dot, a manually-created tree diagram shown at the beginning of the\n  screencast. You can render it via `dot -Tpng tree.dot | ./imgcat` (requires\n  iTerm 2 or a terminal implementing iTerm 2's PNG rendering escape codes).\n\n- imgcat, which is distributed by the authors of iTerm 2 but included here for\n  convenience. It allows us to print PNGs to the terminal. It will only work\n  with iTerm 2 or other terminals that implement iTerm 2's PNG rendering\n  escape codes.\n\nDEPENDENCIES\n\n- Ruby 2.4.1 (or probably older or newer versions, but try 2.4.1 if anything\n  goes wrong).\n- For the tree rendering, iTerm 2 version 3.0.15.\n\nSUPPORT\n\nThis code is entirely unsupported and is for educational purposes only.\n\nLICENSE\n\nAll files here is released under the MIT license, with the exception of\n`imgcat`, whose copyright is held by the authors of iTerm 2. See the file\n`LICENSE`.\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/RUNME.sh",
    "content": "#!/usr/bin/env bash\n\nset -e -o pipefail\n\necho -n \"abbcccc\" | ./garyzip.rb compress | hexdump\n\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/bin.rb",
    "content": "# This file was used in Destroy All Software production 0102, \"Data Compressor\n# From Scratch\". It wasn't shown in the screencast, so it's not designed to be\n# as readable as the main code. All of this code is exactly as used in the\n# screencast except for the addition of comments.\n#\n# To see how this file was used to build a data compressor, visit:\n#\n#   https://www.destroyallsoftware.com/screencasts/catalog/data-compressor-from-scratch\n\nclass BinPacker\n  def initialize\n    @bits = []\n  end\n\n  def int32(int)\n    @bits << [int_to_bits(int, 32), int]\n    self\n  end\n\n  def int16(int)\n    @bits << [int_to_bits(int, 16), int]\n    self\n  end\n\n  def int8(int)\n    @bits << [int_to_bits(int, 8), int]\n    self\n  end\n\n  def int(int, bit_count)\n    @bits << [int_to_bits(int, bit_count), int]\n    self\n  end\n\n  def bits(bits)\n    @bits << [bits, bits]\n    self\n  end\n\n  def pack\n    [flattened_bits.map(&:to_s).join].pack(\"b*\")\n  end\n\n  def flattened_bits\n    @bits.map { |bits, original| bits }.flatten(1)\n  end\n\n  # This function was banged out to be used once during recording, without\n  # regard for whether anyone would ever read it. Oh well!\n  def debug\n    # Some ANSI escape codes that we'll use\n    red = \"\\e[31;49m\"\n    reset = \"\\e[0m\"\n\n    # Pack up the original packed values into DebugValues containing DebugBits.\n    # The DebugValues carry the original data plus the serialized bits; the\n    # DebugBits carry the original bits plus the colorized bits.\n    debug_values = @bits.each_with_index.map do |(bits, original), index|\n      mark_this_group = index % 2 == 0\n      debug_bits = bits.map do |bit|\n        bit_string = mark_this_group ? red + bit.to_s + reset : bit.to_s\n        DebugBit.new(bit_string, bit)\n      end\n      DebugValue.new(debug_bits, original)\n    end\n\n    # Print the original bit groups with their added alternating colors.\n    $stderr.puts \"Original values (#{debug_values.length}):\"\n    bit_strings = debug_values.map(&:debug_bits).map do |group|\n      # Reverse so the bits are printed in decreasing significance order, as\n      # expected by humans.\n      group.map(&:colorized_bit).reverse.join\n    end\n    original_values = debug_values.map(&:original).map(&:to_s)\n    max_original_length = original_values.map(&:length).max\n    bit_strings.zip(original_values).each do |bit_string, original|\n      $stderr.puts \"  \" + original.rjust(max_original_length) + \" | \" + bit_string\n    end\n    $stderr.puts\n\n    # Print the actual bytes in three forms (bits, base-10 number, and ASCII\n    # character), maintaining the alternate coloring so we can see how the\n    # original bit groups map to the output bytes.\n    $stderr.puts \"Bytes (#{pack.length}):\"\n    debug_values.map(&:debug_bits).flatten(1).each_slice(8) do |group|\n      padding = \"0\" * (8 - group.length)\n      colorized_bits = padding + group.map(&:colorized_bit).reverse.join\n      just_the_bits = group.map(&:bit)\n\n      # Convert this group of bits into a character\n      char = [just_the_bits.map(&:to_s).join].pack(\"b*\")\n\n      $stderr.puts \"  \" + colorized_bits + \" | \" + char.ord.to_s.ljust(3) + \" | \" + char.inspect\n    end\n    $stderr.puts\n\n    # Print the final packed string, which will be the same as the ASCII\n    # characters rendered for the packed bits.\n    $stderr.puts \"Packed string (#{pack.length}): #{pack.inspect}\"\n    $stderr.puts\n  end\n\n  DebugValue = Struct.new(:debug_bits, :original)\n  DebugBit = Struct.new(:colorized_bit, :bit)\n\n  private\n\n  def int_to_bits(int, bit_count)\n    bits = []\n    bit_count.times do\n      bits << (int & 0x1)\n      int >>= 1\n    end\n    bits\n  end\nend\n\nclass BinUnpacker\n  def initialize(packed)\n    @bits = packed.unpack(\"b*\").fetch(0).chars.map(&:to_i)\n  end\n\n  def int32\n    int(32)\n  end\n\n  def int16\n    int(16)\n  end\n\n  def int8\n    int(8)\n  end\n\n  def bits(count)\n    bits = @bits[0...count]\n    @bits = @bits[count..-1]\n    bits\n  end\n\n  def peek(n)\n    @bits[0...n]\n  end\n\n  def int(bit_count)\n    bits, @bits = @bits[0...bit_count], @bits[bit_count..-1]\n    int = 0\n    bits.reverse.each do |bit|\n      int <<= 1\n      int |= bit\n    end\n    int\n  end\nend\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/garyzip.rb",
    "content": "#!/usr/bin/env ruby\n\n# This file was used in Destroy All Software production 0102, \"Data Compressor\n# From Scratch\". It wasn't shown in the screencast, so it's not designed to be\n# as readable as the main code. All of this code is exactly as used in the\n# screencast except for:\n#\n# - This comment.\n# - Calls to `render_tree` and `packer.debug` in the `compress` function, so\n#   you can easily run this file and see the debug output from the screencast.\n#\n# To see this work, try the command below. Note that the tree printed by\n# `render_tree` will only work in iTerm 2, or other terminals that support its\n# PNG rendering escape codes. If you're not using iTerm 2, you can comment out\n# the call to `render_tree`.\n#\n#   echo -n \"abbcccc\" | ./garyzip.rb compress | hexdump\n#\n# To see how this file was written, visit:\n#\n#   https://www.destroyallsoftware.com/screencasts/catalog/data-compressor-from-scratch\n\nrequire \"./rendertree\"\nrequire \"./bin\"\n\ndef compress(original)\n  tree = build_tree(original)\n  table = build_table(tree)\n  packer = BinPacker.new\n\n  packer.int32(original.length)\n  pack_table(table, packer)\n\n  original.bytes.each do |byte|\n    bits = look_up_byte(table, byte)\n    packer.bits(bits)\n  end\n\n  render_tree(tree)\n  packer.debug\n\n  packer.pack\nend\n\ndef decompress(compressed)\n  unpacker = BinUnpacker.new(compressed)\n\n  data_length = unpacker.int32\n  table = unpack_table(unpacker)\n\n  data_length.times.map do\n    look_up_bits(table, unpacker)\n  end.map(&:chr).join\nend\n\ndef build_tree(original)\n  bytes = original.bytes\n  unique_bytes = bytes.uniq\n\n  nodes = unique_bytes.map do |byte|\n    Leaf.new(byte, bytes.count(byte))\n  end\n\n  until nodes.length == 1\n    node1 = nodes.delete(nodes.min_by(&:count))\n    node2 = nodes.delete(nodes.min_by(&:count))\n    nodes << Node.new(node1, node2, node1.count + node2.count)\n  end\n\n  nodes.fetch(0)\nend\n\ndef build_table(node, path=[])\n  if node.is_a? Node\n    build_table(node.left, path + [0]) +\n      build_table(node.right, path + [1])\n  else\n    [TableRow.new(node.byte, path)]\n  end\nend\n\ndef look_up_byte(table, byte)\n  table.each do |row|\n    if row.byte == byte\n      return row.bits\n    end\n  end\n\n  awoijf\nend\n\n# a = 00\n# b = 01\n# c = 1\n\ndef look_up_bits(table, unpacker)\n  table.each do |row|\n    if row.bits == unpacker.peek(row.bits.length)\n      unpacker.bits(row.bits.length)\n      return row.byte\n    end\n  end\n\n  awoijf\nend\n\ndef pack_table(table, packer)\n  packer.int8(table.length - 1)\n  table.each do |row|\n    packer.int8(row.byte)\n    packer.int8(row.bits.length)\n    packer.bits(row.bits)\n  end\nend\n\ndef unpack_table(unpacker)\n  table_length = unpacker.int8 + 1\n  table_length.times.map do\n    byte = unpacker.int8\n    bit_count = unpacker.int8\n    bits = unpacker.bits(bit_count)\n    TableRow.new(byte, bits)\n  end\nend\n\nNode = Struct.new(:left, :right, :count)\nLeaf = Struct.new(:byte, :count)\n\nTableRow = Struct.new(:byte, :bits)\n\nif ARGV.fetch(0) == \"compress\"\n  $stdout.write(compress($stdin.read))\nelse\n  $stdout.write(decompress($stdin.read))\nend\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/imgcat",
    "content": "#!/bin/bash\n\n# tmux requires unrecognized OSC sequences to be wrapped with DCS tmux;\n# <sequence> ST, and for all ESCs in <sequence> to be replaced with ESC ESC. It\n# only accepts ESC backslash for ST.\nfunction print_osc() {\n    if [[ $TERM == screen* ]] ; then\n        printf \"\\033Ptmux;\\033\\033]\"\n    else\n        printf \"\\033]\"\n    fi\n}\n\n# More of the tmux workaround described above.\nfunction print_st() {\n    if [[ $TERM == screen* ]] ; then\n        printf \"\\a\\033\\\\\"\n    else\n        printf \"\\a\"\n    fi\n}\n\n# print_image filename inline base64contents\n#   filename: Filename to convey to client\n#   inline: 0 or 1\n#   base64contents: Base64-encoded contents\nfunction print_image() {\n    print_osc\n    printf '1337;File='\n    if [[ -n \"$1\" ]]; then\n      printf 'name='`echo -n \"$1\" | base64`\";\"\n    fi\n    if $(base64 --version 2>&1 | grep GNU > /dev/null)\n    then\n      BASE64ARG=-d\n    else\n      BASE64ARG=-D\n    fi\n    echo -n \"$3\" | base64 $BASE64ARG | wc -c | awk '{printf \"size=%d\",$1}'\n    printf \";inline=$2\"\n    printf \":\"\n    echo -n \"$3\"\n    print_st\n    printf '\\n'\n}\n\nfunction error() {\n    echo \"ERROR: $*\" 1>&2\n}\n\nfunction show_help() {\n    echo \"Usage: imgcat filename ...\" 1>& 2\n    echo \"   or: cat filename | imgcat\" 1>& 2\n}\n\n## Main\n\nif [ -t 0 ]; then\n    has_stdin=f\nelse\n    has_stdin=t\nfi\n\n# Show help if no arguments and no stdin.\nif [ $has_stdin = f -a $# -eq 0 ]; then\n    show_help\n    exit\nfi\n\n# Look for command line flags.\nwhile [ $# -gt 0 ]; do\n    case \"$1\" in\n    -h|--h|--help)\n        show_help\n        exit\n        ;;\n    -*)\n        error \"Unknown option flag: $1\"\n        show_help\n        exit 1\n      ;;\n    *)\n        if [ -r \"$1\" ] ; then\n            print_image \"$1\" 1 \"$(base64 < \"$1\")\"\n        else\n            error \"imgcat: $1: No such file or directory\"\n            exit 2\n        fi\n        ;;\n    esac\n    shift\ndone\n\n# Read and print stdin\nif [ $has_stdin = t ]; then\n    print_image \"\" 1 \"$(cat | base64)\"\nfi\n\nexit 0\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/rendertree.rb",
    "content": "# This file was used in Destroy All Software production 0102, \"Data Compressor\n# From Scratch\". It wasn't shown in the screencast, so it's not designed to be\n# as readable as the main code. All of this code is exactly as used in the\n# screencast except for:\n#\n# - New comments (including this one)\n# - The call to `imgcat` was changed to refer to the `imgcat` script in this\n#   directory Previously, it relied on the `imgcat` in my ~/bin directory,\n#   which is in $PATH.\n#\n# To see how this file was used to build a data compressor, visit:\n#\n#   https://www.destroyallsoftware.com/screencasts/catalog/data-compressor-from-scratch\n\nrequire \"open3\"\n\n# Render a Huffman tree by converting it into Graphviz dot code, then piping\n# the dot into the `dot` command line tool, and finally into the `imgcat`\n# script. This will only work in recent versions of iTerm 2, or any other\n# terminal that supports iTerm 2's PNG rendering escape codes.\ndef render_tree(nodes)\n  nodes = [nodes] unless nodes.is_a? Array\n  dot = [\n    \"digraph {\",\n    \"ordering=out\",\n    \"dpi=120\",\n    nodes.map do |node|\n      tree_to_dot(node)\n    end,\n    \"}\"\n  ].flatten.join(\"\\n\")\n  rendered = Open3.popen3(\"dot -Tpng | ./imgcat\") do |stdin, stdout, stderr, wait_thr|\n    stdin.write(dot)\n    stdin.close\n    stdout.read\n  end\n  $stderr.puts(rendered)\nend\n\ndef tree_to_dot(node)\n  name = \"node_#{node.object_id}\"\n  if node.is_a? Node\n    [\n      %{#{name}[label=\"(n=#{node.count})\"]},\n      %{#{name} -> node_#{node.left.object_id}[label=\"0\"]},\n      %{#{name} -> node_#{node.right.object_id}[label=\"1\"]},\n      tree_to_dot(node.left),\n      tree_to_dot(node.right),\n    ].join(\"\\n\")\n  else\n    [\n      %{node_#{node.object_id}[label=\"#{node.byte.chr.gsub(/\"/,'\\\"')}\\n(n=#{node.count})\"]},\n    ].join(\"\\n\")\n  end\nend\n"
  },
  {
    "path": "das-0102-data-compressor-from-scratch/tree.dot",
    "content": "digraph {\n  dpi=120\n  ordering=out;\n\n  # abbcccc\n  # 1 a\n  # 2 b\n  # 4 c\n\n  a[label=\"a\\n(n=1)\"]\n  b[label=\"b\\n(n=2)\"]\n  c[label=\"c\\n(n=4)\"]\n\n  n1 [label=\"(n=3)\"]\n  n1 -> a [label=0]\n  n1 -> b [label=1]\n\n  n2 [label=\"(n=7)\"]\n  n2 -> n1 [label=0]\n  n2 -> c [label=1]\n}\n"
  }
]