siefca-bufferaffects 0.0.4

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,246 @@
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
+ # extend 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
+ extend AttrInheritable
121
+
122
+ attr_inheritable_hash :__ba_wrapped__
123
+ attr_inheritable :__ba_reset_method__
124
+
125
+ # This method sets name of method that will be used to reset buffers.
126
+
127
+ def buffers_reset_method(name) # :doc:
128
+ name = name.to_s.strip
129
+ raise ArgumentError.new('method name cannot be empty') if name.empty?
130
+ @__ba_reset_method__ = name.to_sym
131
+ end
132
+ private :buffers_reset_method
133
+
134
+ # This method sets the marker for hook to be installed.
135
+ # It ignores methods for which wrapper already exists.
136
+
137
+ def method_affects_buffers(*names) # :doc:
138
+ @__ba_methods__ ||= {}
139
+ names.uniq!
140
+ names.collect! { |name| name.to_sym }
141
+ names.delete_if { |name| @__ba_methods__.has_key?(name) }
142
+ ba_methods_wrap(*names)
143
+ end
144
+ private :method_affects_buffers
145
+
146
+ # This method searches for setter methods for given
147
+ # member names and tries to wrap them into buffers
148
+ # resetting hooks usting method_affects_buffers
149
+
150
+ def attr_affects_buffers(*names) # :doc:
151
+ names.collect! { |name| :"#{name}=" }
152
+ method_affects_buffers(*names)
153
+ end
154
+ private :attr_affects_buffers
155
+
156
+ # This method installs hook for given methods or puts their names
157
+ # on the queue if methods haven't been defined yet. The queue is
158
+ # tested each time ba_check_hook is called.
159
+ #
160
+ # Each processed method can be in one of 2 states:
161
+ # * false - method is not processed now
162
+ # * true - method is now processed
163
+ #
164
+ # After successful wrapping method name (key) and object ID (value) pairs
165
+ # are added two containers: @__ba_wrapped__ and @__ba_methods__
166
+
167
+ def ba_methods_wrap(*names)
168
+ names.delete_if { |name| @__ba_methods__[name] == true } # don't handle methods being processed
169
+ kmethods = public_instance_methods +
170
+ private_instance_methods +
171
+ protected_instance_methods
172
+ install_now = names.select { |name| kmethods.include?(name) } # select methods for immediate wrapping
173
+ install_now.delete_if do |name| # but don't wrap already wrapped, means:
174
+ self.__ba_wrapped__.has_key?(name) && # - wrapped by our class or other class and
175
+ !@__ba_methods__.has_key?(name) && # - not wrapped by our class
176
+ self.__ba_wrapped__[name] == __ba_reset_method__ # - uses the same resetting method
177
+ end
178
+
179
+ install_later = names - install_now # collect undefined and wrapped methods
180
+ install_later.each { |name| @__ba_methods__[name] = false } # and add them to the waiting queue
181
+
182
+ install_now.each { |name| @__ba_methods__[name] = true } # mark methods as currently processed
183
+ installed = ba_install_hook(*install_now) # and install hooks for them
184
+ install_now.each { |name| @__ba_methods__[name] = false } # mark methods as not processed again
185
+ installed.each_pair do |name,id| # and note the reset method assigned to wrapped methods
186
+ @__ba_wrapped__[name] = self.__ba_reset_method__ # inherited container
187
+ @__ba_methods__[name] = self.__ba_reset_method__ # this class's container
188
+ end
189
+ end
190
+ private :ba_methods_wrap
191
+
192
+ # This method checks whether method which name is given
193
+ # is now available and should be installed. In most cases you won't
194
+ # need to use it directly.
195
+
196
+ def ba_check_method(name) # :doc:
197
+ name = name.to_sym
198
+ @__ba_methods__ ||= {}
199
+ if @__ba_methods__.has_key?(name)
200
+ ba_methods_wrap(name)
201
+ end
202
+ end
203
+ private :ba_check_method
204
+
205
+ # This method installs hook which alters given methods by wrapping
206
+ # them into method that invokes buffers resetting routine. It will
207
+ # not install hook for methods beginning with __ba, which signalizes
208
+ # that they are wrappers for other methods.
209
+
210
+ def ba_install_hook(*names)
211
+ @__ba_reset_method__ = 'reset_buffers' if self.__ba_reset_method__.nil?
212
+ installed = {}
213
+ names.uniq.each do |name|
214
+ new_method = name.to_s
215
+ next if new_method[0..3] == '__ba'
216
+ orig_id = instance_method(name.to_sym).object_id
217
+ orig_method = '__ba' + orig_id.to_s + '__'
218
+ reset_method = self.__ba_reset_method__.to_s
219
+ module_eval %{
220
+ alias_method :#{orig_method}, :#{new_method}
221
+ private :#{orig_method}
222
+ def #{new_method}(*args, &block)
223
+ if method(:#{reset_method}).arity == 1
224
+ #{reset_method}(:#{new_method})
225
+ else
226
+ #{reset_method}
227
+ end
228
+ return #{orig_method}(*args, &block)
229
+ end
230
+ }
231
+ installed[name] = orig_id
232
+ end
233
+ return installed
234
+ end
235
+ private :ba_install_hook
236
+
237
+ # Hook that intercepts added methods. It simply calls ba_check_method
238
+ # passing it a name of added method. In most cases there is no need to call it
239
+ # directly.
240
+
241
+ def method_added(name)
242
+ ba_check_method(name)
243
+ end
244
+
245
+ end
246
+
@@ -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
+
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: siefca-bufferaffects
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
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 -07: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
+ has_rdoc: true
28
+ homepage: http://randomseed.pl/bufferaffects
29
+ post_install_message:
30
+ rdoc_options: []
31
+
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: "0"
39
+ version:
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ requirements: []
47
+
48
+ rubyforge_project:
49
+ rubygems_version: 1.2.0
50
+ signing_key:
51
+ specification_version: 2
52
+ summary: BufferAffects makes it easy to set up automatic resetting of buffers when certain methods are called
53
+ test_files: []
54
+