gather 0.0.3

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/History.txt ADDED
@@ -0,0 +1,2 @@
1
+ == 0.0.3
2
+ * Make into a rubyforge gem
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README
4
+ Rakefile
5
+ lib/gather.rb
6
+ lib/changes.rb
7
+ script/destroy
8
+ script/generate
data/README ADDED
@@ -0,0 +1,22 @@
1
+ == gather
2
+
3
+ A gem that provides modules to make working with properties a bit easier.
4
+
5
+ For example:
6
+
7
+ class SumThing
8
+ include Gather
9
+ property :thing, :spla, :gumf, :troob
10
+ end
11
+
12
+ s = SumThing.new.gather :spla => 'five', :thing => 'sigma' do
13
+ troob %w{one two three four}
14
+ gumf 15 % 4
15
+ end
16
+
17
+ Or
18
+
19
+ s = SumThing.new.gather :spla => 'five', :thing => 'sigma' do |s|
20
+ s.troob = %w{one two three four}
21
+ s.gumf = 15 % 4
22
+ end
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/version.rb'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('gather', Gather::VERSION) do |p|
7
+ p.developer('John Anderson', 'panic@semiosix.com')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.rubyforge_name = 'clevic'
10
+ # p.extra_deps = [
11
+ # ['activesupport','>= 2.0.2'],
12
+ # ]
13
+ p.extra_dev_deps = [
14
+ ['newgem', ">= #{::Newgem::VERSION}"]
15
+ ]
16
+
17
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
18
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
19
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
20
+ p.rsync_args = '-av --delete --ignore-errors'
21
+ end
22
+
23
+ require 'newgem/tasks' # load /tasks/*.rake
24
+ Dir['tasks/**/*.rake'].each { |t| load t }
25
+
26
+ # TODO - want other tests/tasks run by default? Add them to the list
27
+ # task :default => [:spec, :features]
data/lib/changes.rb ADDED
@@ -0,0 +1,315 @@
1
+ require 'gather.rb'
2
+
3
+ class NilClass
4
+ def call_if( *args ); end
5
+ end
6
+
7
+ class Proc
8
+ alias_method :call_if, :call
9
+ end
10
+
11
+ =begin rdoc
12
+ Same as Gather, except that history is kept for changes, and on_change
13
+ is called each time a property value changes.
14
+ =end
15
+ module Changes
16
+ include Gather
17
+
18
+ class Change
19
+ attr_accessor :old, :new
20
+ def initialize
21
+ @old = []
22
+ @new = nil
23
+ end
24
+ end
25
+
26
+ # Gather values from the hash and the block, using the gather method.
27
+ unless instance_methods.include? 'initialize'
28
+ def initialize( hash = {}, &block )
29
+ init_properties( hash, &block )
30
+ end
31
+ end
32
+
33
+ # should probably use an object creation hook to do this
34
+ def init_properties( hash, &block )
35
+ @changes = {}
36
+ gather( hash, &block )
37
+ end
38
+
39
+ def changed?
40
+ changes.empty?
41
+ end
42
+
43
+ def changes( *symbols )
44
+ if symbols.empty?
45
+ @changes ||= {}
46
+ else
47
+ symbols.map{|x| changes[x] }
48
+ end
49
+ end
50
+
51
+ # called after the property has changed
52
+ # including classes should define this
53
+ def on_change( property, old, new )
54
+ end
55
+
56
+ attr_writer :change_history
57
+ def change_history
58
+ @change_history ||= []
59
+ end
60
+
61
+ # make a mark in the change history
62
+ def mark
63
+ new_changes = @changes.clone
64
+ new_changes.each{|k,v| new_changes[k] = v.clone}
65
+ change_history << new_changes
66
+ end
67
+
68
+ # restore the last mark. Do not emit any changes.
69
+ def restore
70
+ @changes = change_history.pop unless change_history.empty?
71
+ end
72
+
73
+ # reset the passed properties, or all if none are specified
74
+ def reset!( *properties )
75
+ if properties.empty?
76
+ @changes = {}
77
+ else
78
+ properties.each {|x| changes[x] = Change.new}
79
+ end
80
+ end
81
+
82
+ # execute the block, undo the given symbols, and
83
+ # return the result of the executed block. If no symbols
84
+ # are given, just use mark and restore
85
+ def preserve( *symbols )
86
+ mark if symbols.empty?
87
+ retval = yield
88
+ if symbols.empty?
89
+ restore
90
+ else
91
+ symbols.each {|symbol| undo( symbol ) }
92
+ end
93
+ retval
94
+ end
95
+
96
+ def undo( symbol )
97
+ unless changes[symbol].old.empty?
98
+ current = send( symbol )
99
+ if changes[symbol] && changes[symbol].old
100
+ eval "@#{symbol.to_s} = changes[:#{symbol.to_s}].old.pop"
101
+ end
102
+ current
103
+ end
104
+ end
105
+
106
+ # Add the property methods if
107
+ # they don't already exist, and if dynamic is in effect, which is
108
+ # the default. If static is in effect, the normal method_missing
109
+ # behaviour will be invoked.
110
+ def method_missing(sym, *args)
111
+ if self.class.dynamic?
112
+ self.class.property sym
113
+ send( sym, *args )
114
+ else
115
+ super
116
+ end
117
+ end
118
+
119
+ # Return a collection of property symbols
120
+ def property_names
121
+ self.class.properties.to_a
122
+ end
123
+
124
+ module ClassMethods
125
+ include Gather::ClassMethods
126
+ # Remove all but the absolutely essential methods from this class.
127
+ # Useful if you want a dynamic property list, but
128
+ # can have some surprising side effects, obviously.
129
+ def blankslate!
130
+ # remove unused methods that might clash with user accessors
131
+ keep_methods = %w( __send__ __id__ self send class inspect instance_eval instance_variables )
132
+ instance_methods.each do |method|
133
+ undef_method( method ) unless keep_methods.include?( method )
134
+ end
135
+ end
136
+
137
+ def module_name( symbol )
138
+ "#{symbol.to_s.capitalize}Property"
139
+ end
140
+
141
+ # For each property in symbols, add methods:
142
+ # instance.property as reader
143
+ # instance.property( value ) as writer
144
+ # instance.property = value as writer
145
+ # instance.before_property {|old,new| ... }
146
+ # instance.after_property {|old,new| ... }
147
+ # instance.on_property {|old,new| ... }
148
+ # instance.property_changed?
149
+ # instance.undo_property!
150
+ def property( *symbols )
151
+ @stripper ||= /^([^\= ]+)\s*\=?\s*$/
152
+ symbols.each do |sym|
153
+ property = @stripper.match( sym.to_s )[1]
154
+
155
+ # TODO false here until I figure out how to remove properties
156
+ unless false && const_defined?( module_name( property ).to_sym )
157
+ own_properties << property.to_sym
158
+ line, st = __LINE__, <<-EOF
159
+ module #{module_name( property )}
160
+
161
+ public
162
+ def #{property}(*val)
163
+ if val.empty?
164
+ @#{property}
165
+ else
166
+ self.#{property} = *val
167
+ end
168
+ end
169
+
170
+ def on_#{property}( &block )
171
+ @on_#{property}_block = block
172
+ end
173
+
174
+ def after_#{property}( &block )
175
+ @after_#{property}_block = block
176
+ end
177
+
178
+ def before_#{property}( &block )
179
+ @before_#{property}_block = block
180
+ end
181
+
182
+ def #{property}=(*new_value)
183
+ unwrapped_value = new_value.size == 1 ? new_value[0] : new_value
184
+ return if unwrapped_value == @#{property}
185
+
186
+ @before_#{property}_block.call_if( @#{property}, unwrapped_value )
187
+
188
+ changes[:#{property}] ||= Change.new
189
+ changes[:#{property}].old << @#{property}
190
+ changes[:#{property}].new = unwrapped_value
191
+
192
+ @#{property} = unwrapped_value
193
+
194
+ @after_#{property}_block.call_if( changes[:#{property}].old, unwrapped_value )
195
+ @on_#{property}_block.call_if( changes[:#{property}].old, unwrapped_value )
196
+ on_change( :#{property}, changes[:#{property}].old, unwrapped_value )
197
+ end
198
+
199
+ def #{property}_changed?
200
+ changes.has_key?(:#{property}) && !changes[:#{property}].old.empty?
201
+ end
202
+
203
+ # Undo the last change and return the undone value.
204
+ # return nil if there were no changes to undo
205
+ def undo_#{property}!
206
+ undo( :#{property} )
207
+ end
208
+
209
+ def reset_#{property}!
210
+ changes[x] = Change.new
211
+ end
212
+
213
+ private
214
+ def direct_#{property}=( value )
215
+ @#{property} = value
216
+ end
217
+
218
+ end # module
219
+ EOF
220
+ class_eval st, __FILE__, line + 1
221
+ end
222
+
223
+ # always include the module, for re-definitions
224
+ class_eval "include #{property.capitalize}Property"
225
+ end
226
+ end
227
+
228
+ # not working yet. Once the module has been added, it seems like
229
+ # adding it again does not redefine the methods, even if they were
230
+ # removed in the interim.
231
+ #~ def remove( *symbols )
232
+ #~ symbols.each do |sym|
233
+ #~ if defined?( sym )
234
+ #~ # undefine methods
235
+ #~ module_const = eval "#{self.name}::#{module_name( sym )}"
236
+ #~ module_const.instance_methods.each do |im|
237
+ #~ undef_method( im )
238
+ #~ end
239
+
240
+ #~ # remove from properties
241
+ #~ properties.delete sym
242
+ #~ end
243
+ #~ end
244
+ #~ end
245
+
246
+ # Do not allow accessors to be added dynamically. In other words,
247
+ # a subclass like this
248
+ # class IndexCollector
249
+ # static_properties :row, :column
250
+ # end
251
+ # will fail if used like this
252
+ # collector = IndexCollector.new( :row => 4, :column => 6 ) do
253
+ # other 'oops'
254
+ # end
255
+ # because :other isn't added by static_properties.
256
+ def static_properties( *syms )
257
+ @dynamic = false
258
+ property( *syms )
259
+ end
260
+
261
+ # Allow accessors to be added dynamically, the default.
262
+ def dynamic_properties( *syms )
263
+ @dynamic = true
264
+ property( *syms )
265
+ end
266
+
267
+ def dynamic!; @dynamic = true; end
268
+ def static!; @dynamic = false; end
269
+
270
+ def dynamic?
271
+ # don't optimise this to @dynamic ||= true, because it will reset
272
+ # an @dynamic of false to true
273
+ @dynamic = false if @dynamic.nil?
274
+ @dynamic
275
+ end
276
+
277
+ # might use this instead of operation
278
+ #~ def method_added( *args )
279
+ #~ super
280
+ #~ end
281
+
282
+ # define a method 'name' (which can be symbol or a string)
283
+ # which takes args and a block
284
+ # calls gather and then executes the block. Any changes made
285
+ # from args and block are rolled back
286
+ # name! will not roll back values
287
+ def operation( name, &body )
288
+ # save the block to be executed later
289
+ class_eval "@@#{name.to_s}_operation_block = body"
290
+
291
+ # define the new method called name
292
+ class_eval <<-EOF, __FILE__, __LINE__.to_i + 1
293
+ def #{name.to_s}( args = {}, &block )
294
+ preserve do
295
+ gather( args, &block )
296
+ instance_eval( &@@#{name.to_s}_operation_block )
297
+ end
298
+ end
299
+ EOF
300
+
301
+ # define new method called name!
302
+ class_eval <<-EOF, __FILE__, __LINE__.to_i + 1
303
+ def #{name.to_s}!( args = {}, &block )
304
+ gather( args, &block )
305
+ instance_eval( &@@#{name.to_s}_operation_block )
306
+ end
307
+ EOF
308
+ end
309
+ end
310
+
311
+ def self.included( base )
312
+ base.extend( ClassMethods )
313
+ end
314
+
315
+ end
data/lib/gather.rb ADDED
@@ -0,0 +1,96 @@
1
+ require 'set'
2
+
3
+ # Provides instance method gather and class method property
4
+ # Allows properties to be Enumerable.
5
+ module Gather
6
+ # Evaluate the block and gather options from args. Even if it's nil.
7
+ # Return self
8
+ def gather( args = {}, &block )
9
+ unless args.nil?
10
+ args.each do |key,value|
11
+ self.send( key, value )
12
+ end
13
+ end
14
+
15
+ unless block.nil?
16
+ if block.arity == -1
17
+ instance_eval &block
18
+ else
19
+ yield self
20
+ end
21
+ end
22
+ self
23
+ end
24
+
25
+ # return a hash of the properties
26
+ def to_hash( properties = self.class.properties )
27
+ properties.inject({}) {|s,x| s[x] = send(x); s}
28
+ end
29
+
30
+ # set all values specified in the hash
31
+ def merge!( hash )
32
+ hash.each {|k,v| send( k, v ) }
33
+ end
34
+
35
+ # return a new hash merged with the argument
36
+ def merge( hash )
37
+ to_hash.merge( hash )
38
+ end
39
+
40
+ def []( symbol )
41
+ send( symbol )
42
+ end
43
+
44
+ def []=( symbol, value )
45
+ send( symbol, value )
46
+ end
47
+
48
+ def each( &block )
49
+ self.class.properties.each( &block )
50
+ end
51
+ include Enumerable
52
+
53
+ def self.included( base )
54
+ base.extend( ClassMethods )
55
+ end
56
+
57
+ module ClassMethods
58
+ # return properties for only this class
59
+ def own_properties
60
+ @properties ||= Set.new
61
+ end
62
+
63
+ # return properties for this class and ancestors
64
+ def properties
65
+ own_properties +
66
+ if superclass.respond_to? :properties
67
+ superclass.properties
68
+ else
69
+ []
70
+ end
71
+ end
72
+
73
+ def property( *symbols )
74
+ @stripper ||= /^([^\= ]+)\s*\=?\s*$/
75
+ symbols.each do |sym|
76
+ stripped = @stripper.match( sym.to_s )[1]
77
+ own_properties << stripped.to_sym
78
+ line, st = __LINE__, <<-EOF
79
+ def #{stripped}(*val)
80
+ if val.empty?
81
+ @#{stripped}
82
+ else
83
+ @#{stripped} = val.size == 1 ? val[0] : val
84
+ end
85
+ end
86
+
87
+ def #{stripped}=(*val)
88
+ @#{stripped} = val.size == 1 ? val[0] : val
89
+ end
90
+ EOF
91
+ class_eval st, __FILE__, line + 1
92
+ end
93
+ end
94
+
95
+ end
96
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gather
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - John Anderson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-30 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: newgem
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.0
34
+ version:
35
+ description: ""
36
+ email:
37
+ - panic@semiosix.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ files:
46
+ - History.txt
47
+ - Manifest.txt
48
+ - README
49
+ - Rakefile
50
+ - lib/gather.rb
51
+ - lib/changes.rb
52
+ - script/destroy
53
+ - script/generate
54
+ has_rdoc: true
55
+ homepage: A gem that provides modules to make working with properties a bit easier.
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --main
59
+ - README
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: clevic
77
+ rubygems_version: 1.3.1
78
+ signing_key:
79
+ specification_version: 2
80
+ summary: ""
81
+ test_files: []
82
+