THIS BLOG HAS BEEN ARCHIVED.

PLEASE CLICK HERE TO GO TO THE NEW BLOG.

angr

writings from zac anger, who is not all that angry

More React Notes

April 27, 2016 — Zac Anger

Lifecycle &Co.

  • The three phases of life:
    • Mounting
    • Updating
    • Unmounting
  • Mounting:
    • getInitialState(), before component is mounted.
    • componentWillMount(), immediately before mounting.
    • componentDidMount(), immediately after mounting. DOM-node-dependant initialisation happens here.
  • Updating:
    • componentWillReceiveProps(), invoked when mounted component receives new props from parent. Use this when changing state (this.setState()).
    • shouldComponentUpdate(), invoked when component 'decides' whether or not to push updates to the DOM. Return false from this if we don't want React to update.
    • componentDidUpdate(), invoked immediately after updating.
  • Unmounting:
    • componentWillUnmount(), immediately before component unmounts and is destroyed. Clean-up goes here.
  • While mounted, component.forceUpdate() is also available. This is for any mounted component whose state has changed without this.setState().

The big, big difference between props and state: state is private to components. A parent component (or any other component) can never manipulate the state of another (with setState or whatever).

  • So:
    • parent passes new props to child
    • child handles new props with componentWillReceiveProps
    • calls setState if necessary
    • child handles new state in componentWillUpdate
    • if component is stateful, componentWIllReceiveProps will be enough here

Defaults set in getInitialState will be used for initial rendering.

tags: react, lifecycle, events

so, tweet it?  

Notes from the AngularJS Utah Meetup

April 24, 2016 — Zac Anger

So what with my last computer dying, getting a new phone, trying to find a job, preparing to maybe move, and all the other nonsense going on, I haven't really blogged at all lately. Oh well.

My to-do list is gigantic, like a whole huge directory tree of several gigabytes full of unfinished stuff.

Somewhere in there, there's rewriting my entire website.

I've just switched from GoDaddy's hosting to my own VPS (a droplet), since GoDaddy's cost was going up by about 110%. This means I have a LOT more available to me, now. Not quite as much space, but I can run whatever I need to on my server now, so... I think it's time to finally update everything. I plan on leaving most things as just plain old static sites (because, let's be honest, no one cares how flashy this crap is if it takes a year to render it on an old phone). The blog might change a bit, though. I'm still using that same old script (based originally on BashBlog, heavily modified over the past almost-year). Nothing against that script, it does its job and everything, but a 1300-line shell script to basically turn Markdown into HTML is absurd -- ESPECIALLY since it doesn't include the parser.

Anyway, here are some notes from the NG-JS meetup a couple of weeks ago. They were lost on my old laptop, but the SATA-to-USB thingy came in the mail the other day, so I can finally just do something with these.

read more...

tags: rxjs, immutable, js, utah, meetup, notes

so, tweet it?  

FP in JS

April 03, 2016 — Zac Anger

Notes taken while going through this guy's videos.

You should watch them all. He's great.

'use strict'

//
// HOF
//
// Higher Order Functions
// functions are values
// const something = function > function something
// obvs functions passed into functions
// composition yay
// example: filter (method on array, takes another function as action)
// filter's should return true or false to determine whether item belongs in arr
let something = [{foo : 'bar'}, {quux : 'baz'}, {whatever : 'target'}]
let newArr
for (let i = 0; i < something.length; i++) {
  if (something[i].what === 'target') {
    newArr.push(something[i])
  }
}
// vs
let newArr = something.filter(thing => {
  return thing.what === 'target'
})
// or, using reject
let isTarget = something => thing.what === 'target'
let notTarget = something.reject(isTarget)
// note: there's also find. that returns just the first item that matches.

//
// Map
//
// map's cb returns a transformed object to put into array
let peeps = [
  {name : 'geordyn', relationship : 'bffl'                   }
, {name : 'erin'   , relationship : 'number one bro'         }
, {name : 'ryan'   , relationship : 'nemesis. also, the one.'}
, {name : 'andrew' , relationship : 'special mormon'         }
, {name : 'sarah'  , relationship : 'grandma'                }
]
// non-functional way of doing getting an array of the names
let names = []
for (let i = 0; i < something.length; i++) {
  names.push(something[i].name)
}
// vs
let names = peeps.map(peep => peep.name)
let about = peeps.map(peep => peep.name + ' is my ' + peep.relationship + '.')

//
// Reduce
//
// map, filter, reject, and find are fairly specific list transformations.
// reduce is is more of a swiss-army knife. it can do just about anything.
// reduce wants an object, though. (yes, it's a method on the array prototype.)
let stuffToSum = [
  {amount : 4   }
, {amount : 16  }
, {amount : 1024}
, {amount : 4096}
]
// so...
let total = 0
for (let i = 0; i < stuffToSum.length; i++) {
  total += stuffToSum[i].amount
}
// vs
let total = stuffToSum.reduce((sum, stuff) => sum + stuff.amount, 0)
// this is adding stuffToSum[0].amount to sum (which is initialised at 0)
// and returning that sum, then going again with stuffToSum[1].amount and
// the current sum (4), and so on.
//
// let's say we have some file in TSV format, like below. (i'm using 4 spaces
// to represent a tab here, since tabs = 2 spaces in all my editors, and 2 spaces
// isn't large enough to clearly distinguish fields here, i think.)
// miss lady    an item    7    20
// miss lady    a thing or two    75    2
// some dude    another product    33    9
// some dude    some product    100    1
// we need to transform this into a nice looking object (containing two objects,
// one for each person, each containing an array of objects that are the items bought).
import fs from 'fs'
let contents = fs.readFileSync('./thatFile.tsv', 'utf8')
.trim()                        // remove trailing newline
.split('\n')                   // split into array of strings at newlines
.map(line => line.split('\t')) // \t is a tab character
.reduce((persons, line) => {
  persons[line[0]] = persons[line[0]] || []
  persons[line[0]].push({
    name  : line[1]
  , cost  : line[2]
  , quant : line[3]
  })
  return persons
}, {})
console.log('contents: ', JSON.stringify(contents, null, 2))

//
// Closures
//
function sendReq(){
  let reqId = 'asdf'
  $.ajax({
    url : '/someurl'
  , success(response){
      console.log('request ' + reqId + ' returned')
    }
  })
}
// see, we don't have to pass stuff around here. reqId is going to be 'asdf'
// no matter when jquery's ajax finishes whatever it's doing. nice.
// there's also this classic example:
function makeAdder(x){
  return(y => x + y)
}
let
  add4 = makeAdder(4)
, add8 = makeAdder(8)
console.log(add4(16))
console.log(add8(64))

//
// Currying
//
// so, you've got some arguments. you could have a function that
// takes your bunch of arguments and does stuff with them. or you could
// have a function that takes your first argument and winds up returning
// a function that takes your second argument which returns a function
// that takes your third argument... etc., you get the idea.
let self1 = (name, age, language, location) =>
  `Hi, I'm ${name}, age ${age}. I speak ${language} and live in ${location}.`
console.log(self1('zac', 26, 'english', 'utah, i guess'))
// vs
let self2 = name => age => language => location =>
  `Hi, I'm ${name}, age ${age}. I speak ${language} and live in ${location}.`
console.log(self2('zac')(26)('english')('utah, i guess'))
// why? maybe i don't know everything about myself yet, but my app will find
// out some of this information later. so i call self('zac'), have a birthday,
// call self(27)('english') because it's been another year and i still only
// speak one language, and then i call self('texas??') because i've moved.
// now, finally, i have the return value (the introductory sentence)!
// what about self1? we could always use something from some library to
// transform it, like wu.js's autoCurry, or whatever. for the sake of familiarity,
// try lodash (first npm i -S lodash):
import _ from 'lodash'
let me = _.curry(self1)
console.log(self1('zac'))
// yay! okay, another example.
let
  guitars = [
  {brand : 'ovation'    , type : 'acoustic' }
, {brand : 'silvertone' , type : 'acoustic' }
, {brand : 'esp'        , type : 'electric' }
, {brand : 'teton',     , type : 'acoustic' }
, {brand : 'danburn'    , type : 'electric' }
, {brand : 'homemade'   , type : 'cigar-box'}
]
, isType    = (type, obj) => obj.type === type
, electrics = guitars.filter(x => istype('electric'), x)
console.log(electrics)
// okay, so using the same guitars array:
import _ from 'lodash'
let isTypeCur = _.curry((type, obj) => obj.type === type)
  , acoustics = guitars.filter(isTypeCur('acoustic'))

//
// Recursion
//
// recursion is not at all a difficult idea. won't even
// bother laying it out here, really. a function calls itself
// until it's done calling itself. it's a super useful way to
// program, especially in actual functional languages.
// es6 makes recursion a lot nicer. we can get rid of the
// if statement in the below function in es6 because we won't
// end with a 'RangeError: Maximum call stack size exceeded
// or whatever.
let countDown = num => {
  if (num === 0) {
    return
  }
  console.log(num)
  countDown(num - 1)
}

//
// Promises
//
function loadStuff(url, cb){
  let img = new Image()
  img.onload = () => {
    cb(null, img)
  }
  img.onerror = () => {
    let msg = 'failed loading ' + url
    cb(new Error(msg))
  }
  img.src = url
}
export default loadStuff
// with
import loadStuff from './loadStuff'
let addThing = src => {
  let el = document.createElement('img')
  el.src = src
  document.body.appendChild(el)
}
loadStuff('/thing/to/load.png', (err, img) => {
  if (err) {
    throw err
  }
  addThing(img.src)
  loadStuff('/thing/two.png', (err, newImg) => {
    if (err) {
      throw err
    }
    addThing(img.src)
    // etc
  })
})
// vs
function loadThing(url){
  return new Promise((resolve, reject) => {
    let img = new image()
    img.onload = () => {
      resolve(image)
    }
    img.onerror = () => {
      let msg = 'failed loading ' + url
      reject(new Error(msg))
    }
    img.src = url
  })
}
export default loadThing
//with
import loadThing from './loadThing'
let addThing = src => {
  let el = document.createElement('img')
  el.src = src
  document.body.appendChild(el)
}
Promise.all([
  loadThing('/path/one.png')
, loadThing('/path/two.png')
// , etc
]).then(images => {
  images.forEach(img => addThing(img.src))
}).catch(err => {
  throw err
})

tags: functional-programming, javascript, js, fp, functional, notes, examples, map, filter, reduce, promises, closure, closures, recursion

so, tweet it?  

stuff everybody knows

April 02, 2016 — Zac Anger

Takeaways from Laurie Voss's 'Stuff Everybody Knows Except You'

  • Be good at your tools.
    • If you use Windows, be good at Windows. Also, don't use Windows.
    • Know Bash. Also know Sed and Awk. But really, really, really know Bash.
    • Be good at your editor. It doesn't matter which one. Notepad is not an editor.
  • Don't fuck with users.
    • Paralaralaralax is the devil.
    • A button is an action. A link goes somewhere.
  • Auth is not simple.
    • Identification: Hi, I'm this guy! (To people.)
    • Authentication: Hi, I can prove I'm this guy! (To a computer.)
    • Authorization: Hi, I am allowed to do these things!
    • Salt and hash your passwords. Always. (Bcrypt.)
  • Security is not a joke.
    • Leave it to people who know what they're doing.
    • Or devote your career to doing this.
  • Don't build rich web apps for everything.
    • If your site is all about content, build a plain old static site.
    • This seems obvious, but apparently it's not.
  • Load content first. Fuck your fancy fonts, fuck your gifs, just load stuff.
  • Speed over everything.
  • Write and maintain tests. Test. Write testable code.
  • Read the error logs. And write good error messages.
  • Write obvious code.
  • Get good at git.
  • Automate all the things (with deployment, but also testing, CI, and everything else).
    • But don't go overboard (yak shaving).
  • Break things up (use common patterns/modularise everything/decouple shit).
    • Also, keep in mind that everything you write is/has an API. Make it consistent.

tags: talks, notes

so, tweet it?  

nginx reverse proxy

March 28, 2016 — Zac Anger

reverse proxy: multiple local servers being served out to a client that only really sees ngnix.

cd /etc/ngnix

vi nginx.conf

at the bottom-ish there'll be an http {} block, with a comment about virtual host configs

cd /etc/nginx/site-available

cp default whatever

replace (non-comment) content with something like the following:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    proxy_pass http://127.0.0.1:3000;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header X-Forwarded-Proto $scheme;
  }
}

and then do an ln -s /etc/nginx/sites-available/whatever /etc/nginx/sites-enabled/

after service nginx restart shiz should be working.

redirects www.url.com to url.com:

server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

a full working example:

server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://127.0.0.1:8081;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

server {
    listen 80;
    server_name qwerty.example.com;
    location / {
        proxy_pass http://127.0.0.1:8082;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    listen 80;
    server_name asdf.example.com;
    location / {
        proxy_pass http://127.0.0.1:8083;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

server {
    listen 80;
    server_name ghjkl.example.com;
    location / {
        proxy_pass http://127.0.0.1:8084;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

tags: nginx, deployment, reverse-proxy

so, tweet it?  

scott

March 03, 2016 — Zac Anger

lol

scott

tags: people, gif

so, tweet it?  

kik's api

February 17, 2016 — Zac Anger

Kik's API is kinda basically shit. Their API docs are also basically shit. Probably becaues their API is basically shit. Kik's a lot more focused on people building Kik-friendly websites than they are on people integrating with their service.

Kik's js: http://cdn.kik.com/kik/2.0.5/kik.js

Kik requires that we make terms of use and privacy policy available, like so:

User-related stuff:

kik.getUser(function(user){
  if(!user){
    alert('nope!')
  } else {
    console.log(user.username)
    // user.fullName
    // user.firstName
    // user.lastName
    // user.pic
    // user.thumbnail
    // ^^ these are all strings.
  }
})
if(kik.hasPermission(){
  // cool! user has given us permission to work with their account
})
// to work with a user anonymously (doesn't expose identifying
// information, also doesn't require permission from user):
kik.getAnonymousUser(function(token){
  console.log(token) // str
})
// kik's auth only works over https.
kik.sign('foo bar data etc.', function(signedData, username, host){
  if(!signedData){
    // either failed, or user denied
  } else {
    console.log(signedData, username, host)
    // all strings. need to be passed to verification service.
    // we'd pass stuff to a back-end for that.
  }
})
// to make this work with the anonymous thing, it's almost exactly the same:
kik.anonymousSign('data', function(signedData, anonToken, host)){
  // same stuff, except we would get anonToken instead of username
}

Messaging:

kik.send({
  title     : 'message title'
, text      : 'message content'
, pic       : 'http://if.we/want/to/send/a/photo'
, big       : true // optional, for large images, etc.
, noForward : true // optional, for restricting receiving user from forwarding the message
, data      : {something: 'stuff'} // optional, arbitrary JSON, max 7.5kb
})
kik.send('otherUser', {
  title : 'etc'
// all the same stuff here.
})
if(kik.message){
  // do things; now we know the user has opened the message.
}

Kik has some analytics stuff for messaging, but I really just don't care about that.

Social things:

kik.showProfile('zacanger') // shows profile!
kik.pickUsers(function(users){
  if(!users) // cancelled, maybe.
}  else {
  users.forEach(function(user){
    alert('user.username') // etc., all the same stuff from the user info.
  })
})
kik.pickUsers({minResults:1,maxResults:4}, function(users){
// etc.
})
kik.pickUsers({preselected:[{username:'zacanger'}]}, function(users){
// same objects as from call to pickUsers
})
kik.pickUsers({filtered:['badPerson','iHateThisGuy']}, function(users){
// users we don't want to show. doesn't work with preselected.
})
kik.pickUsers({filterSelf:false}, function(users){
// allow user to see their own profile in the user picker
})

Misc:

// events:
function someEventHandler(){
// do stuff...?
}
kik.on('event', eventHandler) // bind to event
kik.off() // unbind
kik.once() // only bind once, ignore after that.
// event can be, for example, 'message'.
kik.trigger('message', {title:'title'}) // objects are passed to all even listeners.

// platform & browser detection:
var platform = kik.utils.platform.os
console.log(os.name, os.version) // string, int
var browser = kik.utils.platform.browser
console.log(browser.name, browser.version)
var eng = kik.utils.platform.engine // rendering engine
console.log(engine.name, engine.version)
// for unsupported browsers (iOS <= 5, android <= 2.3), use a meta tag:
// <meta name="kik-unsupported" content="android-2.3"> (or whatever).

tags: kik, api

so, tweet it?  

mac disk usage shiz

February 17, 2016 — Zac Anger

i don't have a mac. so. whatever. but here's how to find out where all your disk space went, if you do have a mac.

tags: mac, apple

so, tweet it?  

getting up and running with nw.js really super quickly

February 14, 2016 — Zac Anger

There are basically two main ways to build a desktop app in JS: Electron (formerly called 'Atom-Shell') and NW.js (formerly called 'Node-Webkit'). Electron's really swell, probably offers more options overall, and has a cleaner way of keeping Node and client-side code separate. NW.js is a heckuva lot easier, overall, though.

This is what I've been using to build and run Pharaoh: https://github.com/nwjs/nw-builder

There's also a nifty sort of version manager for NW.js here: https://www.npmjs.com/package/nwjs , which worked a little more smoothly out of the box, but gave me issues when trying to get Node integration to work.

To get up and running with NW.js using nw-builder, you basically just need to put the relevant information in your package.json. The relevant info on that is all here: https://github.com/nwjs/nw.js/wiki/Manifest-format but mostly it's just something like

"window": {
  "height": 800,
  "width": 1200,
  "title": "my app"
}

et cetera. The main field (which usually has, like, server/index.js or whatever in it), needs to be what NW.js is pointed to. That can be an HTML file ("main": "./client/public/index.html" or whatever) or a URL (if you're serving the app, especially locally--this is how Atom, Brackets, LightTable, etc. do things, except using Electron).

That's basically it, I think. There are a lot of great Yeoman generators and stuff to scaffold out NW.js apps, but they're mostly overkill.

Using nw-builder makes things pretty simple. npm i -g nw-builder, then just run nwbuild -h to see the options. (I use nwbuild -r . in the project root/wherever the relevant package.json is, to run the app).

Their docs are really good, and there's a crapload of options and neat stuff you can do: https://github.com/nwjs/nw.js/wiki

tags: nwjs, desktop, node, electron, app

so, tweet it?  

basic auth

February 12, 2016 — Zac Anger
  • Basic HTTP:
    • restricted based on system user/pass
    • http://zacanger:encryptedpassword@zacanger.com
  • Form-based:
    • restricted based on cookies
    • success = stored cookie on client
    • POST whatever.com/login (headers)
  • Token-based:
    • requests with auth token
    • https://foo.bar/whatever?auth_token=asdfghjkl123456789
  • OAuth
    • rate limited, expired, revoked server-side
  • Passport
    • this is, clearly, the preferred method, both with devmtn and with express in general, i feel

read more...

tags: auth, node, js, passport

so, tweet it?  

poststagsrss
zac anger?