manana 0.0.2 → 0.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.
- data/.gitignore +0 -0
- data/.travis.yml +0 -0
- data/.yardopts +0 -0
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +18 -1
- data/Rakefile +0 -0
- data/lib/manana/version.rb +1 -1
- data/lib/manana.rb +13 -7
- data/manana.gemspec +0 -0
- data/samples/common/samples.gemfile +0 -0
- data/samples/common/samples.gemfile.lock +0 -0
- data/samples/common/samples_helper.rb +0 -0
- data/samples/exponential_backoff.rb +1 -1
- data/samples/fast_startup.rb +1 -1
- data/samples/self_healing.rb +1 -1
- data/test/klass.rb +32 -0
- data/test/minitest_helper.rb +3 -1
- data/test/test_manana.rb +43 -40
- metadata +6 -4
data/.gitignore
CHANGED
|
File without changes
|
data/.travis.yml
CHANGED
|
File without changes
|
data/.yardopts
CHANGED
|
File without changes
|
data/Gemfile
CHANGED
|
File without changes
|
data/LICENSE.txt
CHANGED
|
File without changes
|
data/README.md
CHANGED
|
@@ -54,7 +54,24 @@ Or install it yourself as:
|
|
|
54
54
|
|
|
55
55
|
## Usage
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
```ruby
|
|
58
|
+
require 'manana'
|
|
59
|
+
|
|
60
|
+
# initialization...
|
|
61
|
+
client = Manana.deferred_init {
|
|
62
|
+
Weather.setup # web service adapter setup
|
|
63
|
+
Weather # return the class instance
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
runtime_loop {
|
|
67
|
+
# wait for next interval
|
|
68
|
+
weather = client.city_weather("02201") # deferred initialization happens here once
|
|
69
|
+
puts "At %s the temperature is currently %s F and the humidity is %s." % [weather.city, weather.temperature, weather.relative_humidity]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
See the [samples](https://github.com/coldnebo/manana/blob/master/samples) for more detailed examples of use.
|
|
58
75
|
|
|
59
76
|
## Contributing
|
|
60
77
|
|
data/Rakefile
CHANGED
|
File without changes
|
data/lib/manana/version.rb
CHANGED
data/lib/manana.rb
CHANGED
|
@@ -4,7 +4,7 @@ require "manana/version"
|
|
|
4
4
|
# *Manana* lets you defer the initialization of an object until its methods are called.
|
|
5
5
|
# @example basic usage - see {https://github.com/coldnebo/manana/blob/master/samples/self_healing.rb samples/self_healing.rb}
|
|
6
6
|
# # initialization...
|
|
7
|
-
# client = Manana.
|
|
7
|
+
# client = Manana.deferred_init {
|
|
8
8
|
# Weather.setup
|
|
9
9
|
# Weather
|
|
10
10
|
# }
|
|
@@ -16,17 +16,17 @@ require "manana/version"
|
|
|
16
16
|
# }
|
|
17
17
|
#
|
|
18
18
|
class Manana
|
|
19
|
-
|
|
20
|
-
# wraps an
|
|
19
|
+
|
|
20
|
+
# wraps an initialization block so that it can be deferred to a later time when object methods are called.
|
|
21
21
|
# @example wrap an object - see {https://github.com/coldnebo/manana/blob/master/samples/self_healing.rb samples/self_healing.rb}
|
|
22
|
-
# client = Manana.
|
|
22
|
+
# client = Manana.deferred_init {
|
|
23
23
|
# Weather.setup # initialize the class
|
|
24
24
|
# Weather # return the Weather class
|
|
25
25
|
# }
|
|
26
26
|
#
|
|
27
27
|
# @param initialization_block [Proc] object initialization. the block must return the object to be wrapped.
|
|
28
28
|
# @return [Manana] a wrapped version of the object.
|
|
29
|
-
def self.
|
|
29
|
+
def self.deferred_init(&initialization_block)
|
|
30
30
|
Manana.new(&initialization_block)
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -38,16 +38,23 @@ class Manana
|
|
|
38
38
|
# weather = client.city_weather("02201")
|
|
39
39
|
#
|
|
40
40
|
def method_missing(method, *args, &block)
|
|
41
|
-
instance =
|
|
41
|
+
instance = safe_get_instance
|
|
42
42
|
instance.send(method, *args, &block);
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def initialize(&initialization_block)
|
|
48
|
+
@mutex = Mutex.new
|
|
48
49
|
@deferred_initialization = initialization_block
|
|
49
50
|
end
|
|
50
51
|
|
|
52
|
+
def safe_get_instance
|
|
53
|
+
@mutex.synchronize do
|
|
54
|
+
get_instance
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
51
58
|
def get_instance
|
|
52
59
|
if @instance.nil?
|
|
53
60
|
@instance = @deferred_initialization.call
|
|
@@ -55,5 +62,4 @@ class Manana
|
|
|
55
62
|
@instance
|
|
56
63
|
end
|
|
57
64
|
|
|
58
|
-
|
|
59
65
|
end
|
data/manana.gemspec
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/samples/fast_startup.rb
CHANGED
|
@@ -44,7 +44,7 @@ mini_death_star = nil
|
|
|
44
44
|
puts "[Vader]: requsition a new DeathStar and make it snappy!"
|
|
45
45
|
# boss said requisitions have to be faster*! Manana to the rescue!
|
|
46
46
|
puts Benchmark.measure {
|
|
47
|
-
mini_death_star = Manana.
|
|
47
|
+
mini_death_star = Manana.deferred_init {
|
|
48
48
|
DeathStar.new
|
|
49
49
|
}
|
|
50
50
|
}
|
data/samples/self_healing.rb
CHANGED
|
@@ -47,7 +47,7 @@ end
|
|
|
47
47
|
|
|
48
48
|
# Before we start, we use Manana to wrap the client setup so that method calls on the client can be self-healing in case of failure.
|
|
49
49
|
# NOTE: that the caller doesn't have to deal with whether or not this initialization succeeded, they can just call the client methods repeatedly.
|
|
50
|
-
client = Manana.
|
|
50
|
+
client = Manana.deferred_init {
|
|
51
51
|
Weather.setup
|
|
52
52
|
Weather
|
|
53
53
|
}
|
data/test/klass.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
class Klass
|
|
2
|
+
INITIALIZE_MESSAGE = "initialized!"
|
|
3
|
+
DO_SOMETHING_MESSAGE = "I did something!"
|
|
4
|
+
RAISE_MESSAGE = "kablooey!"
|
|
5
|
+
|
|
6
|
+
def self.incr
|
|
7
|
+
@count ||= 0
|
|
8
|
+
@count += 1
|
|
9
|
+
@count
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.count
|
|
13
|
+
@count ||= 0
|
|
14
|
+
@count
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.reset
|
|
18
|
+
@count = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize
|
|
22
|
+
puts INITIALIZE_MESSAGE
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def do_something
|
|
26
|
+
DO_SOMETHING_MESSAGE
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def raise_something
|
|
30
|
+
raise RAISE_MESSAGE
|
|
31
|
+
end
|
|
32
|
+
end
|
data/test/minitest_helper.rb
CHANGED
data/test/test_manana.rb
CHANGED
|
@@ -1,41 +1,26 @@
|
|
|
1
1
|
require 'minitest_helper'
|
|
2
|
+
require 'klass'
|
|
2
3
|
|
|
3
|
-
class TestManana <
|
|
4
|
+
class TestManana < Minitest::Test
|
|
4
5
|
def test_that_it_has_a_version_number
|
|
5
6
|
refute_nil ::Manana::VERSION
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
# initialize a very generic example object that does some stuff in initialization
|
|
9
|
+
# initialize a very generic example object that does some stuff in initialization
|
|
10
|
+
# and has some instance methods you can call.
|
|
9
11
|
def setup
|
|
10
|
-
@klass = Class.new
|
|
11
|
-
@klass.class_eval {
|
|
12
|
-
def initialize
|
|
13
|
-
puts "class initialized!"
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def do_something
|
|
17
|
-
"I did something!" # simulate an arbitrary method
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def add_something(x,y)
|
|
21
|
-
x+y # simulate a method with params
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def raise_something
|
|
25
|
-
raise "kablooey!" # simulate a call that raises an exception
|
|
26
|
-
end
|
|
27
|
-
}
|
|
28
12
|
end
|
|
29
13
|
|
|
30
14
|
# sanity check
|
|
31
15
|
def test_that_things_work_without_manana
|
|
32
16
|
obj = nil
|
|
17
|
+
|
|
33
18
|
out, err = capture_io do
|
|
34
|
-
obj =
|
|
19
|
+
obj = Klass.new
|
|
35
20
|
end
|
|
36
|
-
assert_match(
|
|
21
|
+
assert_match(/#{Klass::INITIALIZE_MESSAGE}/, out)
|
|
37
22
|
assert_instance_of(String, obj.do_something)
|
|
38
|
-
|
|
23
|
+
assert_match(/#{Klass::DO_SOMETHING_MESSAGE}/, obj.do_something)
|
|
39
24
|
assert_raises RuntimeError do
|
|
40
25
|
obj.raise_something
|
|
41
26
|
end
|
|
@@ -44,42 +29,60 @@ class TestManana < MiniTest::Unit::TestCase
|
|
|
44
29
|
obj.raise_something
|
|
45
30
|
rescue Exception => e
|
|
46
31
|
# make sure we get an appropriate stack trace at the point of raise.
|
|
47
|
-
assert_match(
|
|
32
|
+
assert_match(/.*klass.rb:\d+:in .raise_something./, e.backtrace.first)
|
|
48
33
|
end
|
|
49
|
-
|
|
50
34
|
end
|
|
51
35
|
|
|
52
36
|
def test_deferred_init
|
|
53
|
-
|
|
37
|
+
obj = nil
|
|
54
38
|
out, err = capture_io do
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# to fully setup a service instance.
|
|
58
|
-
handle = Manana.wrap {
|
|
59
|
-
obj = @klass.new
|
|
39
|
+
obj = Manana.deferred_init {
|
|
40
|
+
Klass.new
|
|
60
41
|
}
|
|
61
42
|
end
|
|
62
43
|
# make sure the init isn't executed until we call...
|
|
63
|
-
refute_match(
|
|
64
|
-
|
|
44
|
+
refute_match(/#{Klass::INITIALIZE_MESSAGE}/, out)
|
|
45
|
+
|
|
46
|
+
# now call a method on the object...
|
|
65
47
|
result = nil
|
|
66
48
|
out, err = capture_io do
|
|
67
|
-
result =
|
|
49
|
+
result = obj.do_something
|
|
68
50
|
end
|
|
69
|
-
#
|
|
70
|
-
assert_match(
|
|
71
|
-
# and
|
|
51
|
+
# and make sure the init was called
|
|
52
|
+
assert_match(/#{Klass::INITIALIZE_MESSAGE}/, out)
|
|
53
|
+
# and that we got the desired result
|
|
72
54
|
assert_instance_of(String, result)
|
|
73
|
-
|
|
55
|
+
assert_match(/#{Klass::DO_SOMETHING_MESSAGE}/, result)
|
|
56
|
+
assert_raises RuntimeError do
|
|
57
|
+
obj.raise_something
|
|
58
|
+
end
|
|
74
59
|
|
|
75
60
|
begin
|
|
76
|
-
|
|
61
|
+
obj.raise_something
|
|
77
62
|
rescue Exception => e
|
|
78
63
|
# make sure we get an appropriate stack trace at the point of raise.
|
|
79
|
-
assert_match(
|
|
64
|
+
assert_match(/.*klass.rb:\d+:in .raise_something./, e.backtrace.first)
|
|
80
65
|
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# https://github.com/coldnebo/manana/issues/1
|
|
69
|
+
def test_that_init_is_threadsafe
|
|
70
|
+
Klass.reset
|
|
71
|
+
|
|
72
|
+
klass = Manana.deferred_init {
|
|
73
|
+
sleep(1)
|
|
74
|
+
Klass.incr
|
|
75
|
+
Klass
|
|
76
|
+
}
|
|
81
77
|
|
|
78
|
+
t1 = Thread.new{klass.count}
|
|
79
|
+
t2 = Thread.new{klass.count}
|
|
80
|
+
t3 = Thread.new{klass.count}
|
|
81
|
+
t1.join
|
|
82
|
+
t2.join
|
|
83
|
+
t3.join
|
|
82
84
|
|
|
85
|
+
assert_equal(1,klass.count)
|
|
83
86
|
end
|
|
84
87
|
|
|
85
88
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: manana
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-
|
|
12
|
+
date: 2013-12-17 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: bundler
|
|
@@ -83,6 +83,7 @@ files:
|
|
|
83
83
|
- samples/exponential_backoff.rb
|
|
84
84
|
- samples/fast_startup.rb
|
|
85
85
|
- samples/self_healing.rb
|
|
86
|
+
- test/klass.rb
|
|
86
87
|
- test/minitest_helper.rb
|
|
87
88
|
- test/test_manana.rb
|
|
88
89
|
homepage: https://github.com/coldnebo/manana
|
|
@@ -100,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
100
101
|
version: '0'
|
|
101
102
|
segments:
|
|
102
103
|
- 0
|
|
103
|
-
hash: -
|
|
104
|
+
hash: -3306959934859244204
|
|
104
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
none: false
|
|
106
107
|
requirements:
|
|
@@ -109,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
110
|
version: '0'
|
|
110
111
|
segments:
|
|
111
112
|
- 0
|
|
112
|
-
hash: -
|
|
113
|
+
hash: -3306959934859244204
|
|
113
114
|
requirements: []
|
|
114
115
|
rubyforge_project:
|
|
115
116
|
rubygems_version: 1.8.25
|
|
@@ -118,6 +119,7 @@ specification_version: 3
|
|
|
118
119
|
summary: provides a simple way to defer initialization of an object until its methods
|
|
119
120
|
are called
|
|
120
121
|
test_files:
|
|
122
|
+
- test/klass.rb
|
|
121
123
|
- test/minitest_helper.rb
|
|
122
124
|
- test/test_manana.rb
|
|
123
125
|
has_rdoc:
|