bufferaffects 0.2.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.
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Easy and automatic resetting of buffers when certain methods are called.
4
+ #
5
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
6
+ # Copyright:: Copyright (c) 2009 Paweł Wilk
7
+ # License:: LGPL
8
+
9
+ require 'bufferaffects/attr_inheritable'
10
+ require 'bufferaffects/bufferaffects'
11
+
@@ -0,0 +1,48 @@
1
+
2
+ module AttrInheritable
3
+
4
+ def self.included(base) #:nodoc:
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ def attr_inheritable(*variables)
11
+ variables.each do |v|
12
+ module_eval %{
13
+ def #{v}
14
+ @#{v} = superclass.#{v} if !instance_variable_defined?(:@#{v}) && superclass.respond_to?(:#{v})
15
+ @#{v} ||= nil
16
+ return @#{v}
17
+ end
18
+ }
19
+ end
20
+ end
21
+
22
+ def attr_inheritable_hash(*variables)
23
+ variables.each do |v|
24
+ module_eval %{
25
+ def #{v}
26
+ @#{v} = superclass.#{v} if !instance_variable_defined?(:@#{v}) && superclass.respond_to?(:#{v})
27
+ @#{v} ||= {}
28
+ return @#{v}
29
+ end
30
+ }
31
+ end
32
+ end
33
+
34
+ def attr_inheritable_array(*variables)
35
+ variables.each do |v|
36
+ module_eval %{
37
+ def #{v}
38
+ @#{v} = superclass.#{v} if !instance_variable_defined?(:@#{v}) && superclass.respond_to?(:#{v})
39
+ if @#{v} ||= []
40
+ return @#{v}
41
+ end
42
+ }
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
@@ -0,0 +1,254 @@
1
+ # = bufferaffects/bufferaffects
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: Copyright (c) 2009 Paweł Wilk
5
+ # License:: LGPL
6
+ #
7
+
8
+ # This module is intended to be used as extension
9
+ # (class level mixin) for classes using some buffers
10
+ # that may be altered by calling certain methods.
11
+ #
12
+ # It automates resetting of buffers by installing
13
+ # wrappers for invasive methods you choose. It rewrites
14
+ # selected methods by adding to them code that calls
15
+ # buffer(s) flushing method created by you.
16
+ #
17
+ # === Markers
18
+ #
19
+ # To select which methods are invasive for your buffer(s)
20
+ # you should use markers which in usage are similar to
21
+ # accessors, e.g:
22
+ #
23
+ # attr_affects_buffers :domain
24
+ #
25
+ # Markers may be placed anywhere in the class. Wrapping
26
+ # routine will wait for methods to be defined if you
27
+ # mark them too early in your code.
28
+ #
29
+ # ==== Marking methods
30
+ #
31
+ # To mark methods which should trigger reset operation
32
+ # when called use method_affects_buffers which takes
33
+ # comma-separated list of symbols describing names
34
+ # of these methods.
35
+ #
36
+ # ==== Marking attributes (setters)
37
+ #
38
+ # The marker attr_affects_buffers is similar but it takes
39
+ # instance members not methods as arguments. It just installs
40
+ # hooks for corresponding setters.
41
+ #
42
+ # === Buffers flushing method
43
+ #
44
+ # Default instance method called to reset buffers should be
45
+ # defined under name +reset_buffers+
46
+ # You may also want to set up your own name by calling
47
+ # buffers_reset_method class method.
48
+ #
49
+ # Buffers flushing method may take none or exactly one argument.
50
+ # If your method will take an argument then a name of calling
51
+ # method will be passed to it as symbol.
52
+ #
53
+ # The name of your
54
+ # buffers flushing method is passed to subclasses but
55
+ # each subclass may redefine it.
56
+ #
57
+ # Be aware that if you have a class that is subclass of
58
+ # a class using BufferAffects then by setting new buffers_reset_method
59
+ # you may experience two methods being called. That may happen
60
+ # when:
61
+ # * your base class has different resetting method assigned
62
+ # than your derivative class
63
+ # * you mark some method or attribute again using attr_affects_buffers or method_affects_buffers
64
+ # * you will not redefine that method in subclass (despite two facts above)
65
+ # That's because we assume that resetting method assigned to a method in superclass
66
+ # may be needed there and it shouldn't be taken away.
67
+ #
68
+ # === Inherited classes
69
+ #
70
+ # This module tries to be inheritance-safe but you will have to
71
+ # mark methods and members in subclasses if you are going
72
+ # to redefine them. That will install triggers again which is needed
73
+ # since redefining creates new code for method which overrides the code
74
+ # altered by BufferAffects.
75
+ #
76
+ # The smooth way is of course to use +super+
77
+ # in overloaded methods so it will also do the job.
78
+ #
79
+ # To be sure that everything will work fine try to place
80
+ # buffers_reset_method clause before any other markers.
81
+ #
82
+ # === Caution
83
+ #
84
+ # This code uses method_added hook. If you're going
85
+ # to redefine that special method in your class while still using
86
+ # this module then remember to call original version or explicitly invoke
87
+ # ba_check_method in your version of method_added:
88
+ #
89
+ # ba_check_method(name)
90
+ #
91
+ # === Example
92
+ #
93
+ # class Main
94
+ #
95
+ # include BufferAffects
96
+ #
97
+ # buffers_reset_method :reset_path_buffer
98
+ # attr_affects_buffers :subpart
99
+ # attr_accessor :subpart, :otherpart
100
+ #
101
+ # def reset_path_buffer(name)
102
+ # @path = nil
103
+ # p "reset called for #{name}"
104
+ # end
105
+ #
106
+ # def path
107
+ # @path ||= @subpart.to_s + @otherpart.to_s
108
+ # end
109
+ #
110
+ # end
111
+ #
112
+ # obj = Main.new
113
+ # obj.subpart = 'test'
114
+ # p obj.path
115
+ # obj.subpart = '1234'
116
+ # p obj.path
117
+
118
+ module BufferAffects
119
+
120
+ def self.included(base) #:nodoc:
121
+ base.extend(ClassMethods)
122
+ end
123
+
124
+ module ClassMethods
125
+
126
+ include AttrInheritable
127
+
128
+ attr_inheritable_hash :__ba_wrapped__
129
+ attr_inheritable :__ba_reset_method__
130
+
131
+ # This method sets name of method that will be used to reset buffers.
132
+
133
+ def buffers_reset_method(name) # :doc:
134
+ name = name.to_s.strip
135
+ raise ArgumentError.new('method name cannot be empty') if name.empty?
136
+ @__ba_reset_method__ = name.to_sym
137
+ end
138
+ private :buffers_reset_method
139
+
140
+ # This method sets the marker for hook to be installed.
141
+ # It ignores methods for which wrapper already exists.
142
+
143
+ def method_affects_buffers(*names) # :doc:
144
+ @__ba_methods__ ||= {}
145
+ names.uniq!
146
+ names.collect! { |name| name.to_sym }
147
+ names.delete_if { |name| @__ba_methods__.has_key?(name) }
148
+ ba_methods_wrap(*names)
149
+ end
150
+ private :method_affects_buffers
151
+
152
+ # This method searches for setter methods for given
153
+ # member names and tries to wrap them into buffers
154
+ # resetting hooks usting method_affects_buffers
155
+
156
+ def attr_affects_buffers(*names) # :doc:
157
+ names.collect! { |name| :"#{name}=" }
158
+ method_affects_buffers(*names)
159
+ end
160
+ private :attr_affects_buffers
161
+
162
+ # This method installs hook for given methods or puts their names
163
+ # on the queue if methods haven't been defined yet. The queue is
164
+ # tested each time ba_check_hook is called.
165
+ #
166
+ # Each processed method can be in one of 2 states:
167
+ # * false - method is not processed now
168
+ # * true - method is now processed
169
+ #
170
+ # After successful wrapping method name (key) and object ID (value) pairs
171
+ # are added two containers: @__ba_wrapped__ and @__ba_methods__
172
+
173
+ def ba_methods_wrap(*names)
174
+ names.delete_if { |name| @__ba_methods__[name] == true } # don't handle methods being processed
175
+ kmethods = public_instance_methods +
176
+ private_instance_methods +
177
+ protected_instance_methods
178
+ install_now = names.select { |name| kmethods.include?(name) } # select methods for immediate wrapping
179
+ install_now.delete_if do |name| # but don't wrap already wrapped, means:
180
+ self.__ba_wrapped__.has_key?(name) && # - wrapped by our class or other class and
181
+ !@__ba_methods__.has_key?(name) && # - not wrapped by our class
182
+ self.__ba_wrapped__[name] == __ba_reset_method__ # - uses the same resetting method
183
+ end
184
+
185
+ install_later = names - install_now # collect undefined and wrapped methods
186
+ install_later.each { |name| @__ba_methods__[name] = false } # and add them to the waiting queue
187
+
188
+ install_now.each { |name| @__ba_methods__[name] = true } # mark methods as currently processed
189
+ installed = ba_install_hook(*install_now) # and install hooks for them
190
+ install_now.each { |name| @__ba_methods__[name] = false } # mark methods as not processed again
191
+ installed.each_pair do |name,id| # and note the reset method assigned to wrapped methods
192
+ @__ba_wrapped__[name] = self.__ba_reset_method__ # inherited container
193
+ @__ba_methods__[name] = self.__ba_reset_method__ # this class's container
194
+ end
195
+ end
196
+ private :ba_methods_wrap
197
+
198
+ # This method checks whether method which name is given
199
+ # is now available and should be installed. In most cases you won't
200
+ # need to use it directly.
201
+
202
+ def ba_check_method(name) # :doc:
203
+ name = name.to_sym
204
+ @__ba_methods__ ||= {}
205
+ if @__ba_methods__.has_key?(name)
206
+ ba_methods_wrap(name)
207
+ end
208
+ end
209
+ private :ba_check_method
210
+
211
+ # This method installs hook which alters given methods by wrapping
212
+ # them into method that invokes buffers resetting routine. It will
213
+ # not install hook for methods beginning with __ba, which signalizes
214
+ # that they are wrappers for other methods.
215
+
216
+ def ba_install_hook(*names)
217
+ @__ba_reset_method__ = 'reset_buffers' if self.__ba_reset_method__.nil?
218
+ installed = {}
219
+ names.uniq.each do |name|
220
+ new_method = name.to_s
221
+ next if new_method[0..3] == '__ba'
222
+ orig_id = instance_method(name.to_sym).object_id
223
+ orig_method = '__ba' + orig_id.to_s + '__'
224
+ reset_method = self.__ba_reset_method__.to_s
225
+ module_eval %{
226
+ alias_method :#{orig_method}, :#{new_method}
227
+ private :#{orig_method}
228
+ def #{new_method}(*args, &block)
229
+ if method(:#{reset_method}).arity == 1
230
+ #{reset_method}(:#{new_method})
231
+ else
232
+ #{reset_method}
233
+ end
234
+ return #{orig_method}(*args, &block)
235
+ end
236
+ }
237
+ installed[name] = orig_id
238
+ end
239
+ return installed
240
+ end
241
+ private :ba_install_hook
242
+
243
+ # Hook that intercepts added methods. It simply calls ba_check_method
244
+ # passing it a name of added method. In most cases there is no need to call it
245
+ # directly.
246
+
247
+ def method_added(name)
248
+ ba_check_method(name)
249
+ end
250
+
251
+ end
252
+
253
+ end
254
+
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bufferaffects
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - "Pawe\xC5\x82 Wilk"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-22 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: BufferAffects makes it easy to set up automatic resetting of buffers when certain methods are called
17
+ email: pw@gnu.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/bufferaffects.rb
26
+ - lib/bufferaffects/bufferaffects.rb
27
+ - lib/bufferaffects/attr_inheritable.rb
28
+ has_rdoc: true
29
+ homepage: http://randomseed.pl/bufferaffects
30
+ post_install_message:
31
+ rdoc_options: []
32
+
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: "0"
40
+ version:
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ requirements: []
48
+
49
+ rubyforge_project:
50
+ rubygems_version: 1.3.1
51
+ signing_key:
52
+ specification_version: 2
53
+ summary: BufferAffects makes it easy to set up automatic resetting of buffers when certain methods are called
54
+ test_files: []
55
+