Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Structure and Interpretation of Ruby Programs

Tim Uruski
December 06, 2016

Structure and Interpretation of Ruby Programs

A lightning talk about how to structure one-off scripts that don't turn into a huge mess as features are added.

Tim Uruski

December 06, 2016
Tweet

More Decks by Tim Uruski

Other Decks in Programming

Transcript

  1. #!/usr/bin/env ruby require 'cheezburger' require 'json' db = DatabaseWrapper.default_connection user_id

    = ARGV.shift locations = LocationStream.new(db, user_id) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => [location.lng, location.lat] } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  2. #!/usr/bin/env ruby require 'cheezburger' require 'json' db = DatabaseWrapper.default_connection user_id

    = ARGV.shift query_date = ARGV.shift || Tume.now min_recorded_at = query_date.beginning_of_day.utc.to_i * 1000 max_recorded_at = query_date.end_of_day.utc.to_i * 1000 query = { order_by: 'recorded_at', start_at: min_recorded_at, end_at: end_recorded_at } locations = LocationStream.new(db, user_id, query) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => [location.lng, location.lat] } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  3. #!/usr/bin/env ruby require 'cheezburger' require 'json' if database_url = ENV.fetch('DATABASE_URL')

    db = DatabaseWrapper.new(database_url) else db = DatabaseWrapper.default_connection end user_id = ARGV.shift query_date = ARGV.shift || Tume.now min_recorded_at = query_date.beginning_of_day.utc.to_i * 1000 max_recorded_at = query_date.end_of_day.utc.to_i * 1000 query = { order_by: 'recorded_at', start_at: min_recorded_at, end_at: end_recorded_at } locations = LocationStream.new(db, user_id, query) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => [location.lng, location.lat] } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  4. #!/usr/bin/env ruby require 'cheezburger' require 'json' if database_url = ENV.fetch('DATABASE_URL')

    db = DatabaseWrapper.new(database_url) else db = DatabaseWrapper.default_connection end identifier = ARGV.shift begin user = UserStore.new(db).find(identifier) rescue NoUserFound warn "No user found for #{identifier}" exit 1 end query_date = ARGV.shift || Tume.now min_recorded_at = query_date.beginning_of_day.utc.to_i * 1000 max_recorded_at = query_date.end_of_day.utc.to_i * 1000 query = { order_by: 'recorded_at', start_at: min_recorded_at, end_at: end_recorded_at } locations = LocationStream.new(db, user.id, query) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => [location.lng, location.lat] } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  5. #!/usr/bin/env ruby require 'cheezburger' require 'json' window_size = 10 stay_radius

    = 500 activity_threshold = 2.5 OptionParser.new do |opts| window_desc = 'Window size in minutes, default: 5' opts.on('-wSIZE', '--window=SIZE', window_desc, Integer) do |value| window_size = value * 60 end activity_desc = 'Activity within window threshold, default: 23' opts.on('-aCOUNT', '--activity=COUNT', activity_desc, Integer) do |value| activity_threshold = value end radius_desc = 'Size of stay radius in metres, default: 50.0' opts.on('-rSIZE', '--radiusSIZE', radius_desc, Float) do |value| stay_radius = value end end.parse! if database_url = ENV.fetch('DATABASE_URL') db = DatabaseWrapper.new(database_url) else db = DatabaseWrapper.default_connection end identifier = ARGV.shift begin user = UserStore.new(db).find(identifier) rescue NoUserFound warn "No user found for #{identifier}" exit 1 end query_date = ARGV.shift || Tume.now min_recorded_at = query_date.beginning_of_day.utc.to_i * 1000 max_recorded_at = query_date.end_of_day.utc.to_i * 1000 query = { order_by: 'recorded_at', start_at: min_recorded_at, end_at: end_recorded_at } locations = LocationStream.new(db, user.id, query) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => [location.lng, location.lat] } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  6. #!/usr/bin/env ruby require 'json' require 'optparse' require 'bundler/setup' require 'active_support'

    require 'active_support/core_ext/time' require_relative '../lib/cheezburger' include Cheezburger window_size = 10 stay_radius = 500 activity_threshold = 2.5 OptionParser.new do |opts| window_desc = 'Window size in minutes, default: 5' opts.on('-wSIZE', '--window=SIZE', window_desc, Integer) do |value| window_size = value * 60 end activity_desc = 'Activity within window threshold, default: 23' opts.on('-aCOUNT', '--activity=COUNT', activity_desc, Integer) do |value| activity_threshold = value end radius_desc = 'Size of stay radius in metres, default: 50.0' opts.on('-rSIZE', '--radiusSIZE', radius_desc, Float) do |value| stay_radius = value end end.parse! if ARGV.empty? warn "Usage: [DATABASE_URL=<url>] #{$0} <USER_EMAIL or USER_ID> [2016-02-01]" exit 1 end user_id = args.shift date = args.shift database_url = ENV.fetch('DATABASE_URL') database = DatabaseWrapper.new(database_url) def find_user(database, identifier) begin user = UserStore.new(database).find(identifier) rescue NoUserFound warn "No user found for #{identifier}" exit 1 end end def fetch_locations(database, user, query_date) min_recorded_at = query_date.beginning_of_day.utc.to_i * 1000 max_recorded_at = query_date.end_of_day.utc.to_i * 1000 query = { order_by: 'recorded_at', start_at: min_recorded_at, end_at: end_recorded_at } begin LocationStream.new(database, user, query) rescue NoLocationsFound export_date = query_date.strftime('%Y-%m-%d') warn "No locations found for #{export_date}" exit 1 end end def to_coords(location) [location.lng, location.lat] end user = find_user(database, options[:user_id]) query_date = options[:date] ? Time.parse(options[:date]) : Time.now locations = fetch_locations(database, user, query_date) geojson = { "type" => "FeatureCollection", "features" => locations.map { |location| { "type" => "Feature", "geometry" => { "type" => "Point", "coordinates" => coords(location) } "properties" => { "name": "location.name" } } } } end print JSON.pretty_generate(geojson)
  7. #!/usr/bin/env ruby require 'optparse' name = 'world' OptionParser.new do |opts|

    opts.on('-n=NAME', '--name=NAME', 'Name to greet') do |value| name = value end end.parse! puts "Hello, #{name}"
  8. #!/usr/bin/env ruby require 'optparse' def greet(name) puts "Hello, #{name}" end

    options = { name: 'world' } OptionParser.new do |opts| opts.on('-n=NAME', '--name=NAME', 'Name to greet') do |value| options[:name] = value end end.parse! greet(options[:name])
  9. #!/usr/bin/env ruby require 'optparse' def greet(name) puts "Hello, #{name}" end

    options = { name: 'world' } OptionParser.new do |opts| opts.on('-n=NAME', '--name=NAME', 'Name to greet') do |value| options[:name] = value end end.parse! greet(options[:name])
  10. #!/usr/bin/env ruby require 'optparse' at_exit do options = parse_options greet(options[:name])

    end def parse_options(args = ARGV) { name: 'world' }.tap do |options| parser = OptionParser.new do |opts| opts.on('-n=NAME', '--name=NAME', 'Name to greet', String) do |value| options[:name] = value end end parser.parse!(args) end end def greet(name) puts "Hello, #{name}" end
  11. #!/usr/bin/env ruby require 'optparse' at_exit do options = parse_options greet(options[:name])

    end def parse_options(args = ARGV) { name: 'world' }.tap do |options| parser = OptionParser.new do |opts| opts.on('-n=NAME', '--name=NAME', 'Name to greet', String) do |value| options[:name] = value end end parser.parse!(args) end end def greet(name) puts "Hello, #{name}" end
  12. #!/usr/bin/env ruby require 'optparse' at_exit do options = parse_options greet(options[:name])

    end def parse_options(args = ARGV) { name: 'world' }.tap do |options| parser = OptionParser.new do |opts| opts.on('-n=NAME', '--name=NAME', 'Name to greet', String) do |value| options[:name] = value end end parser.parse!(args) end end def greet(name) puts "Hello, #{name}" end