exhibitionist 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +91 -0
- data/Rakefile +12 -0
- data/exhibitionist.gemspec +21 -0
- data/lib/exhibitionist.rb +32 -0
- data/lib/exhibitionist/base.rb +30 -0
- data/lib/exhibitionist/version.rb +3 -0
- data/spec/exhibitionist/base_spec.rb +74 -0
- data/spec/exhibitionist_spec.rb +70 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/test_exhibits/empty_exhibit.rb +2 -0
- data/spec/support/test_exhibits/shout_exhibit.rb +9 -0
- data/spec/support/test_exhibits/stutter_exhibit.rb +9 -0
- data/spec/support/test_exhibits/whisper_exhibit.rb +9 -0
- metadata +86 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Najaf Ali
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
# Exhibitionist
|
3
|
+
|
4
|
+
Super-lightweight decorators, as inspired by Avdi Grimms [Objects on Rails](http://objectsonrails.com/).
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'exhibitionist'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install exhibitionist
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
An Exhibit is just a subclass of SimpleDelegator, organized via the Exhibitionist module.
|
23
|
+
|
24
|
+
Create exhibits by subclassing ```Exhibitionist::Base``:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class StringExhibit < Exhibitionist::Base
|
28
|
+
# this exhibit applies to objects that respond to :to_s
|
29
|
+
applies_if {|object| object.responds_to?(:to_s) }
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Here are some example exhibits.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class ShoutExhibit < StringExhibit
|
37
|
+
def shout
|
38
|
+
__getobj__.to_s.upcase
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class WhisperExhibit < StringExhibit
|
43
|
+
def whisper
|
44
|
+
"<whisper>#{__getobj__.to_s.downcase}</whisper>"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ExclaimExhibit < StringExhibit
|
49
|
+
def exclaim
|
50
|
+
"#{__getobj__.to_s}!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
Another exhibit, that applies to nothing:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class NeverAppliedExhibit < Exhibitionist::Base
|
59
|
+
applies_if { |object| false }
|
60
|
+
|
61
|
+
def boom
|
62
|
+
"#{__getobj__.to_s} shakalaka"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Register all your exhibits with the ```Exhibitionist``` module:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
Exhibitionist.register ShoutExhibit, WhisperExhibit, ExclaimExhibit, NeverAppliedExhibit
|
71
|
+
```
|
72
|
+
|
73
|
+
Now calling ```Exhibitionist.exhibit(obj)``` will get you your object, wrapped with applicable exhibits.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
exhibit = Exhibitionist.exhibit "Hello, world"
|
77
|
+
|
78
|
+
exhibit.shout #=> "HELLO, WORLD"
|
79
|
+
exhibit.whisper #=> "<whisper>hello, world</whisper>"
|
80
|
+
exhibit.exclaim #=> "Hello, world!"
|
81
|
+
exhibit.boom #=> NoMethodError, as object is not wrapped by NeverAppliedExhibit
|
82
|
+
```
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
91
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/exhibitionist/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Najaf Ali"]
|
6
|
+
gem.email = ["ali.najaf@gmail.com"]
|
7
|
+
gem.description = %q{A simple, framework-agnostic implementation of the decorator pattern, inspired by Avdi Grimms exhibits in 'objects on rails'.}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = "http://github.com/Najaf/exhibitionist"
|
10
|
+
|
11
|
+
%w( minitest-reporters ).each do |dep|
|
12
|
+
gem.add_development_dependency dep
|
13
|
+
end
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($\)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.name = "exhibitionist"
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
gem.version = Exhibitionist::VERSION
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "exhibitionist/version"
|
2
|
+
require 'exhibitionist/base'
|
3
|
+
|
4
|
+
module Exhibitionist
|
5
|
+
@@exhibits = []
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def exhibits
|
9
|
+
@@exhibits
|
10
|
+
end
|
11
|
+
|
12
|
+
def exhibits=(exhibits)
|
13
|
+
@@exhibits = exhibits
|
14
|
+
end
|
15
|
+
|
16
|
+
def register(*exhibits)
|
17
|
+
@@exhibits += exhibits
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
#def register_dir(directory)
|
22
|
+
#raise Exhibitionist::Base.subclasses.inspect
|
23
|
+
#File.expand_path(directory)
|
24
|
+
#end
|
25
|
+
|
26
|
+
def exhibit(object)
|
27
|
+
@@exhibits.reduce object do |object, exhibit|
|
28
|
+
exhibit.exhibit_if_applicable(object)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
module Exhibitionist
|
3
|
+
class Base < SimpleDelegator
|
4
|
+
def class
|
5
|
+
__getobj__.class
|
6
|
+
end
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def applicable_to?(object)
|
10
|
+
if @condition
|
11
|
+
return @condition.yield(object)
|
12
|
+
else
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def exhibit_if_applicable(object)
|
18
|
+
applicable_to?(object) ? self.new(object) : object
|
19
|
+
end
|
20
|
+
|
21
|
+
def applies_if(&block)
|
22
|
+
@condition = block
|
23
|
+
end
|
24
|
+
|
25
|
+
def applies_unless(&block)
|
26
|
+
@conditions = Proc.new { |object| !(block.yield(object)) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_exhibits/empty_exhibit'
|
3
|
+
require 'test_exhibits/shout_exhibit'
|
4
|
+
require 'test_exhibits/stutter_exhibit'
|
5
|
+
|
6
|
+
describe Exhibitionist::Base do
|
7
|
+
|
8
|
+
class Speaker
|
9
|
+
def say(string)
|
10
|
+
string
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:bare) { Speaker.new }
|
15
|
+
let(:exhibit) { ShoutExhibit.new(bare) }
|
16
|
+
let(:double_exhibit) { StutterExhibit.new(exhibit) }
|
17
|
+
|
18
|
+
it 'decorates methods' do
|
19
|
+
bare.say('woof').must_equal 'woof'
|
20
|
+
exhibit.say('woof').must_equal 'WOOF'
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'allows multiple exhibits to wrap an object' do
|
24
|
+
double_exhibit.say('meow').must_equal 'Ummm... MEOW'
|
25
|
+
end
|
26
|
+
|
27
|
+
it '#class returns the class of the wrapped object' do
|
28
|
+
double_exhibit.class.must_equal Speaker
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'responds to applicable_to?' do
|
32
|
+
StutterExhibit.must_respond_to :applicable_to?
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns false for applicable_to? by default' do
|
36
|
+
EmptyExhibit.applicable_to?(Object.new).must_equal false
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '.exhibit_if_applicable' do
|
40
|
+
it 'returns original object if not applicable' do
|
41
|
+
original = ShoutExhibit.exhibit_if_applicable(bare)
|
42
|
+
original.say('howl').must_equal 'howl'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'returns exhibited object if applicable' do
|
46
|
+
exhibited = StutterExhibit.exhibit_if_applicable(bare)
|
47
|
+
exhibited.say('growl').must_equal 'Ummm... growl'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.applies_if' do
|
52
|
+
it 'can be called in definitions of subclasses of Exhibitionist::Base' do
|
53
|
+
class TestExhibit < Exhibitionist::Base
|
54
|
+
applies_if {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'takes a lambda that is evaluated to determine whether it is applicable to a given object' do
|
59
|
+
class AnotherTestExhibit < Exhibitionist::Base
|
60
|
+
applies_if { |object| object.class == Speaker }
|
61
|
+
end
|
62
|
+
AnotherTestExhibit.applicable_to?(bare).must_equal true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '.applies_unless' do
|
67
|
+
it 'does the opposite of .applies_if' do
|
68
|
+
class YetAnotherTestExhibit < Exhibitionist::Base
|
69
|
+
applies_unless { |object| object.class == Speaker }
|
70
|
+
end
|
71
|
+
YetAnotherTestExhibit.applicable_to?(bare).must_equal false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'test_exhibits/shout_exhibit'
|
3
|
+
require 'test_exhibits/stutter_exhibit'
|
4
|
+
require 'test_exhibits/whisper_exhibit'
|
5
|
+
|
6
|
+
describe Exhibitionist do
|
7
|
+
|
8
|
+
class Speaker
|
9
|
+
def say(string)
|
10
|
+
string
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:speaker) { Speaker.new }
|
15
|
+
|
16
|
+
before do
|
17
|
+
Exhibitionist.exhibits = []
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.exhibits' do
|
21
|
+
it 'returns an empty array by default' do
|
22
|
+
Exhibitionist.exhibits.must_equal []
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.register' do
|
27
|
+
it 'returns nil' do
|
28
|
+
Exhibitionist.register(Object.new).must_be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'registers exhibit' do
|
32
|
+
Exhibitionist.register ShoutExhibit
|
33
|
+
Exhibitionist.exhibits.must_equal [ShoutExhibit]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'takes multiple exhibits' do
|
37
|
+
Exhibitionist.register ShoutExhibit, StutterExhibit
|
38
|
+
Exhibitionist.exhibits.must_equal [ShoutExhibit, StutterExhibit]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '.exhibit' do
|
43
|
+
it 'returns original object if there are no exhibits registered' do
|
44
|
+
Exhibitionist.exhibits.must_equal []
|
45
|
+
original = Exhibitionist.exhibit(speaker)
|
46
|
+
original.say('woof').must_equal('woof')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'wraps original object if applicable' do
|
50
|
+
Exhibitionist.register StutterExhibit
|
51
|
+
wrapped = Exhibitionist.exhibit(speaker)
|
52
|
+
wrapped.say('woof').must_equal('Ummm... woof')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'wraps multiple registered exhibits, if applicable' do
|
56
|
+
Exhibitionist.register ShoutExhibit, StutterExhibit, WhisperExhibit
|
57
|
+
wrapped = Exhibitionist.exhibit(speaker)
|
58
|
+
# we make no assumptions about order, so it could be either of the following
|
59
|
+
possible = ['<whisper>Ummm... meow</whisper>', 'Ummm... <whisper>meow</whisper>']
|
60
|
+
possible.must_include wrapped.say('meow')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#describe '.register_dir' do
|
65
|
+
#it 'registers all the exhibits in a directory' do
|
66
|
+
#Exhibitionist.register_dir 'test_exhibits'
|
67
|
+
#Exhibitionist.exhibits.must_equal [EmptyExhibit, ShoutExhibit, StutterExhibit, WhisperExhibit]
|
68
|
+
#end
|
69
|
+
#end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/pride'
|
4
|
+
require 'minitest/reporters'
|
5
|
+
|
6
|
+
MiniTest::Unit.runner = MiniTest::SuiteRunner.new
|
7
|
+
MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
|
8
|
+
|
9
|
+
require 'exhibitionist'
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: exhibitionist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Najaf Ali
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest-reporters
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: A simple, framework-agnostic implementation of the decorator pattern,
|
31
|
+
inspired by Avdi Grimms exhibits in 'objects on rails'.
|
32
|
+
email:
|
33
|
+
- ali.najaf@gmail.com
|
34
|
+
executables: []
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- LICENSE
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- exhibitionist.gemspec
|
44
|
+
- lib/exhibitionist.rb
|
45
|
+
- lib/exhibitionist/base.rb
|
46
|
+
- lib/exhibitionist/version.rb
|
47
|
+
- spec/exhibitionist/base_spec.rb
|
48
|
+
- spec/exhibitionist_spec.rb
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- spec/support/test_exhibits/empty_exhibit.rb
|
51
|
+
- spec/support/test_exhibits/shout_exhibit.rb
|
52
|
+
- spec/support/test_exhibits/stutter_exhibit.rb
|
53
|
+
- spec/support/test_exhibits/whisper_exhibit.rb
|
54
|
+
homepage: http://github.com/Najaf/exhibitionist
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.21
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: A simple, framework-agnostic implementation of the decorator pattern, inspired
|
78
|
+
by Avdi Grimms exhibits in 'objects on rails'.
|
79
|
+
test_files:
|
80
|
+
- spec/exhibitionist/base_spec.rb
|
81
|
+
- spec/exhibitionist_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- spec/support/test_exhibits/empty_exhibit.rb
|
84
|
+
- spec/support/test_exhibits/shout_exhibit.rb
|
85
|
+
- spec/support/test_exhibits/stutter_exhibit.rb
|
86
|
+
- spec/support/test_exhibits/whisper_exhibit.rb
|