Skip to content

Instantly share code, notes, and snippets.

@mehmetboraezer
Forked from ayrton/taggable.rb
Created October 19, 2018 12:29
Show Gist options
  • Save mehmetboraezer/28ad18e0d9f92e8d83cf282b682d6c00 to your computer and use it in GitHub Desktop.
Save mehmetboraezer/28ad18e0d9f92e8d83cf282b682d6c00 to your computer and use it in GitHub Desktop.
Testing Active Support concerns with rspec in isolation. See first comment for more information.
module Taggable
extend ActiveSupport::Concern
included do
attr_accessible :tags
after_save :set_tags
after_destroy :unset_tags
end
module ClassMethods
def tags
$redis.smembers("#{self.table_name}:tags").sort
end
end
def tags=(tags)
@tags = tags.map(&:downcase)
end
def tags
@tags ||= $redis.smembers("#{self.class.table_name}:#{id}:tags")
end
def related_tag_ids
tags.inject([]) { |total, tag|
total |= $redis.smembers("#{self.class.table_name}:tags:#{tag}")
} - [id.to_s]
end
def find_related_tags
self.class.where(id: related_tag_ids)
end
private
def set_tags
return if tags.empty?
$redis.sadd("#{self.class.table_name}:tags", tags)
$redis.sadd("#{self.class.table_name}:#{id}:tags", tags)
tags.each do |tag|
$redis.sadd("#{self.class.table_name}:tags:#{tag}", id)
end
end
def unset_tags
return if tags.empty?
tags.each do |tag|
$redis.srem("#{self.class.table_name}:tags:#{tag}", id)
$redis.srem("#{self.class.table_name}:tags", tag) if $redis.scard("#{self.class.table_name}:tags:#{tag}").zero?
end
$redis.del("#{self.class.table_name}:#{id}:tags")
end
end
require 'spec_helper'
describe Taggable do
let!(:t0) { Train.create(tags: %w(rails track)) }
let!(:t1) { Train.create(tags: %w(track cargo)) }
let!(:t2) { Train.create(tags: %w(vehicle)) }
let!(:t3) { Train.create(tags: %w(RaIlS VEHICLE)) }
describe 'class methods' do
describe 'tags' do
it 'returns the four tags sorted' do
expect(Train.tags).to eq(%w(cargo rails track vehicle))
end
end
end
describe 'instance methods' do
describe 'related_tag_ids' do
it { expect(t0.related_tag_ids.map(&:to_i)).to include(t1.id, t3.id) }
it { expect(t0.related_tag_ids.map(&:to_i)).not_to include(t0.id, t2.id) }
end
end
# ...
end
class Train
include ActiveModel::MassAssignmentSecurity, ActiveSupport::Callbacks
define_callbacks :create, :destroy
def self.after_create(*filters, &blk)
set_callback(:create, :after, *filters, &blk)
end
def self.create(args = {})
new(args).tap do |obj|
obj.create
end
end
def self.before_destroy(*filters, &blk)
set_callback(:destroy, :before, *filters, &blk)
end
def self.table_name
'trains'
end
class_attribute :instances
attr_accessor :id
def initialize(args)
self.class.instances ||= {}
self.id = self.class.instances.keys.empty? ? 1 : self.class.instances.keys.last + 1
args.each do |attr, value|
self.send("#{attr}=".to_sym, value)
end
self.class.instances = { id => self }
end
def create
run_callbacks :create
end
def destroy
run_callbacks :destroy
end
include Taggable
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment