mixers 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
File without changes
@@ -1,9 +1,3 @@
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
1
  # = Advisable
8
2
  #
9
3
  # Advisable provides a means of using before, after and
@@ -11,138 +5,76 @@ require 'facets/unboundmethod/arguments'
11
5
  #
12
6
  module Advisable
13
7
 
14
- def self.append_features(base)
15
- base.extend self
16
- end
8
+ require 'facets/proc/bind'
17
9
 
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] = []}
10
+ def self.append_features(base)
11
+ base.extend(self)
28
12
  end
29
13
 
30
14
  def before(meth, &block)
31
- name = "#{meth}:before:#{block.object_id}"
32
- define_method(name, &block)
33
- (advice_before[meth.to_sym] ||= []) << name
15
+ advice_before[meth.to_sym] << block
16
+ method_added(meth) if method_defined?(meth)
34
17
  end
35
18
 
36
19
  def after(meth, &block)
37
- name = "#{meth}:after:#{block.object_id}"
38
- define_method(name, &block)
39
- (advice_after[meth.to_sym] ||= []) << name
20
+ advice_after[meth.to_sym] << block
21
+ method_added(meth) if method_defined?(meth)
40
22
  end
41
23
 
42
24
  def around(meth, &block)
43
- name = "#{meth}:around:#{block.object_id}"
44
- define_method(name, &block)
45
- (advice_around[meth.to_sym] ||= []) << name
25
+ advice_around[meth.to_sym] << block
26
+ method_added(meth) if method_defined?(meth)
46
27
  end
47
28
 
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
29
+ def advice_before
30
+ @@advice_before ||= Hash.new{|h,k| h[k] = []}
70
31
  end
71
32
 
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
33
+ def advice_after
34
+ @@advice_after ||= Hash.new{|h,k| h[k] = []}
88
35
  end
89
36
 
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
37
+ def advice_around
38
+ @@advice_around ||= Hash.new{|h,k| h[k] = []}
39
+ end
103
40
 
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
41
+ # TODO: Should around advice be fed the result of the last around advice?
42
+ def method_added(meth)
43
+ @method_added_stack ||= []
44
+ return if @method_added_stack.last == meth
45
+ @method_added_stack << meth
107
46
 
108
- # Call with advice.
47
+ if advice_before.key?(meth) or advice_after.key?(meth)
109
48
 
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
49
+ before = advice_before[meth] || []
50
+ around = advice_around[meth] || []
51
+ after = advice_after[meth] || []
115
52
 
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
53
+ alias_method "#{meth}:advised", meth
54
+ target = instance_method("#{meth}:advised")
121
55
 
122
- advice_after.reverse_each do |name|
123
- #advice.call(*args, &blk)
124
- obj.send(name, *args, &blk)
56
+ define_method meth do |*a, &b|
57
+ before.reverse_each do |block|
58
+ block.bind(self).call(*a,&b)
59
+ end
60
+ if around.empty?
61
+ result = target.bind(self).call(*a,&b)
62
+ else
63
+ around.each do |block|
64
+ result = block.call(target.bind(self),*a,&b)
65
+ end
66
+ end
67
+ after.each do |block|
68
+ block.bind(self).call(*a,&b)
69
+ end
70
+ result
125
71
  end
126
-
127
- return ret
128
72
  end
129
73
 
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
74
+ super(meth)
140
75
 
76
+ @method_added_stack.pop
141
77
  end
142
78
 
143
79
  end
144
80
 
145
- class Method #:nodoc:
146
- include Advisable::Method
147
- end
148
-
@@ -4,25 +4,34 @@
4
4
  #
5
5
  module Expirable
6
6
 
7
- attr_accessor :expires
7
+ attr_accessor :expiration
8
8
 
9
9
  # Set the expires timeout for this entry.
10
10
 
11
- def expires_after(timeout = (60*60*24))
12
- @expires = Time.now + timeout
11
+ def expiration=(time)
12
+ case time
13
+ when Time, Date, DateTime
14
+ @expiration = time
15
+ else
16
+ @expiration = Time.now + time
17
+ end
13
18
  end
14
19
 
15
- # Set the expire timeout for this entry. The timeout happens
16
- # after (base + rand(spread)) seconds.
20
+ #def expires_after(timeout = 86400)
21
+ # @expires = Time.now + timeout
22
+ #end
17
23
 
18
- def expires_spread(base, spread)
19
- @expires = Time.now + base + rand(spread)
20
- end
24
+ ## Set the expire timeout for this entry. The timeout happens
25
+ ## after (base + rand(spread)) seconds.
26
+ #
27
+ #def expires_spread(base, spread)
28
+ # @expires = Time.now + base + rand(spread)
29
+ #end
21
30
 
22
31
  # Is this entry expired?
23
32
 
24
33
  def expired?
25
- if @expires.nil? or (Time.now > @expires)
34
+ if @expiration.nil? or (Time.now > @expiration)
26
35
  return true
27
36
  else
28
37
  return false
@@ -25,6 +25,7 @@
25
25
  # end
26
26
  #
27
27
  # class X
28
+ # include Preinitializable
28
29
  # include M
29
30
  # def a ; @a ; end
30
31
  # end
@@ -4,9 +4,9 @@ module Registerable
4
4
 
5
5
  # Register format names.
6
6
 
7
- def register(*names)
7
+ def register(obj, *names)
8
8
  names.each do |name|
9
- registry[name.to_s] = self
9
+ registry[name] = obj
10
10
  end
11
11
  end
12
12
 
@@ -27,3 +27,4 @@ module Registerable
27
27
  end
28
28
 
29
29
  end
30
+
data/meta/authors ADDED
@@ -0,0 +1 @@
1
+ Thomas Sawyer
data/meta/repository ADDED
@@ -0,0 +1 @@
1
+ git://github.com/rubyworks/mixers.git
data/meta/sites/api ADDED
@@ -0,0 +1 @@
1
+ http://rubyworks.github.com/mixers/rdoc
data/meta/sites/blog ADDED
@@ -0,0 +1 @@
1
+ http://rubyworks.github.com/
File without changes
@@ -0,0 +1 @@
1
+ http://wiki.github.com/rubyworks/mixers
File without changes
data/meta/sites/mail ADDED
@@ -0,0 +1 @@
1
+ http://groups.google.com/group/rubyworks-mailinglist
data/meta/sites/source ADDED
@@ -0,0 +1 @@
1
+ http://github.com/rubyworks/mixers
data/meta/sites/wiki ADDED
@@ -0,0 +1 @@
1
+ http://wiki.github.com/rubyworks/mixers
data/meta/version CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.1.0
@@ -0,0 +1,71 @@
1
+ = Advisable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/advisable'
6
+
7
+ We will use this fixture.
8
+
9
+ class X
10
+ include Advisable
11
+
12
+ attr_reader :out
13
+
14
+ def initialize
15
+ @out = []
16
+ end
17
+
18
+ before :x do
19
+ @out << "BEFORE X#x"
20
+ end
21
+
22
+ after :x do
23
+ @out << "AFTER X#x"
24
+ end
25
+
26
+ def x
27
+ @out << "X#x"
28
+ "x"
29
+ end
30
+ end
31
+
32
+ Our results:
33
+
34
+ x = X.new
35
+ r = x.x
36
+ o = x.out
37
+ r.assert == "x"
38
+ o.assert == ["BEFORE X#x", "X#x", "AFTER X#x"]
39
+
40
+ And let's see what a subclass of the example does:
41
+
42
+ class Y < X
43
+ before :x do
44
+ @out << "BEFORE Y#x"
45
+ end
46
+
47
+ after :x do
48
+ @out << "AFTER Y#x"
49
+ end
50
+
51
+ around :x do |target|
52
+ "{" + target.call + "}"
53
+ end
54
+
55
+ def x
56
+ @out << "Y#x"
57
+ super
58
+ end
59
+ end
60
+
61
+ y = Y.new
62
+
63
+ The result of #x should be a bracketed "x".
64
+
65
+ y.x.assert == "{x}"
66
+
67
+ Then the out variable should indicate where the call has been.
68
+
69
+ y.out.assert == ["BEFORE Y#x", "BEFORE X#x", "Y#x", "X#x", "AFTER X#x", "AFTER Y#x"]
70
+
71
+
@@ -0,0 +1,46 @@
1
+ = Cloneable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/cloneable'
6
+
7
+ We'll use this dummy class.
8
+
9
+ class Foo
10
+ include Cloneable
11
+ def initialize
12
+ @bar=[]
13
+ end
14
+ def bar_id
15
+ @bar.object_id
16
+ end
17
+ end
18
+
19
+ Try #dup.
20
+
21
+ a = Foo.new
22
+ b = a.dup
23
+ b.bar_id.refute == a.bar_id
24
+
25
+ a.taint
26
+ b = a.dup
27
+ b.assert.tainted?
28
+
29
+ a.freeze
30
+ b = a.dup
31
+ b.refute.frozen?
32
+
33
+ Note try #clone.
34
+
35
+ a = Foo.new
36
+ b = a.clone
37
+ b.bar_id.refute == a.bar_id
38
+
39
+ a.taint
40
+ b = a.dup
41
+ b.assert.tainted?
42
+
43
+ a.freeze
44
+ b = a.clone
45
+ b.assert.frozen?
46
+
@@ -0,0 +1,75 @@
1
+ = Enumerable::Arguments
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/enumargs'
6
+
7
+ This will serve as our example class.
8
+
9
+ class PlusArray
10
+ include Enumerable::Arguments
11
+
12
+ def initialize(arr)
13
+ @arr = arr
14
+ end
15
+
16
+ def each(n=0)
17
+ @arr.each{ |e| yield(e+n) }
18
+ end
19
+ end
20
+
21
+ Now Enumerable methods such as #map and #collect
22
+ take an argument as well.
23
+
24
+ t = PlusArray.new([1,2,3])
25
+ t.collect(4){ |e| e }.assert == [5,6,7]
26
+
27
+ Filtering methods such as #select and reject work
28
+ as well.
29
+
30
+ t = PlusArray.new([1,2,3])
31
+ t.select(4){ |x| x == 6 }.assert == [6]
32
+
33
+ t = PlusArray.new([1,2,3])
34
+ t.reject(4){ |x| x == 6 }.assert == [5,7]
35
+
36
+ We can covert to an array using #to_a with an argument.
37
+
38
+ t = PlusArray.new([1,2,3])
39
+ t.to_a(4).assert == [5,6,7]
40
+
41
+ We can get the minimum value using #min.
42
+
43
+ t = PlusArray.new([1,2,3])
44
+ t.min(4).assert == 5
45
+
46
+ And get the maximum value using #min.
47
+
48
+ t = PlusArray.new([1,2,3])
49
+ t.max(4).assert == 7
50
+
51
+ Methods that already take and argument, now take two
52
+ such as #include?
53
+
54
+ t = PlusArray.new([1,2,3])
55
+ t.assert.include?(7,4)
56
+
57
+ And #each_slice.
58
+
59
+ t = PlusArray.new([1,2,3,4])
60
+ a = []
61
+ t.each_slice(2,4){ |e,f| a << [e,f] }
62
+ a.assert == [[5,6],[7,8]]
63
+
64
+ The #find method has a slightly different interface
65
+ than the original Enumerable.
66
+
67
+ t = PlusArray.new([1,2,3,4])
68
+ f = t.find(2, :ifnone=>lambda{:NOPE}) { |a| a == 10 }
69
+ f.assert == :NOPE
70
+
71
+ The #grep method should also work.
72
+
73
+ t = PlusArray.new(['a1','b2','a3','b4'])
74
+ t.grep(/^b/, 'x').assert == ['b2x','b4x']
75
+
@@ -0,0 +1,35 @@
1
+ = Equitqable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/equitable'
6
+
7
+ We will use this simple class as an example.
8
+
9
+ class C
10
+ include Equitable(:a,:b)
11
+
12
+ attr_accessor :a, :b
13
+
14
+ def initialize(a,b)
15
+ @a = a
16
+ @b = b
17
+ end
18
+ end
19
+
20
+ Now, if two instance of our sample class +C+ have
21
+ equal attributes +@a+ and +@b+ then they will
22
+ be equal.
23
+
24
+ c1 = C.new(10,20)
25
+ c2 = C.new(10,20)
26
+ c2.assert == c1
27
+
28
+ Otherwise they will not be equal.
29
+
30
+ c1 = C.new(10, 10)
31
+ c2 = C.new(10, 20)
32
+ c2.refute == c1
33
+
34
+
35
+
@@ -0,0 +1,42 @@
1
+ = Instantiable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/instantiable'
6
+
7
+ We will use this module as an example.
8
+
9
+ module M
10
+ include Instantiable
11
+
12
+ attr :a
13
+
14
+ def initialize(a)
15
+ @a = a
16
+ end
17
+ end
18
+
19
+ As we can see M is now fully instantiable, as any
20
+ class would be.
21
+
22
+ m = M.new(1)
23
+ m.a.assert == 1
24
+
25
+ We can also use #extend rather than #include and
26
+ get the same result.
27
+
28
+ module N
29
+ extend Instantiable
30
+
31
+ attr :a
32
+
33
+ def initialize( a )
34
+ @a = a
35
+ end
36
+ end
37
+
38
+ As we saw with +M+ so too with +N+.
39
+
40
+ n = N.new( 1 )
41
+ n.a.assert == 1
42
+
@@ -0,0 +1,32 @@
1
+ = Expirable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/expirable'
6
+
7
+ Example class.
8
+
9
+ class X
10
+ extend Expirable
11
+ end
12
+
13
+ Now the experation can be set to a Time object, or a number of seconds
14
+ from +Time.now+. For instance, to set the expiration for one second
15
+ from now:
16
+
17
+ X.expiration = 1
18
+
19
+ We can see the the object has not yet expired.
20
+
21
+ X.refute.expired?
22
+
23
+ But if we wait one second.
24
+
25
+ sleep 1
26
+
27
+ Then it will have expired.
28
+
29
+ X.assert.expired?
30
+
31
+ Expiration does not invoke a trigger, it is merely queriable.
32
+
@@ -0,0 +1,23 @@
1
+ = OpenStructable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/ostructable'
6
+
7
+ We will use the simpliest example class:
8
+
9
+ class X
10
+ include OpenStructable
11
+ end
12
+
13
+ x = X.new
14
+
15
+ So arbitrary attributes can be added to the object
16
+ just as if it were an OpenStruct.
17
+
18
+ x.a = 1
19
+ x.b = 2
20
+
21
+ x.assert.a == 1
22
+ x.assert.a == 2
23
+
@@ -0,0 +1,28 @@
1
+ = Preinitialize
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/preinitialize'
6
+
7
+ Use example module.
8
+
9
+ module M
10
+ def preinitialize
11
+ @a = 23
12
+ end
13
+ end
14
+
15
+ Define a class to support it.
16
+
17
+ class X
18
+ include Preinitializable
19
+ include M
20
+ def a ; @a ; end
21
+ end
22
+
23
+ The result should be:
24
+
25
+ x = X.new
26
+ x.a.assert == 23
27
+
28
+
@@ -0,0 +1,40 @@
1
+ = Registerable
2
+
3
+ Require the library.
4
+
5
+ require 'mixers/registerable'
6
+
7
+ Registerable can be used directly.
8
+
9
+ module MyStuff
10
+ extend Registerable
11
+ end
12
+
13
+ MyStuff.register(10, :x)
14
+
15
+ Then the MyStuff registry will contain 10 indexed by :x.
16
+
17
+ MyStuff.registry[:x].assert == 10
18
+
19
+ Registrable can also be used as a base class callback to
20
+ track subclasses.
21
+
22
+ class Abstract
23
+ extend Registerable
24
+
25
+ def self.inherited(base)
26
+ register(base, base.name.split('::').last.downcase)
27
+ end
28
+ end
29
+
30
+ Now when we inherit from Abstract, the subclass will
31
+ be automatically registered.
32
+
33
+ class Example < Abstract
34
+ end
35
+
36
+ We see that the class is in the Abstract registry.
37
+
38
+ Abstract.registry['example'].assert == Example
39
+
40
+
metadata CHANGED
@@ -4,17 +4,17 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
+ - 1
7
8
  - 0
8
- - 0
9
- version: 1.0.0
9
+ version: 1.1.0
10
10
  platform: ruby
11
- authors: []
12
-
11
+ authors:
12
+ - Thomas Sawyer
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-27 00:00:00 -04:00
17
+ date: 2010-05-07 00:00:00 -04:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -29,8 +29,8 @@ executables: []
29
29
 
30
30
  extensions: []
31
31
 
32
- extra_rdoc_files:
33
- - README
32
+ extra_rdoc_files: []
33
+
34
34
  files:
35
35
  - lib/mixers/advisable.rb
36
36
  - lib/mixers/cloneable.rb
@@ -40,17 +40,33 @@ files:
40
40
  - lib/mixers/hook.rb
41
41
  - lib/mixers/instantiable.rb
42
42
  - lib/mixers/ostructable.rb
43
- - lib/mixers/preinitilizable.rb
43
+ - lib/mixers/preinitialize.rb
44
44
  - lib/mixers/registerable.rb
45
+ - meta/authors
45
46
  - meta/contact
46
47
  - meta/description
47
- - meta/homepage
48
48
  - meta/name
49
- - meta/resource/development
50
- - meta/resource/homepage
49
+ - meta/repository
50
+ - meta/sites/api
51
+ - meta/sites/blog
52
+ - meta/sites/development
53
+ - meta/sites/documentation
54
+ - meta/sites/homepage
55
+ - meta/sites/mail
56
+ - meta/sites/source
57
+ - meta/sites/wiki
51
58
  - meta/suite
52
59
  - meta/summary
53
60
  - meta/version
61
+ - qed/01_advisable.rdoc
62
+ - qed/02_cloneable.rdoc
63
+ - qed/03_enumargs.rdoc
64
+ - qed/04_equitable.rdoc
65
+ - qed/05_instantiable.rdoc
66
+ - qed/06_expirable.rdoc
67
+ - qed/08_ostructable.rdoc
68
+ - qed/09_preinitialize.rdoc
69
+ - qed/10_registerable.rdoc
54
70
  - test/test_advisable.rb
55
71
  - test/test_cloneable.rb
56
72
  - test/test_enumargs.rb
@@ -58,7 +74,7 @@ files:
58
74
  - test/test_instantiable.rb
59
75
  - test/test_preinitilizable.rb
60
76
  - LICENSE
61
- - README
77
+ - README.rdoc
62
78
  - HISTORY
63
79
  has_rdoc: true
64
80
  homepage: http://rubyworks.github.com/mixers
@@ -68,8 +84,6 @@ post_install_message:
68
84
  rdoc_options:
69
85
  - --title
70
86
  - Mixers API
71
- - --main
72
- - README
73
87
  require_paths:
74
88
  - lib
75
89
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -1 +0,0 @@
1
- http://rubyworks.github.com/mixers