Skip to content

Commit

Permalink
extract(ish) exhibits from OOR code, get tests running
Browse files Browse the repository at this point in the history
  • Loading branch information
codeodor committed May 2, 2012
1 parent d784180 commit a2d9d99
Show file tree
Hide file tree
Showing 38 changed files with 1,545 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Gemfile
@@ -0,0 +1,6 @@
source 'http://rubygems.org'
group :development, :test do
gem 'minitest', '~> 2.12.0'
gem 'rr'
gem 'activerecord-nulldb-adapter', :git => "git://github.com/nulldb/nulldb.git"
end
36 changes: 36 additions & 0 deletions Gemfile.lock
@@ -0,0 +1,36 @@
GIT
remote: git://github.com/nulldb/nulldb.git
revision: d17674e68c80cb8bb0351ff666797b9f34f9a877
specs:
activerecord-nulldb-adapter (0.2.1)
activerecord (>= 2.0.0)

GEM
remote: http://rubygems.org/
specs:
activemodel (3.2.3)
activesupport (= 3.2.3)
builder (~> 3.0.0)
activerecord (3.2.3)
activemodel (= 3.2.3)
activesupport (= 3.2.3)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.3)
i18n (~> 0.6)
multi_json (~> 1.0)
arel (3.0.2)
builder (3.0.0)
i18n (0.6.0)
minitest (2.12.1)
multi_json (1.3.4)
rr (1.0.4)
tzinfo (0.3.33)

PLATFORMS
ruby

DEPENDENCIES
activerecord-nulldb-adapter!
minitest (~> 2.12.0)
rr
26 changes: 26 additions & 0 deletions Rakefile
@@ -1 +1,27 @@
require 'rake/testtask'
require_relative 'tasks/gemspec'

namespace 'test' do
test_files = FileList['spec/**/*_spec.rb']
integration_test_files = FileList['spec/**/*_integration_spec.rb']
unit_test_files = test_files - integration_test_files

desc "Run unit tests"
Rake::TestTask.new('unit') do |t|
t.libs.push "lib"
t.test_files = unit_test_files
t.verbose = true
end

desc "Run integration tests"
Rake::TestTask.new('integration') do |t|
t.libs.push "lib"
t.test_files = integration_test_files
t.verbose = true
end
end

#Rake::Task['test'].clear
desc "Run all tests"
task 'test' => %w[test:unit test:integration]
task 'default' => 'test'
53 changes: 53 additions & 0 deletions lib/display-case/enumerable_exhibit.rb
@@ -0,0 +1,53 @@
require_relative 'exhibit'

class EnumerableExhibit < Exhibit
include Enumerable

def self.applicable_to?(object)
# ActiveRecord::Relation, surprisingly, is not Enumerable. But it
# behaves sufficiently similarly for our purposes.
object_is_any_of?(object, 'Enumerable', 'ActiveRecord::Relation')
end

# Wrap an Enumerable method which returns another collection
def self.exhibit_enum(*method_names, &post_process)
post_process ||= ->(result){exhibit(result)}
method_names.each do |method_name|
define_method(method_name) do |*args, &block|
result = __getobj__.public_send(method_name, *args, &block)
instance_exec(result, &post_process)
end
end
end
private_class_method :exhibit_enum

exhibit_query :[], :fetch, :slice, :values_at, :last
exhibit_enum :select, :grep, :reject, :to_enum, :sort, :sort_by, :reverse
exhibit_enum :partition do |result|
result.map{|group| exhibit(group)}
end
exhibit_enum :group_by do |result|
result.inject({}) { |h,(k,v)|
h.merge!(k => exhibit(v))
}
end

def each(*)
super do |e|
yield exhibit(e)
end
end

# `render '...', :collection => self` will call #to_ary on this
# before rendering, so we need to be prepared.
def to_ary
self
end

def render(template)
inject(ActiveSupport::SafeBuffer.new) { |output,element|
output << element.render(template)
}
end

end
129 changes: 129 additions & 0 deletions lib/display-case/exhibit.rb
@@ -0,0 +1,129 @@
require 'delegate'
require 'active_support/core_ext'

class Exhibit < SimpleDelegator
def self.exhibits
[
EnumerableExhibit,
BlogExhibit,
TextPostExhibit,
PicturePostExhibit,
LinkExhibit,
TagListExhibit
]
end

def self.exhibit(object, context)
return object if exhibited_object?(object)
Rails.logger.debug "Exhibiting #{object.inspect}"
Rails.logger.debug "Exhibit context: #{context}"
object = Exhibited.new(object, context)
exhibits.inject(object) do |object, exhibit_class|
exhibit_class.exhibit_if_applicable(object, context)
end.tap do |obj|
Rails.logger.debug "Exhibits applied: #{obj.inspect_exhibits}"
end
end

def self.exhibit_if_applicable(object, context)
if applicable_to?(object)
new(object, context)
else
object
end
end

def self.applicable_to?(object)
false
end

def self.exhibited_object?(object)
object.respond_to?(:exhibited?) && object.exhibited?
end

def self.exhibit_query(*method_names)
method_names.each do |name|
define_method(name) do |*args, &block|
exhibit(super(*args, &block))
end
end
end
private_class_method :exhibit_query

# A helper for matching models to classes/modules, intended for use
# in .applicable_to?.
def self.object_is_any_of?(object, *classes)
# What with Rails development mode reloading making class matching
# unreliable, plus wanting to avoid adding dependencies to
# external class definitions if we can avoid it, we just match
# against class/module name strings rather than the actual class
# objects.

# Note that '&' is the set intersection operator for Arrays.
(classes.map(&:to_s) & object.class.ancestors.map(&:name)).any?
end
private_class_method :object_is_any_of?

attr_reader :context

def initialize(model, context)
@context = context
super(model)
end

alias_method :__class__, :class
def class
__getobj__.class
end

def exhibit(model)
Exhibit.exhibit(model, context)
end

def to_partial_path
if __getobj__.respond_to?(:to_partial_path)
__getobj__.to_partial_path
else
partialize_name(__getobj__.class.name)
end
end

def render(template)
template.render(:partial => to_partial_path, :object => self)
end

def exhibit_chain
inner_exhibits = defined?(super) ? super : []
[__class__] + inner_exhibits
end

def inspect_exhibits
exhibit_chain.map(&:to_s).join(':')
end

def inspect
"#{inspect_exhibits}(#{__getobj__.inspect})"
end

def exhibited?
true
end

private

# The terminator for the exhibit chain, and a marker that an object
# has been through the exhibit process
class Exhibited < Exhibit
def exhibit_chain
[]
end

def to_model
__getobj__
end
end

def partialize_name(name)
"/#{name.underscore.pluralize}/#{name.demodulize.underscore}"
end
end
5 changes: 5 additions & 0 deletions lib/display-case/exhibits_helper.rb
@@ -0,0 +1,5 @@
module ExhibitsHelper
def exhibit(model, context=self)
Exhibit.exhibit(model, context)
end
end
26 changes: 26 additions & 0 deletions spec/exhibits/blog_exhibit_spec.rb
@@ -0,0 +1,26 @@
require_relative '../spec_helper_lite'
require_relative '../fixtures/exhibits/blog_exhibit'

describe BlogExhibit do
subject { BlogExhibit.new(blog, context) }
let(:blog) { OpenStruct.new(:tags => tags) }
let(:tags) { Object.new }
let(:context) { Object.new }

it 'exhibits its tags' do
exhibited_tags = Object.new
mock(subject).exhibit(tags) { exhibited_tags }
subject.tags.must_be_same_as(exhibited_tags)
end

describe '#filter_by_tag' do
it 'exhibits the result' do
exhibited_blog = Object.new
filtered_blog = Object.new
mock(blog).filter_by_tag("foo") { filtered_blog }
mock(subject).exhibit(filtered_blog) { exhibited_blog }
subject.filter_by_tag("foo").must_be_same_as(exhibited_blog)

end
end
end

0 comments on commit a2d9d99

Please sign in to comment.