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

Structure and Interpretation of Ruby Programs

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Tim Uruski 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.

Avatar for Tim Uruski

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