dunder 0.1.1 → 0.2.1
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/Readme.md +41 -26
- data/VERSION +1 -1
- data/dunder.gemspec +2 -2
- data/lib/dunder.rb +37 -65
- data/test/test_dunder.rb +30 -8
- metadata +4 -4
data/Readme.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
A simple way of doing heavy work in a background process and when you really need the object
|
1
|
+
A simple way of doing heavy work in a background process and blocking until done when you really need the object.
|
2
2
|
|
3
3
|
Preloading using the [proxy pattern](http://sourcemaking.com/design_patterns/proxy)
|
4
4
|
Heavily inspired by Adam Sandersons [post](http://endofline.wordpress.com/2011/01/18/ruby-standard-library-delegator/)
|
@@ -11,30 +11,55 @@ Typically one might want start multiple heavy tasks concurrent.
|
|
11
11
|
This is already solvable with threads or the [reactor-pattern](http://rubyeventmachine.com/) but setting this up could be cumbersome or require direct interactions with threads ex.
|
12
12
|
|
13
13
|
Dunder is a simple way of abstracting this:
|
14
|
-
you simply pass a block to Dunder.load
|
14
|
+
you simply pass a block to Dunder.load and Dunder will execute this in a thread behind the scenes.
|
15
|
+
When later accessing the lazy_object will block until the thread is done and has returned or if the thread is done returns the value
|
16
|
+
|
17
|
+
The implementation itself relies only on the ruby standard library and is below 50 lines of code
|
15
18
|
|
16
19
|
Usage
|
17
20
|
|
18
|
-
|
21
|
+
lazy_object = Dunder.load {
|
22
|
+
# heavy stuff
|
23
|
+
value
|
24
|
+
}
|
25
|
+
|
26
|
+
or through dunder_load
|
27
|
+
|
28
|
+
lazy_sorted_articles = @articles.dunder_load.sort_by do |a|
|
29
|
+
a.title
|
30
|
+
end
|
31
|
+
|
32
|
+
lazy_sorted_array = array.dunder_load.sort
|
33
|
+
|
34
|
+
lazy_obj = obj.dunder_load.do_something_heavy(a,b,c) {
|
35
|
+
#maybe something other heavy here
|
36
|
+
}
|
37
|
+
|
38
|
+
Read more further down
|
39
|
+
|
40
|
+
lazy_foo = Dunder.load {
|
19
41
|
# Simulate heavy IO
|
20
42
|
sleep 2
|
21
|
-
"
|
43
|
+
"foo"
|
22
44
|
}
|
23
45
|
|
24
|
-
|
25
|
-
lazy_b = Dunder.load(String) {
|
46
|
+
lazy_bar = Dunder.load {
|
26
47
|
# Simulate heavy IO
|
27
48
|
sleep 2
|
28
49
|
"bar"
|
29
50
|
}
|
30
51
|
|
31
|
-
|
32
|
-
|
52
|
+
# Do something other heavy
|
53
|
+
|
54
|
+
puts lazy_bar # => "bar"
|
55
|
+
puts lazy_bar.class # => String
|
56
|
+
puts lazy_foo # => "foo"
|
57
|
+
puts lazy_foo.class # => String
|
33
58
|
# Will finish after 2 seconds
|
34
59
|
|
35
60
|
worth mentioning is that if you access the variable in someway before that it will block earlier
|
36
61
|
ex
|
37
|
-
lazy_array = Dunder.load
|
62
|
+
lazy_array = Dunder.load do
|
38
63
|
sleep 1
|
39
64
|
[1,2,3]
|
40
65
|
end
|
@@ -42,9 +67,9 @@ ex
|
|
42
67
|
sleep 1 # other heavy stuff
|
43
68
|
puts lazy_array # <- will be printed after 2 seconds
|
44
69
|
|
45
|
-
changing the order will fix this though
|
70
|
+
changing the order of the statements will fix this though
|
46
71
|
|
47
|
-
lazy_array = Dunder.load
|
72
|
+
lazy_array = Dunder.load do
|
48
73
|
sleep 1
|
49
74
|
[1,2,3]
|
50
75
|
end
|
@@ -52,20 +77,14 @@ changing the order will fix this though
|
|
52
77
|
puts lazy_array.length # <- will block here until the above sleep in the block is done
|
53
78
|
puts lazy_array # <- will be printed after 1 second
|
54
79
|
|
55
|
-
API
|
56
|
-
Dunder.load(Klass,instance = nil) {
|
57
|
-
#things to do
|
58
|
-
value
|
59
|
-
}
|
60
|
-
Klass must be the class of value
|
61
|
-
instance, During the process Dunder will call Klass.new if your class has a mandatory argument in initialize (the constructor) you could create a dummy instance yourself or you could set the return type to Array and return [value] and then use Dunder.load(Array) do [value] end.first
|
62
80
|
|
63
81
|
Rails
|
82
|
+
====================
|
64
83
|
|
65
|
-
@lazy_posts = Dunder.load
|
84
|
+
@lazy_posts = Dunder.load do
|
66
85
|
Post.all
|
67
86
|
end
|
68
|
-
@lazy_users = Dunder.load
|
87
|
+
@lazy_users = Dunder.load do
|
69
88
|
User.all
|
70
89
|
end
|
71
90
|
|
@@ -78,11 +97,7 @@ and then later in views
|
|
78
97
|
|
79
98
|
Known problems
|
80
99
|
|
81
|
-
|
82
|
-
Integer fails with
|
83
|
-
NotImplementedError: super from singleton method that is defined to multiple classes is not supported;
|
84
|
-
this will be fixed in 1.9.3 or later
|
85
|
-
Or a SystemStackError: stack level too deep
|
100
|
+
Has only been tested with 1.9.2
|
86
101
|
|
87
102
|
Install
|
88
103
|
=======
|
@@ -91,7 +106,7 @@ Install
|
|
91
106
|
|
92
107
|
(The MIT License)
|
93
108
|
|
94
|
-
Copyright (c)
|
109
|
+
Copyright (c) 2011 Erik Fonselius
|
95
110
|
|
96
111
|
Permission is hereby granted, free of charge, to any person obtaining
|
97
112
|
a copy of this software and associated documentation files (the
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.1
|
data/dunder.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{dunder}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Fonsan"]
|
12
|
-
s.date = %q{2011-01-
|
12
|
+
s.date = %q{2011-01-28}
|
13
13
|
s.description = %q{For tasks that can be started early and evaluated late.
|
14
14
|
|
15
15
|
Typically one might want start multiple heavy tasks concurrent.
|
data/lib/dunder.rb
CHANGED
@@ -1,78 +1,50 @@
|
|
1
|
-
# From http://endofline.wordpress.com/2011/01/18/ruby-standard-library-delegator/
|
2
1
|
require 'delegate'
|
3
|
-
|
4
|
-
class FutureArray < DelegateClass(Array )
|
5
|
-
def initialize(&block)
|
6
|
-
super(Array.new)
|
7
|
-
@_thread = Thread.start(&block)
|
8
|
-
end
|
9
|
-
|
10
|
-
def __getobj__
|
11
|
-
__setobj__(@_thread.value) if @_thread.alive?
|
12
|
-
super
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
2
|
class Dunder
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def lazy_instance
|
22
|
-
# Will be filled in later
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(&block)
|
26
|
-
@_thread = Thread.start(&block)
|
27
|
-
super(self.lazy_instance)
|
28
|
-
end
|
29
|
-
|
30
|
-
def __getobj__
|
31
|
-
result = @_thread.value
|
32
|
-
__setobj__(result) if @_thread.alive?
|
33
|
-
|
34
|
-
instance_eval do
|
35
|
-
def class
|
36
|
-
__getobj__.class
|
37
|
-
end
|
38
|
-
end
|
39
|
-
result
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
43
|
-
instance ||= klass.new
|
44
|
-
def c.lazy_instance
|
45
|
-
instance
|
3
|
+
class Future < SimpleDelegator
|
4
|
+
def initialize(&block)
|
5
|
+
@_thread = Thread.start(&block)
|
46
6
|
end
|
47
7
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
8
|
+
def __getobj__
|
9
|
+
__setobj__(@_thread.value) if @_thread.alive?
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def class
|
14
|
+
__getobj__.class
|
15
|
+
end
|
53
16
|
end
|
54
17
|
|
55
|
-
|
56
|
-
|
18
|
+
def self.load(&block)
|
19
|
+
Future.new(&block)
|
57
20
|
end
|
58
21
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
22
|
+
class Dispacter < SimpleDelegator
|
23
|
+
def initialize(object)
|
24
|
+
@_dunder_obj = object
|
25
|
+
end
|
26
|
+
|
27
|
+
def class
|
28
|
+
@_dunder_obj.class
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method_sym, *arguments,&block)
|
32
|
+
Dunder.load do
|
33
|
+
@_dunder_obj.send(method_sym, *arguments,&block)
|
34
|
+
end
|
35
|
+
end
|
67
36
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@@lazy_classes[klass]
|
74
|
-
lazy_class.new(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
class Object
|
40
|
+
def dunder_load
|
41
|
+
Dunder::Dispacter.new(self)
|
75
42
|
end
|
76
43
|
end
|
77
44
|
|
78
45
|
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
data/test/test_dunder.rb
CHANGED
@@ -1,16 +1,38 @@
|
|
1
1
|
require 'helper'
|
2
|
-
|
2
|
+
require 'timeout'
|
3
3
|
class TestDunder < Test::Unit::TestCase
|
4
4
|
should "have some simple testing" do
|
5
|
-
|
6
5
|
b = "bar"
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
lazy_b = nil
|
7
|
+
assert_nothing_raised do
|
8
|
+
Timeout::timeout(0.5) do
|
9
|
+
|
10
|
+
lazy_b = Dunder.load {
|
11
|
+
sleep 1
|
12
|
+
"bar"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
12
16
|
assert_equal b,lazy_b
|
13
|
-
puts lazy_b.class
|
14
17
|
assert_equal b.class, lazy_b.class
|
15
18
|
end
|
19
|
+
|
20
|
+
should "respond to dunder_load" do
|
21
|
+
assert Object.public_instance_methods.index(:dunder_load)
|
22
|
+
b = "bar"
|
23
|
+
b.instance_eval do
|
24
|
+
def something_heavy
|
25
|
+
yield
|
26
|
+
self
|
27
|
+
end
|
28
|
+
end
|
29
|
+
res = nil
|
30
|
+
assert_nothing_raised do
|
31
|
+
Timeout::timeout(0.5) do
|
32
|
+
res = b.dunder_load.something_heavy { sleep 1 }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
assert_equal b,res
|
36
|
+
assert_equal b.class,res.class
|
37
|
+
end
|
16
38
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
+
- 2
|
7
8
|
- 1
|
8
|
-
|
9
|
-
version: 0.1.1
|
9
|
+
version: 0.2.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Fonsan
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-28 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -143,7 +143,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
143
143
|
requirements:
|
144
144
|
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
hash:
|
146
|
+
hash: 1083880034677177238
|
147
147
|
segments:
|
148
148
|
- 0
|
149
149
|
version: "0"
|