mixers 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Thomas Sawyer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README ADDED
@@ -0,0 +1,17 @@
1
+ = Ruby Mixers
2
+
3
+ == DESCRIPTION
4
+
5
+ Ruby Mixers is collection mixin modules for the Ruby
6
+ programming language.
7
+
8
+ Mixers is a spin-off of Ruby Facets. When Ruby Facets
9
+ scaled back to extensions only project, it's mixin
10
+ modules were gathered to create this new project.
11
+
12
+ == COPYRIGHTS
13
+
14
+ Ruby Mixers Copyright (c) 2010 Thomas Sawyer
15
+
16
+ Ruby Mixers is distibuted under the terms of the MIT license.
17
+
@@ -0,0 +1,148 @@
1
+ # Copyright (c) 2007 Thomas Sawyer
2
+
3
+ require 'facets/kernel/method'
4
+ require 'facets/module/instance_method'
5
+ require 'facets/unboundmethod/arguments'
6
+
7
+ # = Advisable
8
+ #
9
+ # Advisable provides a means of using before, after and
10
+ # around adivce in dynamic fashion.
11
+ #
12
+ module Advisable
13
+
14
+ def self.append_features(base)
15
+ base.extend self
16
+ end
17
+
18
+ def advice_before
19
+ @advice_before ||= {} #Hash.new{|h,k| h[k] = []}
20
+ end
21
+
22
+ def advice_after
23
+ @advice_after ||= {} #Hash.new{|h,k| h[k] = []}
24
+ end
25
+
26
+ def advice_around
27
+ @advice_around ||= {} #Hash.new{|h,k| h[k] = []}
28
+ end
29
+
30
+ def before(meth, &block)
31
+ name = "#{meth}:before:#{block.object_id}"
32
+ define_method(name, &block)
33
+ (advice_before[meth.to_sym] ||= []) << name
34
+ end
35
+
36
+ def after(meth, &block)
37
+ name = "#{meth}:after:#{block.object_id}"
38
+ define_method(name, &block)
39
+ (advice_after[meth.to_sym] ||= []) << name
40
+ end
41
+
42
+ def around(meth, &block)
43
+ name = "#{meth}:around:#{block.object_id}"
44
+ define_method(name, &block)
45
+ (advice_around[meth.to_sym] ||= []) << name
46
+ end
47
+
48
+ # Advise a method.
49
+
50
+ def advise(meth)
51
+ #return false if defined?(meth_origin)
52
+ args = instance_method(meth).arguments
53
+
54
+ module_eval(<<-END, __FILE__, __LINE__)
55
+ alias_method '#{meth}_origin', '#{meth}'
56
+ def #{meth}(#{args})
57
+ target = method!('#{meth}_origin')
58
+
59
+ unless target.advised
60
+ ancs = self.class.ancestors.select{ |anc| anc.respond_to?(:advice_before) }
61
+ target.advice_before = ancs.collect{ |anc| anc.advice_before[:'#{meth}'] }
62
+ target.advice_after = ancs.collect{ |anc| anc.advice_after[:'#{meth}'] }
63
+ target.advice_around = ancs.collect{ |anc| anc.advice_around[:'#{meth}'] }
64
+ target.advised = true
65
+ end
66
+
67
+ target.call_with_advice(self, *[#{args}])
68
+ end
69
+ END
70
+ end
71
+
72
+ #advise(:method_added)
73
+
74
+ #after :method_added do |meth|
75
+ # advise(meth) unless defined?("#{meth}_orig")
76
+ #end
77
+
78
+ def method_added(meth)
79
+ return if meth == :method_added
80
+ @added_stack ||= []
81
+ return if @added_stack.last == meth
82
+ return if /_(origin)$/ =~ meth.to_s
83
+ return if /:(before|after|around)/ =~ meth.to_s
84
+ @added_stack << meth
85
+ #return if instance_methods(false).include?("#{meth}_orig")
86
+ advise(meth)
87
+ @added_stack.pop
88
+ end
89
+
90
+ # Extensions for Method class.
91
+
92
+ module Method
93
+
94
+ attr_accessor :advised
95
+
96
+ attr_reader :advice_before
97
+ attr_reader :advice_after
98
+ attr_reader :advice_around
99
+
100
+ def advised?
101
+ @advised
102
+ end
103
+
104
+ def advice_before=(set); @advice_before = set.flatten.compact; end
105
+ def advice_after=(set) ; @advice_after = set.flatten.compact; end
106
+ def advice_around=(set); @advice_around = set.flatten.compact; end
107
+
108
+ # Call with advice.
109
+
110
+ def call_with_advice(obj, *args, &blk)
111
+ advice_before.each do |name|
112
+ #advice.call(*args, &blk)
113
+ obj.send(name, *args, &blk)
114
+ end
115
+
116
+ target = lambda{ call(*args, &blk) }
117
+ advice_around.each do |name|
118
+ target = lambda_target(obj, name, target, *args, &blk)
119
+ end
120
+ ret = target.call
121
+
122
+ advice_after.reverse_each do |name|
123
+ #advice.call(*args, &blk)
124
+ obj.send(name, *args, &blk)
125
+ end
126
+
127
+ return ret
128
+ end
129
+
130
+ private
131
+
132
+ # Using separate method for this prevents infinite loop.
133
+
134
+ def lambda_target(obj, name, target, *args, &blk)
135
+ lambda do
136
+ #advice.call(target, *args, &blk)
137
+ obj.send(name, target, *args, &blk)
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+
145
+ class Method #:nodoc:
146
+ include Advisable::Method
147
+ end
148
+
@@ -0,0 +1,54 @@
1
+ # = Clonable
2
+ #
3
+ # Standard basis for adding deep #dup and #clone to a class.
4
+ # Provides a class with deep cloneablity via the standard
5
+ # #dup and #clone methods.
6
+ #
7
+ # == Credit
8
+ #
9
+ # Cloneable was originally ported from Jim Weirich's Rake.
10
+ # The current version is the work of Ken Bloom.
11
+ #
12
+ #--
13
+ # TODO: Is there a more robust means of determining if clone or dup is used?
14
+ #++
15
+
16
+ module Cloneable
17
+
18
+ def initialize_copy(sibling)
19
+ #first duplicate my superclass' state. Note that if it's duplicating
20
+ #instance variables, this will be overwritten, but this is important
21
+ #because we could be dealing with a C extension with state hidden from
22
+ #the Ruby interpreter
23
+ super
24
+
25
+ #we want to know if we're being dup'ed or clone'd, because we want to
26
+ #preserve the state of our internals the same way our state is being
27
+ #preserved. (If we can't figure it out, we'll just use #dup.)
28
+ operation = caller.find{|x| x !~ /'initialize_copy'/}.match(/`(dup|clone)'/)[1] or :dup
29
+
30
+ sibling.instance_variables.each do |ivar|
31
+ value = sibling.instance_variable_get(ivar)
32
+
33
+ #set my instance variable to be a #dup or #clone
34
+ #or my sibling, depending on what's happening to me right now
35
+ instance_variable_set(ivar, value.send(operation))
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ # OLD VERSION
42
+
43
+ #module Cloneable
44
+ # def clone
45
+ # sibling = self.class.new
46
+ # instance_variables.each do |ivar|
47
+ # value = self.instance_variable_get(ivar)
48
+ # sibling.instance_variable_set(ivar, value.dup) #rake_dup)
49
+ # end
50
+ # sibling
51
+ # end
52
+ # alias_method :dup, :clone
53
+ #end
54
+
@@ -0,0 +1,195 @@
1
+ # Enumerable::Arguments
2
+ #
3
+ # Copyright (c) 2004 Thomas Sawyer
4
+ #
5
+ # LGPL(3) License
6
+ #
7
+ # This module is free software. You may use, modify, and/or redistribute this
8
+ # software under the same terms as Ruby.
9
+ #
10
+ # This program is distributed in the hope that it will be useful, but WITHOUT
11
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
+ # FOR A PARTICULAR PURPOSE.
13
+
14
+ begin
15
+ require 'enumerator'
16
+ rescue LoadError
17
+ end
18
+
19
+ # This is a simple reimplementation of the core Enumerable module
20
+ # to allow the methods to take and pass-on arbitrary arguments to the
21
+ # underlying #each call. This library uses Enumerator and scans
22
+ # Enumerable so it can alwasy stay in sync.
23
+ #
24
+ # NOTE Any Enumerable method with a negative arity cannot do pass arguments
25
+ # due to ambiguity in the argument count. So the methods #inject and #zip
26
+ # do NOT work this way, but simply work as they do in Enumerable.
27
+ # The method #find (and #detect) though has been made to work by removing
28
+ # its rarely used optional parameter and providing instead an optional
29
+ # keyword parameter (:ifnone => ...). Please keep these difference in mind.
30
+ #
31
+ # require 'enumargs'
32
+ #
33
+ # class T
34
+ # include Enumerable::Arguments
35
+ # def initialize(arr)
36
+ # @arr = arr
37
+ # end
38
+ # def each(n)
39
+ # arr.each{ |e| yield(e+n) }
40
+ # end
41
+ # end
42
+ #
43
+ # t = T.new([1,2,3])
44
+ # t.collect(4)
45
+ # #=> [5,6,7]
46
+ #
47
+ module Enumerable
48
+ module Arguments
49
+
50
+ def self.wrap_enumerable_method( methodname )
51
+
52
+ m = methodname
53
+ meth = Enumerable.instance_method(m)
54
+ arity = meth.arity
55
+
56
+ case arity <=> 0
57
+ when 0
58
+ class_eval %{
59
+ def #{m}( *args, &yld )
60
+ enum_for(:each, *args).#{m}( &yld )
61
+ end
62
+ }
63
+ when 1
64
+ class_eval %{
65
+ def #{m}( *args, &yld )
66
+ args, each_args = args[0...#{arity}], args[#{arity}..-1]
67
+ enum_for(:each, *each_args).#{m}( *args, &yld )
68
+ end
69
+ }
70
+ else
71
+ class_eval %{
72
+ def #{m}( *args, &yld )
73
+ enum_for(:each).#{m}( *args, &yld )
74
+ end
75
+ }
76
+ end
77
+ end
78
+
79
+ Enumerable.instance_methods(false).each do |m|
80
+ wrap_enumerable_method( m )
81
+ end
82
+
83
+ #
84
+ def to_a(*args)
85
+ map(*args){ |x| x }
86
+ end
87
+
88
+ # Make exception for #find (a negative arity method) to accept
89
+ # keyword argument.
90
+ #
91
+ # ObjectSpace.find(Class, :ifnone=>lambda{1}) { |e| ... }
92
+ # ObjectSpace.find(Class, :ifnone=>lambda{1}) { |e| ... }
93
+ #
94
+ def find(*args, &yld) # future use **keys ?
95
+ if Hash === args.last and args.last.key?(:ifnone)
96
+ ifnone = args.last.delete(:ifnone)
97
+ args.pop if args.last.empty?
98
+ enum_for(:each, *args).find( ifnone, &yld )
99
+ else
100
+ enum_for(:each, *args).find( &yld )
101
+ end
102
+ end
103
+ alias_method :detect, :find
104
+
105
+ end
106
+ end
107
+
108
+
109
+ =begin OLD CODE
110
+ module EnumerableArgs
111
+
112
+ def collect(*args) # :yield:
113
+ a = []
114
+ each(*args){ |n| a << yield(n) }
115
+ a
116
+ end
117
+ alias_method( :map, :collect )
118
+
119
+ def detect(*args) # :yield:
120
+ each(*args){ |n| return n if yield(n) }
121
+ nil
122
+ end
123
+ alias_method( :find, :detect )
124
+
125
+ def each_with_index(*args)
126
+ i=0
127
+ each(*args){ |*n| n << i; yield(*n); i+=1 }
128
+ self
129
+ end
130
+
131
+ def to_a(*args)
132
+ a = []
133
+ each(*args){ |n| a << n }
134
+ a
135
+ end
136
+ alias_method( :entries, :to_a )
137
+
138
+ # An additional method not part of standard Enumerable.
139
+ # The regular version of this method can be found in Facets,
140
+ # but it is a bit more advanced then this one.
141
+ # At some point they need to be put into sync.
142
+ def each_slice(*args, &yld)
143
+ a = []; s = []
144
+ ar = yld.arity.abs
145
+ each(*args){ |n|
146
+ s << n
147
+ if s.length >= ar
148
+ yld.call(*s)
149
+ s = []
150
+ end
151
+ }
152
+ a
153
+ end
154
+ alias_method( :each_by, :each_slice )
155
+
156
+ def select(*args) # :yield:
157
+ a = []
158
+ each(*args){ |n| a << n if yield(n) }
159
+ a
160
+ end
161
+ alias_method( :find_all, :select )
162
+
163
+ def grep(pattern, *args)
164
+ a = []
165
+ each(*args){ |n| a << (block_given? ? yield(n) : n) if pattern === n }
166
+ a
167
+ end
168
+
169
+ def include?(anObj, *args)
170
+ each(*args){ |n| return true if anObj == n }
171
+ false
172
+ end
173
+ alias_method( :member?, :include? )
174
+
175
+ def max(*args)
176
+ to_a(*args).max
177
+ end
178
+
179
+ def min(*args)
180
+ to_a(*args).min
181
+ end
182
+
183
+ def reject(*args)
184
+ a = []
185
+ each(*args){ |n| a << n if ! yield(n) }
186
+ a
187
+ end
188
+
189
+ def sort(*args)
190
+ # TODO
191
+ end
192
+
193
+ end
194
+ =end
195
+
@@ -0,0 +1,76 @@
1
+ # = Equitable
2
+ #
3
+ # This mixin provides methods of equality based
4
+ # on a single #identity method which must return
5
+ # a list of accessors used as the identity keys.
6
+ #
7
+ # It also provides a "shortcut" for creating the
8
+ # #identity method based on given accessors and returns
9
+ # the Equitable module for inclusion.
10
+ #
11
+ # include Equitable(:a, :b)
12
+ #
13
+ # is equivalent to including a module containing:
14
+ #
15
+ # def ==(other)
16
+ # self.a == other.a && self.b == other.b
17
+ # end
18
+ #
19
+ # def eql?(other)
20
+ # self.a.eql?(other.a) && self.b.eql?(other.b)
21
+ # end
22
+ #
23
+ # def hash()
24
+ # self.a.hash ^ self.b.hash
25
+ # end
26
+ #
27
+ module Equitable
28
+
29
+ def self.identify(base, *accessors)
30
+ base.send(:define_method, :identity){ accessors }
31
+ self
32
+ end
33
+
34
+ def ==(o)
35
+ identity.all?{ |a| send(a) == o.send(a) }
36
+ end
37
+
38
+ def eql?(o)
39
+ identity.all?{ |a| send(a).eql?(o.send(a)) }
40
+ end
41
+
42
+ def hash
43
+ identity.inject(0){ |memo, a| memo ^ send(a).hash }
44
+ end
45
+
46
+ end
47
+
48
+ class Module
49
+
50
+ # This function provided a "shortcut" for creating the
51
+ # #identity method based on given accessors and returns
52
+ # the Equitable module for inclusion.
53
+ #
54
+ # include Equitable(:a, :b)
55
+ #
56
+ # is equivalent to including a module containing:
57
+ #
58
+ # def ==(other)
59
+ # self.a == other.a && self.b == other.b
60
+ # end
61
+ #
62
+ # def eql?(other)
63
+ # self.a.eql?(other.a) && self.b.eql?(other.b)
64
+ # end
65
+ #
66
+ # def hash()
67
+ # self.a.hash ^ self.b.hash
68
+ # end
69
+ #
70
+
71
+ def Equitable(*accessors)
72
+ Equitable.identify(self, *accessors)
73
+ end
74
+
75
+ end
76
+
@@ -0,0 +1,38 @@
1
+ # = Expirable
2
+ #
3
+ # Generic expirability mixin.
4
+ #
5
+ module Expirable
6
+
7
+ attr_accessor :expires
8
+
9
+ # Set the expires timeout for this entry.
10
+
11
+ def expires_after(timeout = (60*60*24))
12
+ @expires = Time.now + timeout
13
+ end
14
+
15
+ # Set the expire timeout for this entry. The timeout happens
16
+ # after (base + rand(spread)) seconds.
17
+
18
+ def expires_spread(base, spread)
19
+ @expires = Time.now + base + rand(spread)
20
+ end
21
+
22
+ # Is this entry expired?
23
+
24
+ def expired?
25
+ if @expires.nil? or (Time.now > @expires)
26
+ return true
27
+ else
28
+ return false
29
+ end
30
+ end
31
+
32
+ # Update the expiration period. Override in your application.
33
+
34
+ def touch!
35
+ end
36
+
37
+ end
38
+
@@ -0,0 +1,38 @@
1
+ #require 'facets/kernel/instance_exec'
2
+
3
+ # TODO: hooks should be an inheritor
4
+ #
5
+ module Hook
6
+
7
+ def self.append_features(base)
8
+ base.extend self
9
+ end
10
+
11
+ def hooks
12
+ @hooks ||= Hash.new{ |h,k| h[k] = [] }
13
+ end
14
+
15
+ def hook(name)
16
+ name = name.to_sym
17
+
18
+ (class << self; self; end).class_eval %{
19
+ def #{name}(meth=nil, &blk)
20
+ hooks[:#{name}] << (meth || blk)
21
+ end
22
+ }
23
+
24
+ module_eval %{
25
+ def #{name}(*args)
26
+ self.class.hooks[:#{name}].each do |blk|
27
+ if Proc === blk
28
+ instance_exec(:#{name}, *args, &blk)
29
+ else
30
+ __send__(blk, :#{name}, *args)
31
+ end
32
+ end
33
+ end
34
+ }
35
+ end
36
+
37
+ end
38
+
@@ -0,0 +1,25 @@
1
+ # = Instantiable
2
+ #
3
+ # Initialize modules, almost as if they were classes.
4
+ #
5
+ # Alows a module to be used much like a class, by defining
6
+ # a #new method that creates a class on demand.
7
+ #
8
+ module Instantiable
9
+
10
+ def self.append_features(mod)
11
+ mod.extend self
12
+ end
13
+
14
+ # Never use a class agian! ;)
15
+
16
+ def new(*args,&blk)
17
+ mod = self
18
+ @instantiable_class ||= Class.new{include mod}
19
+ @instantiable_class.new(*args,&blk)
20
+ end
21
+
22
+ end
23
+
24
+
25
+
@@ -0,0 +1,179 @@
1
+ # = Ostructable
2
+ #
3
+ # OpensStructable is a mixin module which can provide OpenStruct behavior to
4
+ # any class or object. OpenStructable allows extention of data objects
5
+ # with arbitrary attributes.
6
+ #
7
+ # == Usage
8
+ #
9
+ # require 'ostructable'
10
+ #
11
+ # class Record
12
+ # include OpenStructable
13
+ # end
14
+ #
15
+ # record = Record.new
16
+ # record.name = "John Smith"
17
+ # record.age = 70
18
+ # record.pension = 300
19
+ #
20
+ # puts record.name # -> "John Smith"
21
+ # puts record.address # -> nil
22
+ #
23
+ #--
24
+ # TODO: Keep this uptodate with ostruct.rb
25
+ #
26
+ # TODO: As with OpenStruct, marshalling is problematic at the moment.
27
+ #++
28
+
29
+ module OpenStructable
30
+
31
+ def initialize(hash=nil)
32
+ @__table__ = {}
33
+ if hash
34
+ for k,v in hash
35
+ @__table__[k.to_sym] = v
36
+ new_ostruct_member(k)
37
+ end
38
+ end
39
+ end
40
+
41
+ # duplicate an OpenStruct object members.
42
+ def initialize_copy(orig)
43
+ super
44
+ @__table__ = @__table__.dup
45
+ end
46
+
47
+ def marshal_dump
48
+ @table
49
+ end
50
+ def marshal_load(x)
51
+ @table = x
52
+ @table.each_key{|key| new_ostruct_member(key)}
53
+ end
54
+
55
+ def new_ostruct_member(name)
56
+ unless self.respond_to?(name)
57
+ self.instance_eval %{
58
+ def #{name}; @__table__[:#{name}]; end
59
+ def #{name}=(x); @__table__[:#{name}] = x; end
60
+ }
61
+ end
62
+ end
63
+
64
+ #
65
+ # Generate additional attributes and values.
66
+ #
67
+ def update(hash)
68
+ @__table__ ||= {}
69
+ if hash
70
+ for k,v in hash
71
+ @__table__[k.to_sym] = v
72
+ new_ostruct_member(k)
73
+ end
74
+ end
75
+ end
76
+
77
+ def method_missing(mid, *args) # :nodoc:
78
+ mname = mid.to_s
79
+ len = args.length
80
+ if mname =~ /=$/
81
+ if len != 1
82
+ raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
83
+ end
84
+ if self.frozen?
85
+ raise TypeError, "can't modify frozen #{self.class}", caller(1)
86
+ end
87
+ mname.chop!
88
+ @__table__ ||= {}
89
+ @__table__[mname.intern] = args[0]
90
+ self.new_ostruct_member(mname)
91
+ elsif len == 0
92
+ @__table__ ||= {}
93
+ @__table__[mid]
94
+ else
95
+ raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
96
+ end
97
+ end
98
+
99
+ #
100
+ # Remove the named field from the object.
101
+ #
102
+ def delete_field(name)
103
+ @__table__ ||= {}
104
+ @__table__.delete name.to_sym
105
+ end
106
+
107
+ #
108
+ # Returns a string containing a detailed summary of the keys and values.
109
+ #
110
+ def inspect
111
+ str = "<#{self.class}"
112
+ for k,v in (@__table__ ||= {})
113
+ str << " #{k}=#{v.inspect}"
114
+ end
115
+ str << ">"
116
+ end
117
+
118
+ def __table__ # :nodoc:
119
+ @__table__ ||= {}
120
+ end
121
+ protected :__table__
122
+
123
+ # Compare this object and +other+ for equality.
124
+ def ==(other)
125
+ return false unless(other.kind_of?(OpenStruct))
126
+ return @__table__ == other.table
127
+ end
128
+
129
+ end
130
+
131
+ =begin
132
+ #
133
+ # It is possibe to implement OpenStruct itself with
134
+ # this OpenStructable module as follows:
135
+ #
136
+ class OpenStruct
137
+ include OpenStructable
138
+ end
139
+ =end
140
+
141
+
142
+
143
+ # _____ _
144
+ # |_ _|__ ___| |_
145
+ # | |/ _ \/ __| __|
146
+ # | | __/\__ \ |_
147
+ # |_|\___||___/\__|
148
+ #
149
+
150
+ =begin testing
151
+
152
+ require 'test/unit'
153
+
154
+ # fixture
155
+
156
+ class Record
157
+ include OpenStructable
158
+ end
159
+
160
+ # test
161
+
162
+ class TC_OpenStructable < Test::Unit::TestCase
163
+
164
+ def test_record
165
+ record = nil
166
+ assert_nothing_raised {
167
+ record = Record.new
168
+ record.name = "John Smith"
169
+ record.age = 70
170
+ record.pension = 300
171
+ }
172
+ assert_equal( "John Smith", record.name )
173
+ assert_equal( 70, record.age )
174
+ assert_equal( nil, record.address )
175
+ end
176
+
177
+ end
178
+
179
+ =end
@@ -0,0 +1,93 @@
1
+ # = Preinitialize
2
+ #
3
+ # This is an object preinitialize system, which provides
4
+ # an elegant way to initialize an object allowing the
5
+ # class to provide additional default structure to an
6
+ # object prior to calling initialize.
7
+ #
8
+ # In effect it does two things after allocating the object
9
+ # but prior to initializing it.
10
+ #
11
+ # First, it calls the class method #default_instance_variables,
12
+ # which returns a hash and by default returns the hash
13
+ # stored in @default_instance_variables. It uses this to
14
+ # pre-define instance variables.
15
+ #
16
+ # Then it goes to the top of the class hierarchy and works
17
+ # it's way down calling #preinitialize if defined.
18
+ # WARNING! It is rather useless to use <tt>super</tt>
19
+ # inside the any preinitialize hook.
20
+ #
21
+ # module M
22
+ # def preinitialize
23
+ # @a = 23
24
+ # end
25
+ # end
26
+ #
27
+ # class X
28
+ # include M
29
+ # def a ; @a ; end
30
+ # end
31
+ #
32
+ # x = X.new
33
+ # x.a #=> 23
34
+ #
35
+ # If neded the original new method has been aliased, albeit
36
+ # <tt>postinitialize_new</tt> is probably a bit of a misnomer.
37
+
38
+ #--
39
+ # class Module
40
+ #
41
+ # def default_instance_variables(complete=false)
42
+ # @default_instance_variables ||= {}
43
+ # unless complete
44
+ # return @default_instance_variables
45
+ # else
46
+ # parent = ancestors[1]
47
+ # if parent
48
+ # return @default_instance_variables.merge(parent.default_instance_variables)
49
+ # else
50
+ # return @default_instance_variables
51
+ # end
52
+ # end
53
+ # end
54
+ #
55
+ # end
56
+ #++
57
+
58
+ module Preinitializable
59
+
60
+ def self.included(base)
61
+ if Class===base
62
+ (class << base; self; end).__send__(:alias_method, :post_new, :new)
63
+ base.extend Meta
64
+ else
65
+ (class << base; self; end).__send__(:define_method, :included, &method(:included))
66
+ end
67
+ end
68
+
69
+ module Meta
70
+
71
+ def new(*args, &blk)
72
+ o = allocate
73
+ #if respond_to?(:default_instance_variables)
74
+ # default_instance_variables.each{|k,v| o.instance_variable_set( "@#{k.to_s.gsub(/\W$/,'')}",v )}
75
+ #end
76
+ a = ancestors
77
+ until a.empty?
78
+ m = a.pop
79
+ if m.private_instance_methods(false).include?('preinitialize') or
80
+ m.protected_instance_methods(false).include?('preinitialize') or
81
+ m.public_instance_methods(false).include?('preinitialize')
82
+ im = instance_method('preinitialize')
83
+ im.arity == 0 ? im.bind(o).call : im.bind(o).call(*args, &blk)
84
+ end
85
+ end
86
+ o.__send__(:initialize, *args, &blk) if o.class.private_method_defined?(:initialize)
87
+ o
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
@@ -0,0 +1,29 @@
1
+ # = Registerable
2
+ #
3
+ module Registerable
4
+
5
+ # Register format names.
6
+
7
+ def register(*names)
8
+ names.each do |name|
9
+ registry[name.to_s] = self
10
+ end
11
+ end
12
+
13
+ # Access registry.
14
+
15
+ def registry
16
+ @@registry ||= {}
17
+ end
18
+
19
+ #
20
+
21
+ def registry_invalid?(*types)
22
+ bad = []
23
+ types.each do |type|
24
+ bad << type unless @@registry[type]
25
+ end
26
+ return bad.empty? ? false : bad
27
+ end
28
+
29
+ end
data/meta/contact ADDED
@@ -0,0 +1 @@
1
+ rubyworks-mailinglist@googlegroups.com
data/meta/description ADDED
@@ -0,0 +1,6 @@
1
+ Ruby Mixers is collection mixin modules for the Ruby
2
+ programming language. Mixers is a spin-off of Ruby
3
+ Facets. When Ruby Facets scaled back to extensions
4
+ only project, it's mixin modules were gathered to
5
+ create this new project.
6
+
data/meta/homepage ADDED
@@ -0,0 +1 @@
1
+ http://rubyworks.github.com/mixers
data/meta/name ADDED
@@ -0,0 +1 @@
1
+ mixers
@@ -0,0 +1 @@
1
+ http://github.com/rubyworks/mixers
@@ -0,0 +1 @@
1
+ http://rubyworks.github.com/mixers
data/meta/suite ADDED
@@ -0,0 +1 @@
1
+ rubyworks
data/meta/summary ADDED
@@ -0,0 +1 @@
1
+ Collection of helpful mixin modules
data/meta/version ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,69 @@
1
+ require 'mixers/advisable'
2
+ require 'test/unit'
3
+
4
+ class TestAdvice < Test::Unit::TestCase
5
+
6
+ class X
7
+ include Advisable
8
+
9
+ attr_reader :out
10
+
11
+ def initialize
12
+ @out = []
13
+ end
14
+
15
+ before :x do
16
+ @out << "BEFORE X#x"
17
+ end
18
+
19
+ after :x do
20
+ @out << "AFTER X#x"
21
+ end
22
+
23
+ def x
24
+ @out << "X#x"
25
+ "x"
26
+ end
27
+ end
28
+
29
+ class Y < X
30
+ before :x do
31
+ @out << "BEFORE Y#x"
32
+ end
33
+
34
+ after :x do
35
+ @out << "AFTER Y#x"
36
+ end
37
+
38
+ around :x do |target|
39
+ "{" + target.call + "}"
40
+ end
41
+
42
+ def x
43
+ @out << "Y#x"
44
+ super
45
+ end
46
+ end
47
+
48
+ # tests
49
+
50
+ def setup
51
+ @x = X.new
52
+ @y = Y.new
53
+ end
54
+
55
+ def test_x
56
+ r = @x.x
57
+ o = @x.out
58
+ assert_equal("x", r)
59
+ assert_equal(["BEFORE X#x", "X#x", "AFTER X#x"], o)
60
+ end
61
+
62
+ def test_y
63
+ r = @y.x
64
+ o = @y.out
65
+ assert_equal("{x}", r)
66
+ assert_equal(["BEFORE Y#x", "BEFORE X#x", "Y#x", "X#x", "AFTER X#x", "AFTER Y#x"], o)
67
+ end
68
+
69
+ end
@@ -0,0 +1,41 @@
1
+ require 'mixers/cloneable'
2
+ require 'test/unit'
3
+
4
+ class Foo
5
+ include Cloneable
6
+ def initialize
7
+ @bar=[]
8
+ end
9
+ def bar_id
10
+ @bar.object_id
11
+ end
12
+ end
13
+
14
+ class TestCloneable < Test::Unit::TestCase
15
+ def test_dup
16
+ a=Foo.new
17
+ b=a.dup
18
+ assert_not_equal a.bar_id,b.bar_id
19
+
20
+ a.taint
21
+ b=a.dup
22
+ assert b.tainted?, "b should be tainted"
23
+
24
+ a.freeze
25
+ b=a.dup
26
+ assert !b.frozen?, "b should not be frozen"
27
+ end
28
+ def test_clone
29
+ a=Foo.new
30
+ b=a.clone
31
+ assert_not_equal a.bar_id,b.bar_id
32
+
33
+ a.taint
34
+ b=a.dup
35
+ assert b.tainted?, "b should be tainted"
36
+
37
+ a.freeze
38
+ b=a.clone
39
+ assert b.frozen?, "b should be frozen"
40
+ end
41
+ end
@@ -0,0 +1,70 @@
1
+ require 'mixers/enumargs'
2
+ require 'test/unit'
3
+
4
+ # fixture
5
+
6
+ class PlusArray
7
+ include Enumerable::Arguments
8
+ def initialize(arr)
9
+ @arr = arr
10
+ end
11
+ def each(n=0)
12
+ @arr.each{ |e| yield(e+n) }
13
+ end
14
+ end
15
+
16
+ class TC_Enumerable < Test::Unit::TestCase
17
+
18
+ def test_collect
19
+ t = PlusArray.new([1,2,3])
20
+ assert_equal( [5,6,7], t.collect(4){ |e| e } )
21
+ end
22
+
23
+ #def test_each_slice
24
+ # t = PlusArray.new([1,2,3,4])
25
+ # a = []
26
+ # t.each_slice(2,4){ |e,f| a << [e,f] }
27
+ # assert_equal( [[5,6],[7,8]], a )
28
+ #end
29
+
30
+ #def test_find
31
+ # t = PlusArray.new([1,2,3,4])
32
+ # f = t.find(2, :ifnone=>lambda{:NOPE}) { |a| a == 10 }
33
+ # assert_equal(:NOPE, f)
34
+ #end
35
+
36
+ def test_grep
37
+ # TODO
38
+ end
39
+
40
+ def test_to_a
41
+ t = PlusArray.new([1,2,3])
42
+ assert_equal( [5,6,7], t.to_a(4) )
43
+ end
44
+
45
+ def test_min
46
+ t = PlusArray.new([1,2,3])
47
+ assert_equal( 5, t.min(4) )
48
+ end
49
+
50
+ def test_max
51
+ t = PlusArray.new([1,2,3])
52
+ assert_equal( 7, t.max(4) )
53
+ end
54
+
55
+ def test_include?
56
+ t = PlusArray.new([1,2,3])
57
+ assert( t.include?(7,4) )
58
+ end
59
+
60
+ def test_select
61
+ t = PlusArray.new([1,2,3])
62
+ assert_equal( [6], t.select(4){ |x| x == 6 } )
63
+ end
64
+
65
+ def test_reject
66
+ t = PlusArray.new([1,2,3])
67
+ assert_equal( [5,7], t.reject(4){ |x| x == 6 } )
68
+ end
69
+
70
+ end
@@ -0,0 +1,42 @@
1
+ require 'mixers/equitable'
2
+ require 'test/unit'
3
+
4
+ class TestModuleEquatable < Test::Unit::TestCase
5
+
6
+ def test_equatable_with_arguments
7
+ c = Class.new
8
+ c.class_eval {
9
+ include Equitable(:a,:b)
10
+ attr_accessor :a, :b
11
+ }
12
+ c1,c2 = c.new,c.new
13
+ c1.a = 10; c1.b = 20
14
+ c2.a = 10; c2.b = 20
15
+ assert_equal( c1, c2 )
16
+ c1.a = 10; c1.b = 10
17
+ c2.a = 10; c2.b = 20
18
+ assert_not_equal( c1, c2 )
19
+ c1.a = 10; c1.b = 20
20
+ c2.a = 20; c2.b = 20
21
+ assert_not_equal( c1, c2 )
22
+ end
23
+
24
+ =begin
25
+ def test_equate_on_old
26
+ c = Class.new
27
+ c.class_eval { attr_accessor :a, :b ; equate_on :a,:b }
28
+ c1,c2 = c.new,c.new
29
+ c1.a = 10; c1.b = 20
30
+ c2.a = 10; c2.b = 20
31
+ assert_equal( c1, c2 )
32
+ c1.a = 10; c1.b = 10
33
+ c2.a = 10; c2.b = 20
34
+ assert_not_equal( c1, c2 )
35
+ c1.a = 10; c1.b = 20
36
+ c2.a = 20; c2.b = 20
37
+ assert_not_equal( c1, c2 )
38
+ end
39
+ =end
40
+
41
+ end
42
+
@@ -0,0 +1,37 @@
1
+ require 'mixers/instantiable'
2
+ require 'test/unit'
3
+
4
+ class TestInstantiable < Test::Unit::TestCase
5
+
6
+ module M
7
+ extend Instantiable
8
+
9
+ attr :a
10
+
11
+ def initialize( a )
12
+ @a = a
13
+ end
14
+ end
15
+
16
+ module N
17
+ include Instantiable
18
+
19
+ attr :a
20
+
21
+ def initialize( a )
22
+ @a = a
23
+ end
24
+ end
25
+
26
+ def test_m_new
27
+ m = M.new( 1 )
28
+ assert_equal( 1, m.a )
29
+ end
30
+
31
+ def test_n_new
32
+ m = N.new( 1 )
33
+ assert_equal( 1, m.a )
34
+ end
35
+
36
+ end
37
+
@@ -0,0 +1,35 @@
1
+ require 'mixers/preinitilizable'
2
+ require 'test/unit'
3
+
4
+ class TC_Preinitalizable < Test::Unit::TestCase
5
+
6
+ module M
7
+ include Preinitializable
8
+ def preinitialize
9
+ @a = 10
10
+ end
11
+ end
12
+
13
+ class X
14
+ include M
15
+ def a; @a ; end
16
+ end
17
+
18
+ class Y < X
19
+ def initialize
20
+ super
21
+ end
22
+ end
23
+
24
+ def test_x
25
+ x = X.new
26
+ assert_equal(10, x.a)
27
+ end
28
+
29
+ def test_y
30
+ y = Y.new
31
+ assert_equal(10, y.a)
32
+ end
33
+
34
+ end
35
+
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mixers
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors: []
12
+
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-27 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: |-
22
+ Ruby Mixers is collection mixin modules for the Ruby
23
+ programming language. Mixers is a spin-off of Ruby
24
+ Facets. When Ruby Facets scaled back to extensions
25
+ only project, it's mixin modules were gathered to
26
+ create this new project.
27
+ email:
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README
34
+ files:
35
+ - lib/mixers/advisable.rb
36
+ - lib/mixers/cloneable.rb
37
+ - lib/mixers/enumargs.rb
38
+ - lib/mixers/equitable.rb
39
+ - lib/mixers/expirable.rb
40
+ - lib/mixers/hook.rb
41
+ - lib/mixers/instantiable.rb
42
+ - lib/mixers/ostructable.rb
43
+ - lib/mixers/preinitilizable.rb
44
+ - lib/mixers/registerable.rb
45
+ - meta/contact
46
+ - meta/description
47
+ - meta/homepage
48
+ - meta/name
49
+ - meta/resource/development
50
+ - meta/resource/homepage
51
+ - meta/suite
52
+ - meta/summary
53
+ - meta/version
54
+ - test/test_advisable.rb
55
+ - test/test_cloneable.rb
56
+ - test/test_enumargs.rb
57
+ - test/test_equitable.rb
58
+ - test/test_instantiable.rb
59
+ - test/test_preinitilizable.rb
60
+ - LICENSE
61
+ - README
62
+ - HISTORY
63
+ has_rdoc: true
64
+ homepage: http://rubyworks.github.com/mixers
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --title
70
+ - Mixers API
71
+ - --main
72
+ - README
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project: mixers
92
+ rubygems_version: 1.3.6
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Collection of helpful mixin modules
96
+ test_files:
97
+ - test/test_advisable.rb
98
+ - test/test_cloneable.rb
99
+ - test/test_enumargs.rb
100
+ - test/test_equitable.rb
101
+ - test/test_instantiable.rb
102
+ - test/test_preinitilizable.rb