Skip to content

Instantly share code, notes, and snippets.

@luizfonseca
Last active March 7, 2018 14:21
Show Gist options
  • Save luizfonseca/8e58cad29b27d5a20d2343bba772ed9f to your computer and use it in GitHub Desktop.
Save luizfonseca/8e58cad29b27d5a20d2343bba772ed9f to your computer and use it in GitHub Desktop.
BaseRepository example
require 'csv'
# There are a bunch of ways of doing this class
# There's not one 'best' way, but different approaches
class BaseRepository
# This is an initialize method that is
# Common for 90% of the Repositories
# It just takes a CSV file, and creates
# @elements empty list (array)
def initialize(csv_file)
@elements = []
@csv_file = csv_file
end
# Nothing, just return all elements, that's it
def all
@elements
end
# Take a look in the documentation, find
# returns the object that matched with the condition
# inside (in this case, the ID of the MEAL to be equal to the ID in
# the method definition)
def find(id)
@elements.find { |el| el.id == id }
end
# Just add the element(meal, customer, employee, whatever)
# To the elements array and setup the next id
# I removed the sync from here, because it's up to the
# child repository to find out if he wants to use it
# That's why we can call SUPER on the CHILDREN, it will just
# execute these 2 lines, then give the control back to you
def add(element)
element.id = next_id
@elements << element
end
private
# Just finding out the next_id,
# If @elements is empty, 1
# If @elements has (meals/customer/employee),
# get the last ID from the item
def next_id
@elements.empty? ? 1 : @elements.last.id + 1
end
# This is a weird thing, right?
# It's just a method that receives one argument: a CLASS itself
# Not a string, not an array, but a class
# So we can call .NEW on it - pretty cool, right?
# Look, the name in the method definition is up to you
# I decided for klass, but you can call it class_name and
# it will work the same way
def sync_csv(klass)
CSV.open(@csv_file, 'wb') do |csv|
# This line just find all instance variables and returns them as strings
# Inside an array (['@name', '@price'])
# We just remove the "@" from the string ([1..-1] instead of [0..-1])
csv << klass.new.instance_variables.map { |x| x[1..-1] }
@elements.each do |element|
# This is tricky right?
# YIELD is quite simple once you understand it.
# If you use `DO` on this method
# E.g.: `sync_csv DO |some_variable|`
# This will call yield - and because we passed a variable to yield
# We have access to this variable (to do whatever we want) all the
# way up there in the children class
# So we are just converting this yield to whatever we pass
# INSIDE the block there in the children.
# This will be converted to whatever you want,
# In our case, it's ["Something", "Something", "Something"]
# Look at the child class (MealRepository, CustomerRepository)
csv << yield(element)
end
end
end
# Again, we receive a CLASS as an argument when we call it
# it's just a variable name, call whatever you want
# This method only is only used if we need to convert the rows
# to a Ruby format
# Like "true" to true (boolean) and "1" to 1 (integer)
def load_csv(klass)
csv_options = { headers: :first_row, header_converters: :symbol }
# We dont run anything, just skip - if the file doesnt exist
return unless File.exist?(@csv_file)
CSV.foreach(@csv_file, csv_options) do |row|
row[:id] = row[:id].to_i
# Again, if you use DO on this method
# E.g.: `load_csv(Meal) do |some_variable|`
# It will replace the value of yield with the value
# inside your DO block
# BLOCK_GIVEN? means: only run this line if we use `DO` on this
# method
yield(row) if block_given?
@elements << klass.new(row)
end
end
end
require_relative '../models/customer'
require_relative 'base_repository'
require 'csv'
# We are inheriting from the Base repository
# That has ALL methods that we need
# We are only overriding the ones we actually
# need to change
class CustomerRepository < BaseRepository
def initialize(csv)
super(csv)
load_csv Customer
end
def add(element)
super
sync_csv Customer do |customer|
[customer.id, customer.name, customer.address]
end
end
end
require_relative "../models/meal"
require_relative 'base_repository'
require 'csv'
class MealRepository < BaseRepository
def initialize(csv)
super(csv)
load_csv Meal do |row|
row[:price] = row[:price].to_i
end
end
def add(element)
super
sync_csv Meal do |meal|
[meal.id, meal.name, meal.price]
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment