active_helper 0.1.0
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/README.textile +188 -0
- data/Rakefile +62 -0
- data/lib/active_helper.rb +35 -0
- data/lib/active_helper/base.rb +54 -0
- data/lib/active_helper/version.rb +5 -0
- data/test/active_helper_test.rb +267 -0
- data/test/test_helper.rb +4 -0
- metadata +71 -0
data/README.textile
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
h1. ActiveHelper
|
2
|
+
|
3
|
+
_Finally - helpers with proper encapsulation, delegation, interfaces and inheritance!_
|
4
|
+
|
5
|
+
|
6
|
+
h2. Introduction
|
7
|
+
|
8
|
+
Helpers suck. They've always sucked, and they will suck on if we keep them in modules.
|
9
|
+
|
10
|
+
ActiveHelper is an attempt to pack helpers into *classes*. This brings us a few benefits
|
11
|
+
|
12
|
+
* *inheritance* helpers can be derived other helpers
|
13
|
+
* *delegation* helpers are no longer mixed into a target- the targets @use@ the helper, where the new
|
14
|
+
methods are _delegated_ to the helper instances
|
15
|
+
* *proper encapsulation* helpers don't rely blindly on instance variables - a helper defines its @needs@, the target has to provide readers
|
16
|
+
* *interfaces* a helper clearly @provides@ methods and might @use@ additional helpers
|
17
|
+
|
18
|
+
Note that ActiveHelper is a generic helper framework. Not coupled to anything like Rails or Merb. Not providing any concrete helpers. Feel free to use clean helpers in _any_ framework (including Rails and friends)!
|
19
|
+
|
20
|
+
h2. Example
|
21
|
+
|
22
|
+
Let's use the bloody MVC-View example as we find in Rails or Merb (Sinatra, too?).
|
23
|
+
|
24
|
+
We have a view which needs additional methods in order to render bullshit.
|
25
|
+
|
26
|
+
|
27
|
+
h3. Using helpers
|
28
|
+
|
29
|
+
The view wants to render tags using the TagHelper.
|
30
|
+
|
31
|
+
<pre>
|
32
|
+
class View
|
33
|
+
include ActiveHelper
|
34
|
+
end
|
35
|
+
|
36
|
+
> view.use TagHelper
|
37
|
+
</pre>
|
38
|
+
|
39
|
+
To pull-in ("import") a helper we invoke @use@ on the target instance.
|
40
|
+
|
41
|
+
|
42
|
+
h3. Interfaces
|
43
|
+
|
44
|
+
The exemplary _#tag_ method took me days to implement.
|
45
|
+
|
46
|
+
<pre>
|
47
|
+
class TagHelper < ActiveHelper::Base
|
48
|
+
provides :tag
|
49
|
+
|
50
|
+
def tag(name, attributes="")
|
51
|
+
"<#{name} #{attributes}>"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
</pre>
|
55
|
+
|
56
|
+
The helper defines a part of its interface (what goes out) as it @provides@ methods.
|
57
|
+
|
58
|
+
<pre>
|
59
|
+
> view.tag(:form) # => "<form>"
|
60
|
+
</pre>
|
61
|
+
|
62
|
+
|
63
|
+
h3. Inheritance
|
64
|
+
|
65
|
+
The real power of OOP is inheritance, so why should we throw away that in favor of modules?
|
66
|
+
|
67
|
+
<pre>
|
68
|
+
class FormHelper < TagHelper
|
69
|
+
provides :form_tag
|
70
|
+
|
71
|
+
def form_tag(destination)
|
72
|
+
tag(:form, "action=#{destination}") # inherited from TagHelper.
|
73
|
+
end
|
74
|
+
end
|
75
|
+
</pre>
|
76
|
+
|
77
|
+
That's _a bit_ cleaner than blindly including 30 helper modules in another helper in another helper, isn't it?
|
78
|
+
|
79
|
+
<pre>
|
80
|
+
> view.use FormHelper
|
81
|
+
> view.tag(:form) # => "<form>"
|
82
|
+
> view.form('apotomo.de') # => "<form action=apotomo.de>"
|
83
|
+
</pre>
|
84
|
+
|
85
|
+
Obviously the view can invoke stuff from the _FormHelper_ itself and inherited methods that were exposed with @provides@.
|
86
|
+
|
87
|
+
h3. Delegation as Multiple Inheritance
|
88
|
+
|
89
|
+
What if the _#form_tag_ method needs to access another helper? In Rails, this would simply be
|
90
|
+
|
91
|
+
<pre>
|
92
|
+
def form_tag(destination)
|
93
|
+
destination = url_for(destination)
|
94
|
+
tag(:form, "action=#{destination}")
|
95
|
+
end
|
96
|
+
</pre>
|
97
|
+
|
98
|
+
The _#url_for_ methods comes from, na, do you know it? Me neither! It's mixed-in somewhere in the depths of the helper modules.
|
99
|
+
|
100
|
+
In ActiveHelper this is slightly different.
|
101
|
+
|
102
|
+
<pre>
|
103
|
+
class FormHelper < TagHelper
|
104
|
+
provides :form_tag
|
105
|
+
uses UrlHelper
|
106
|
+
|
107
|
+
def form_tag(destination)
|
108
|
+
destination = url_for(destination) # in UrlHelper.
|
109
|
+
tag(:form, "action=#{destination}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
</pre>
|
113
|
+
|
114
|
+
Hmm, our _FormHelper_ is already derived from _ActiveHelper_, how do we import additional methods?
|
115
|
+
|
116
|
+
Easy as well, the helper class @uses@ it.
|
117
|
+
|
118
|
+
So we have to know _#url_for_ is located in the _UrlHelper_ and we even have to declare our helper @uses@ another one.
|
119
|
+
That's a good thing for a) *code tidiness*, b) *good architecture* and c) *debugging*.
|
120
|
+
|
121
|
+
How would the _UrlHelper_ look like?
|
122
|
+
|
123
|
+
|
124
|
+
h3. Delegation as Interface
|
125
|
+
|
126
|
+
A traditional url helper would roughly look like this:
|
127
|
+
|
128
|
+
<pre>
|
129
|
+
def url_for(url)
|
130
|
+
protocol = @https_request? ? 'https' : 'http'
|
131
|
+
"#{protocol}://#{url}"
|
132
|
+
end
|
133
|
+
</pre>
|
134
|
+
|
135
|
+
Next chance, who or what did create _@https_request?_ and where does it live? That's _ugly_, boys!
|
136
|
+
|
137
|
+
Our helper bets on declaring its interface, again! This time we define what goes in (a "dependency").
|
138
|
+
|
139
|
+
<pre>
|
140
|
+
class UrlHelper < ActiveHelper::Base
|
141
|
+
provides :url_for
|
142
|
+
needs :https_request?
|
143
|
+
|
144
|
+
def url_for(url)
|
145
|
+
protocol = https_request? ? 'https' : 'http'
|
146
|
+
"#{protocol}://#{url}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
</pre>
|
150
|
+
|
151
|
+
It defines what it @needs@ and that's all for it. Any call to _#https_request?_ (that's a _method_) is strictly delegated back to the view instance, which has to care about satisfying dependencies.
|
152
|
+
|
153
|
+
Here's what happens in productive mode.
|
154
|
+
|
155
|
+
<pre>
|
156
|
+
> view.form('apotomo.de')
|
157
|
+
# => 11:in `url_for': undefined method `https_request?' for #<View:0xb749d4fc> (NoMethodError)
|
158
|
+
</pre>
|
159
|
+
|
160
|
+
That's conclusive, the view is insufficiently geared.
|
161
|
+
|
162
|
+
<pre>
|
163
|
+
class View
|
164
|
+
include ActiveHelper
|
165
|
+
|
166
|
+
def https_request?; false; end
|
167
|
+
end
|
168
|
+
</pre>
|
169
|
+
|
170
|
+
Now, does it work?
|
171
|
+
|
172
|
+
<pre>
|
173
|
+
> view.form_tag('go.and.use/active_helper')
|
174
|
+
# => <form action=http://go.and.use/active_helper>
|
175
|
+
</pre>
|
176
|
+
|
177
|
+
Yeah.
|
178
|
+
|
179
|
+
h2. Concepts
|
180
|
+
* Helpers are instances, when accessing a raw @@ivar@ it refers to their own instance variables
|
181
|
+
* Dependencies between different helpers and between the target (e.g. a _View_ instance) are modelled with OOP strategies: Inheritance and the declarative @#needs@.
|
182
|
+
* Naturally helpers can @use@ other helpers on instance level, a helper class @uses@ supporting helpers.
|
183
|
+
|
184
|
+
h2. License
|
185
|
+
|
186
|
+
Copyright (c) 2010, Nick Sutterer
|
187
|
+
|
188
|
+
Released under the MIT License.
|
data/Rakefile
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require File.join(File.dirname(__FILE__), 'lib', 'active_helper', 'version')
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test the active_helper library.'
|
11
|
+
Rake::TestTask.new(:test) do |test|
|
12
|
+
test.libs << ['lib', 'test']
|
13
|
+
test.pattern = 'test/*_test.rb'
|
14
|
+
test.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
# Gem managment tasks.
|
19
|
+
#
|
20
|
+
# == Bump gem version (any):
|
21
|
+
#
|
22
|
+
# rake version:bump:major
|
23
|
+
# rake version:bump:minor
|
24
|
+
# rake version:bump:patch
|
25
|
+
#
|
26
|
+
# == Generate gemspec, build & install locally:
|
27
|
+
#
|
28
|
+
# rake gemspec
|
29
|
+
# rake build
|
30
|
+
# sudo rake install
|
31
|
+
#
|
32
|
+
# == Git tag & push to origin/master
|
33
|
+
#
|
34
|
+
# rake release
|
35
|
+
#
|
36
|
+
# == Release to Gemcutter.org:
|
37
|
+
#
|
38
|
+
# rake gemcutter:release
|
39
|
+
#
|
40
|
+
begin
|
41
|
+
gem 'jeweler'
|
42
|
+
require 'jeweler'
|
43
|
+
|
44
|
+
Jeweler::Tasks.new do |spec|
|
45
|
+
spec.name = "active_helper"
|
46
|
+
spec.version = ::ActiveHelper::VERSION
|
47
|
+
spec.summary = %{Finally - helpers with proper encapsulation, delegation, interfaces and inheritance!}
|
48
|
+
spec.description = spec.summary
|
49
|
+
spec.homepage = "http://github.com/apotonick/active_helper"
|
50
|
+
spec.authors = ["Nick Sutterer"]
|
51
|
+
spec.email = "apotonick@gmail.com"
|
52
|
+
|
53
|
+
spec.files = FileList["[A-Z]*", File.join(*%w[{lib,test} ** *]).to_s]
|
54
|
+
|
55
|
+
spec.add_dependency 'activesupport', '>= 2.3.0' # Dependencies and minimum versions?
|
56
|
+
end
|
57
|
+
|
58
|
+
Jeweler::GemcutterTasks.new
|
59
|
+
rescue LoadError
|
60
|
+
puts "Jeweler - or one of its dependencies - is not available. " <<
|
61
|
+
"Install it with: sudo gem install jeweler -s http://gemcutter.org"
|
62
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
|
5
|
+
module ActiveHelper
|
6
|
+
module GenericMethods
|
7
|
+
def use_for(helper_class, target)
|
8
|
+
extend ::SingleForwardable
|
9
|
+
|
10
|
+
helper_ivar_name = ivar_name_for(helper_class)
|
11
|
+
helper_instance = helper_class.new(target)
|
12
|
+
|
13
|
+
instance_variable_set(helper_ivar_name, helper_instance)
|
14
|
+
helper_class.helper_methods.each do |meth|
|
15
|
+
def_delegator helper_ivar_name, meth
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
# Unique ivar name for the helper class in the expanding target.
|
21
|
+
def ivar_name_for(object)
|
22
|
+
('@__active_helper_'+("#{object.to_s}".underscore.gsub(/[\/<>@#:]/, ""))).to_sym
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
include GenericMethods
|
28
|
+
# Expands the target *instance* with the provided methods from +helper_class+ by delegating 'em back to a private helper
|
29
|
+
# Expands only the helped instance itself, not the class.
|
30
|
+
def use (helper_class)
|
31
|
+
use_for(helper_class, self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'active_helper/base'
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ActiveHelper
|
2
|
+
class Base
|
3
|
+
class_inheritable_array :helper_methods
|
4
|
+
self.helper_methods = []
|
5
|
+
|
6
|
+
class_inheritable_array :parent_readers
|
7
|
+
self.parent_readers = []
|
8
|
+
|
9
|
+
class_inheritable_array :class_helpers
|
10
|
+
self.class_helpers = []
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Add public methods to the helper's interface. Only methods listed here will
|
14
|
+
# be used to expand the target.
|
15
|
+
def provides(*methods)
|
16
|
+
helper_methods.push(*methods)
|
17
|
+
end
|
18
|
+
|
19
|
+
def needs(*methods)
|
20
|
+
parent_readers.push(*methods).uniq!
|
21
|
+
end
|
22
|
+
|
23
|
+
def uses(*classes)
|
24
|
+
class_helpers.push(*classes).uniq!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
include GenericMethods
|
30
|
+
attr_reader :parent
|
31
|
+
|
32
|
+
def initialize(parent=nil)
|
33
|
+
@parent = parent
|
34
|
+
extend SingleForwardable
|
35
|
+
add_parent_readers!
|
36
|
+
add_class_helpers!
|
37
|
+
end
|
38
|
+
|
39
|
+
def use(helper_class)
|
40
|
+
use_for(helper_class, parent) # in GenericMethods.
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
# Delegates methods declared with #needs back to parent.
|
45
|
+
def add_parent_readers!
|
46
|
+
return if @parent.blank? or self.class.parent_readers.blank?
|
47
|
+
def_delegator(:@parent, self.class.parent_readers)
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_class_helpers!
|
51
|
+
self.class.class_helpers.each { |helper| use helper }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class ActiveHelperTest < Test::Unit::TestCase
|
4
|
+
def helper_mock(*args)
|
5
|
+
Class.new(::ActiveHelper::Base).new(*args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def helper_in(helper_class, target)
|
9
|
+
target.instance_variable_get(target.send(:ivar_name_for, helper_class))
|
10
|
+
end
|
11
|
+
|
12
|
+
context "#initialize" do
|
13
|
+
setup do
|
14
|
+
@target = Object.new
|
15
|
+
@target.class.instance_eval { include ::ActiveHelper }
|
16
|
+
@helper_class = Class.new(::ActiveHelper::Base)
|
17
|
+
@greedy_class = @helper_class
|
18
|
+
end
|
19
|
+
|
20
|
+
should "receive a parent per default" do
|
21
|
+
@helper = ::ActiveHelper::Base.new @target
|
22
|
+
assert_equal @helper.parent, @target
|
23
|
+
end
|
24
|
+
|
25
|
+
should "also accept no parent" do
|
26
|
+
@helper = ::ActiveHelper::Base.new
|
27
|
+
assert_equal @helper.parent, nil
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
context "declaring with #needs" do
|
33
|
+
setup do
|
34
|
+
@target.instance_eval { def bottle; "cheers!"; end }
|
35
|
+
end
|
36
|
+
|
37
|
+
should_eventually "complain if parent doesn't provide accessors"
|
38
|
+
|
39
|
+
should "delegate the method to the parent when called" do
|
40
|
+
@helper_class.instance_eval { needs :bottle }
|
41
|
+
@helper = @helper_class.new(@target)
|
42
|
+
@target.use @helper.class
|
43
|
+
|
44
|
+
assert_equal "cheers!", @helper.bottle
|
45
|
+
end
|
46
|
+
|
47
|
+
# DiningHelper.use GreedyHelper
|
48
|
+
#
|
49
|
+
# GreedyHelper
|
50
|
+
# needs :bottle
|
51
|
+
#
|
52
|
+
# target
|
53
|
+
# use DiningHelper
|
54
|
+
# def bottle
|
55
|
+
should "always delegate to @target in helpers, for now" do
|
56
|
+
@greedy_class.instance_eval { needs :bottle }
|
57
|
+
|
58
|
+
@dining = helper_mock(@target)
|
59
|
+
@dining.use @greedy_class
|
60
|
+
|
61
|
+
assert_equal 'cheers!', helper_in(@greedy_class, @dining).bottle
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "With #parent_readers and #uses," do
|
67
|
+
setup do
|
68
|
+
@target = Object.new
|
69
|
+
@target.class.instance_eval { include ::ActiveHelper }
|
70
|
+
@helper_class = Class.new(::ActiveHelper::Base)
|
71
|
+
@helper = @helper_class.new
|
72
|
+
end
|
73
|
+
|
74
|
+
context "#parent_reader" do
|
75
|
+
should "yield an empty array on a fresh instance" do
|
76
|
+
assert_equal [], @helper.parent_readers
|
77
|
+
end
|
78
|
+
|
79
|
+
should "return the method names defined with #needs" do
|
80
|
+
@helper.class.instance_eval { needs :controller, :view }
|
81
|
+
assert_equal [:controller, :view], @helper.parent_readers
|
82
|
+
end
|
83
|
+
|
84
|
+
context "with inheritance" do
|
85
|
+
setup do
|
86
|
+
@helper.class.instance_eval { needs :bottle, :glass }
|
87
|
+
@dining = Class.new(@helper.class).new
|
88
|
+
end
|
89
|
+
|
90
|
+
should "return the inherited parent_readers names" do
|
91
|
+
assert_equal [:bottle, :glass], @dining.parent_readers
|
92
|
+
end
|
93
|
+
|
94
|
+
should "return also the inherited parent_readers names" do
|
95
|
+
@dining.class.instance_eval { needs :fork }
|
96
|
+
assert_equal [:bottle, :glass, :fork], @dining.parent_readers
|
97
|
+
end
|
98
|
+
|
99
|
+
should "return a unique'd parent_readers names list" do
|
100
|
+
@dining.class.instance_eval { needs :bottle }
|
101
|
+
assert_equal [:bottle, :glass], @dining.parent_readers
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
context "#helper_methods and #provides" do
|
110
|
+
setup do
|
111
|
+
@helper = Class.new(::ActiveHelper::Base).new
|
112
|
+
end
|
113
|
+
|
114
|
+
should "initialy yield an empty array" do
|
115
|
+
assert_equal [], @helper.class.helper_methods
|
116
|
+
end
|
117
|
+
|
118
|
+
should "grow with calls to #provide" do
|
119
|
+
assert_equal [:sleep, :drink], @helper.class.provides(:sleep, :drink)
|
120
|
+
assert_equal [:sleep, :drink], @helper.class.helper_methods
|
121
|
+
end
|
122
|
+
|
123
|
+
should "inherit provided methods from its ancestor classes" do
|
124
|
+
@helper.class.provides(:sleep, :drink)
|
125
|
+
@kid = Class.new(@helper.class).new
|
126
|
+
@kid.class.provides(:eat)
|
127
|
+
|
128
|
+
assert_equal [:sleep, :drink, :eat], @kid.class.helper_methods
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "On a Helper" do
|
133
|
+
setup do
|
134
|
+
assert_respond_to ::ActiveHelper::Base, :uses
|
135
|
+
@helper = Class.new(::ActiveHelper::Base).new
|
136
|
+
assert_respond_to @helper.class, :uses
|
137
|
+
assert ! @helper.respond_to?(:eat)
|
138
|
+
|
139
|
+
class GreedyHelper < ::ActiveHelper::Base; provides :eat; end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "#use" do
|
143
|
+
should "delegate the new Helper methods" do
|
144
|
+
@helper.use GreedyHelper
|
145
|
+
assert_respond_to @helper, :eat
|
146
|
+
end
|
147
|
+
|
148
|
+
should "set @parent => @target in the used Helper" do
|
149
|
+
@target = Object.new
|
150
|
+
@helper = Class.new(::ActiveHelper::Base).new(@target)
|
151
|
+
@helper.use GreedyHelper
|
152
|
+
assert_equal @target, helper_in(GreedyHelper, @helper).parent # parent of used handler is target, not the using handler!
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "#uses" do
|
157
|
+
setup do
|
158
|
+
@greedy_class = Class.new(::ActiveHelper::Base)
|
159
|
+
@greedy_class.instance_eval do
|
160
|
+
uses GreedyHelper
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "with #class_helpers" do
|
165
|
+
should "yield an empty array on a fresh instance" do
|
166
|
+
@greedy_class = Class.new(::ActiveHelper::Base)
|
167
|
+
assert_equal [], @greedy_class.class_helpers
|
168
|
+
end
|
169
|
+
|
170
|
+
should "remember the passed helpers in #class_helpers" do
|
171
|
+
assert_equal [GreedyHelper], @greedy_class.class_helpers
|
172
|
+
end
|
173
|
+
|
174
|
+
should "inherit ancesting class_helpers" do
|
175
|
+
@dining_class = Class.new(@greedy_class)
|
176
|
+
@dining_class.instance_eval do
|
177
|
+
uses Object
|
178
|
+
end
|
179
|
+
|
180
|
+
assert_equal [GreedyHelper, Object], @dining_class.class_helpers
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
should "respond to the new delegated Helper methods" do
|
185
|
+
@helper.class.uses GreedyHelper
|
186
|
+
assert_respond_to @helper.class.new, :eat
|
187
|
+
end
|
188
|
+
|
189
|
+
should "inherit helper methods to ancestors" do
|
190
|
+
class DiningHelper < ::ActiveHelper::Base
|
191
|
+
provides :drink
|
192
|
+
uses GreedyHelper
|
193
|
+
|
194
|
+
def drink;end
|
195
|
+
end
|
196
|
+
|
197
|
+
@helper = Class.new(DiningHelper).new
|
198
|
+
assert_respond_to @helper, :eat # from uses GreedyHelper.
|
199
|
+
assert_respond_to @helper, :drink # from DiningHelper inheritance.
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "On a non-helper" do
|
205
|
+
setup do
|
206
|
+
@target_class = Class.new(Object) # don't pollute Object directly.
|
207
|
+
@target_class.instance_eval { include ::ActiveHelper }
|
208
|
+
assert_respond_to @target_class, :use
|
209
|
+
|
210
|
+
@target = @target_class.new
|
211
|
+
assert ! @target.respond_to?(:eat)
|
212
|
+
|
213
|
+
class GreedyHelper < ::ActiveHelper::Base; provides :eat; end
|
214
|
+
end
|
215
|
+
|
216
|
+
context "#use" do
|
217
|
+
should "delegate new delegated helper methods" do
|
218
|
+
@target.use GreedyHelper
|
219
|
+
assert_respond_to @target, :eat
|
220
|
+
end
|
221
|
+
|
222
|
+
should "set @parent => @target in the used Helper" do
|
223
|
+
@target.use GreedyHelper
|
224
|
+
assert_equal @target, helper_in(GreedyHelper, @target).parent
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
#context "#uses" do
|
229
|
+
# should "delegate new delegated helper methods" do
|
230
|
+
# @target.class.uses GreedyHelper
|
231
|
+
# assert_respond_to @target, :eat
|
232
|
+
# end
|
233
|
+
#
|
234
|
+
# should "inherit helper methods to non-helper class" do
|
235
|
+
# class DiningHelper < ::ActiveHelper::Base
|
236
|
+
# provides :drink
|
237
|
+
# uses GreedyHelper
|
238
|
+
#
|
239
|
+
# def drink;end
|
240
|
+
# end
|
241
|
+
#
|
242
|
+
# @helper = Class.new(DiningHelper).new
|
243
|
+
# assert_respond_to @helper, :eat # from uses GreedyHelper.
|
244
|
+
# assert_respond_to @helper, :drink # from DiningHelper inheritance.
|
245
|
+
# end
|
246
|
+
#end
|
247
|
+
end
|
248
|
+
|
249
|
+
context "#ivar_name_for" do
|
250
|
+
setup do
|
251
|
+
@helper = helper_mock
|
252
|
+
end
|
253
|
+
|
254
|
+
should "create a symbol for the class name" do
|
255
|
+
assert_equal '@__active_helper_object', @helper.send(:ivar_name_for, Object.new).to_s.sub(/0x.+/, "")
|
256
|
+
end
|
257
|
+
|
258
|
+
should "create a symbol for an anonym class" do
|
259
|
+
assert_equal '@__active_helper_class', @helper.send(:ivar_name_for, Class.new).to_s.sub(/0x.+/, "")
|
260
|
+
end
|
261
|
+
|
262
|
+
should "create a symbol for namespaced class" do
|
263
|
+
|
264
|
+
assert_equal '@__active_helper_active_helperbase', @helper.send(:ivar_name_for, ActiveHelper::Base).to_s.sub(/0x.+/, "")
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Sutterer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-30 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.3.0
|
24
|
+
version:
|
25
|
+
description: Finally - helpers with proper encapsulation, delegation, interfaces and inheritance!
|
26
|
+
email: apotonick@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.textile
|
33
|
+
files:
|
34
|
+
- README.textile
|
35
|
+
- Rakefile
|
36
|
+
- lib/active_helper.rb
|
37
|
+
- lib/active_helper/base.rb
|
38
|
+
- lib/active_helper/version.rb
|
39
|
+
- test/active_helper_test.rb
|
40
|
+
- test/test_helper.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/apotonick/active_helper
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: "0"
|
55
|
+
version:
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
requirements: []
|
63
|
+
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.3.5
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Finally - helpers with proper encapsulation, delegation, interfaces and inheritance!
|
69
|
+
test_files:
|
70
|
+
- test/active_helper_test.rb
|
71
|
+
- test/test_helper.rb
|