decoratable 0.0.4 → 0.0.5

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.
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