Skip to content

Instantly share code, notes, and snippets.

@sshaw
Last active November 11, 2017 23:12
Show Gist options
  • Save sshaw/6ac0f489f5c58fe9fd033659f0a22147 to your computer and use it in GitHub Desktop.
Save sshaw/6ac0f489f5c58fe9fd033659f0a22147 to your computer and use it in GitHub Desktop.
Examples on the ways to separate an ActiveRecord domain model from UI layer in Rails/Ruby.
module PropertyManagement
class OnBoarding
#
# **********
# Setup
# **********
#
# ActiveRecord: None
# ActiveModel+freeze: None
# Hash: None, but param massaging may be necessary unless everything matches ActiveRecord
# which I'm sure in some (many?) cases it shouldn't or wont.
# Data transfer object:
#
class2 :building_object => Building.attribute_names.dup.concat([
:rentals => Rental.attribute_names
])
# Draper:
class BuildingObject < Draper::Decorator
# This will delegate save(), update() etc... which we don't want to do
# delegate_all
delegate *Building.attribute_names
decorates_association :rentals
end
#
# **********
# Retrieving Records
# **********
#
def buildings(id)
# ActiveRecord
Building.find(id)
# ActiveModel + freeze:
Building.find(id).freeze
# Hash
Building.find(id).as_json(:include_root => false)
# Data transfer object
BuildingObject.new(
Building.find(id).as_json(:include_root => false,
:include => [:rentals])
)
# Draper
Building.find(id).decorate
end
def search_buildings(term)
return [] unless term.present?
# In all cases Building may be replaced with another class to handle
# data fetching and/or whatever logic
# ActiveRecord
Building.includes(:rentals).where("addr_street like :term or landmark_name like :term", :term => "%#{term}%").limit(10).to_a
# ActiveModel + freeze:
Building.includes(:rentals).where("addr_street like :term or landmark_name like :term", :term => "%#{term}%").limit(10).map(&:freeze)
# Hash
Building.includes(:rentals).where("addr_street like :term or landmark_name like :term", :term => "%#{term}%").limit(10).map(&:as_json)
# Data transfer object
Building.includes(:rentals).where("addr_street like :term or landmark_name like :term", :term => "%#{term}%").limit(10).map do |b|
BuildingObject.new(b.as_json(:include_root => false, :includes => [:rentals]))
end
# Draper:
# Note: as_json can be called on Draper collections
Building.includes(:rentals).where("addr_street like :term or landmark_name like :term", :term => "%#{term}%").limit(10).decorate
end
end
#
# **********
# Saving Records
# **********
#
# ActiveRecord:
# company here has not been retrieved from DB. We only do that stuff in here.
def update_company(company)
CompanyDS.update(company)
end
# ActiveMddel + freeze: Any of these examples
# Hash:
def update_company(id, attributes)
Company.new(attributes) { |c| c.id = id }
# can also do update(attributes), but attributes may need massaging
CompanyDS.update(company)
end
# Data access object:
def update_company(company)
# company here is PORO
# can also do update(company)
CompanyDS.update(company.to_h)
end
# Draper: Any of the above. Would have to use model or DraperDecorator that accepts model
end
@sshaw
Copy link
Author

sshaw commented Aug 15, 2017

Class2 can be found here: https://github.com/sshaw/class2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment