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

A (re)-factoring Story

A (re)-factoring Story

A set of example a up an comer ruby programer can use to improve their code.

Ivan Acosta-Rubio

June 08, 2012
Tweet

More Decks by Ivan Acosta-Rubio

Other Decks in Programming

Transcript

  1. def positive_reviews happyfaces = 0 reviews.each do |review| happyfaces +=

    1 if review.face == "happy" end happyfaces end Friday, June 8, 12
  2. def get_report_data return if session[:account].blank? or session[:account].zero? start_date = params[:start].blank?

    ? (session[:report_start_date].blank? ? Date.today - 8.days : Date.parse(session[:report_start_date])) : Date.civil(params[:start][:year].to_i, params[:start][:month].to_i, params[:start][:day].to_i) end_date = params[:end].blank? ? (session[:report_end_date].blank? ? Date.today - 1.days : Date.parse(session[:report_end_date])) : Date.civil(params[:end][:year].to_i, params[:end][:month].to_i, params[:end][:day].to_i) end_date = start_date if start_date > end_date session[:report_end_date] = end_date.to_s session[:report_start_date] = start_date.to_s type = action_name session[:report_type] = type if session[:account].blank? or session[:account].zero? @venues = Venue.find(:all) end span = case (end_date - start_date).to_i when 0:'day' when 1..8:'week' else 'month' end superuser_data = { :total_events => 0, :events_with_data => 0, :accounts => {}, :venues => {} } total_reservations = 0 total_guests = 0 total_passes = 0 report_venues = [] # Only venues with events show up in the reports. @venues.each do |venue| events = venue.events.find(:all, :conditions => [ "date >= :start_date AND date <= :end_date", { :start_date => start_date, :end_date => end_date } ], :order => 'date').uniq # This 'uniq' is required because this query can return # the same event record more than once in the response. venue_reservations = 0 venue_guests = 0 venue_passes = 0 venue_revenue = 0 venue_returns = 0 venue_tables = 0 staff_stats = {} daily_staff_stats = {} meta_events = [] events.each do |event| superuser_data[:total_events] += 1 event_reservations = 0 event_guests = 0 event_passes = 0 event_revenue = 0 event_returns = 0 event_tables = 0 # Get total revenue for each staff member. staff_data = {} staff_data = {} reservation_count = event.reservations.find(:all).size total_reservations += reservation_count venue_reservations += reservation_count event_reservations += reservation_count reservations = event.reservations.find(:all, :conditions => "total_spend > 0 or checked_in") reservations.each do |reservation| next if reservation.staff_id.blank? # Skip orphan reservations. # For event: staff_data[reservation.staff_id] = {} if staff_data[reservation.staff_id].nil? staff_data[reservation.staff_id][:total_revenue] = 0 if staff_data[reservation.staff_id][:total_revenue].nil? staff_data[reservation.staff_id][:total_revenue] += reservation.total_spend unless reservation.total_spend.nil? staff_data[reservation.staff_id][:tables] = {} if staff_data[reservation.staff_id][:tables].nil? staff_data[reservation.staff_id][:tables][reservation.table_id] = 1 event_revenue += reservation.total_spend unless reservation.total_spend.nil? event_tables += 1 # For date: date_hash = reservation.event.date.to_s :db staff_stats[reservation.staff_id] = {} if staff_stats[reservation.staff_id].nil? staff_stats[reservation.staff_id][:total_revenue] = 0 if staff_stats[reservation.staff_id][:total_revenue].nil? staff_stats[reservation.staff_id][:total_revenue] += reservation.total_spend unless reservation.total_spend.nil? daily_staff_stats[date_hash] = {} if daily_staff_stats[date_hash].nil? daily_staff_stats[date_hash][reservation.staff_id] = {} if daily_staff_stats[date_hash][reservation.staff_id].nil? daily_staff_stats[date_hash][reservation.staff_id][:total_revenue] = 0 if daily_staff_stats[date_hash][reservation.staff_id][:total_revenue].nil? daily_staff_stats[date_hash][reservation.staff_id][:total_revenue] += reservation.total_spend unless reservation.total_spend.nil? daily_staff_stats[date_hash][reservation.staff_id][:tables] = {} if daily_staff_stats[date_hash][reservation.staff_id][:tables].nil? daily_staff_stats[date_hash][reservation.staff_id][:tables][reservation.table_id] = 1 # For venue: venue_revenue += reservation.total_spend unless reservation.total_spend.nil? venue_tables += 1 # Superuser totals superuser_data[:accounts][reservation.account_id] = 1 unless reservation.account_id.nil? superuser_data[:venues][venue.id] = 1 end # Get guest list returns for each staff member. unless type.eql? 'staff' guests = event.guests.find(:all, :conditions => "checked_in = 1 OR total_returns > 0") guest_count = guests.size guests.each do |guest| next if guest.guestlist.staff_id.blank? staff_data[guest.guestlist.staff_id] = {} if staff_data[guest.guestlist.staff_id].nil? staff_data[guest.guestlist.staff_id][:total_returns] = 0 if staff_data[guest.guestlist.staff_id][:total_returns].nil? staff_data[guest.guestlist.staff_id][:total_returns] += guest.total_returns unless guest.total_returns.nil? staff_data[guest.guestlist.staff_id][:guestlist_returns] = 0 if staff_data[guest.guestlist.staff_id][:guestlist_returns].nil? staff_data[guest.guestlist.staff_id][:guestlist_returns] += guest.total_returns unless guest.total_returns.nil? event_returns += guest.total_returns unless guest.total_returns.nil? total_guests += guest.total_returns unless guest.total_returns.nil? venue_guests += guest.total_returns unless guest.total_returns.nil? event_guests += guest.total_returns unless guest.total_returns.nil? date_hash = guest.guestlist.event.date.to_s :db staff_stats[guest.guestlist.staff_id] = {} if staff_stats[guest.guestlist.staff_id].nil? staff_stats[guest.guestlist.staff_id][:total_returns] = 0 if staff_stats[guest.guestlist.staff_id][:total_returns].nil? staff_stats[guest.guestlist.staff_id][:total_returns] += guest.total_returns unless guest.total_returns.nil? staff_stats[guest.guestlist.staff_id][:guestlist_returns] = 0 if staff_stats[guest.guestlist.staff_id][:guestlist_returns].nil? staff_stats[guest.guestlist.staff_id][:guestlist_returns] += guest.total_returns unless guest.total_returns.nil? daily_staff_stats[date_hash] = {} if daily_staff_stats[date_hash].nil? daily_staff_stats[date_hash][guest.guestlist.staff_id] = {} if daily_staff_stats[date_hash][guest.guestlist.staff_id].nil? daily_staff_stats[date_hash][guest.guestlist.staff_id][:total_returns] = 0 if daily_staff_stats[date_hash][guest.guestlist.staff_id][:total_returns].nil? daily_staff_stats[date_hash][guest.guestlist.staff_id][:total_returns] += guest.total_returns unless guest.total_returns.nil? daily_staff_stats[date_hash][guest.guestlist.staff_id][:guestlist_returns] = 0 if daily_staff_stats[date_hash][guest.guestlist.staff_id][:guestlist_returns].nil? daily_staff_stats[date_hash][guest.guestlist.staff_id][:guestlist_returns] += guest.total_returns unless guest.total_returns.nil? venue_returns += guest.total_returns unless guest.total_returns.nil? superuser_data[:accounts][guest.guestlist.account_id] = 1 unless guest.guestlist.account_id.nil? superuser_data[:venues][venue.id] = 1 end # Get guest list returns for each staff member. unless type.eql? 'staff' passes = event.passes.find(:all, :conditions => "total > 0") pass_count = passes.size passes.each do |pass| next if pass.staff_id.blank? staff_data[pass.staff_id] = {} if staff_data[pass.staff_id].nil? staff_data[pass.staff_id][:total_returns] = 0 if staff_data[pass.staff_id][:total_returns].nil? staff_data[pass.staff_id][:total_returns] += pass.total unless pass.total.nil? staff_data[pass.staff_id][:pass_returns] = 0 if staff_data[pass.staff_id][:pass_returns].nil? staff_data[pass.staff_id][:pass_returns] += pass.total unless pass.total.nil? event_returns += pass.total unless pass.total.nil? total_passes += pass.total unless pass.total.nil? venue_passes += pass.total unless pass.total.nil? event_passes += pass.total unless pass.total.nil? date_hash = pass.event.date.to_s :db staff_stats[pass.staff_id] = {} if staff_stats[pass.staff_id].nil? staff_stats[pass.staff_id][:total_returns] = 0 if staff_stats[pass.staff_id][:total_returns].nil? staff_stats[pass.staff_id][:total_returns] += pass.total unless pass.total.nil? staff_stats[pass.staff_id][:pass_returns] = 0 if staff_stats[pass.staff_id][:pass_returns].nil? staff_stats[pass.staff_id][:pass_returns] += pass.total unless pass.total.nil? daily_staff_stats[date_hash] = {} if daily_staff_stats[date_hash].nil? daily_staff_stats[date_hash][pass.staff_id] = {} if daily_staff_stats[date_hash][pass.staff_id].nil? daily_staff_stats[date_hash][pass.staff_id][:total_returns] = 0 if daily_staff_stats[date_hash][pass.staff_id][:total_returns].nil? daily_staff_stats[date_hash][pass.staff_id][:total_returns] += pass.total unless pass.total.nil? daily_staff_stats[date_hash][pass.staff_id][:pass_returns] = 0 if daily_staff_stats[date_hash][pass.staff_id][:pass_returns].nil? daily_staff_stats[date_hash][pass.staff_id][:pass_returns] += pass.total unless pass.total.nil? venue_returns += pass.total unless pass.total.nil? superuser_data[:accounts][pass.account_id] = 1 unless pass.account_id.nil? superuser_data[:venues][venue.id] = 1 end end end # Sort by revenue and put a whole object into an array. meta_staff = [] staff_data.keys.each do |staff_id| unless staff_id.blank? begin person = Staff.find(staff_id) rescue next # Orphan reservations cause this. end tables = (staff_data[staff_id].nil? or staff_data[staff_id][:tables].nil?) ? 0 : staff_data[staff_id][:tables].keys.size revenue = staff_data[staff_id].nil? ? 0 : staff_data[staff_id][:total_revenue] returns = (staff_data.nil? or staff_data[staff_id].nil?) ? 0 : staff_data[staff_id][:total_returns] guestlists = (staff_data.nil? or staff_data[staff_id].nil?) ? 0 : staff_data[staff_id][:guestlist_returns] passes = (staff_data.nil? or staff_data[staff_id].nil?) ? 0 : staff_data[staff_id][:pass_returns] unless (tables.blank? or tables.zero?) and (revenue.blank? or revenue.zero?) and (returns.blank? or returns.zero?) meta_staff << { :object => person, :total_tables => tables, :total_revenue => revenue || 0, :total_returns => returns, :guestlist_returns => guestlists, :pass_returns => passes } end end end meta_staff.sort! {|a,b| b[:total_revenue] <=> a[:total_revenue]} meta_events << { :object => event, :staff => meta_staff, :total_reservations => event_reservations, :total_guests => event_guests, :total_passes => event_passes, :total_tables => event_tables, :total_returns => event_returns, :total_revenue => event_revenue } superuser_data[:events_with_data] += 1 if(event_tables > 0 or event_returns > 0 or event_revenue > 0) end # Sort by revenue and put a whole object into an array. dates = [] daily_staff_stats.keys.sort.each do |date_hash| dates << Date.parse(date_hash) end meta_staff = [] staff_stats.keys.each do |staff_id| unless staff_id.blank? begin person = Staff.find(staff_id) rescue next end tables = (staff_stats[staff_id].nil? or staff_stats[staff_id][:tables].nil?) ? 0 : staff_stats[staff_id][:tables].keys.size revenue = staff_stats[staff_id].nil? ? 0 : staff_stats[staff_id][:total_revenue] returns = staff_stats[staff_id].nil? ? 0 : staff_stats[staff_id][:total_returns] unless (tables.blank? or tables.zero?) and (revenue.blank? or revenue.zero?) and (returns.blank? or returns.zero?) # logger.warn "STAFF: #{person.name} '#{tables}' '#{revenue}' '#{returns}'" meta_staff << { :object => person, :total_tables => tables, :total_revenue => revenue, :total_returns => returns } end end end meta_staff.sort! {|a,b| (b[:total_revenue].blank? ? 0 : b[:total_revenue]) <=> (a[:total_revenue].blank? ? 0 : a[:total_revenue]) } if events.size > 0 report_venues << { :object => venue, :events => meta_events, # Array of meta hashes for events with object pointer and meta data. :staff => meta_staff, # A sorted list of staff for this venue, for this report. Only totals. :daily_staff_stats => daily_staff_stats, # The actual day-by-day breakdown for staff members. :dates => dates, # Array of meta hashes for each date. :total_reservations => venue_reservations, :total_guests => venue_guests, :total_tables => venue_tables, :total_returns => venue_returns, :total_revenue => venue_revenue } end end venue_data_index = {} report_venues.each_with_index do |venue, index| venue_data_index[venue[:object].id] = index end if span.eql? 'month' report_venues.each do |venue| weeks = {} venue[:weekly_staff_stats] = {} venue[:staff].each do |person| venue[:dates].each do |date| days_in = date - start_date week_number = (days_in / 7).to_i weeks[week_number] = true venue[:weekly_staff_stats][week_number] = {} if venue[:weekly_staff_stats][week_number].nil? if venue[:weekly_staff_stats][week_number][person[:object].id].nil? venue[:weekly_staff_stats][week_number][person[:object].id] = { :total_revenue => 0, :total_returns => 0 } end stats_for_date = venue[:daily_staff_stats][date.to_s(:db)] next if stats_for_date[person[:object].id].nil? stats_for_week = venue[:weekly_staff_stats][week_number] stats_for_week[person[:object].id] = {} if stats_for_week[person[:object].id].nil? stats_for_week[person[:object].id][:total_revenue] += stats_for_date[person[:object].id][:total_revenue] unless stats_for_date[person[:object].id][:total_revenue].nil? stats_for_week[person[:object].id][:total_returns] += stats_for_date[person[:object].id][:total_returns] unless stats_for_date[person[:object].id][:total_returns].nil? end end venue[:weeks] = weeks.keys.sort end end # logger.warn "VENUES: #{report_venues.to_json}" # logger.warn "DATA: #{superuser_data[:venues].keys.join(", ")}" superuser_data[:total_reservations] = total_reservations superuser_data[:total_guests] = total_guests @report_data = { :start_date => start_date, :end_date => end_date, :span => span, :type => type, :venues => report_venues, :venue_data_index => venue_data_index, :superuser_data => superuser_data } end un metodo en el controllador de 356 lineas Friday, June 8, 12
  3. +- 20 => super! +- 40 => OK +- 60

    => needs refactor + 80 => WTF! Friday, June 8, 12
  4. module MongoReplication module Event def self.included(base) base.class_eval do after_create :create_delayed_replication

    after_update :update_delayed_replication after_destroy :destroy_cached_event end end # define methods as usual def create_delayed_replicated ; .... ; end # .... end end Friday, June 8, 12
  5. MIENTE class Event < ActiveRecord::Base include ReportAgregator end VS class

    EventFake include ReportAgregator end Friday, June 8, 12
  6. def index if (session[:account] != nil) && (session[:venue] == nil

    or session[:venue] == 0) # ... elsif (session[:venue] != nil) && (session[:venue] != 0) # ... else # ... end end Friday, June 8, 12
  7. def have_account_but_not_venue (session[:account] != nil) && (session[:venue] == nil or

    session[:venue] == 0) end def have_venue_selected (session[:venue] != nil) && (session[:venue] != 0) end Friday, June 8, 12
  8. class MyView attr_reader :target def initialize(target) @target = target end

    def double case target when Student then target * 2 when Profesor then target.next when DictrictAdmin then target ** 2 else raise "don't know how to double #{target.class} #{target.inspect}" end end end Friday, June 8, 12
  9. class MyView attr_reader :target def initialize(target) @target = target end

    def double target.double end end Friday, June 8, 12
  10. class Student def double self * 2 end end class

    Profesor def double self.next end end Friday, June 8, 12
  11. class DistrictAdmin def double self ** 2 end end class

    Object def double raise "...." end end h/t Sandi Metz Friday, June 8, 12
  12. geek speak for: - Pasalo como un parametro - dejame

    cambiarlo sin tocar tu clase Friday, June 8, 12
  13. class Object def blank? if respond_to? :empty? empty? elsif respond_to?

    :zero? zero? else !self end end end Friday, June 8, 12
  14. class Object def blank? respond_to?(:empty?) ? empty? : !self end

    end class NilClass def blank? true end end class FalseClass def blank? true end end Friday, June 8, 12
  15. class Array alias_method :blank?, :empty? end class Hash alias_method :blank?,

    :empty? end class String def blank? self !~ /[^[:space:]]/ end end Friday, June 8, 12
  16. google: fast rails tests destroyallsoftware.com http://cleancoders.com/ Sandi Metz | Practical

    Object oriented Design in Ruby http://martinfowler.com/refactoring/ Friday, June 8, 12