Thursday, October 13, 2011

Internationalize a seed file


A seed file is a file that contains data your app needs to function. It's a better way to manage that data than keeping "canonical" copies of databases hanging around. I took internationalizing this seed file as an opportunity to flex some command-line muscle. Starting with this file...


Foo.fast_bootstrap(
  {id:2,  key: "General",                     position: 1},
  {id:15, key: "Endocrine",                   position: 2},
  {id:12, key: "Skin",                        position: 3},
  {id:14, key: "Musculoskeletal",             position: 4},
  {id:6,  key: "Neurological",                position: 5},
  {id:16, key: "Psychological",               position: 6},
  {id:7,  key: "Eyes",                        position: 7},
  {id:8,  key: "Ears",                        position: 8},
  {id:9,  key: "Mouth, Nose & Throat",        position: 9},
  {id:13, key: "Breast/Chest",                position: 10},
  {id:3,  key: "Lungs & Breathing",           position: 11},
  {id:4,  key: "Heart, Blood & Circulation",  position: 12},
  {id:5,  key: "Digestive/Gastrointestinal",  position: 13},
  {id:17, key: "Urinary",                     position: 14},
  {id:18, key: "Men",                         position: 16},
  {id:19, key: "Women",                       position: 17},
  'id'
)

...and using this command...

cat db/seeds/foo.rb | cut -d '"' -f 2 | head -17 | tail -16 | ruby -e "s = *ARGF; s.each { |s| puts s.downcase.chomp.gsub('&', '').gsub('/', ' ').gsub(',', '').squeeze(' ').gsub(' ', '_') + ': ' + '\"' + s.chomp + '\"'}"

...we end up with something that's ready to paste into a YML file!


general: "General"
endocrine: "Endocrine"
skin: "Skin"
musculoskeletal: "Musculoskeletal"
neurological: "Neurological"
psychological: "Psychological"
eyes: "Eyes"
ears: "Ears"
mouth_nose_throat: "Mouth, Nose & Throat"
breast_chest: "Breast/Chest"
lungs_breathing: "Lungs & Breathing"
heart_blood_circulation: "Heart, Blood & Circulation"
digestive_gastrointestinal: "Digestive/Gastrointestinal"
urinary: "Urinary"
men: "Men"
women: "Women"

Here's the breakdown of what the command does:

cut -d '"' -f 2

Cut the file at each ", then give me the things in the second section post-cutting.

head -17 | tail -16

Give me the first 17 lines, then the last 16 lines. This trims the last two lines and the first line from our text. TODO: figure out how to do this dynamically instead of counting lines.

ruby -e...

This allows us to drop into Ruby to do the rest of our string manipulation. The -e signals Ruby that we're going to pass it a string, not a file.

s = *ARGF;

*ARGF is the data getting piped in. In this case, Ruby interprets it as an array of strings.

s.each { |s| puts s.downcase.chomp.gsub('&', '').gsub('/', ' ').gsub(',', '').squeeze(' ').gsub(' ', '_') + ': ' + '\"' + s.chomp + '\"'}

Now we manipulate the strings and output them so we can copy-paste from the terminal. TODO: Extract everything before the first + into a "yml_keyify" method. We replace the upper-case letters, remove the trailing \n character, get rid of any ampersands, slashes, and commas, reduce any instances of multiple spaces into one space, and turn our spaces into underscores. Whew! After that, we just add the colon and output the original string (surrounded by ") as the value of our YML key-value pair.


Now to replace the bad keys within the seed file, we use:


cat db/seeds/foo.rb | ruby -e "s = *ARGF; s.each { |s| in_quotes = s.match(/\"{1}(.*)\"{1}/); next unless in_quotes; bad_key = in_quotes[1]; puts s.gsub(bad_key, bad_key.downcase.chomp.gsub('&', '').gsub('/', ' ').gsub(',', '').squeeze(' ').gsub(' ', '_')) }"


a little copy-paste and whitespace fixing and our new seed file looks like:



Foo.fast_bootstrap(
  {id:2,  key: "general",                     position: 1},
  {id:15, key: "endocrine",                   position: 2},
  {id:12, key: "skin",                        position: 3},
  {id:14, key: "musculoskeletal",             position: 4},
  {id:6,  key: "neurological",                position: 5},
  {id:16, key: "psychological",               position: 6},
  {id:7,  key: "eyes",                        position: 7},
  {id:8,  key: "ears",                        position: 8},
  {id:9,  key: "mouth_nose_throat",           position: 9},
  {id:13, key: "breast_chest",                position: 10},
  {id:3,  key: "lungs_breathing",             position: 11},
  {id:4,  key: "heart_blood_circulation",     position: 12},
  {id:5,  key: "digestive_gastrointestinal",  position: 13},
  {id:17, key: "urinary",                     position: 14},
  {id:18, key: "men",                         position: 16},
  {id:19, key: "women",                       position: 17},
  'id'
)


Much better, and ready for localization!


For a second, more complex version of the same program, I wrote this script: https://gist.github.com/1285825

Monday, October 3, 2011

Interview question: Set to subsets

Given a set as input, return the set of all possible subsets. e.g. {a, b} => { {} , {a}, {b}, {a, b} }

Thursday, September 29, 2011

SOLID Object-Oriented Design lecture notes

http://confreaks.net/videos/240-goruco2009-solid-object-oriented-design

Applications can be:
  • rigid: changes cascade through the system
  • fragile: changes cause unpredictable side effects
  • immobile: code reuse is hard/impossible
  • viscous: behaving badly is the most attractive option

Apps start not-sucking, end up sucking because of changes. To resist negative side effects of change, good code should be:
  • loosely coupled
  • highly cohesive
  • easily composable
  • context-independent
SOLID:
  • single-responsibility: only does one thing
  • open/close: open to extension, closed to modification
  • liskov: obeys liskov substitution. when working with derived objects, if you have to use is_a? to figure out whether you're dealing with the parent or child, then they aren't interchangeable enough to justify derivation
  • interface segregation: for statically-typed languages only. yay ruby!
  • dependency inversion: inject dependencies instead of linking classes together

"Design is emergent when you follow object-oriented principles, just like features are emergent when you follow TDD."

Resistance is a resource - if you feel like the code is wrong, it probably is, even if you can't express why.

Normal loop is Red -> Green -> Refactor. After Green, ask these questions:
  • Is it DRY?
  • Does it have one responsibility?
  • Does everything in it change at the same rate?
  • Does it depend on things that change less than it does?
If any of these are "No", use the responsibility refactoring loop: Extract -> Inject -> Refactor, then through the Red -> Green -> Refactor loop again.

Tip for detecting violation of single-responsibility principle: Read your object's spec from top to bottom and connect the sentences with prepositions. Listen for things that don't seem to go together. e.g. "It downloads the file AND it populates the database."

"Refactor not because you know the abstraction, but because you want to find it."

Wednesday, September 28, 2011

detecting code smells through tests

I'm spending a lot of time adding tests to completed, working code in our codebase. It's surprising how closely "ability to be easily tested" maps to the quality of the code in question. If I find myself thinking any of these things, the code is probably due for a refactor:
  • "The test for this is going to be really long."
  • "This is going to be hard to test."
  • "This test is going to have a million conditionals."
  • "Crap, I'll need to generate new test data for this scenario."

Monday, May 23, 2011

Starting from scratch: OSX

http://yehudakatz.com/2010/07/29/everyone-who-tried-to-convince-me-to-use-vim-was-wrong/

Install Xcode
Install git
Install MacVIM
Install vim plugin NERDTree. (create a ~/.vim/ directory and put the contents of the zip inside it. Should end up with ~/.vim/doc, ~/.vim/nerdtree_plugin, and ~/.vim/plugin) NERDTree is started with :NERDTree from inside MacVIM.
Download PeepOpen (note to self: put backup in Amazon Cloud Drive, /Archive). Open it and install the MacVIM plugin. To enable command + T bringing it up, cat ~/.vim/plugin/peepopen.vim and follow the directions.
Install vim-rails
Install vim-fugitive
Install snipmate.vim
within MacVim, run :helptags ~/.vim/doc (loads that directory's textfiles into the :help command)
To flatten the mouse acceleration, use these commands:
defaults write .GlobalPreferences com.apple.mouse.scaling -1
defaults write .GlobalPreferences com.apple.trackpad.scaling -1
Download and install TotalTerminal (nee Visor)
Download MySQL Community Server, 64-bit. Install using:
mv ~/Downloads/mysql- /usr/local/mysql
echo "PATH=\$PATH:/usr/local/mysql/bin" >> ~/.profile
cd /usr/local/mysql
./scripts/mysql_install_db

Tuesday, December 15, 2009

Starting from scratch: Ubuntu

Work in progress.

  • cd ~
  • git init
  • git remote add unfuddle git@guidingcancer.unfuddle.com:guidingcancer/gc.git
  • git config remote.unfuddle.push refs/heads/master:refs/heads/master
  • ssh-keygen -C "dagan@navigatingcancer.com" -t rsa
  • cat .ssh/id_rsa.pub
  • copy the public key into unfuddle profile settings
  • git clone git@guidingcancer.unfuddle.com:guidingcancer/gc.git DO NOT SUDO
  • cd gc
  • install rvm (ruby version manager). follow the instructions about updating .bash_profile with the rvm load script
  • quit the terminal
  • open terminal again and cd into gc. do whatever rvm tells you
  • cd config/
  • cp database.yml.example database.yml
  • open database.yml, change the adapters from mysql to mysql2 for both dev and test
  • cd ~/gc
  • http://cjohansen.no/en/ruby/ruby_version_manager_ubuntu_and_openssl
  • bundle install
  • create the database
    (using SQL Pro's gui for OS X)
  • script/server - everything should work!
  • For fixing sound: sudo apt-get install linux-backports-modules-alsa-[distro]-generic. [distro] was lucid
  • open /etc/modprobe.d/alsa-base.conf, add to the end: options snd-hda-intel model=hp-dv5
  • restart
  • For IDE: Install RubyMine.
  • sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
  • sudo apt-get update
  • sudo apt-get install sun-java6-jdk
  • add 'export JDK_HOME=/usr/lib/jvm/java-6-sun' to the top of RubyMine/bin/rubymine.sh

Create a database from a DB dump

  1. Get into the directory where you want the DB.
  2. mysqladmin -uroot create DB_NAME_HERE
  3. mysql -uroot DB_NAME_HERE < DUMP_NAME_HERE.yml
  4. If you get "No such file" referring to database.yml, copy database.example.yml and rename the copy database.yml
  5. rake db:migrate