decoratable 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '0090c17ce4e8f9334a6bdebfb059b47fb7c7350d'
4
- data.tar.gz: 185f96d6a0f5402dda2d66eb913f731ce1944a39
3
+ metadata.gz: 4faaa57297c2f587f2d751980cee3d857b0442a7
4
+ data.tar.gz: d6c7d401b73f284dbd4a9ff3a22f152e95481dc6
5
5
  SHA512:
6
- metadata.gz: ae18c7e17a48897596a56bdcc3f8e97ccf83d45d34edbd3ee7bea5aeb7f24b66a6eaf3bde36bc7ae726c299fe0c20e5ee09022d179ecb4ef549caa15b33186a8
7
- data.tar.gz: 6d6c3293e01975fe16a305fc4a9494a272c82fec94511eabe83b663ee63e122974c9361e39f992412c803fc7000d2a7bd773d52f9dfe390edb57b3270b87f80b
6
+ metadata.gz: cce79e57059864cf7b5d8414f71b077ace17d48231706ca0485ec50901dc6037a2e701ac98c9d2d24a39759cceee546b7f90007efb4a81723d7b89731c560efa
7
+ data.tar.gz: 14816d8694e7e63a38a85dad25f5f86f4b96e88e17cb14f516a4b07fd8d5950b7c8cd4f124581bfe5f1a8fb5f571af23d7028a2af4dc1bb13807fe152787efc9
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "decoratable"
6
- spec.version = "0.0.4"
6
+ spec.version = "0.0.5"
7
7
  spec.authors = ["ecin"]
8
8
  spec.email = ["ecin@copypastel.com"]
9
9
  spec.description = "Decorate your methods."
@@ -0,0 +1,146 @@
1
+ require "thread"
2
+
3
+ # Public: provide an easy way to define decorations
4
+ # that add common behaviour to a method.
5
+ #
6
+ # More info on decorations as implemented in Python:
7
+ # http://en.wikipedia.org/wiki/Python_syntax_and_semantics#Decorators
8
+ #
9
+ # Examples
10
+ #
11
+ # module Decorations
12
+ # extend Decoratable
13
+ #
14
+ # def retryable(tries = 1, options = { on: [RuntimeError] })
15
+ # attempts = 0
16
+ #
17
+ # begin
18
+ # yield
19
+ # rescue *options[:on]
20
+ # attempts += 1
21
+ # attempts > tries ? raise : retry
22
+ # end
23
+ # end
24
+ #
25
+ # def measurable(logger = STDOUT)
26
+ # start = Time.now
27
+ # yield
28
+ # ensure
29
+ # original_method = __decorated_method__
30
+ # method_location, line = original_method.source_location
31
+ # marker = "#{original_method.owner}##{original_method.name}[#{method_location}:#{line}]"
32
+ # duration = (Time.now - start).round(2)
33
+ #
34
+ # logger.puts "#{marker} took #{duration}s to run."
35
+ # end
36
+ #
37
+ # def debuggable
38
+ # begin
39
+ # yield
40
+ # rescue => e
41
+ # puts "Caught #{e}!!!"
42
+ # require "debug"
43
+ # end
44
+ # end
45
+ #
46
+ # def memoizable
47
+ # key = :"@#{__decorated_method__.name}_cache"
48
+ #
49
+ # instance_variable_set(key, {}) unless defined?(key)
50
+ # cache = instance_variable_get(key)
51
+ #
52
+ # if cache.key?(__args__)
53
+ # cache[__args__]
54
+ # else
55
+ # cache[__args__] = yield
56
+ # end
57
+ # end
58
+ # end
59
+ #
60
+ # class Client
61
+ # extend Decorations
62
+ #
63
+ # # Let's keep track of how long #get takes to run,
64
+ # # and memoize the return value
65
+ # def get
66
+ # …
67
+ # end
68
+ # measurable :get
69
+ # memoizable :get
70
+ #
71
+ # # Rescue and retry any Timeout::Errors, up to 5 times
72
+ # def post
73
+ # …
74
+ # end
75
+ # retryable :post, 5, on: [Timeout::Error]
76
+ # end
77
+ module Decoratable
78
+ @@lock = Mutex.new
79
+
80
+ def self.extended(klass)
81
+ # This #method_added affects all methods defined in the module
82
+ # that extends Decoratable.
83
+ def klass.method_added(decoration_name)
84
+ return unless @@lock.try_lock
85
+
86
+ decoration_method = instance_method(decoration_name)
87
+
88
+ define_method(decoration_name) do |method_name, *decorator_args|
89
+ unless method_defined?(:__original_caller__)
90
+ define_method(:__original_caller__) { @__original_caller__ }
91
+ end
92
+
93
+ unless method_defined?(:__decorated_method__)
94
+ define_method(:__decorated_method__) { @__decorated_method__ }
95
+ end
96
+
97
+ unless method_defined?(:__args__)
98
+ define_method(:__args__) { @__args__ }
99
+ end
100
+
101
+ unless method_defined?(:__block__)
102
+ define_method(:__block__) { @__block__ }
103
+ end
104
+
105
+ original_method = instance_method(method_name)
106
+
107
+ decoration = Module.new do
108
+ self.singleton_class.instance_eval do
109
+ define_method(:name) do
110
+ "Decoratable::#{decoration_name}(#{method_name})"
111
+ end
112
+
113
+ alias_method :inspect, :name
114
+ alias_method :to_s, :name
115
+ end
116
+
117
+ define_method(method_name) do |*args, &block|
118
+ begin
119
+ # The decoration should have access to the original
120
+ # method it's modifying, along with the method call's
121
+ # arguments.
122
+ @__decorated_method__ = original_method
123
+ @__args__ = args
124
+ @__block__ = block
125
+ @__original_caller__ ||= caller
126
+
127
+ decoration_method.bind(self).call(*decorator_args) do
128
+ super(*args, &block)
129
+ end
130
+ ensure
131
+ @__decorated_method__ = nil
132
+ @__args__ = nil
133
+ @__block__ = nil
134
+ @__original_caller__ = nil
135
+ end
136
+ end
137
+ end
138
+
139
+ # Call aspect before "real" method.
140
+ prepend decoration
141
+ end
142
+ ensure
143
+ @@lock.unlock if @@lock.locked?
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,43 @@
1
+ require "test_helper"
2
+
3
+ require "decoratable/classic"
4
+
5
+ require "logger"
6
+
7
+ describe "Decoratable: Classic Style" do
8
+ # Test-only decorations
9
+ module Decorations
10
+ extend Decoratable
11
+
12
+ def loggable(logger: Logger.new(STDOUT))
13
+ logger.info __decorated_method__.name
14
+ end
15
+ end
16
+ it "decorates a method, classic style" do
17
+ logger = mock_logger
18
+ klass = Class.new do
19
+ extend Decorations
20
+
21
+ def call; end
22
+ loggable :call, logger: logger
23
+ end
24
+
25
+ object = klass.new
26
+ 3.times { object.call }
27
+ end
28
+
29
+ def mock_logger
30
+ logger = Minitest::Mock.new
31
+
32
+ # We expect info to be called thrice
33
+ logger.expect(:info, nil, [:call])
34
+ logger.expect(:info, nil, [:call])
35
+ logger.expect(:info, nil, [:call])
36
+
37
+ logger
38
+ end
39
+
40
+ # Remove Decoratable constant so we reset back to original
41
+ Object.send(:remove_const, :Decoratable)
42
+ load "decoratable.rb"
43
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decoratable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - ecin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-04 00:00:00.000000000 Z
11
+ date: 2018-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -65,6 +65,7 @@ files:
65
65
  - Rakefile
66
66
  - decoratable.gemspec
67
67
  - lib/decoratable.rb
68
+ - lib/decoratable/classic.rb
68
69
  - lib/decoratable/countable.rb
69
70
  - lib/decoratable/debuggable.rb
70
71
  - lib/decoratable/deprecatable.rb
@@ -73,6 +74,7 @@ files:
73
74
  - lib/decoratable/pryable.rb
74
75
  - lib/decoratable/retryable.rb
75
76
  - lib/decoratable/synchronizable.rb
77
+ - test/decoratable/classic_test.rb
76
78
  - test/decoratable/countable_test.rb
77
79
  - test/decoratable/deprecatable_test.rb
78
80
  - test/decoratable/hintable_test.rb
@@ -107,6 +109,7 @@ signing_key:
107
109
  specification_version: 4
108
110
  summary: Easily define decorations for your methods. Put a bow on it.
109
111
  test_files:
112
+ - test/decoratable/classic_test.rb
110
113
  - test/decoratable/countable_test.rb
111
114
  - test/decoratable/deprecatable_test.rb
112
115
  - test/decoratable/hintable_test.rb