Skip to content

Instantly share code, notes, and snippets.

@carolineartz
Last active February 27, 2018 15:50
Show Gist options
  • Save carolineartz/5e6f831dc8b5366b64d23c5daa9cd142 to your computer and use it in GitHub Desktop.
Save carolineartz/5e6f831dc8b5366b64d23c5daa9cd142 to your computer and use it in GitHub Desktop.
example testing out `ActiveRecord::Aggregations`

Usage

# in your controller you can use
@domain = CreateDomains.new('car.oline.codes').call #=> returns Domain with name 'car' that is the child of `oline.codes`
@domain.web_address.to_s #=> 'car.oline.codes'

You can use your logic for subtree equality to compare the actual web_address value objects, which are immutable. You could also see all the 'parent' strings for a given web_address object

@domain = CreateDomains.new('c.a.r.oline.codes').call
web_address = @domain.web_address #=> array of WebAddress objects
parent_addresses = web_address.parent_addresses
# and mapping to_s would give you
parent_addresses.map(&:to_s) #=> ["oline.codes", "r.oline.codes", "a.r.oline.codes"]

# so you can check for inclusion in parent addresses from some string like
parent_address = WebAddress.from_string('r.oline.codes')
parent_addresses.include?(parent_address) #=> true
# this is your service you call from the controller
class CreateDomains
def initialize(input)
@input = input
end
def call
create_base
create_children
end
private
def create_children
Domain.transaction do
child_domains = chunks[0..-3].reverse
child_domains.inject(base) do |parent, domain|
parent.children.find_or_create_by!(name: domain)
end
end
end
def create_base
base = chunks.last(2).join('.')
Domain.find_or_create_by!(name: base)
end
def base
@base ||= create_base
end
def chunks
@chunks ||= @input.split('.')
end
end
# == Schema Information
#
# Table name: domains
#
# id :integer not null, primary key
# name :string
# ancestry :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Domain < ApplicationRecord
has_ancestry
composed_of :web_address,
mapping: [%w(ancestry parents), %w(name child)],
constructor: ->(ancestry, name) {
name_path = (ancestry || "").split('/').map { |parent_id| find(parent_id).name }
WebAddress.new(name_path, name)
},
converter: :from_string
end
# this is the value object
class WebAddress
include Comparable # only if you define use <=>
attr_reader :parents, :root
def self.from_string(str_address)
first, *middle, base, tld = str_address.split(".")
return new([], str_address) unless tld.present? # it's a root domain blah.com
new(["#{base}.#{tld}"] + middle.reverse, first) # this is realllly sloppy and could be a LOT clearer
end
def initialize(parents, child)
if parents.any?
@parents = parents
@root, *chunks = parents
@child = child
else
@parents = []
@root = child
end
end
def ==(other)
to_s == other.to_s
end
# could add some comparison methods...you could straight up use the spaceship and customize the return logic...
def <=>(other)
# could return 1 (greater than) if the length is longer? (i.e., other.to is a substrign of self.to_s)
return unless same_root?(other) # return nil if the two shouldn't be "comparable"
end
def same_root?(other)
root == other.root
end
def parent_addresses
@parents[1..-1].each_with_object([WebAddress.from_string(@root)]) do |subdomain, addresses|
addresses << WebAddress.from_string("#{subdomain}.#{addresses.last}")
end
end
def to_s
return root unless parents.any?
(parents + [@child]).compact.reverse.join('.')
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment