mixers 1.0.0 → 1.1.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.
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