proxy_party 0.1.0 → 0.2.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/{spec/spec.opts → .rspec} +0 -0
- data/README.textile +128 -0
- data/Rakefile +5 -26
- data/VERSION +1 -1
- data/lib/proxy_party.rb +319 -7
- data/proxy_party.gemspec +57 -0
- data/spec/proxy_party_complex_spec.rb +62 -0
- data/spec/proxy_party_spec.rb +131 -19
- data/spec/spec_helper.rb +1 -1
- metadata +51 -42
- data/.gitignore +0 -21
- data/README.markdown +0 -67
data/{spec/spec.opts → .rspec}
RENAMED
File without changes
|
data/README.textile
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
h1. Proxy Party
|
2
|
+
|
3
|
+
Greatly facilitates adding proxies through the proxy method_missing pattern.
|
4
|
+
Can now also dynamically add proxy methods and use other amazing meta strategies!
|
5
|
+
|
6
|
+
h2. Install
|
7
|
+
|
8
|
+
@$ gem install party_proxy@
|
9
|
+
|
10
|
+
h2. Use
|
11
|
+
|
12
|
+
@require 'party_proxy'@
|
13
|
+
|
14
|
+
h2. Latest news
|
15
|
+
|
16
|
+
Now supports renaming of proxy method so you can avoid method name collisions!
|
17
|
+
|
18
|
+
h2. TODO
|
19
|
+
|
20
|
+
Major DRYing up needed. Currently a lot of code duplication :O
|
21
|
+
|
22
|
+
h2. Usage
|
23
|
+
|
24
|
+
<pre>
|
25
|
+
class State
|
26
|
+
attr_accessor :name
|
27
|
+
|
28
|
+
def initialize(name)
|
29
|
+
@name = name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Info
|
34
|
+
attr_accessor :text
|
35
|
+
|
36
|
+
def initialize(text)
|
37
|
+
@text = text
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module Party
|
42
|
+
class Subject
|
43
|
+
# proxy state and info objects to make their methods
|
44
|
+
# directly accessible from subject objects
|
45
|
+
proxy :state, :info
|
46
|
+
|
47
|
+
def initialize(name)
|
48
|
+
@state = State.new name
|
49
|
+
@info = Info.new 'hello'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
</pre>
|
54
|
+
|
55
|
+
<pre>
|
56
|
+
describe Party::Proxy do
|
57
|
+
describe '#proxy' do
|
58
|
+
it "proxies state and info so it can call name directly on subject" do
|
59
|
+
subject = Party::Subject.new 'kristian'
|
60
|
+
subject.name.should == 'kristian'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#proxy_for' do
|
65
|
+
it "proxies speak_it! on mic" do
|
66
|
+
subject = Party::Subject.new 'kristian'
|
67
|
+
subject.mic = Mic.new 'hello'
|
68
|
+
Party::Subject.proxy_for :mic, :speak_it!
|
69
|
+
|
70
|
+
subject.proxy_for :mic, :speak_it!
|
71
|
+
|
72
|
+
subject.speak_it!.should == 'hello'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "proxies speak_it! as speak_up! on mic" do
|
76
|
+
subject = Party::Subject.new 'kristian'
|
77
|
+
subject.mic = Mic.new 'hello'
|
78
|
+
Party::Subject.proxy_for :mic, :rename => {:speak_it! => :speak_up!}
|
79
|
+
subject.speak_up!.should == 'hello'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '#proxy_accessor_for' do
|
84
|
+
it "proxies speak accessor methods on mic" do
|
85
|
+
subject = Party::Subject.new 'kristian'
|
86
|
+
subject.mic = Mic.new 'hello'
|
87
|
+
subject.proxy_accessors_for :mic, :speak
|
88
|
+
subject.speak = 'do it!'
|
89
|
+
subject.speak.should == 'do it!'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "proxies speak accessor method on mic as yell" do
|
93
|
+
subject = Party::Subject.new 'kristian'
|
94
|
+
subject.mic = Mic.new 'hello'
|
95
|
+
subject.proxy_accessors_for :mic, :rename => {:speak => :yell}
|
96
|
+
subject.yell = 'do it!'
|
97
|
+
subject.yell.should == 'do it!'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#named_proxies' do
|
102
|
+
it "proxies select :mic and :state methods" do
|
103
|
+
subject = Party::Subject.new 'kristian'
|
104
|
+
subject.mic = Mic.new 'hello'
|
105
|
+
subject.named_proxies :mic => :speak, :state => :name
|
106
|
+
subject.speak = 'do it!'
|
107
|
+
subject.speak.should == 'do it!'
|
108
|
+
|
109
|
+
subject.name = 'kris'
|
110
|
+
subject.name.should == 'kris'
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
</pre>
|
115
|
+
|
116
|
+
h2. Note on Patches/Pull Requests
|
117
|
+
|
118
|
+
* Fork the project.
|
119
|
+
* Make your feature addition or bug fix.
|
120
|
+
* Add tests for it. This is important so I don't break it in a
|
121
|
+
future version unintentionally.
|
122
|
+
* Commit, do not mess with rakefile, version, or history.
|
123
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
124
|
+
* Send me a pull request. Bonus points for topic branches.
|
125
|
+
|
126
|
+
h2. Copyright
|
127
|
+
|
128
|
+
Copyright (c) 2010 Kristian Mandrup. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'psych'
|
2
3
|
require 'rake'
|
3
4
|
|
4
5
|
begin
|
@@ -10,7 +11,10 @@ begin
|
|
10
11
|
gem.email = "kmandrup@gmail.com"
|
11
12
|
gem.homepage = "http://github.com/kristianmandrup/proxy_party"
|
12
13
|
gem.authors = ["Kristian Mandrup"]
|
13
|
-
|
14
|
+
|
15
|
+
gem.add_dependency "sugar-high", ">= 0.3.5"
|
16
|
+
gem.add_development_dependency "rspec", ">= 2.4.0"
|
17
|
+
|
14
18
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
19
|
end
|
16
20
|
Jeweler::GemcutterTasks.new
|
@@ -18,28 +22,3 @@ rescue LoadError
|
|
18
22
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
23
|
end
|
20
24
|
|
21
|
-
# require 'spec/rake/spectask'
|
22
|
-
# Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
-
# spec.libs << 'lib' << 'spec'
|
24
|
-
# spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
-
# end
|
26
|
-
#
|
27
|
-
# Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
-
# spec.libs << 'lib' << 'spec'
|
29
|
-
# spec.pattern = 'spec/**/*_spec.rb'
|
30
|
-
# spec.rcov = true
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# task :spec => :check_dependencies
|
34
|
-
#
|
35
|
-
# task :default => :spec
|
36
|
-
#
|
37
|
-
# require 'rake/rdoctask'
|
38
|
-
# Rake::RDocTask.new do |rdoc|
|
39
|
-
# version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
-
#
|
41
|
-
# rdoc.rdoc_dir = 'rdoc'
|
42
|
-
# rdoc.title = "proxy_party #{version}"
|
43
|
-
# rdoc.rdoc_files.include('README*')
|
44
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
-
# end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1
|
1
|
+
0.2.1
|
data/lib/proxy_party.rb
CHANGED
@@ -1,34 +1,346 @@
|
|
1
|
+
require 'sugar-high/array'
|
2
|
+
require 'sugar-high/kind_of'
|
3
|
+
require 'sugar-high/arguments'
|
4
|
+
|
1
5
|
module Party
|
2
|
-
module Proxy
|
6
|
+
module Proxy
|
7
|
+
class Factory
|
8
|
+
attr_reader :create_method, :klass
|
9
|
+
|
10
|
+
def initialize klass, create_method = nil
|
11
|
+
@klass = klass
|
12
|
+
@create_method = create_method
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
create_method ? klass.send(create_method) : klass.new
|
17
|
+
end
|
18
|
+
end
|
3
19
|
|
4
20
|
def self.included(base)
|
5
21
|
base.extend ClassMethods
|
22
|
+
base.send :include, InstanceMethods
|
23
|
+
end
|
24
|
+
|
25
|
+
module InstanceMethods
|
26
|
+
attr_accessor :proxy_factories
|
27
|
+
|
28
|
+
def proxy_factory
|
29
|
+
proxy_factories
|
30
|
+
end
|
31
|
+
|
32
|
+
def add_proxy_factories hash
|
33
|
+
hash.each_pair do |name, factory|
|
34
|
+
factory = if factory.kind_of?(Class)
|
35
|
+
Party::Proxy::Factory.new(factory)
|
36
|
+
else
|
37
|
+
case factory
|
38
|
+
when Array
|
39
|
+
fac = factory.first
|
40
|
+
meth = factory.last if factory.size == 2
|
41
|
+
raise ArgumentError, "Factory must be a Class, was #{fac}" if !fac.kind_of?(Class)
|
42
|
+
raise ArgumentError, "Factory method be a label, was #{meth}" if meth && !meth.kind_of_label?
|
43
|
+
Party::Proxy::Factory.new(fac, meth)
|
44
|
+
else
|
45
|
+
raise ArgumentError, "Factory must be a Class or have a #create method: #{factory}" if !factory.respond_to?(:create)
|
46
|
+
factory
|
47
|
+
end
|
48
|
+
end
|
49
|
+
self.proxy_factories ||= {}
|
50
|
+
self.proxy_factories.merge!(name.to_sym => factory) if name.kind_of_label?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias_method :add_proxy_factory, :add_proxy_factories
|
54
|
+
|
55
|
+
# Add instance proxy methods
|
56
|
+
def proxy_for obj, *methods
|
57
|
+
check = last_arg_value({:check => false}, methods)
|
58
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
59
|
+
methods.delete(:rename) if rename_methods
|
60
|
+
|
61
|
+
methods.to_symbols.flat_uniq.each do |meth|
|
62
|
+
if check
|
63
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
64
|
+
raise ArgumentError, "No such method to proxy for #{obj}" if !self.send(obj).respond_to?(meth)
|
65
|
+
end
|
66
|
+
class_eval %{
|
67
|
+
define_method :#{meth} do
|
68
|
+
#{obj}.send(:#{meth}) if #{obj}
|
69
|
+
end
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
rename_methods.each_pair do |meth, new_meth|
|
74
|
+
if check
|
75
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
76
|
+
raise ArgumentError, "No such method to proxy for #{obj}" if !self.send(obj).respond_to?(meth)
|
77
|
+
end
|
78
|
+
class_eval %{
|
79
|
+
define_method :#{new_meth} do
|
80
|
+
#{obj}.send(:#{meth}) if #{obj}
|
81
|
+
end
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Add instance proxy methods
|
87
|
+
def proxy_accessors_for obj, *methods
|
88
|
+
proxy_for obj, methods
|
89
|
+
check = last_arg_value({:check => false}, methods)
|
90
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
91
|
+
methods.delete(:rename) if rename_methods
|
92
|
+
|
93
|
+
methods.to_symbols.flat_uniq.each do |meth|
|
94
|
+
if check
|
95
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
96
|
+
raise ArgumentError, "No such method #{meth} to proxy for #{obj}" if !self.send(obj).respond_to?(:"#{meth}=")
|
97
|
+
end
|
98
|
+
class_eval %{
|
99
|
+
define_method :#{meth}= do |arg|
|
100
|
+
self.#{obj} ||= create_in_factory(:#{obj})
|
101
|
+
self.#{obj} ||= self.class.send(:create_in_factory, :#{obj})
|
102
|
+
#{obj}.send(:#{meth}=, arg) if #{obj}
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
rename_methods.each_pair do |meth, new_meth|
|
108
|
+
if check
|
109
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
110
|
+
raise ArgumentError, "No such method #{meth} to proxy for #{obj}" if !self.send(obj).respond_to?(:"#{meth}=")
|
111
|
+
end
|
112
|
+
class_eval %{
|
113
|
+
define_method :#{new_meth}= do |arg|
|
114
|
+
self.#{obj} ||= create_in_factory(:#{obj})
|
115
|
+
self.#{obj} ||= self.class.send(:create_in_factory, :#{obj})
|
116
|
+
#{obj}.send(:#{meth}=, arg) if #{obj}
|
117
|
+
end
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Add proxy methods only to the instance object
|
123
|
+
def instance_proxy_for obj, *methods
|
124
|
+
check = last_arg_value({:check => false}, methods)
|
125
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
126
|
+
methods.delete(:rename) if rename_methods
|
127
|
+
|
128
|
+
methods.to_symbols.flat_uniq.each do |meth|
|
129
|
+
if check
|
130
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
131
|
+
raise ArgumentError, "No such method to proxy for #{obj}" if !self.send(obj).respond_to?(meth)
|
132
|
+
end
|
133
|
+
instance_eval %{
|
134
|
+
class << self
|
135
|
+
define_method :#{meth} do
|
136
|
+
#{obj}.send(:#{meth}) if #{obj}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
rename_methods.each_pair do |meth, new_meth|
|
143
|
+
if check
|
144
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
145
|
+
raise ArgumentError, "No such method to proxy for #{obj}" if !self.send(obj).respond_to?(meth)
|
146
|
+
end
|
147
|
+
instance_eval %{
|
148
|
+
class << self
|
149
|
+
define_method :#{new_meth} do
|
150
|
+
#{obj}.send(:#{meth}) if #{obj}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# Add proxy methods only to the instance object
|
158
|
+
def instance_proxy_accessors_for obj, *methods
|
159
|
+
instance_proxy_for obj, methods
|
160
|
+
check = last_arg_value({:check => false}, methods)
|
161
|
+
|
162
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
163
|
+
methods.delete(:rename) if rename_methods
|
164
|
+
|
165
|
+
methods.to_symbols.flat_uniq.each do |meth|
|
166
|
+
if check
|
167
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
168
|
+
raise ArgumentError, "No such method #{meth} to proxy for #{obj}" if !self.send(obj).respond_to?(:"#{meth}=")
|
169
|
+
end
|
170
|
+
instance_eval %{
|
171
|
+
class << self
|
172
|
+
define_method :#{meth}= do |arg|
|
173
|
+
self.#{obj} ||= create_in_factory(:#{obj})
|
174
|
+
self.#{obj} ||= self.class.send(:create_in_factory, :#{obj})
|
175
|
+
#{obj}.send(:#{meth}=, arg) if #{obj}
|
176
|
+
end
|
177
|
+
end
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
rename_methods.each_pair do |meth, new_meth|
|
182
|
+
if check
|
183
|
+
raise ArgumentError, "No such object to proxy #{obj}" if !self.respond_to?(obj)
|
184
|
+
raise ArgumentError, "No such method to proxy for #{obj}" if !self.send(obj).respond_to?(meth)
|
185
|
+
end
|
186
|
+
instance_eval %{
|
187
|
+
class << self
|
188
|
+
define_method :#{new_meth}= do |arg|
|
189
|
+
self.#{obj} ||= create_in_factory(:#{obj})
|
190
|
+
self.#{obj} ||= self.class.send(:create_in_factory, :#{obj})
|
191
|
+
#{obj}.send(:#{meth}=, arg) if #{obj}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def named_proxies hash
|
199
|
+
raise ArgumentError, "Argument must be a hash" if !hash.kind_of? Hash
|
200
|
+
self.class.send :include, Party::Proxy
|
201
|
+
hash.each_pair do |proxy, methods|
|
202
|
+
instance_proxy_accessors_for proxy, methods
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
protected
|
207
|
+
|
208
|
+
def create_in_factory name
|
209
|
+
raise ArgumentError, "Factory name must be a label, was #{name}" if !name.kind_of_label?
|
210
|
+
proxy_factory[name].create if !send(name) && proxy_factory && proxy_factory[name]
|
211
|
+
end
|
6
212
|
end
|
7
213
|
|
8
214
|
# Define class methods here.
|
9
|
-
module ClassMethods
|
10
|
-
attr_accessor :proxies
|
215
|
+
module ClassMethods
|
216
|
+
attr_accessor :proxies
|
217
|
+
attr_accessor :proxy_factories
|
218
|
+
|
219
|
+
def proxy_factory
|
220
|
+
proxy_factories
|
221
|
+
end
|
222
|
+
|
223
|
+
def remove_proxy_factory name
|
224
|
+
@proxy_factories[name] = nil
|
225
|
+
end
|
226
|
+
|
227
|
+
def remove_proxy_factories
|
228
|
+
self.proxy_factories = nil
|
229
|
+
end
|
230
|
+
|
231
|
+
def add_proxy_factories hash
|
232
|
+
hash.each_pair do |name, factory|
|
233
|
+
factory = if factory.kind_of?(Class)
|
234
|
+
Party::Proxy::Factory.new(factory)
|
235
|
+
else
|
236
|
+
case factory
|
237
|
+
when Array
|
238
|
+
fac = factory.first
|
239
|
+
meth = factory.last if factory.size == 2
|
240
|
+
raise ArgumentError, "Factory must be a Class, was #{fac}" if !fac.kind_of?(Class)
|
241
|
+
raise ArgumentError, "Factory method be a label, was #{meth}" if meth && !meth.kind_of_label?
|
242
|
+
Party::Proxy::Factory.new(fac, meth)
|
243
|
+
else
|
244
|
+
raise ArgumentError, "Factory must be a Class or have a #create method: #{factory}" if !factory.respond_to?(:create)
|
245
|
+
factory
|
246
|
+
end
|
247
|
+
end
|
248
|
+
self.proxy_factories ||= {}
|
249
|
+
self.proxy_factories.merge!(name.to_sym => factory) if name.kind_of_label?
|
250
|
+
end
|
251
|
+
end
|
252
|
+
alias_method :add_proxy_factory, :add_proxy_factories
|
253
|
+
|
254
|
+
|
255
|
+
def remove_proxy name
|
256
|
+
proxies.delete(name)
|
257
|
+
end
|
258
|
+
|
259
|
+
def proxy_for obj, *methods
|
260
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
261
|
+
methods.delete(:rename) if rename_methods
|
262
|
+
|
263
|
+
methods.flat_uniq.each do |meth|
|
264
|
+
name = meth.to_sym
|
265
|
+
define_method name do
|
266
|
+
send(obj).send(name) if send(obj)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
rename_methods.each_pair do |meth, new_meth|
|
271
|
+
name = meth.to_sym
|
272
|
+
define_method new_meth.to_sym do
|
273
|
+
send(obj).send(name) if send(obj)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def proxy_accessors_for obj, *methods
|
279
|
+
proxy_for obj, methods
|
280
|
+
|
281
|
+
rename_methods = last_arg_value({:rename => {}}, methods)
|
282
|
+
methods.delete(:rename) if rename_methods
|
283
|
+
|
284
|
+
methods.flat_uniq.each do |meth|
|
285
|
+
name = meth.to_sym
|
286
|
+
obj_name = obj.to_sym
|
287
|
+
define_method name do |arg|
|
288
|
+
send(obj_name).send('||=', create_in_factory(obj_name))
|
289
|
+
send(obj_name).send("#{name}=", arg) if send(obj)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
rename_methods.each_pair do |meth, new_meth|
|
294
|
+
name = meth.to_sym
|
295
|
+
obj_name = obj.to_sym
|
296
|
+
define_method new_meth.to_sym do |arg|
|
297
|
+
send(obj_name).send('||=', create_in_factory(obj_name))
|
298
|
+
send(obj_name).send("#{name}=", arg) if send(obj)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
protected
|
304
|
+
|
305
|
+
def create_in_factory name
|
306
|
+
proxy_factory[name].create if proxy_factory && proxy_factory[name]
|
307
|
+
end
|
11
308
|
end
|
12
309
|
|
13
310
|
# proxy to state
|
14
311
|
def method_missing(name, *args, &block)
|
312
|
+
return if !self.class.proxies
|
15
313
|
self.class.proxies.each do |proxi|
|
16
314
|
proxy_obj = self.send proxi
|
17
|
-
return proxy_obj.send
|
315
|
+
return proxy_obj.send(name, *args, &block) if proxy_obj.respond_to? :"#{name}"
|
18
316
|
end
|
19
317
|
super
|
20
318
|
end
|
21
319
|
end
|
22
320
|
end
|
23
321
|
|
24
|
-
class
|
322
|
+
class Module
|
323
|
+
def party_proxy
|
324
|
+
include Party::Proxy
|
325
|
+
end
|
326
|
+
alias_method :proxy_party, :party_proxy
|
327
|
+
|
328
|
+
def named_proxies hash
|
329
|
+
raise ArgumentError, "Argument must be a hash" if !hash.kind_of? Hash
|
330
|
+
include Party::Proxy
|
331
|
+
hash.each_pair do |proxy, methods|
|
332
|
+
proxy_accessors_for proxy, methods
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
25
336
|
def proxy *proxy_objs
|
26
337
|
include Party::Proxy
|
27
338
|
|
28
|
-
proxy_objs.each do |proxy_obj|
|
339
|
+
proxy_objs.flat_uniq.each do |proxy_obj|
|
340
|
+
raise ArgumentError, "bad proxy object #{proxy_obj}" if !proxy_obj.kind_of_label?
|
29
341
|
attr_accessor proxy_obj
|
30
342
|
@proxies ||= []
|
31
|
-
@proxies << proxy_obj
|
343
|
+
@proxies << proxy_obj if !@proxies.include? proxy_obj
|
32
344
|
end
|
33
345
|
end
|
34
346
|
end
|
data/proxy_party.gemspec
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{proxy_party}
|
8
|
+
s.version = "0.2.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Kristian Mandrup"]
|
12
|
+
s.date = %q{2011-02-21}
|
13
|
+
s.description = %q{Method missing proxy pattern}
|
14
|
+
s.email = %q{kmandrup@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.textile"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"LICENSE",
|
23
|
+
"README.textile",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/proxy_party.rb",
|
27
|
+
"proxy_party.gemspec",
|
28
|
+
"spec/proxy_party_complex_spec.rb",
|
29
|
+
"spec/proxy_party_spec.rb",
|
30
|
+
"spec/spec_helper.rb"
|
31
|
+
]
|
32
|
+
s.homepage = %q{http://github.com/kristianmandrup/proxy_party}
|
33
|
+
s.require_paths = ["lib"]
|
34
|
+
s.rubygems_version = %q{1.5.2}
|
35
|
+
s.summary = %q{Add proxy functionality the fun way}
|
36
|
+
s.test_files = [
|
37
|
+
"spec/proxy_party_complex_spec.rb",
|
38
|
+
"spec/proxy_party_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
|
+
]
|
41
|
+
|
42
|
+
if s.respond_to? :specification_version then
|
43
|
+
s.specification_version = 3
|
44
|
+
|
45
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
46
|
+
s.add_runtime_dependency(%q<sugar-high>, [">= 0.3.5"])
|
47
|
+
s.add_development_dependency(%q<rspec>, [">= 2.4.0"])
|
48
|
+
else
|
49
|
+
s.add_dependency(%q<sugar-high>, [">= 0.3.5"])
|
50
|
+
s.add_dependency(%q<rspec>, [">= 2.4.0"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<sugar-high>, [">= 0.3.5"])
|
54
|
+
s.add_dependency(%q<rspec>, [">= 2.4.0"])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Module
|
4
|
+
include Party::Proxy
|
5
|
+
end
|
6
|
+
|
7
|
+
class Address
|
8
|
+
attr_accessor :street
|
9
|
+
end
|
10
|
+
|
11
|
+
module Proxies
|
12
|
+
proxy_accessors_for :address, :street
|
13
|
+
end
|
14
|
+
|
15
|
+
class Place
|
16
|
+
attr_accessor :address
|
17
|
+
# include Proxies
|
18
|
+
|
19
|
+
def self.inherited(base)
|
20
|
+
# Alternative
|
21
|
+
# base.class_eval do
|
22
|
+
# send :include, Proxies
|
23
|
+
# end
|
24
|
+
base.send :include, Proxies
|
25
|
+
# puts base.methods.sort
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class OtherPlace
|
30
|
+
attr_accessor :address
|
31
|
+
|
32
|
+
proxy_accessors_for :address, :street
|
33
|
+
end
|
34
|
+
|
35
|
+
class Pickup
|
36
|
+
attr_accessor :address
|
37
|
+
|
38
|
+
include Proxies
|
39
|
+
end
|
40
|
+
|
41
|
+
class Dropoff < Place
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Party::Proxy do
|
45
|
+
context 'Dropoff inherits from Place' do
|
46
|
+
before do
|
47
|
+
@dropoff = Dropoff.new
|
48
|
+
@pickup = Pickup.new
|
49
|
+
@other = OtherPlace.new
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should add proxies for address' do
|
53
|
+
@dropoff.should respond_to :street
|
54
|
+
@pickup.should respond_to :street
|
55
|
+
@other.should respond_to :street
|
56
|
+
|
57
|
+
@dropoff.should respond_to :street=
|
58
|
+
@pickup.should respond_to :street=
|
59
|
+
@other.should respond_to :street=
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/proxy_party_spec.rb
CHANGED
@@ -1,37 +1,149 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
3
|
class State
|
4
4
|
attr_accessor :name
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@name = name
|
8
|
-
end
|
9
|
-
end
|
5
|
+
|
6
|
+
def initialize name = nil
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
end
|
10
10
|
|
11
11
|
class Info
|
12
12
|
attr_accessor :text
|
13
|
-
|
14
|
-
def initialize(text)
|
15
|
-
@text = text
|
16
|
-
end
|
17
|
-
end
|
18
13
|
|
14
|
+
def initialize text = nil
|
15
|
+
@text = text
|
16
|
+
end
|
17
|
+
end
|
19
18
|
|
20
|
-
|
19
|
+
class Mic
|
20
|
+
attr_accessor :speak, :yawn
|
21
|
+
|
22
|
+
def initialize text = nil
|
23
|
+
@speak = text
|
24
|
+
end
|
25
|
+
|
26
|
+
def speak_it!
|
27
|
+
speak
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.create_empty
|
31
|
+
mic = self.new
|
32
|
+
mic.speak = 'empty'
|
33
|
+
mic.yawn = 'miau'
|
34
|
+
mic
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Party
|
21
39
|
class Subject
|
40
|
+
attr_accessor :mic
|
41
|
+
|
22
42
|
proxy :state, :info
|
23
|
-
|
24
|
-
def initialize
|
43
|
+
|
44
|
+
def initialize name = nil
|
25
45
|
@state = State.new name
|
26
|
-
@info = Info.new 'hello'
|
46
|
+
@info = Info.new 'hello'
|
27
47
|
end
|
28
48
|
end
|
29
49
|
end
|
30
50
|
|
51
|
+
module PartyModule
|
52
|
+
party_proxy
|
53
|
+
end
|
54
|
+
|
55
|
+
class PartyClass
|
56
|
+
proxy_party
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
|
31
61
|
describe Party::Proxy do
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
62
|
+
describe '#party_proxy' do
|
63
|
+
it "Should add party proxy to Module" do
|
64
|
+
PartyModule.methods.grep(/proxy_for/).should_not be_empty
|
65
|
+
PartyModule.should respond_to(:add_proxy_factory)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "Should add party proxy to Class" do
|
69
|
+
PartyClass.methods.grep(/proxy_for/).should_not be_empty
|
70
|
+
PartyClass.should respond_to(:add_proxy_factory)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '#proxy' do
|
75
|
+
it "proxies state and info so it can call name directly on subject" do
|
76
|
+
subject = Party::Subject.new 'kristian'
|
77
|
+
subject.name.should == 'kristian'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#proxy_for' do
|
82
|
+
it "proxies speak_it! on mic" do
|
83
|
+
subject = Party::Subject.new 'kristian'
|
84
|
+
subject.mic = Mic.new 'hello'
|
85
|
+
subject.instance_proxy_for :mic, :speak_it!
|
86
|
+
subject.speak_it!.should == 'hello'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "handles proxy when the proxied object (mic) is nil" do
|
90
|
+
subject = Party::Subject.new 'kristian'
|
91
|
+
subject.instance_proxy_for :mic, :speak_it!
|
92
|
+
subject.speak_it!.should == nil
|
93
|
+
end
|
94
|
+
|
95
|
+
it "handles proxy when the proxied object (mic) is set to nil later" do
|
96
|
+
subject = Party::Subject.new 'kristian'
|
97
|
+
subject.mic = Mic.new 'hello'
|
98
|
+
subject.instance_proxy_for :mic, :speak_it!, :check => true
|
99
|
+
subject.mic = nil
|
100
|
+
subject.speak_it!.should == nil
|
101
|
+
end
|
102
|
+
|
103
|
+
it "errors when proxy when the proxied object (mic) is nil and nil check is on" do
|
104
|
+
subject = Party::Subject.new 'kristian'
|
105
|
+
subject.mic = nil
|
106
|
+
lambda {subject.proxy_for :mic, :speak_it!, :check => true}.should raise_error
|
107
|
+
end
|
108
|
+
|
109
|
+
it "proxies uses class level proxy factory with factory method" do
|
110
|
+
subject = Party::Subject.new 'kristian'
|
111
|
+
subject.add_proxy_factory :mic => [Mic, :create_empty]
|
112
|
+
subject.instance_proxy_accessors_for :mic, :speak, :yawn
|
113
|
+
subject.speak = 'blip'
|
114
|
+
subject.speak.should == 'blip'
|
115
|
+
subject.yawn.should == 'miau'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#proxy_for renaming using :as' do
|
120
|
+
it "proxies speak_it! on mic" do
|
121
|
+
subject = Party::Subject.new 'kristian'
|
122
|
+
subject.mic = Mic.new 'hello'
|
123
|
+
subject.instance_proxy_for :mic, :rename => {:speak_it! => :speak_up!}
|
124
|
+
subject.speak_up!.should == 'hello'
|
125
|
+
end
|
126
|
+
|
127
|
+
it "proxies uses class level proxy factory with factory method" do
|
128
|
+
subject = Party::Subject.new 'kristian'
|
129
|
+
subject.add_proxy_factory :mic => [Mic, :create_empty]
|
130
|
+
subject.instance_proxy_accessors_for :mic, :rename => {:speak => :yell, :yawn => :gaab}
|
131
|
+
subject.yell = 'blip'
|
132
|
+
subject.yell.should == 'blip'
|
133
|
+
subject.gaab.should == 'miau'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#named_proxies' do
|
138
|
+
it "proxies select :mic and :state methods" do
|
139
|
+
subject = Party::Subject.new 'kristian'
|
140
|
+
subject.mic = Mic.new 'hello'
|
141
|
+
subject.named_proxies :mic => :speak, :state => :name
|
142
|
+
subject.speak = 'do it!'
|
143
|
+
subject.speak.should == 'do it!'
|
144
|
+
|
145
|
+
subject.name = 'kris'
|
146
|
+
subject.name.should == 'kris'
|
147
|
+
end
|
36
148
|
end
|
37
149
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,75 +1,84 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: proxy_party
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
version: 0.1.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Kristian Mandrup
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
date: 2010-06-18 00:00:00 +02:00
|
12
|
+
date: 2011-02-21 00:00:00.000000000 +01:00
|
18
13
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sugar-high
|
17
|
+
requirement: &2157117440 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.3.5
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2157117440
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rspec
|
28
|
+
requirement: &2157116940 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.4.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *2157116940
|
21
37
|
description: Method missing proxy pattern
|
22
38
|
email: kmandrup@gmail.com
|
23
39
|
executables: []
|
24
|
-
|
25
40
|
extensions: []
|
26
|
-
|
27
|
-
extra_rdoc_files:
|
41
|
+
extra_rdoc_files:
|
28
42
|
- LICENSE
|
29
|
-
- README.
|
30
|
-
files:
|
43
|
+
- README.textile
|
44
|
+
files:
|
31
45
|
- .document
|
32
|
-
- .
|
46
|
+
- .rspec
|
33
47
|
- LICENSE
|
34
|
-
- README.
|
48
|
+
- README.textile
|
35
49
|
- Rakefile
|
36
50
|
- VERSION
|
37
51
|
- lib/proxy_party.rb
|
52
|
+
- proxy_party.gemspec
|
53
|
+
- spec/proxy_party_complex_spec.rb
|
38
54
|
- spec/proxy_party_spec.rb
|
39
|
-
- spec/spec.opts
|
40
55
|
- spec/spec_helper.rb
|
41
56
|
has_rdoc: true
|
42
57
|
homepage: http://github.com/kristianmandrup/proxy_party
|
43
58
|
licenses: []
|
44
|
-
|
45
59
|
post_install_message:
|
46
|
-
rdoc_options:
|
47
|
-
|
48
|
-
require_paths:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
49
62
|
- lib
|
50
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
64
|
none: false
|
52
|
-
requirements:
|
53
|
-
- -
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
|
56
|
-
|
57
|
-
version: "0"
|
58
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
70
|
none: false
|
60
|
-
requirements:
|
61
|
-
- -
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
|
64
|
-
- 0
|
65
|
-
version: "0"
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
66
75
|
requirements: []
|
67
|
-
|
68
76
|
rubyforge_project:
|
69
|
-
rubygems_version: 1.
|
77
|
+
rubygems_version: 1.5.2
|
70
78
|
signing_key:
|
71
79
|
specification_version: 3
|
72
80
|
summary: Add proxy functionality the fun way
|
73
|
-
test_files:
|
81
|
+
test_files:
|
82
|
+
- spec/proxy_party_complex_spec.rb
|
74
83
|
- spec/proxy_party_spec.rb
|
75
84
|
- spec/spec_helper.rb
|
data/.gitignore
DELETED
data/README.markdown
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# Proxy Party
|
2
|
-
|
3
|
-
Greatly facilitates adding proxies through the proxy method_missing pattern
|
4
|
-
|
5
|
-
## Install ##
|
6
|
-
|
7
|
-
<code>$ gem install party_proxy</code>
|
8
|
-
|
9
|
-
## Usage Configuration ##
|
10
|
-
|
11
|
-
<code>require 'party_proxy'</code>
|
12
|
-
|
13
|
-
## Usage ##
|
14
|
-
|
15
|
-
<pre><code>
|
16
|
-
class State
|
17
|
-
attr_accessor :name
|
18
|
-
|
19
|
-
def initialize(name)
|
20
|
-
@name = name
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class Info
|
25
|
-
attr_accessor :text
|
26
|
-
|
27
|
-
def initialize(text)
|
28
|
-
@text = text
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
module Party
|
33
|
-
class Subject
|
34
|
-
# proxy state and info objects to make their methods
|
35
|
-
# directly accessible from subject objects
|
36
|
-
proxy :state, :info
|
37
|
-
|
38
|
-
def initialize(name)
|
39
|
-
@state = State.new name
|
40
|
-
@info = Info.new 'hello'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
subject = Party::Subject.new 'kristian'
|
46
|
-
# access 'name' directly through 'state' proxy
|
47
|
-
puts subject.name
|
48
|
-
# access 'text' directly through 'info' proxy
|
49
|
-
puts subject.text
|
50
|
-
|
51
|
-
=> 'kristian'
|
52
|
-
=> 'hello'
|
53
|
-
</code></pre>
|
54
|
-
|
55
|
-
## Note on Patches/Pull Requests ##
|
56
|
-
|
57
|
-
* Fork the project.
|
58
|
-
* Make your feature addition or bug fix.
|
59
|
-
* Add tests for it. This is important so I don't break it in a
|
60
|
-
future version unintentionally.
|
61
|
-
* Commit, do not mess with rakefile, version, or history.
|
62
|
-
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
63
|
-
* Send me a pull request. Bonus points for topic branches.
|
64
|
-
|
65
|
-
## Copyright ##
|
66
|
-
|
67
|
-
Copyright (c) 2010 Kristian Mandrup. See LICENSE for details.
|