├── .gitignore ├── .travis.yml ├── 1-intention-revealing-method ├── .solucion-tute-2.rb ├── .solucion-tute.rb ├── app.rb ├── setup.rb └── test_app.rb ├── 2-special-case-objects ├── .solucion-tute-2.rb ├── .solucion-tute.rb ├── app.rb ├── setup.rb └── test_app.rb ├── 3-replace-method-with-method-object ├── .solucion-tute-2.rb ├── .solucion-tute-3.rb ├── .solucion-tute.rb ├── app.rb ├── fixtures │ ├── input.csv │ └── output.csv └── test_app.rb ├── 4-service-objects ├── .solucion-tute.rb ├── app.rb ├── setup.rb └── test_app.rb ├── Gemfile ├── LICENSE ├── README.es.md ├── README.md ├── Rakefile └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.7 4 | - 3.0 5 | - jruby-19mode 6 | -------------------------------------------------------------------------------- /1-intention-revealing-method/.solucion-tute-2.rb: -------------------------------------------------------------------------------- 1 | require_relative 'setup' 2 | 3 | # Intention Revealing Method 4 | # Extract relevant concepts into methods or variables with proper names 5 | # Code should explain itself 6 | 7 | class ProjectsController 8 | def index 9 | if user_is_admitted_at_least_a_week_ago? 10 | @projects = current_user.active_projects 11 | 12 | else 13 | if user_is_new? 14 | show_marketing_flash_message 15 | end 16 | 17 | @projects = Project.featured 18 | end 19 | end 20 | 21 | private 22 | 23 | def user_is_new? 24 | current_user && current_user.created_at > a_week_ago 25 | end 26 | 27 | def user_is_admitted_at_least_a_week_ago? 28 | current_user && current_user.created_at < a_week_ago 29 | end 30 | 31 | def show_marketing_flash_message 32 | @flash_msg = 'Sign up for having your own projects, and see promo ones!' 33 | end 34 | 35 | def a_week_ago 36 | Time.now - 7*24*3600 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /1-intention-revealing-method/.solucion-tute.rb: -------------------------------------------------------------------------------- 1 | # Tute's approach (after applying the pattern to `app.rb`). 2 | # Tests run here in the same way. 3 | 4 | require './setup' 5 | 6 | class ProjectsController 7 | def index 8 | if user_admitted? 9 | @projects = current_user.active_projects 10 | else 11 | set_flash_msg_if_new_user 12 | @projects = Project.featured 13 | end 14 | end 15 | 16 | private 17 | 18 | def set_flash_msg_if_new_user 19 | @flash_msg = 'Sign up for having your own projects, and see promo ones!' 20 | end 21 | 22 | def user_admitted? 23 | current_user && current_user.created_at < weeks_ago(1) 24 | end 25 | 26 | def weeks_ago(n) 27 | Time.now - n * 7*24*3600 28 | end 29 | end 30 | 31 | 32 | # Run tests if running as script 33 | require './tests' if __FILE__ == $0 34 | -------------------------------------------------------------------------------- /1-intention-revealing-method/app.rb: -------------------------------------------------------------------------------- 1 | require_relative 'setup' 2 | 3 | # Intention Revealing Method 4 | # Extract relevant concepts into methods or variables with proper names 5 | # Code should explain itself 6 | 7 | class ProjectsController 8 | def index 9 | # When user is admitted at least a week ago we show its active projects 10 | if current_user && current_user.created_at < (Time.now - 7*24*3600) 11 | @projects = current_user.active_projects 12 | 13 | # If not admitted we show some featured projects, and set a marketing flash 14 | # message when user is new 15 | else 16 | if current_user && current_user.created_at > (Time.now - 7*24*3600) 17 | @flash_msg = 'Sign up for having your own projects, and see promo ones!' 18 | end 19 | @projects = Project.featured 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /1-intention-revealing-method/setup.rb: -------------------------------------------------------------------------------- 1 | class User 2 | attr_accessor :created_at 3 | 4 | def initialize(options = {}) 5 | options.each { |k, v| send("#{k}=", v) } 6 | end 7 | 8 | def active_projects 9 | [:user_active_projects] 10 | end 11 | end 12 | 13 | class Project 14 | def self.featured 15 | [:public_featured_projects] 16 | end 17 | end 18 | 19 | class ProjectsController 20 | attr_accessor :current_user, :flash_msg, :projects 21 | end 22 | -------------------------------------------------------------------------------- /1-intention-revealing-method/test_app.rb: -------------------------------------------------------------------------------- 1 | require_relative "../test_helper.rb" 2 | 3 | require_relative 'app' 4 | 5 | class TestProjectsController < Minitest::Test 6 | def setup 7 | @controller = ProjectsController.new 8 | @admitted_user = User.new(created_at: Time.new(2000, 1, 1)) 9 | @new_user = User.new(created_at: Time.new) 10 | end 11 | 12 | def test_for_admitted_user 13 | @controller.current_user = @admitted_user 14 | @controller.index 15 | assert_equal [:user_active_projects], @controller.projects 16 | assert_nil @controller.flash_msg 17 | end 18 | 19 | def test_for_new_user 20 | @controller.current_user = @new_user 21 | @controller.index 22 | assert_equal [:public_featured_projects], @controller.projects 23 | assert_equal 'Sign up for having your own projects, and see promo ones!', @controller.flash_msg 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /2-special-case-objects/.solucion-tute-2.rb: -------------------------------------------------------------------------------- 1 | require_relative 'setup' 2 | 3 | class User 4 | def last_subscription 5 | subscriptions.last || NoSubscription.new 6 | end 7 | 8 | def cancel_subscription 9 | last_subscription.cancel 10 | end 11 | end 12 | 13 | class NoSubscription 14 | def name 15 | "none" 16 | end 17 | 18 | def trial_days 19 | "-" 20 | end 21 | 22 | def status 23 | "-" 24 | end 25 | 26 | def cancel 27 | end 28 | end 29 | 30 | class StatusReportJob 31 | def perform 32 | users = {} 33 | User.all.map do |user| 34 | users[user.name] = { 35 | name: user.last_subscription.name, 36 | status: user.last_subscription.status, 37 | trial_days: user.last_subscription.trial_days 38 | } 39 | end 40 | users 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /2-special-case-objects/.solucion-tute.rb: -------------------------------------------------------------------------------- 1 | # Tute's approach (after applying the pattern to `app.rb`). 2 | # Tests run here in the same way. 3 | 4 | require './setup' 5 | 6 | class Subscription 7 | class Null 8 | def name; 'none'; end 9 | def status; '-'; end 10 | def trial_days; '-'; end 11 | def cancel 12 | false 13 | end 14 | end 15 | end 16 | 17 | class User 18 | def last_subscription 19 | subscriptions.last || Subscription::Null.new 20 | end 21 | 22 | def cancel_subscription 23 | last_subscription.cancel 24 | end 25 | end 26 | 27 | class StatusReportJob 28 | def perform 29 | users = {} 30 | User.all.map do |user| 31 | users[user.name] = { 32 | name: user.last_subscription.name, 33 | status: user.last_subscription.status, 34 | trial_days: user.last_subscription.trial_days 35 | } 36 | end 37 | users 38 | end 39 | end 40 | 41 | require './tests' if __FILE__ == $0 42 | -------------------------------------------------------------------------------- /2-special-case-objects/app.rb: -------------------------------------------------------------------------------- 1 | require_relative 'setup' 2 | 3 | class User 4 | def last_subscription 5 | subscriptions.last 6 | end 7 | 8 | def cancel_subscription 9 | if last_subscription 10 | last_subscription.cancel 11 | end 12 | end 13 | end 14 | 15 | class StatusReportJob 16 | def perform 17 | users = {} 18 | User.all.map do |user| 19 | users[user.name] = { 20 | name: last_name(user), 21 | status: last_status(user), 22 | trial_days: last_trial_days(user) 23 | } 24 | end 25 | users 26 | end 27 | 28 | private 29 | 30 | def last_name(user) 31 | if user.last_subscription && user.last_subscription.respond_to?(:name) 32 | user.last_subscription.name 33 | else 34 | 'none' 35 | end 36 | end 37 | 38 | def last_status(user) 39 | if user.last_subscription && user.last_subscription.respond_to?(:status) 40 | user.last_subscription.status 41 | else 42 | '-' 43 | end 44 | end 45 | 46 | def last_trial_days(user) 47 | if user.last_subscription && user.last_subscription.respond_to?(:trial_days) 48 | user.last_subscription.trial_days 49 | else 50 | '0' 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /2-special-case-objects/setup.rb: -------------------------------------------------------------------------------- 1 | class Subscription 2 | def name 3 | 'Monthly Subscription' 4 | end 5 | def status 6 | 'active' 7 | end 8 | def trial_days 9 | 14 10 | end 11 | def cancel 12 | # telling payment gateway... 13 | true 14 | end 15 | end 16 | 17 | class User 18 | attr_accessor :name, :subscriptions 19 | 20 | def initialize(options = {}) 21 | options.each { |k, v| send("#{k}=", v) } 22 | end 23 | 24 | def self.all 25 | suscripto = User.new(name: 'Bob', subscriptions: [Subscription.new]) 26 | no_suscripto = User.new(name: 'Patricio', subscriptions: []) 27 | [suscripto, no_suscripto] 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /2-special-case-objects/test_app.rb: -------------------------------------------------------------------------------- 1 | require_relative "../test_helper.rb" 2 | 3 | require_relative 'app' 4 | 5 | class TestApp < Minitest::Test 6 | def test_status_report_job 7 | response = { 8 | "Bob" => { name: "Monthly Subscription", status: "active", trial_days: 14}, 9 | "Patricio" => { name: "none", status: "-", trial_days: "0"} 10 | } 11 | assert_equal response, StatusReportJob.new.perform 12 | end 13 | 14 | def test_subscription_cancel 15 | user = User.all.first 16 | assert user.cancel_subscription 17 | 18 | user = User.all.last 19 | assert !user.cancel_subscription 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/.solucion-tute-2.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | # 1. Create a class with same initialization arguments as BIGMETHOD 4 | # 2. Copy & Paste the method's body in the new class, with no arguments 5 | # 3. Replace original method with a call to the new class 6 | # 4. Apply "Intention Revealing Method" to the new class. Woot! 7 | 8 | class FormatAtoB 9 | def initialize(file_name:) 10 | @file_name = file_name 11 | @hash = { '1' => {}, '2' => {} } 12 | @dates = [] 13 | end 14 | 15 | def call 16 | read_from_csv 17 | output_parsed_content 18 | end 19 | 20 | private 21 | 22 | attr_reader :file_name, :hash, :dates 23 | 24 | def read_from_csv 25 | CSV.parse(file, col_sep: ';').each do |row| 26 | next if empty_or_comment?(row: row) 27 | process_each(row: row) 28 | end 29 | end 30 | 31 | def process_each(row:) 32 | date = Date.parse(row[2]) 33 | (13..43).each do |i| 34 | measurement_date = date + (i-13) 35 | 36 | # If NumDiasDeChuva is empty it means no data 37 | value = row[7].nil? ? -99.9 : row[i] 38 | status = row[i + 31] 39 | hash_value = [value, status] 40 | 41 | dates << measurement_date 42 | hash[row[1]][measurement_date] = hash_value 43 | end 44 | end 45 | 46 | def output_parsed_content 47 | dates.uniq.map do |date| 48 | if !hash['1'][date].nil? && hash['2'][date].nil? 49 | # Only 'bruto' (good) 50 | value = hash['1'][date] 51 | "#{date}\t#{value[0]}\t#{value[1]}" 52 | elsif hash['1'][date].nil? && !hash['2'][date].nil? 53 | # Only 'consistido' (kind of good) 54 | value = hash['2'][date] 55 | "#{date}\t#{value[0]}\t#{value[1]}" 56 | else 57 | # 'bruto' y 'consistido' (has new and old data) 58 | old_value = hash['1'][date] 59 | new_value = hash['2'][date] 60 | "#{date}\t#{new_value[0]}\t#{old_value[1]}\t#{old_value[0]}" 61 | end 62 | end.join("\n") << "\n" 63 | end 64 | 65 | def empty_or_comment?(row:) 66 | row.empty? || row[0] =~ /^\/\// 67 | end 68 | 69 | def file 70 | File.open file_name, 'r:ISO-8859-1' 71 | end 72 | end 73 | 74 | class Formatter 75 | # More code, methods, and stuff in this big class 76 | 77 | def row_per_day_format(file_name) 78 | FormatAtoB.new(file_name: file_name).call 79 | end 80 | 81 | # More code, methods, and stuff in this big class 82 | end 83 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/.solucion-tute-3.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | class RowPerDayFormatter 4 | NO_DATA = -99.9 5 | 6 | def initialize(file_name) 7 | @file_name = file_name 8 | @dates = [] 9 | @result = { '1' => {}, '2' => {} } 10 | end 11 | 12 | def call 13 | parse_csv_file_and_build_result 14 | parsed_data 15 | end 16 | 17 | private 18 | 19 | attr_reader :dates, :file_name, :result 20 | 21 | def parse_csv_file_and_build_result 22 | file = File.open file_name, 'r:ISO-8859-1' 23 | CSV.parse(file, col_sep: ';').each do |row| 24 | next if row.empty? 25 | next if row[0] =~ /^\/\// 26 | (13..43).each { |i| parse_and_populate_result(row, i) } 27 | end 28 | end 29 | 30 | def parsed_data 31 | dates.uniq.map do |date| 32 | parsed_row_for(date).join("\t") + "\n" 33 | end.join 34 | end 35 | 36 | def parsed_row_for(date) 37 | if bruto?(date) 38 | value = result['1'][date] 39 | [date, value[0], value[1]] 40 | elsif consistido?(date) 41 | value = result['2'][date] 42 | [date, value[0], value[1]] 43 | else 44 | # 'bruto' y 'consistido' (has new and old data) 45 | old_value = result['1'][date] 46 | new_value = result['2'][date] 47 | [date, new_value[0], old_value[1], old_value[0]] 48 | end 49 | end 50 | 51 | def bruto?(date) 52 | !result['1'][date].nil? && result['2'][date].nil? 53 | end 54 | 55 | def consistido?(date) 56 | result['1'][date].nil? && !result['2'][date].nil? 57 | end 58 | 59 | def parse_and_populate_result(row, i) 60 | date = Date.parse(row[2]) 61 | measurement_date = date + (i-13) 62 | 63 | # If NumDiasDeChuva is empty it means no data 64 | value = row[7].nil? ? NO_DATA : row[i] 65 | status = row[i + 31] 66 | 67 | dates << measurement_date 68 | nivel_consistencia = row[1] 69 | result[nivel_consistencia][measurement_date] = [value, status] 70 | end 71 | end 72 | 73 | class Formatter 74 | # More code, methods, and stuff in this big class 75 | 76 | def row_per_day_format(file_name) 77 | RowPerDayFormatter.new(file_name).call 78 | end 79 | 80 | # More code, methods, and stuff in this big class 81 | end 82 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/.solucion-tute.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby -w 2 | # Tute's approach (after applying the pattern to `app.rb`). 3 | # Tests run here in the same way. 4 | 5 | require 'csv' 6 | 7 | class FormatAtoB 8 | SIN_DATOS = -99.9 9 | 10 | def initialize(file_name) 11 | @file_name = file_name 12 | @hash = { '1' => {}, '2' => {} } 13 | @dates = [] 14 | end 15 | 16 | def perform 17 | load_format_a_file 18 | format_data_to_b 19 | end 20 | 21 | private 22 | 23 | def load_format_a_file 24 | file = File.open @file_name, 'r:ISO-8859-1' 25 | CSV.parse(file, col_sep: ';').each do |row| 26 | next if row.empty? || row[0] =~ /^\/\// 27 | load_month_in_hash(row) 28 | end 29 | end 30 | 31 | def format_data_to_b 32 | formatted_rows = @dates.uniq.map { |date| formatted_row_for(date) } 33 | "#{formatted_rows.join("\n")}\n" 34 | end 35 | 36 | def formatted_row_for(date) 37 | old_value = @hash['1'][date] 38 | new_value = @hash['2'][date] 39 | if dato_bruto?(date) 40 | day = [date, old_value[0], old_value[1]] 41 | elsif dato_consistido?(date) 42 | day = [date, new_value[0], new_value[1]] 43 | else # 'bruto' y 'consistido' 44 | day = [date, new_value[0], old_value[1], old_value[0]] 45 | end 46 | day.join("\t") 47 | end 48 | 49 | def load_month_in_hash(row) 50 | @beginning_of_month = Date.parse(row[2]) 51 | (13..43).each do |i| 52 | load_day_in_hash(row, i) 53 | end 54 | end 55 | 56 | def load_day_in_hash(row, i) 57 | nivel_consistencia = row[1] 58 | date = @beginning_of_month + (i - 13) 59 | @dates << date 60 | @hash[nivel_consistencia][date] = datos(row, i) 61 | end 62 | 63 | # If NumDiasDeChuva is empty it means no data 64 | def datos(row, i) 65 | data = row[7].nil? ? SIN_DATOS : row[i] 66 | status = row[i + 31] 67 | [data, status] 68 | end 69 | 70 | def dato_bruto?(date) 71 | @hash['1'][date] && @hash['2'][date].nil? 72 | end 73 | 74 | def dato_consistido?(date) 75 | @hash['1'][date].nil? && @hash['2'][date] 76 | end 77 | end 78 | 79 | class Formatter 80 | def row_per_day_format(file_name) 81 | FormatAtoB.new(file_name).perform 82 | end 83 | end 84 | 85 | require './tests' if __FILE__ == $0 86 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/app.rb: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | # 1. Create a class with same initialization arguments as BIGMETHOD 4 | # 2. Copy & Paste the method's body in the new class, with no arguments 5 | # 3. Replace original method with a call to the new class 6 | # 4. Apply "Intention Revealing Method" to the new class. Woot! 7 | class ExportJob 8 | # More code, methods, and stuff in this big class 9 | 10 | def row_per_day_format(file_name) 11 | file = File.open file_name, 'r:ISO-8859-1' 12 | # hash[NivelConsistencia][date] = [[value, status]] 13 | hash = { '1' => {}, '2' => {} } 14 | dates = [] 15 | str = '' 16 | CSV.parse(file, col_sep: ';').each do |row| 17 | next if row.empty? 18 | next if row[0] =~ /^\/\// 19 | date = Date.parse(row[2]) 20 | (13..43).each do |i| 21 | measurement_date = date + (i-13) 22 | 23 | # If NumDiasDeChuva is empty it means no data 24 | value = row[7].nil? ? -99.9 : row[i] 25 | status = row[i + 31] 26 | hash_value = [value, status] 27 | 28 | dates << measurement_date 29 | hash[row[1]][measurement_date] = hash_value 30 | end 31 | end 32 | 33 | dates.uniq.each do |date| 34 | if !hash['1'][date].nil? && hash['2'][date].nil? 35 | # Only 'bruto' (good) 36 | value = hash['1'][date] 37 | str << "#{date}\t#{value[0]}\t#{value[1]}\n" 38 | elsif hash['1'][date].nil? && !hash['2'][date].nil? 39 | # Only 'consistido' (kind of good) 40 | value = hash['2'][date] 41 | str << "#{date}\t#{value[0]}\t#{value[1]}\n" 42 | else 43 | # 'bruto' y 'consistido' (has new and old data) 44 | old_value = hash['1'][date] 45 | new_value = hash['2'][date] 46 | str << "#{date}\t#{new_value[0]}\t#{old_value[1]}\t#{old_value[0]}\n" 47 | end 48 | end 49 | 50 | str 51 | end 52 | 53 | # More code, methods, and stuff in this big class 54 | end 55 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/fixtures/input.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tute/refactoring-workshop/21b79a233aa80c004c1fa13a2b0b88704d77592c/3-replace-method-with-method-object/fixtures/input.csv -------------------------------------------------------------------------------- /3-replace-method-with-method-object/fixtures/output.csv: -------------------------------------------------------------------------------- 1 | 2009-01-01 -99.9 2 | 2009-01-02 -99.9 3 | 2009-01-03 -99.9 4 | 2009-01-04 -99.9 5 | 2009-01-05 -99.9 6 | 2009-01-06 -99.9 7 | 2009-01-07 -99.9 8 | 2009-01-08 -99.9 9 | 2009-01-09 -99.9 10 | 2009-01-10 -99.9 11 | 2009-01-11 -99.9 12 | 2009-01-12 -99.9 13 | 2009-01-13 -99.9 14 | 2009-01-14 -99.9 15 | 2009-01-15 -99.9 16 | 2009-01-16 -99.9 17 | 2009-01-17 -99.9 18 | 2009-01-18 -99.9 19 | 2009-01-19 -99.9 20 | 2009-01-20 -99.9 21 | 2009-01-21 -99.9 22 | 2009-01-22 -99.9 23 | 2009-01-23 -99.9 24 | 2009-01-24 -99.9 25 | 2009-01-25 -99.9 26 | 2009-01-26 -99.9 27 | 2009-01-27 -99.9 28 | 2009-01-28 -99.9 29 | 2009-01-29 -99.9 30 | 2009-01-30 -99.9 31 | 2009-01-31 -99.9 32 | 2009-02-01 0 33 | 2009-02-02 0 34 | 2009-02-03 6,6 35 | 2009-02-04 12,4 36 | 2009-02-05 0 37 | 2009-02-06 0 38 | 2009-02-07 83,4 39 | 2009-02-08 0 40 | 2009-02-09 0 41 | 2009-02-10 4,2 42 | 2009-02-11 12,4 43 | 2009-02-12 24,4 44 | 2009-02-13 0 45 | 2009-02-14 0 46 | 2009-02-15 0 47 | 2009-02-16 0 48 | 2009-02-17 0 49 | 2009-02-18 0 50 | 2009-02-19 0 51 | 2009-02-20 43,4 52 | 2009-02-21 9,9 53 | 2009-02-22 9,7 54 | 2009-02-23 0 55 | 2009-02-24 0 56 | 2009-02-25 37,1 57 | 2009-02-26 0 58 | 2009-02-27 0 59 | 2009-02-28 0 60 | 2009-03-01 2,4 61 | 2009-03-02 0 62 | 2009-03-03 4,2 63 | 2009-03-04 0 64 | 2009-03-05 0 65 | 2009-03-06 0 66 | 2009-03-07 0 67 | 2009-03-08 0 68 | 2009-03-09 2,1 69 | 2009-03-10 0 70 | 2009-03-11 0 71 | 2009-03-12 0 72 | 2009-03-13 0 73 | 2009-03-14 0 74 | 2009-03-15 0 75 | 2009-03-16 0 76 | 2009-03-17 0 77 | 2009-03-18 0 78 | 2009-03-19 0 79 | 2009-03-20 0 80 | 2009-03-21 0 81 | 2009-03-22 3,2 82 | 2009-03-23 0 83 | 2009-03-24 8,9 84 | 2009-03-25 0 85 | 2009-03-26 0 86 | 2009-03-27 0 87 | 2009-03-28 0 88 | 2009-03-29 0 89 | 2009-03-30 0 90 | 2009-03-31 0 91 | 2009-04-01 0 92 | 2009-04-02 0 93 | 2009-04-03 0 94 | 2009-04-04 0 95 | 2009-04-05 23,4 96 | 2009-04-06 26,8 97 | 2009-04-07 0 98 | 2009-04-08 0 99 | 2009-04-09 13,4 100 | 2009-04-10 0 101 | 2009-04-11 0 102 | 2009-04-12 0 103 | 2009-04-13 0 104 | 2009-04-14 0 105 | 2009-04-15 0 106 | 2009-04-16 0 107 | 2009-04-17 0 108 | 2009-04-18 0 109 | 2009-04-19 0 110 | 2009-04-20 0 111 | 2009-04-21 0 112 | 2009-04-22 33,4 113 | 2009-04-23 16,4 114 | 2009-04-24 0 115 | 2009-04-25 0 116 | 2009-04-26 0 117 | 2009-04-27 0 118 | 2009-04-28 0 119 | 2009-04-29 0 120 | 2009-04-30 0 121 | 2009-05-01 0 122 | 2009-05-02 0 123 | 2009-05-03 32,2 124 | 2009-05-04 0 125 | 2009-05-05 0 126 | 2009-05-06 0 127 | 2009-05-07 0 128 | 2009-05-08 13,4 129 | 2009-05-09 23,6 130 | 2009-05-10 0 131 | 2009-05-11 0 132 | 2009-05-12 0 133 | 2009-05-13 4,2 134 | 2009-05-14 45,6 135 | 2009-05-15 33,6 136 | 2009-05-16 0 137 | 2009-05-17 0 138 | 2009-05-18 0 139 | 2009-05-19 0 140 | 2009-05-20 0 141 | 2009-05-21 0 142 | 2009-05-22 0 143 | 2009-05-23 0 144 | 2009-05-24 0 145 | 2009-05-25 124 146 | 2009-05-26 13,7 147 | 2009-05-27 0 148 | 2009-05-28 4,4 149 | 2009-05-29 18 150 | 2009-05-30 40,4 151 | 2009-05-31 24,5 152 | 2009-06-01 0 153 | 2009-06-02 3,4 154 | 2009-06-03 0 155 | 2009-06-04 0 156 | 2009-06-05 0 157 | 2009-06-06 6,3 158 | 2009-06-07 0 159 | 2009-06-08 0 160 | 2009-06-09 0 161 | 2009-06-10 0 162 | 2009-06-11 8,9 163 | 2009-06-12 3,4 164 | 2009-06-13 0 165 | 2009-06-14 0 166 | 2009-06-15 0 167 | 2009-06-16 31,7 168 | 2009-06-17 0 169 | 2009-06-18 0 170 | 2009-06-19 0 171 | 2009-06-20 0 172 | 2009-06-21 0 173 | 2009-06-22 0 174 | 2009-06-23 3,2 175 | 2009-06-24 46,5 176 | 2009-06-25 0 177 | 2009-06-26 0 178 | 2009-06-27 0 179 | 2009-06-28 0 180 | 2009-06-29 0 181 | 2009-06-30 16,4 182 | 2009-07-01 0 183 | 2009-07-02 17 184 | 2009-07-03 0 185 | 2009-07-04 0 186 | 2009-07-05 6,3 187 | 2009-07-06 1,1 188 | 2009-07-07 0 189 | 2009-07-08 0 190 | 2009-07-09 12,4 191 | 2009-07-10 19,9 192 | 2009-07-11 36,6 193 | 2009-07-12 0 194 | 2009-07-13 0 195 | 2009-07-14 0 196 | 2009-07-15 0 197 | 2009-07-16 15,3 198 | 2009-07-17 72,4 199 | 2009-07-18 0 200 | 2009-07-19 0 201 | 2009-07-20 0 202 | 2009-07-21 11,4 203 | 2009-07-22 29,2 204 | 2009-07-23 0 205 | 2009-07-24 0 206 | 2009-07-25 0 207 | 2009-07-26 0 208 | 2009-07-27 0 209 | 2009-07-28 0 210 | 2009-07-29 3,4 211 | 2009-07-30 0 212 | 2009-07-31 0 213 | 2009-08-01 25,5 214 | 2009-08-02 48,8 215 | 2009-08-03 0 216 | 2009-08-04 0 217 | 2009-08-05 0 218 | 2009-08-06 0 219 | 2009-08-07 0 220 | 2009-08-08 0 221 | 2009-08-09 0 222 | 2009-08-10 0 223 | 2009-08-11 0 224 | 2009-08-12 0 225 | 2009-08-13 0 226 | 2009-08-14 0 227 | 2009-08-15 0 228 | 2009-08-16 0 229 | 2009-08-17 35,5 230 | 2009-08-18 41,1 231 | 2009-08-19 0 232 | 2009-08-20 0 233 | 2009-08-21 0 234 | 2009-08-22 0 235 | 2009-08-23 17,6 236 | 2009-08-24 2,4 237 | 2009-08-25 13,6 238 | 2009-08-26 0 239 | 2009-08-27 0 240 | 2009-08-28 0 241 | 2009-08-29 0 242 | 2009-08-30 0 243 | 2009-08-31 0 244 | 2009-09-01 0 245 | 2009-09-02 0 246 | 2009-09-03 53,7 247 | 2009-09-04 39,5 248 | 2009-09-05 0 249 | 2009-09-06 0 250 | 2009-09-07 0 251 | 2009-09-08 15,4 252 | 2009-09-09 19,3 253 | 2009-09-10 17,8 254 | 2009-09-11 4,4 255 | 2009-09-12 13 256 | 2009-09-13 0 257 | 2009-09-14 0 258 | 2009-09-15 0 259 | 2009-09-16 0 260 | 2009-09-17 3,4 261 | 2009-09-18 0 262 | 2009-09-19 46,3 263 | 2009-09-20 0 264 | 2009-09-21 0 265 | 2009-09-22 0 266 | 2009-09-23 48,5 267 | 2009-09-24 0 268 | 2009-09-25 0 269 | 2009-09-26 0 270 | 2009-09-27 0 271 | 2009-09-28 11,4 272 | 2009-09-29 12,4 273 | 2009-09-30 0 274 | 2009-10-01 1,5 1 275 | 2009-10-02 7,4 1 276 | 2009-10-03 17,4 1 277 | 2009-10-04 13,4 1 278 | 2009-10-05 0 1 279 | 2009-10-06 0 1 280 | 2009-10-07 16 1 281 | 2009-10-08 0 1 282 | 2009-10-09 0 1 283 | 2009-10-10 0 1 284 | 2009-10-11 0 1 285 | 2009-10-12 86,9 1 286 | 2009-10-13 0 1 287 | 2009-10-14 0 1 288 | 2009-10-15 78,6 1 289 | 2009-10-16 4,2 1 290 | 2009-10-17 51 1 291 | 2009-10-18 0 1 292 | 2009-10-19 12,2 1 293 | 2009-10-20 10,4 1 294 | 2009-10-21 0 1 295 | 2009-10-22 0 1 296 | 2009-10-23 50,4 1 297 | 2009-10-24 0 1 298 | 2009-10-25 87,5 1 299 | 2009-10-26 0 1 300 | 2009-10-27 0 1 301 | 2009-10-28 0 1 302 | 2009-10-29 0 1 303 | 2009-10-30 0 1 304 | 2009-10-31 0 1 305 | 2009-11-01 0 1 306 | 2009-11-02 0 1 307 | 2009-11-03 0 1 308 | 2009-11-04 0 1 309 | 2009-11-05 0 1 310 | 2009-11-06 13,4 1 311 | 2009-11-07 16,9 1 312 | 2009-11-08 35,5 1 313 | 2009-11-09 0 1 314 | 2009-11-10 0 1 315 | 2009-11-11 52,4 1 316 | 2009-11-12 22,4 1 317 | 2009-11-13 0 1 318 | 2009-11-14 0 1 319 | 2009-11-15 12,4 1 320 | 2009-11-16 29,2 1 321 | 2009-11-17 0 1 322 | 2009-11-18 0 1 323 | 2009-11-19 0 1 324 | 2009-11-20 0 1 325 | 2009-11-21 42,7 1 326 | 2009-11-22 0 1 327 | 2009-11-23 38,3 1 328 | 2009-11-24 0 1 329 | 2009-11-25 0,4 1 330 | 2009-11-26 0 1 331 | 2009-11-27 9,8 1 332 | 2009-11-28 0 1 333 | 2009-11-29 0 1 334 | 2009-11-30 0 1 335 | 2009-12-01 21,1 1 336 | 2009-12-02 0 1 337 | 2009-12-03 41,2 1 338 | 2009-12-04 0 1 339 | 2009-12-05 0 1 340 | 2009-12-06 0 1 341 | 2009-12-07 0 1 342 | 2009-12-08 24 1 343 | 2009-12-09 0 1 344 | 2009-12-10 0 1 345 | 2009-12-11 0 1 346 | 2009-12-12 63,5 1 347 | 2009-12-13 0 1 348 | 2009-12-14 0 1 349 | 2009-12-15 0 1 350 | 2009-12-16 0 1 351 | 2009-12-17 0 1 352 | 2009-12-18 9,3 1 353 | 2009-12-19 0 1 354 | 2009-12-20 0 1 355 | 2009-12-21 0 1 356 | 2009-12-22 0 1 357 | 2009-12-23 33 1 358 | 2009-12-24 0 1 359 | 2009-12-25 49,1 1 360 | 2009-12-26 0 1 361 | 2009-12-27 0 1 362 | 2009-12-28 0 1 363 | 2009-12-29 0 1 364 | 2009-12-30 0 1 365 | 2009-12-31 0 1 366 | 2010-01-01 0 1 367 | 2010-01-02 0 1 368 | 2010-01-03 10 1 369 | 2010-01-04 0 1 370 | 2010-01-05 0 1 371 | 2010-01-06 0 1 372 | 2010-01-07 6,7 1 373 | 2010-01-08 0 1 374 | 2010-01-09 0 1 375 | 2010-01-10 0 1 376 | 2010-01-11 7,8 1 377 | 2010-01-12 0 1 378 | 2010-01-13 45,4 1 379 | 2010-01-14 0 1 380 | 2010-01-15 0 1 381 | 2010-01-16 0 1 382 | 2010-01-17 18,2 1 383 | 2010-01-18 13,8 1 384 | 2010-01-19 0 1 385 | 2010-01-20 38 1 386 | 2010-01-21 10 1 387 | 2010-01-22 0 1 388 | 2010-01-23 0 1 389 | 2010-01-24 0 1 390 | 2010-01-25 0 1 391 | 2010-01-26 0 1 392 | 2010-01-27 0 1 393 | 2010-01-28 24,6 1 394 | 2010-01-29 0 1 395 | 2010-01-30 0 1 396 | 2010-01-31 0 1 397 | 2010-02-01 0 1 398 | 2010-02-02 0 1 399 | 2010-02-03 0 1 400 | 2010-02-04 0 1 401 | 2010-02-05 0 1 402 | 2010-02-06 0 1 403 | 2010-02-07 0 1 404 | 2010-02-08 0 1 405 | 2010-02-09 24,2 1 406 | 2010-02-10 0 1 407 | 2010-02-11 0 1 408 | 2010-02-12 0 1 409 | 2010-02-13 0 1 410 | 2010-02-14 0 1 411 | 2010-02-15 11 1 412 | 2010-02-16 45,6 1 413 | 2010-02-17 23,4 1 414 | 2010-02-18 0 1 415 | 2010-02-19 0 1 416 | 2010-02-20 0 1 417 | 2010-02-21 0 1 418 | 2010-02-22 0 1 419 | 2010-02-23 0 1 420 | 2010-02-24 0 1 421 | 2010-02-25 16,9 1 422 | 2010-02-26 0 1 423 | 2010-02-27 0 1 424 | 2010-02-28 1,4 1 425 | 2010-03-01 0 1 426 | 2010-03-02 0 1 427 | 2010-03-03 0 1 428 | 2010-03-04 0 1 429 | 2010-03-05 0 1 430 | 2010-03-06 0 1 431 | 2010-03-07 0 1 432 | 2010-03-08 0 1 433 | 2010-03-09 0 1 434 | 2010-03-10 0 1 435 | 2010-03-11 0 1 436 | 2010-03-12 0 1 437 | 2010-03-13 0 1 438 | 2010-03-14 30,3 1 439 | 2010-03-15 76,7 1 440 | 2010-03-16 0 1 441 | 2010-03-17 0 1 442 | 2010-03-18 0 1 443 | 2010-03-19 0 1 444 | 2010-03-20 11,6 1 445 | 2010-03-21 10,3 1 446 | 2010-03-22 1 1 447 | 2010-03-23 55,2 1 448 | 2010-03-24 26,6 1 449 | 2010-03-25 0 1 450 | 2010-03-26 0 1 451 | 2010-03-27 3,2 1 452 | 2010-03-28 3,2 1 453 | 2010-03-29 0 1 454 | 2010-03-30 0 1 455 | 2010-03-31 0 1 456 | 2010-04-01 0 1 457 | 2010-04-02 0 1 458 | 2010-04-03 0 1 459 | 2010-04-04 11,3 1 460 | 2010-04-05 0 1 461 | 2010-04-06 0 1 462 | 2010-04-07 4,8 1 463 | 2010-04-08 0 1 464 | 2010-04-09 0 1 465 | 2010-04-10 0 1 466 | 2010-04-11 0 1 467 | 2010-04-12 0 1 468 | 2010-04-13 0 1 469 | 2010-04-14 0 1 470 | 2010-04-15 0 1 471 | 2010-04-16 0 1 472 | 2010-04-17 0 1 473 | 2010-04-18 0 1 474 | 2010-04-19 0 1 475 | 2010-04-20 0 1 476 | 2010-04-21 0 1 477 | 2010-04-22 60,9 1 478 | 2010-04-23 40,5 1 479 | 2010-04-24 0 1 480 | 2010-04-25 29 1 481 | 2010-04-26 60,1 1 482 | 2010-04-27 31,6 1 483 | 2010-04-28 0 1 484 | 2010-04-29 5,4 1 485 | 2010-04-30 0 1 486 | 2010-05-01 0 1 487 | 2010-05-02 0 1 488 | 2010-05-03 0 1 489 | 2010-05-04 3,4 1 490 | 2010-05-05 0 1 491 | 2010-05-06 0 1 492 | 2010-05-07 0 1 493 | 2010-05-08 58,8 1 494 | 2010-05-09 0 1 495 | 2010-05-10 0 1 496 | 2010-05-11 0 1 497 | 2010-05-12 7,8 1 498 | 2010-05-13 0 1 499 | 2010-05-14 0 1 500 | 2010-05-15 0 1 501 | 2010-05-16 0 1 502 | 2010-05-17 23,2 1 503 | 2010-05-18 46,5 1 504 | 2010-05-19 10 1 505 | 2010-05-20 10,4 1 506 | 2010-05-21 0 1 507 | 2010-05-22 0 1 508 | 2010-05-23 0 1 509 | 2010-05-24 0 1 510 | 2010-05-25 6,4 1 511 | 2010-05-26 0 1 512 | 2010-05-27 0 1 513 | 2010-05-28 0 1 514 | 2010-05-29 0 1 515 | 2010-05-30 0 1 516 | 2010-05-31 14,6 1 517 | 2010-06-01 0 1 518 | 2010-06-02 0 1 519 | 2010-06-03 0 1 520 | 2010-06-04 16,2 1 521 | 2010-06-05 8,2 1 522 | 2010-06-06 0 1 523 | 2010-06-07 0 1 524 | 2010-06-08 0 1 525 | 2010-06-09 0 1 526 | 2010-06-10 0 1 527 | 2010-06-11 0 1 528 | 2010-06-12 0 1 529 | 2010-06-13 0 1 530 | 2010-06-14 0 1 531 | 2010-06-15 0 1 532 | 2010-06-16 0 1 533 | 2010-06-17 0 1 534 | 2010-06-18 0 1 535 | 2010-06-19 0 1 536 | 2010-06-20 5,2 1 537 | 2010-06-21 0 1 538 | 2010-06-22 7,4 1 539 | 2010-06-23 6,9 1 540 | 2010-06-24 0 1 541 | 2010-06-25 0 1 542 | 2010-06-26 0 1 543 | 2010-06-27 0 1 544 | 2010-06-28 0 1 545 | 2010-06-29 0 1 546 | 2010-06-30 0 1 547 | 2010-07-01 0 1 548 | 2010-07-02 0 1 549 | 2010-07-03 0 1 550 | 2010-07-04 0 1 551 | 2010-07-05 0 1 552 | 2010-07-06 0 1 553 | 2010-07-07 0 1 554 | 2010-07-08 0 1 555 | 2010-07-09 0 1 556 | 2010-07-10 13 1 557 | 2010-07-11 0 1 558 | 2010-07-12 0 1 559 | 2010-07-13 22,2 1 560 | 2010-07-14 0 1 561 | 2010-07-15 0 1 562 | 2010-07-16 0 1 563 | 2010-07-17 16 1 564 | 2010-07-18 7 1 565 | 2010-07-19 7,9 1 566 | 2010-07-20 6,3 1 567 | 2010-07-21 0 1 568 | 2010-07-22 44,3 1 569 | 2010-07-23 7,4 1 570 | 2010-07-24 0 1 571 | 2010-07-25 0 1 572 | 2010-07-26 0 1 573 | 2010-07-27 0 1 574 | 2010-07-28 0 1 575 | 2010-07-29 0 1 576 | 2010-07-30 0 1 577 | 2010-07-31 0 1 578 | 2010-08-01 15,3 1 579 | 2010-08-02 0 1 580 | 2010-08-03 0 1 581 | 2010-08-04 5,7 1 582 | 2010-08-05 0 1 583 | 2010-08-06 0 1 584 | 2010-08-07 0 1 585 | 2010-08-08 0 1 586 | 2010-08-09 0 1 587 | 2010-08-10 0 1 588 | 2010-08-11 0 1 589 | 2010-08-12 0 1 590 | 2010-08-13 7,1 1 591 | 2010-08-14 0 1 592 | 2010-08-15 0 1 593 | 2010-08-16 0 1 594 | 2010-08-17 0 1 595 | 2010-08-18 0 1 596 | 2010-08-19 0 1 597 | 2010-08-20 11,3 1 598 | 2010-08-21 0 1 599 | 2010-08-22 0 1 600 | 2010-08-23 0 1 601 | 2010-08-24 0 1 602 | 2010-08-25 0 1 603 | 2010-08-26 0 1 604 | 2010-08-27 0 1 605 | 2010-08-28 0 1 606 | 2010-08-29 0 1 607 | 2010-08-30 0 1 608 | 2010-08-31 0 1 609 | 2010-09-01 0 1 610 | 2010-09-02 0 1 611 | 2010-09-03 0 1 612 | 2010-09-04 0 1 613 | 2010-09-05 0 1 614 | 2010-09-06 0 1 615 | 2010-09-07 0 1 616 | 2010-09-08 0 1 617 | 2010-09-09 0 1 618 | 2010-09-10 0 1 619 | 2010-09-11 0 1 620 | 2010-09-12 0 1 621 | 2010-09-13 0 1 622 | 2010-09-14 16,2 1 623 | 2010-09-15 0 1 624 | 2010-09-16 0 1 625 | 2010-09-17 0 1 626 | 2010-09-18 0 1 627 | 2010-09-19 0 1 628 | 2010-09-20 0 1 629 | 2010-09-21 0 1 630 | 2010-09-22 0 1 631 | 2010-09-23 11,4 1 632 | 2010-09-24 28,6 1 633 | 2010-09-25 3,4 1 634 | 2010-09-26 0 1 635 | 2010-09-27 11,8 1 636 | 2010-09-28 0 1 637 | 2010-09-29 0 1 638 | 2010-09-30 0 1 639 | 2010-10-01 0 1 640 | 2010-10-02 16,6 1 641 | 2010-10-03 0 1 642 | 2010-10-04 14,4 1 643 | 2010-10-05 16,5 1 644 | 2010-10-06 0 1 645 | 2010-10-07 67,9 1 646 | 2010-10-08 0 1 647 | 2010-10-09 0 1 648 | 2010-10-10 0 1 649 | 2010-10-11 0 1 650 | 2010-10-12 0 1 651 | 2010-10-13 0 1 652 | 2010-10-14 0 1 653 | 2010-10-15 12 1 654 | 2010-10-16 41,2 1 655 | 2010-10-17 0 1 656 | 2010-10-18 0 1 657 | 2010-10-19 0 1 658 | 2010-10-20 0 1 659 | 2010-10-21 0 1 660 | 2010-10-22 4,7 1 661 | 2010-10-23 0 1 662 | 2010-10-24 13,6 1 663 | 2010-10-25 0 1 664 | 2010-10-26 0 1 665 | 2010-10-27 0 1 666 | 2010-10-28 0 1 667 | 2010-10-29 0 1 668 | 2010-10-30 68,7 1 669 | 2010-10-31 4,3 1 670 | 2010-11-01 0 1 671 | 2010-11-02 0 1 672 | 2010-11-03 0 1 673 | 2010-11-04 0 1 674 | 2010-11-05 0 1 675 | 2010-11-06 13,4 1 676 | 2010-11-07 0 1 677 | 2010-11-08 0 1 678 | 2010-11-09 0 1 679 | 2010-11-10 44,8 1 680 | 2010-11-11 0 1 681 | 2010-11-12 0 1 682 | 2010-11-13 0 1 683 | 2010-11-14 0 1 684 | 2010-11-15 0 1 685 | 2010-11-16 0 1 686 | 2010-11-17 0 1 687 | 2010-11-18 0 1 688 | 2010-11-19 0 1 689 | 2010-11-20 0 1 690 | 2010-11-21 0 1 691 | 2010-11-22 13,4 1 692 | 2010-11-23 0 1 693 | 2010-11-24 0 1 694 | 2010-11-25 0 1 695 | 2010-11-26 0 1 696 | 2010-11-27 0 1 697 | 2010-11-28 0 1 698 | 2010-11-29 0 1 699 | 2010-11-30 4,4 1 700 | 2010-12-01 0 1 701 | 2010-12-02 0 1 702 | 2010-12-03 74,9 1 703 | 2010-12-04 38,8 1 704 | 2010-12-05 48,5 1 705 | 2010-12-06 66,3 1 706 | 2010-12-07 0 1 707 | 2010-12-08 29,7 1 708 | 2010-12-09 0 1 709 | 2010-12-10 0 1 710 | 2010-12-11 18,4 1 711 | 2010-12-12 36,6 1 712 | 2010-12-13 27,4 1 713 | 2010-12-14 0 1 714 | 2010-12-15 0 1 715 | 2010-12-16 0 1 716 | 2010-12-17 0 1 717 | 2010-12-18 0 1 718 | 2010-12-19 3,4 1 719 | 2010-12-20 0 1 720 | 2010-12-21 18,5 1 721 | 2010-12-22 23,5 1 722 | 2010-12-23 13,7 1 723 | 2010-12-24 0 1 724 | 2010-12-25 12,8 1 725 | 2010-12-26 0 1 726 | 2010-12-27 0 1 727 | 2010-12-28 0 1 728 | 2010-12-29 7,4 1 729 | 2010-12-30 0 1 730 | 2010-12-31 23,2 1 731 | -------------------------------------------------------------------------------- /3-replace-method-with-method-object/test_app.rb: -------------------------------------------------------------------------------- 1 | require_relative "../test_helper.rb" 2 | 3 | require_relative 'app' 4 | 5 | class ExportJobTest < Minitest::Test 6 | def test_transformation 7 | output = ExportJob.new.row_per_day_format path('fixtures/input.csv') 8 | assert_equal output, File.read(path('fixtures/output.csv')) 9 | end 10 | 11 | def path(file_name) 12 | File.expand_path File.join(File.dirname(__FILE__), file_name) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /4-service-objects/.solucion-tute.rb: -------------------------------------------------------------------------------- 1 | # Tute's approach (after applying the pattern to `app.rb`). 2 | # Tests run here in the same way. 3 | 4 | require './setup' 5 | 6 | class SubscriptionService < Struct.new(:user) 7 | def subscribe 8 | api_result = try_api { PaymentGateway.subscribe } 9 | if api_result == :success 10 | user.subscription = :monthly_plan 11 | end 12 | api_result 13 | end 14 | 15 | def unsubscribe 16 | api_result = try_api { PaymentGateway.unsubscribe } 17 | if api_result == :success 18 | user.subscription = nil 19 | end 20 | api_result 21 | end 22 | 23 | private 24 | 25 | # Try API connection, trap and log it on failures 26 | def try_api 27 | yield 28 | rescue SystemCallError => e 29 | # log API connection failure 30 | :network_error 31 | end 32 | end 33 | 34 | class User 35 | attr_accessor :subscription 36 | 37 | # validations 38 | # callbacks 39 | # authentication logic 40 | # notifications logic 41 | 42 | # Podría ser delegate :subscribe, :unsubscribe, to: :subscription_service 43 | 44 | def subscribe 45 | subscription_service.subscribe 46 | end 47 | 48 | def unsubscribe 49 | subscription_service.unsubscribe 50 | end 51 | 52 | private 53 | 54 | def subscription_service 55 | @subscription_service ||= SubscriptionService.new(self) 56 | end 57 | end 58 | 59 | require './tests' if __FILE__ == $0 60 | -------------------------------------------------------------------------------- /4-service-objects/app.rb: -------------------------------------------------------------------------------- 1 | require_relative 'setup' 2 | 3 | class User 4 | attr_accessor :subscription 5 | 6 | # validations 7 | # callbacks 8 | # authentication logic 9 | # notifications logic 10 | 11 | def subscribe 12 | api_result = try_api { PaymentGateway.subscribe } 13 | if api_result == :success 14 | self.subscription = :monthly_plan 15 | end 16 | api_result 17 | end 18 | 19 | def unsubscribe 20 | api_result = try_api { PaymentGateway.unsubscribe } 21 | if api_result == :success 22 | self.subscription = nil 23 | end 24 | api_result 25 | end 26 | 27 | private 28 | 29 | # Try API connection, trap and log it on failures 30 | def try_api 31 | yield 32 | rescue SystemCallError 33 | # log API connection failure 34 | :network_error 35 | end 36 | 37 | # Other private methods 38 | end 39 | -------------------------------------------------------------------------------- /4-service-objects/setup.rb: -------------------------------------------------------------------------------- 1 | class PaymentGateway 2 | def self.subscribe 3 | :success 4 | end 5 | 6 | def self.unsubscribe 7 | :success 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /4-service-objects/test_app.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | require_relative 'app' 3 | 4 | class TestApp < Minitest::Test 5 | def setup 6 | @user = User.new 7 | end 8 | 9 | def test_successful_subscription 10 | assert_equal @user.subscribe, :success 11 | assert_equal :monthly_plan, @user.subscription 12 | 13 | assert_equal @user.unsubscribe, :success 14 | assert_nil @user.subscription 15 | end 16 | 17 | def test_unsuccessful_subscription 18 | PaymentGateway.stub :subscribe, :error do 19 | assert_equal @user.subscribe, :error 20 | end 21 | assert_nil @user.subscription 22 | end 23 | 24 | def test_unsuccessful_unsubscription 25 | assert_equal @user.subscribe, :success 26 | assert_equal :monthly_plan, @user.subscription 27 | 28 | PaymentGateway.stub :unsubscribe, :error do 29 | assert_equal @user.unsubscribe, :error 30 | end 31 | assert_equal :monthly_plan, @user.subscription 32 | end 33 | 34 | def test_network_error_on_subscription 35 | PaymentGateway.stub :subscribe, ->{ raise SystemCallError.new 'Network Error' } do 36 | assert_equal @user.subscribe, :network_error 37 | end 38 | assert_nil @user.subscription 39 | end 40 | 41 | def test_network_error_on_unsubscription 42 | assert_equal @user.subscribe, :success 43 | assert_equal :monthly_plan, @user.subscription 44 | PaymentGateway.stub :unsubscribe, ->{ raise SystemCallError.new 'Network Error' } do 45 | assert_equal @user.unsubscribe, :network_error 46 | end 47 | assert_equal :monthly_plan, @user.subscription 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'minitest' 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | == MIT License 2 | 3 | Copyright (c) 2014 [Tute Costa - tutecosta@gmail.com] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 | # Taller de Refactorización 2 | 3 | Requerimientos: Ruby >= 2.7, git, bundler. 4 | 5 | Cada carpeta lleva por nombre el patrón de refactoring a practicar, y los 6 | siguientes archivos: 7 | 8 | * `app.rb` código sobre el que trabajaremos. 9 | * `setup.rb` código de inicialización necesario para `app.rb` (sería el 10 | "framework", o el resto de la aplicación). No necesitamos conocerlo o editarlo, 11 | a no ser que sea por curiosidad. 12 | * `test_app.rb` el nombre lo explica. Necesarios para la vida, especialmente si 13 | pensamos refactorizar. 14 | 15 | Ejecutar `bundle` para asegurarse de tener las rubygems necesarias para correr 16 | tests. 17 | 18 | Ejecutar tests via `rake`: 19 | 20 | ```bash 21 | $ rake 22 | Run options: --seed 61694 23 | 24 | # Running: 25 | 26 | .......... 27 | 28 | Finished in 0.016857s, 593.2254 runs/s, 1423.7409 assertions/s. 29 | 30 | 10 runs, 24 assertions, 0 failures, 0 errors, 0 skips 31 | ``` 32 | 33 | # Patrones de Refactorización 34 | 35 | ## 1. Intention Revealing Method 36 | 37 | ## 2. Special Case Object 38 | 39 | ## 3. Replace Method with Method Object 40 | 41 | Nota de implementación: contiene dos archivos adicionales necesarios para los 42 | tests: `fixtures/input.csv` y `fixtures/output.csv`, y no contiene `setup.rb`. 43 | 44 | ## 4. Service Object 45 | 46 | 47 | # Slides 48 | 49 | * [Slides (English)](https://github.com/tute/refactoring-workshop-slides-sep-2015/) 50 | * [Slides (Español)](https://www.tutecosta.com/refactoring-workshop-slides-sep-2015/#/) [repo](https://github.com/tute/refactoring-workshop-slides-sep-2015/tree/es) 51 | 52 | # In English 53 | 54 | [English version of this doc available](https://github.com/tute/refactoring-workshop/blob/master/README.md). 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Refactoring Workshop 2 | 3 | Requirements: Ruby >= 2.7, git, `minitest` gem. 4 | 5 | Each folder is named after the refactoring pattern they tackle, and contains: 6 | 7 | * `app.rb` code we'll refactor. 8 | * `setup.rb` necessary code for `app.rb` to run (mimicks a SUPER BASIC Rails) 9 | We don't need to see/edit it unless just curious. :-) 10 | * `test_app.rb` self explanatory. Necessary for daily life, specially for 11 | refactoring. 12 | 13 | Run `bundle` to make sure you have the gems needed for running tests (`rake` 14 | and `minitest`). 15 | 16 | Run tests running `rake`: 17 | 18 | ```bash 19 | $ rake 20 | Run options: --seed 61694 21 | 22 | # Running: 23 | 24 | .......... 25 | 26 | Finished in 0.016857s, 593.2254 runs/s, 1423.7409 assertions/s. 27 | 28 | 10 runs, 24 assertions, 0 failures, 0 errors, 0 skips 29 | ``` 30 | 31 | # Refactoring Patterns 32 | 33 | ## 1. Intention Revealing Method 34 | 35 | ## 2. Special Case Object 36 | 37 | ## 3. Replace Method with Method Object 38 | 39 | Implementation note: folder contains two fixture files needed for tests, 40 | `fixtures/input.csv` and `fixtures/output.csv`, and doesn't include `setup.rb`. 41 | 42 | ## 4. Service Object 43 | 44 | 45 | # Slides 46 | 47 | * [Slides (English)](https://github.com/tute/refactoring-workshop-slides-sep-2015/) 48 | * [Slides (Español)](https://www.tutecosta.com/refactoring-workshop-slides-sep-2015/#/) [repo](https://github.com/tute/refactoring-workshop-slides-sep-2015/tree/es) 49 | 50 | # In Spanish 51 | 52 | [Spanish version of this doc available](https://github.com/tute/refactoring-workshop/blob/master/README.es.md). 53 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'rake/testtask' 4 | 5 | Rake::TestTask.new(:test) do |t| 6 | t.libs = FileList['*-*/'] 7 | t.test_files = FileList['*/test_*.rb'] 8 | end 9 | 10 | task default: :test 11 | -------------------------------------------------------------------------------- /test_helper.rb: -------------------------------------------------------------------------------- 1 | require "minitest/autorun" 2 | --------------------------------------------------------------------------------