Skip to content

Instantly share code, notes, and snippets.

@jystewart
Created June 10, 2012 16:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jystewart/2906430 to your computer and use it in GitHub Desktop.
Save jystewart/2906430 to your computer and use it in GitHub Desktop.
Tag code cleanup sketches
# A class to filter input parameters coming from a form in rails
# and collate them into a form suitable for use with a taggable
# object.
#
# This will mutate the params hash it is given.
#
# This could be used in a controller to take input that represents
# tags under its various names and collapse them to hand to a domain
# model that only knows about tags/tag_ids. Used with a decorator
# such as https://gist.github.com/2906392 it could handle everything
# if we wanted to keep knowledge of section/audience/etc. outside of
# the domain objects.
class TagCollationService
attr_accessor :our_parameters
def initialize(params, key)
@our_parameters = params[key]
end
def consolidate_tag_params
general_tags = []
special_tags = []
our_parameters.each do |key, value|
if key.match(/_tags$/)
general_tags += our_parameters.delete(key)
elsif key.match(/primary_(.+?)_tag/)
special_tags += our_parameters.delete(key)
end
end
general_tags -= special_tags
special_tags + general_tags
end
def mutate_params
our_parameters[:tag_ids] = consolidate_tag_params
end
end
require 'minitest/autorun'
class TaggingTest < MiniTest::Unit::TestCase
def core_params
params = {
artefact: {
slug: 'a',
name: 'a',
kind: 'answer',
section_tags: ['crime/the-police', 'blah/blah-blah'],
primary_section_tag: ['crime/hospitals']
}
}
end
def test_compiles_section_tags
ts = TagCollationService.new(core_params, :artefact)
ts.mutate_params
refute params[:artefact][:section_tags]
refute params[:artefact][:primary_section_tag]
assert params[:artefact][:tag_ids] = ['crime/hospitals', 'crime/the-police', 'blah/blah-blah']
end
test "can set sections" do
a = Artefact.create!(slug => "a", name: "a", kind: "answer", need_id: 1, owning_app: 'x')
a.sections = ['crime', 'crime/the-police']
a.primary_section = 'crime'
a.reconcile_tags
assert_equal ['crime', 'crime/the-police'], a.tag_ids, 'Mismatched tags'
assert_equal ['crime', 'crime/the-police'], a.sections, 'Mismatched sections'
assert_equal 'Crime', a.section
end
end
# An unfinished sketch of a module to add 'taggable' behaviour to
# an item in the gov.uk publishing domain model. It won't work as is
# but is intended to demonstrate one mechanism for representing
# tagging.
#
# This could work and is quite a rails-y way to do it, but it feels
# like it's perhaps doing too much work to tie together the domain
# model and its persistence and so it'd be worth exploring alternatives.
#
module Taggable
module ClassMethods
def stores_tags_for(*keys)
keys.map!(&:to_s)
keys.each { |k|
attr_accessor k
# define_method "#{k.singularize}=" do |values|
# # tag_ids.clear
# # tags.clear
# values.each do |value|
# tag_id, tag = Tag.id_and_entity(value)
# tag_ids.push(tag_id) unless tag_ids.include?(tag_id)
# tags.push(tag_id) unless tags.include?(tag)
# end
# end
# define_method "#{k.singularize}_ids" do
# tags.select { |t| t.tag_type == k.singularize }.collect(&:tag_id)
# end
# define_method k do
# tags.select { |t| t.tag_type == k.singularize }
# end
}
self.tag_keys = keys
end
def has_primary_tag_for(key)
raise "Only one primary tag type allowed" unless key.is_a?(Symbol)
method_name = "primary_#{key.to_s.singularize}"
attr_accessor method_name
# define_method "#{method_name}=" do |value|
# tag_id, tag = Tag.id_and_entity(value)
# tag_ids.delete(tag_id)
# tag_ids.unshift(tag_id)
# tags.delete(tag)
# tags.unshift(tag)
# end
# define_method method_name do
# __send__(key.to_s.pluralize).first
# end
end
end
def self.included(klass)
klass.extend ClassMethods
klass.field :tag_ids, type: Array, default: []
klass.attr_protected :tags, :tag_ids
klass.cattr_accessor :tag_keys, :primary_tag_keys
klass.private :tags, :tag_ids
end
def reconcile_tags
general_tags = []
special_tags = []
self.class.tag_keys.each do |key|
general_tags += __send__(key).to_a
end
self.class.primary_tag_keys.each do |key|
special_tags << __send__("primary_#{key.to_s.singularize}")
end
# Don't duplicate tags
general_tags -= special_tags
# Fill up tag_ids
self.tag_ids = (special_tags + general_tags).reject { |t| t.blank? }
end
# TODO: Work out best way to memoise this
def tags
TagRepository.load_all_with_ids(tag_ids).to_a
end
def save
reconcile_tags
parent
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment