Little Big Ruby
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Little Big Ruby

on

  • 3,451 views

 

Statistics

Views

Total Views
3,451
Views on SlideShare
3,445
Embed Views
6

Actions

Likes
3
Downloads
35
Comments
0

1 Embed 6

http://www.slideshare.net 6

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Little Big Ruby Presentation Transcript

  • 1. Code Reading 101
  • 2. TWO THINGS TO KNOW ABOUT ME I wrote the TextMate book My name is Jim Weirich
  • 3. JAMES EDWARD GRAY II I wrote two books for the Pragmatic Programmers: Best of Ruby Quiz and TextMate: Power Editing for the Mac I’ve contributed documentation and patches for some standard libraries, which I now help to maintain I built FasterCSV (now CSV), HighLine (with Greg), Elif, and a few other libraries people don’t use I created the Ruby Quiz and ran it for the first three years
  • 4. HI. I’M JAMES AND I READ CODE.
  • 5. HOW MUCH SHOULD YOU READ? 0% 25% 50% 75% 100% Novice Advanced Beginner Competent Proficient Expert My opinion based on the Dreyfus Model of Skill Acquisition.
  • 6. WHY IS CODE READING IMPORTANT? It can show you common idioms It’s good to practice breaking down possibly challenging code, because you will always have to work with other’s code Understanding how something works gives you insight into any limitations it has Seeing bad code helps you write better code Knowledge workers always need more ideas
  • 7. INTRODUCING RESTCLIENT
  • 8. WHAT IS RESTCLIENT? Sinatra’s sister library (sometimes called “reverse Sinatra”) It provides a very clean interface to RESTful web services Simple well-written code (around 500 lines of clear code for the core functionality) Plus a couple of exciting features
  • 9. require quot;rubygemsquot; require quot;rest_clientquot; require quot;jsonquot; twitter = RestClient::Resource.new( quot;http://twitter.com/statusesquot;, :user => quot;JEG2quot;, :password => quot;secretquot; ) json = twitter[quot;friends_timeline.jsonquot;].get tweets = JSON.parse(json) tweets.each do |tweet| # ... end BASIC GET Reading tweets with Twitter’s API
  • 10. require quot;rubygemsquot; require quot;rest_clientquot; require quot;jsonquot; twitter = RestClient::Resource.new( quot;http://twitter.com/statusesquot;, :user => quot;JEG2quot;, :password => quot;secretquot; ) json = twitter[quot;update.jsonquot;].post(:status => quot;Hello from #mwrc!quot;) tweet = JSON.parse(json) # ... BASIC POST Posting a tweet with Twitter’s API
  • 11. NETWORKING CODE DONE RIGHT
  • 12. def process_result(res) if res.code =~ /A2d{2}z/ decode res['content-encoding'], res.body if res.body elsif %w(301 302 303).include? res.code url = res.header['Location'] def transmit(uri, req, payload) setup_credentials(req) if url !~ /^http/ uri = URI.parse(@url) net = net_http_class.new(uri.host, uri.port) uri.path = quot;/#{url}quot;.squeeze('/') net.use_ssl = uri.is_a?(URI::HTTPS) url = uri.to_s net.verify_mode = OpenSSL::SSL::VERIFY_NONE end net.read_timeout = @timeout if @timeout net.open_timeout = @open_timeout if @open_timeout raise Redirect, url elsif res.code == quot;304quot; display_log request_log raise NotModified, res elsif res.code == quot;401quot; net.start do |http| raise Unauthorized, res res = http.request(req, payload) elsif res.code == quot;404quot; display_log response_log(res) raise ResourceNotFound, res string = process_result(res) else raise RequestFailed, res if string or @method == :head end Response.new(string, res) end else nil end end rescue EOFError def decode(content_encoding, body) raise RestClient::ServerBrokeConnection if content_encoding == 'gzip' and not body.empty? rescue Timeout::Error Zlib::GzipReader.new(StringIO.new(body)).read raise RestClient::RequestTimeout elsif content_encoding == 'deflate' end Zlib::Inflate.new.inflate(body) else body end end
  • 13. A RESTFUL SHELL (IN 90 LOC)
  • 14. $ restclient > get http://twitter.com/statuses/friends_timeline.json?count=1 > JEG2 secret [{quot;textquot;:quot;Sent out first round of Twitter client betas…quot;, quot;userquot;:{quot;namequot;:quot;Jeremy McAnallyquot;,quot;screen_namequot;:quot;jeremymcanallyquot;,…}, …}] CURL-ISH REQUESTS Fetching the latest tweet !om Twitter’s API
  • 15. $ restclient http://twitter.com/statuses JEG2 secret >> post quot;update.jsonquot;, :status => quot;The RestClient shell is fun.quot; => quot;{quot;textquot;:quot;The RestClient shell is fun.quot;,…}quot; >> get quot;friends_timeline.json?count=1quot; => quot;[{quot;textquot;:quot;The RestClient shell is fun.quot;,…}]quot; RESTFUL IRB Interacting with the Twitter API
  • 16. LOGGING IN RUBY
  • 17. $ RESTCLIENT_LOG=twitter_fun.rb restclient … >> post quot;update.jsonquot;, :status => quot;The RestClient shell is fun.quot; => … >> get quot;friends_timeline.json?count=1quot; => … # twitter_fun.rb RestClient.post quot;http://twitter.com/statuses/update.jsonquot;, quot;status=The%20RestClient%20shell%20is%20fun.quot;, :content_type=>quot;application/x-www-form-urlencodedquot; # => 200 OK | application/json 379 bytes RestClient.get quot;http://twitter.com/statuses/friends_timeline.json?count=1quot; # => 200 OK | application/json 381 bytes GENERATING RUBY Interactively building a RESTful Ruby script
  • 18. FASTERCSV IS THE NEW CSV
  • 19. THE LESS BORING PARTS OF CSV Tricky data structures are needed Rows need ordered access for their columns Users also like to work with them by header name Column names often repeat Now that we have m17n, CSV parses in the encoding of your data (no transcoding is done on your data)
  • 20. THE ARRAY-HASH- WITH-DUPLICATES DATA THING
  • 21. require quot;csvquot; # using Ruby 1.9 data = <<END_DATA Console,Units Sold 2007,Percent,Units Sold 2008,Percent Wii,quot;719,141quot;,49.4%,quot;1,184,651quot;,49.6% XBox 360,quot;333,084quot;,22.9%,quot;743,976quot;,31.1% PlayStation 3,quot;404,900quot;,27.8%,quot;459,777quot;,19.3% END_DATA ps3 = CSV.parse(data, :headers => true, :header_converters => :symbol)[-1] ps3[0] # => quot;PlayStation 3quot; ps3[:percent] # => quot;27.8%quot; ps3[:percent, 3] # => quot;19.3%quot; ps3[:percent, ps3.index(:units_sold_2008)] # => quot;19.3%quot; CSV::ROW The various ways to refer to data
  • 22. M17N IN ACTION
  • 23. @io = if data.is_a? String then StringIO.new(data) else data end @encoding = if @io.respond_to? :internal_encoding @io.internal_encoding || @io.external_encoding elsif @io.is_a? StringIO @io.string.encoding end @encoding ||= Encoding.default_internal || Encoding.default_external def encode_re(*chunks) Regexp.new(encode_str(*chunks)) end def encode_str(*chunks) chunks.map { |chunk| chunk.encode(@encoding.name) }.join end sample = read_to_char(1024) sample += read_to_char(1) if sample[-1..-1] == encode_str(quot;rquot;) and not @io.eof? if sample =~ encode_re(quot;rn?|nquot;) @row_sep = $& break end
  • 24. OTHER POINTS OF INTEREST The parser Ruby 1.9’s CSV library uses primarily one ugly regular expression from Master Regular Expressions The old FasterCSV has switched to a non-regex parser to dodge some regex engine weaknesses FasterCSV::Table is another interesting data structure that can work in columns or rows
  • 25. BJ, SLAVE, AND TERMINATOR
  • 26. WHY THESE LIBRARIES? They teach how to build multiprocessing Unix software Covers Thread and fork(), apart and together Using pipes Signal handling And much more Robust code written by an expert
  • 27. HOW TO ASK YOUR CHILD TO KILL YOU
  • 28. def terminate options = {}, &block options = { :seconds => Float(options).to_i } unless Hash === options seconds = getopt :seconds, options trap = getopt :trap, options, lambda{ eval(quot;raise(::Terminator::Error, '#{ seconds }s')quot;, block) } handler = Signal.trap(signal, &trap) plot_to_kill pid, :in => seconds, :with => signal begin block.call ensure Signal.trap(signal, handler) end end
  • 29. def plot_to_kill pid, options = {} seconds = getopt :in, options signal = getopt :with, options process.puts [pid, seconds, signal].join(' ') process.flush end fattr :process do process = IO.popen quot;#{ ruby } #{ program.inspect }quot;, 'w+' at_exit do begin Process.kill -9, process.pid rescue Object end end process.sync = true process end
  • 30. fattr :program do code = <<-code while(( line = STDIN.gets )) pid, seconds, signal, *ignored = line.strip.split pid = Float(pid).to_i seconds = Float(seconds) signal = Float(signal).to_i rescue String(signal) sleep seconds begin Process.kill signal, pid rescue Object end end code tmp = Tempfile.new quot;#{ ppid }-#{ pid }-#{ rand }quot; tmp.write code tmp.close tmp.path end
  • 31. OTHER POINTS OF INTEREST bj – A robust background priority queue for Rails Noticing changes from the outside world via signals Managing and monitoring an external job slave – Trivial multiprocessing with built-in IPC How to set up a “heartbeat” between processes How to run DRb over Unix domain sockets
  • 32. THE ART OF CODE READING
  • 33. PROCESS TIPS Take a deep breath and relax Not all code sucks Don’t start with Rails There’s a ton of great stuff in there But it’s a big and complex beast Have a goal
  • 34. GETTING THE CODE gem unpack GEM_NAME Use anonymous VCS access to pull a local copy of the code Open it in your standard environment as you would if you were going to edit it
  • 35. FINDING THINGS Try the conventions first Executables are probably in the bin/ directory Look for MyModule::MyClass in lib/my_module/ my_class.rb Look for methods in super classes and mixed in modules But remember Ruby is quite dynamic Hunt for some “core extensions”
  • 36. UNDERSTANDING THE CODE Start with the tests/specs if there are any Check for an “examples/” directory Try to load and play with certain classes in isolation irb -r a_class_to_play_with Remember Ruby’s reflection methods, like methods()
  • 37. QUESTIONS? About code or other important topics…