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